From 0b636d1bf94088358e266f5af64b70abe5e2788a Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 19 Feb 2024 08:33:36 +0900
Subject: [PATCH 001/266] =?UTF-8?q?fix:=20nodeinfo=E3=81=ABenableMcaptcha?=
 =?UTF-8?q?=E3=81=A8enableTurnstile=E3=81=8C=E7=84=A1=E3=81=84=20(#13387)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                         | 9 +++++++++
 packages/backend/src/models/Meta.ts                  | 2 ++
 packages/backend/src/server/NodeinfoServerService.ts | 2 ++
 3 files changed, 13 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32c9bd0aec9f..09f0dbd09b63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,15 @@
 
 -->
 
+## 202x.x.x (unreleased)
+
+### General
+
+### Client
+
+### Server
+- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
+
 ## 2024.2.0
 
 ### Note
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 6ed0ec6ce50d..66f19ce1975c 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -253,6 +253,8 @@ export class MiMeta {
 	})
 	public turnstileSecretKey: string | null;
 
+	// chaptcha系を追加した際にはnodeinfoのレスポンスに追加するのを忘れないようにすること
+
 	@Column('enum', {
 		enum: ['none', 'all', 'local', 'remote'],
 		default: 'none',
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index 81318ab5ac95..c1e5af08c903 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -117,6 +117,8 @@ export class NodeinfoServerService {
 					emailRequiredForSignup: meta.emailRequiredForSignup,
 					enableHcaptcha: meta.enableHcaptcha,
 					enableRecaptcha: meta.enableRecaptcha,
+					enableMcaptcha: meta.enableMcaptcha,
+					enableTurnstile: meta.enableTurnstile,
 					maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
 					enableEmail: meta.enableEmail,
 					enableServiceWorker: meta.enableServiceWorker,

From 1b1046bcdb7ce8d3f177462c92aace62de720091 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 19 Feb 2024 08:34:31 +0900
Subject: [PATCH 002/266] =?UTF-8?q?fix:=20syuilo/misskey=E6=99=82=E4=BB=A3?=
 =?UTF-8?q?=E3=81=8B=E3=82=89=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=A6=E3=82=8B?=
 =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=81=8C=E6=94=B9=E5=A4=89?=
 =?UTF-8?q?=E3=81=97=E3=81=9F=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3?=
 =?UTF-8?q?=E3=81=A0=E3=81=A8=E8=AA=A4=E5=88=A4=E5=AE=9A=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=20(DB=20migration=E3=81=A7=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3)=20(#13389)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                     |  9 +++++++++
 ...1-repositoryUrl-from-syuilo-to-misskey-dev.js | 16 ++++++++++++++++
 2 files changed, 25 insertions(+)
 create mode 100644 packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09f0dbd09b63..ae1bd97eac82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,15 @@
 -
 
 -->
+## 202x.x.x (unreleased)
+
+### General
+- Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
+
+### Client
+
+### Server
+
 
 ## 202x.x.x (unreleased)
 
diff --git a/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js b/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js
new file mode 100644
index 000000000000..e4dbaa16d0f1
--- /dev/null
+++ b/packages/backend/migration/1708266695091-repositoryUrl-from-syuilo-to-misskey-dev.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class RepositoryUrlFromSyuiloToMisskeyDev1708266695091 {
+    name = 'RepositoryUrlFromSyuiloToMisskeyDev1708266695091'
+
+    async up(queryRunner) {
+        await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://github.com/misskey-dev/misskey' WHERE "repositoryUrl" = 'https://github.com/syuilo/misskey'`);
+    }
+
+    async down(queryRunner) {
+        // no valid down migration
+    }
+}

From 034f47205eea0ab32e88f9b0595943804e7e030f Mon Sep 17 00:00:00 2001
From: 1Step621 <86859447+1STEP621@users.noreply.github.com>
Date: Mon, 19 Feb 2024 08:36:06 +0900
Subject: [PATCH 003/266] =?UTF-8?q?Fix(frontend):=20=E3=82=AA=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=82=B3=E3=83=B3=E3=83=97=E3=83=AA=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E3=81=8C=E5=87=BA=E3=82=8B=E3=81=B9=E3=81=8D=E7=8A=B6=E6=B3=81?=
 =?UTF-8?q?=E3=81=A7=E5=87=BA=E3=81=AA=E3=81=84=E3=81=93=E3=81=A8=E3=81=8C?=
 =?UTF-8?q?=E3=81=82=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20=20(#1?=
 =?UTF-8?q?3376)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* オートコンプリートが出るべき状況で出ないことがあるのを修正

* update CHANGELOG.md
---
 CHANGELOG.md                                  | 5 +++++
 packages/frontend/src/scripts/autocomplete.ts | 4 +++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae1bd97eac82..7c1eaa959848 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,11 @@
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
 
+## 202x.x.x (unreleased)
+
+### Client
+- Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
+
 ## 2024.2.0
 
 ### Note
diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index fe515d81a1c4..9fc8f7843ed5 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -93,9 +93,11 @@ export class Autocomplete {
 			return;
 		}
 
+		const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop();
+
 		const isMention = mentionIndex !== -1;
 		const isHashtag = hashtagIndex !== -1;
-		const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.');
+		const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' ');
 		const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
 		const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
 

From 9be3890827b92e1f6c56b6528ffa5e1b1391903d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 19 Feb 2024 17:52:55 +0900
Subject: [PATCH 004/266] Fix Changelog

---
 CHANGELOG.md | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c1eaa959848..a91c3e43d49c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,28 +13,15 @@
 -->
 ## 202x.x.x (unreleased)
 
-### General
-- Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
-
-### Client
-
-### Server
-
-
-## 202x.x.x (unreleased)
-
 ### General
 
 ### Client
+- Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
+- Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
 
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
 
-## 202x.x.x (unreleased)
-
-### Client
-- Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
-
 ## 2024.2.0
 
 ### Note

From ddd7b26f1c17e9ce1e0ea9961381d30979e6dc22 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Tue, 20 Feb 2024 11:59:49 +0900
Subject: [PATCH 005/266] =?UTF-8?q?enhance(frontend):=20=E3=83=8E=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E4=BD=9C=E6=88=90=E7=94=BB=E9=9D=A2=E3=81=AE=E6=B7=BB?=
 =?UTF-8?q?=E4=BB=98=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=AEdivider?=
 =?UTF-8?q?=E3=81=AE=E4=BD=8D=E7=BD=AE=E3=82=92"=E6=B7=BB=E4=BB=98?=
 =?UTF-8?q?=E5=8F=96=E3=82=8A=E6=B6=88=E3=81=97"=E3=81=AE=E4=B8=8A?=
 =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=20(#13409)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): change divider position for MkPostFormAttaches

* docs(changelog): update
---
 CHANGELOG.md                                            | 1 +
 packages/frontend/src/components/MkPostFormAttaches.vue | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a91c3e43d49c..fb4d1e7ad4e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 ### General
 
 ### Client
+- Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
 - Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
 
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index 3f775bc6e226..95eb36731827 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -152,11 +152,11 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent): void {
 		icon: 'ti ti-crop',
 		action: () : void => { crop(file); },
 	}] : [], {
+		type: 'divider',
+	}, {
 		text: i18n.ts.attachCancel,
 		icon: 'ti ti-circle-x',
 		action: () => { detachMedia(file.id); },
-	}, {
-		type: 'divider',
 	}, {
 		text: i18n.ts.deleteFile,
 		icon: 'ti ti-trash',

From 39c4e3a4f550b1383d574b9823b99a228b47a475 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Tue, 20 Feb 2024 14:00:57 +0900
Subject: [PATCH 006/266] =?UTF-8?q?fix(frontend):=20=E3=83=81=E3=83=A3?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AE=E3=83=A9=E3=83=99=E3=83=AB=E3=81=8C?=
 =?UTF-8?q?=E6=B6=88=E3=81=88=E3=81=A6=E3=81=84=E3=82=8B=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13416)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): チャートのラベルが消えている問題を修正

* Update CHANGELOG.md
---
 CHANGELOG.md                                 | 1 +
 packages/frontend/src/components/MkChart.vue | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fb4d1e7ad4e5..77a03ff68434 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
 - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
 - Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
+- Fix: チャートのラベルが消えている問題を修正
 
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index dd745c21404a..04b6d2f29c0e 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -240,7 +240,7 @@ const render = () => {
 					},
 					external: externalTooltipHandler,
 					callbacks: {
-						label: (item) => chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString(),
+						label: (item) => `${item.dataset.label}: ${chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString()}`,
 					},
 				},
 				zoom: props.detailed ? {

From f18a31c628aa2ce21599a3706bda06953bfe6e09 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Tue, 20 Feb 2024 15:26:11 +0900
Subject: [PATCH 007/266] =?UTF-8?q?fix(frontend):=20=E7=94=BB=E9=9D=A2?=
 =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E5=BE=8C=E6=9C=80=E5=88=9D=E3=81=AE=E9=9F=B3?=
 =?UTF-8?q?=E5=A3=B0=E5=86=8D=E7=94=9F=E3=81=8C=E7=88=86=E9=9F=B3=E3=81=AB?=
 =?UTF-8?q?=E3=81=AA=E3=82=8B=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B?=
 =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13379)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 画面表示後最初の音声再生が爆音になることがある問題を修正

* Update CHANGELOG.md

* Update CHANGELOG.md
---
 CHANGELOG.md                           | 1 +
 packages/frontend/src/scripts/sound.ts | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77a03ff68434..e1de194da178 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
 - Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
 - Fix: チャートのラベルが消えている問題を修正
+- Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正
 
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 9555579e0d6a..67818320b325 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -126,7 +126,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
  */
 export function playMisskeySfx(operationType: OperationType) {
 	const sound = defaultStore.state[`sound_${operationType}`];
-	if (sound.type == null || !canPlay) return;
+	if (sound.type == null || !canPlay || !navigator.userActivation.hasBeenActive) return;
 
 	canPlay = false;
 	playMisskeySfxFile(sound).finally(() => {

From bbbb16795d9d09d48ba9efe5a790b28dd4e99cd4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 21 Feb 2024 14:27:06 +0900
Subject: [PATCH 008/266] =?UTF-8?q?refactor(frontend):=20=E4=B8=8D?=
 =?UTF-8?q?=E5=BF=85=E8=A6=81=E3=81=AAconsole.log=E3=82=92=E9=99=A4?=
 =?UTF-8?q?=E5=8E=BB=E3=83=BB=E6=8A=91=E5=88=B6=20(#13400)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(frontend): 不必要なconsole.logを除去

* Update MkCode.core.vue

* Update game.board.vue
---
 packages/frontend/src/components/MkCode.core.vue   | 2 +-
 packages/frontend/src/pages/admin/modlog.vue       | 2 --
 packages/frontend/src/pages/reversi/game.board.vue | 2 +-
 3 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue
index f993e983e426..872517b6aaab 100644
--- a/packages/frontend/src/components/MkCode.core.vue
+++ b/packages/frontend/src/components/MkCode.core.vue
@@ -52,7 +52,7 @@ async function fetchLanguage(to: string): Promise<void> {
 			return bundle.id === language || bundle.aliases?.includes(language);
 		});
 		if (bundles.length > 0) {
-			console.log(`Loading language: ${language}`);
+			if (_DEV_) console.log(`Loading language: ${language}`);
 			await highlighter.loadLanguage(bundles[0].import);
 			codeLang.value = language;
 		} else {
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index 5e251b8a6f87..8590ee1651d5 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -54,8 +54,6 @@ const pagination = {
 	})),
 };
 
-console.log(Misskey);
-
 const headerActions = computed(() => []);
 
 const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index cf7cec6b5e22..6f7f5b8f3861 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -248,7 +248,7 @@ if (game.value.isStarted && !game.value.isEnded) {
 			crc32: crc32.toString(),
 		}).then((res) => {
 			if (res.desynced) {
-				console.log('resynced');
+				if (_DEV_) console.log('resynced');
 				restoreGame(res.game!);
 			}
 		});

From 750d2626041b355459265a4a4148d08f6ac517fd Mon Sep 17 00:00:00 2001
From: okayurisotto <47853651+okayurisotto@users.noreply.github.com>
Date: Wed, 21 Feb 2024 14:31:50 +0900
Subject: [PATCH 009/266] refactor(backend):
 `ReactionService.prototype.convertLegacyReactions` (#13375)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* add unit tests

* cleanup unnecessary type assertions

* `convertedReaction`変数の定義と変換表に対する存在確認処理の整理

* `count`変数の定義とループ処理での`Object.entries()`の活用

* 条件式の整理

* `Array.prototype.reduce`を使うように

* `Array.prototype.reduce`を使うように

* 配列操作を1つのメソッドチェーンに整理

これまでの実装では、`decodeReaction`の返り値が同一になる異なる入力値が同時に複数個存在した場合、後ろのもので上書きされてしまっていたはず。
これからの実装では、後ろのものは前のものに加算される。
(実際にこの挙動の変更が問題になるシチュエーションはまずないはず。)

* add unit test

* ドキュメントコメントの追加と型定義の調整
---
 packages/backend/src/core/ReactionService.ts  | 49 ++++++++++---------
 packages/backend/test/unit/ReactionService.ts | 41 ++++++++++++++++
 2 files changed, 66 insertions(+), 24 deletions(-)

diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts
index 5014156a5c45..cb0b079df0dd 100644
--- a/packages/backend/src/core/ReactionService.ts
+++ b/packages/backend/src/core/ReactionService.ts
@@ -322,35 +322,36 @@ export class ReactionService {
 		//#endregion
 	}
 
+	/**
+	 * 文字列タイプのレガシーな形式のリアクションを現在の形式に変換しつつ、
+	 * データベース上には存在する「0個のリアクションがついている」という情報を削除する。
+	 */
 	@bindThis
-	public convertLegacyReactions(reactions: Record<string, number>) {
-		const _reactions = {} as Record<string, number>;
+	public convertLegacyReactions(reactions: MiNote['reactions']): MiNote['reactions'] {
+		return Object.entries(reactions)
+			.filter(([, count]) => {
+				// `ReactionService.prototype.delete`ではリアクション削除時に、
+				// `MiNote['reactions']`のエントリの値をデクリメントしているが、
+				// デクリメントしているだけなのでエントリ自体は0を値として持つ形で残り続ける。
+				// そのため、この処理がなければ、「0個のリアクションがついている」ということになってしまう。
+				return count > 0;
+			})
+			.map(([reaction, count]) => {
+				// unchecked indexed access
+				const convertedReaction = legacies[reaction] as string | undefined;
 
-		for (const reaction of Object.keys(reactions)) {
-			if (reactions[reaction] <= 0) continue;
+				const key = this.decodeReaction(convertedReaction ?? reaction).reaction;
 
-			if (Object.keys(legacies).includes(reaction)) {
-				if (_reactions[legacies[reaction]]) {
-					_reactions[legacies[reaction]] += reactions[reaction];
-				} else {
-					_reactions[legacies[reaction]] = reactions[reaction];
-				}
-			} else {
-				if (_reactions[reaction]) {
-					_reactions[reaction] += reactions[reaction];
-				} else {
-					_reactions[reaction] = reactions[reaction];
-				}
-			}
-		}
-
-		const _reactions2 = {} as Record<string, number>;
+				return [key, count] as const;
+			})
+			.reduce<MiNote['reactions']>((acc, [key, count]) => {
+				// unchecked indexed access
+				const prevCount = acc[key] as number | undefined;
 
-		for (const reaction of Object.keys(_reactions)) {
-			_reactions2[this.decodeReaction(reaction).reaction] = _reactions[reaction];
-		}
+				acc[key] = (prevCount ?? 0) + count;
 
-		return _reactions2;
+				return acc;
+			}, {});
 	}
 
 	@bindThis
diff --git a/packages/backend/test/unit/ReactionService.ts b/packages/backend/test/unit/ReactionService.ts
index d1c31cac3adf..1957f4544cf7 100644
--- a/packages/backend/test/unit/ReactionService.ts
+++ b/packages/backend/test/unit/ReactionService.ts
@@ -90,4 +90,45 @@ describe('ReactionService', () => {
 			assert.strictEqual(await reactionService.normalize('unknown'), '❤');
 		});
 	});
+
+	describe('convertLegacyReactions', () => {
+		test('空の入力に対しては何もしない', () => {
+			const input = {};
+			assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
+		});
+
+		test('Unicode絵文字リアクションを変換してしまわない', () => {
+			const input = { '👍': 1, '🍮': 2 };
+			assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
+		});
+
+		test('カスタム絵文字リアクションを変換してしまわない', () => {
+			const input = { ':like@.:': 1, ':pudding@example.tld:': 2 };
+			assert.deepStrictEqual(reactionService.convertLegacyReactions(input), input);
+		});
+
+		test('文字列によるレガシーなリアクションを変換する', () => {
+			const input = { 'like': 1, 'pudding': 2 };
+			const output = { '👍': 1, '🍮': 2 };
+			assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
+		});
+
+		test('host部分が省略されたレガシーなカスタム絵文字リアクションを変換する', () => {
+			const input = { ':custom_emoji:': 1 };
+			const output = { ':custom_emoji@.:': 1 };
+			assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
+		});
+
+		test('「0個のリアクション」情報を削除する', () => {
+			const input = { 'angry': 0 };
+			const output = {};
+			assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
+		});
+
+		test('host部分の有無によりデコードすると同じ表記になるカスタム絵文字リアクションの個数情報を正しく足し合わせる', () => {
+			const input = { ':custom_emoji:': 1, ':custom_emoji@.:': 2 };
+			const output = { ':custom_emoji@.:': 3 };
+			assert.deepStrictEqual(reactionService.convertLegacyReactions(input), output);
+		});
+	});
 });

From ae27085f691f331591117f531860b9c510897ae8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Wed, 21 Feb 2024 14:42:37 +0900
Subject: [PATCH 010/266] fix: Bump sharp to 0.33.2 (#13391)

---
 packages/backend/package.json                |   4 +-
 packages/backend/src/core/FileInfoService.ts |   9 +-
 pnpm-lock.yaml                               | 335 +++++++++++++------
 3 files changed, 240 insertions(+), 108 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 86a52faa05a7..3a3d8e04110b 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -79,7 +79,7 @@
 		"@fastify/multipart": "8.1.0",
 		"@fastify/static": "6.12.0",
 		"@fastify/view": "8.2.0",
-		"@misskey-dev/sharp-read-bmp": "^1.1.1",
+		"@misskey-dev/sharp-read-bmp": "^1.2.0",
 		"@misskey-dev/summaly": "^5.0.3",
 		"@nestjs/common": "10.2.10",
 		"@nestjs/core": "10.2.10",
@@ -164,7 +164,7 @@
 		"rxjs": "7.8.1",
 		"sanitize-html": "2.11.0",
 		"secure-json-parse": "2.7.0",
-		"sharp": "0.32.6",
+		"sharp": "0.33.2",
 		"slacc": "0.0.10",
 		"strict-event-emitter-types": "2.0.0",
 		"stringz": "2.1.0",
diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts
index b177367a166e..b8babcb3a724 100644
--- a/packages/backend/src/core/FileInfoService.ts
+++ b/packages/backend/src/core/FileInfoService.ts
@@ -15,6 +15,7 @@ import isSvg from 'is-svg';
 import probeImageSize from 'probe-image-size';
 import { type predictionType } from 'nsfwjs';
 import sharp from 'sharp';
+import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
 import { encode } from 'blurhash';
 import { createTempDir } from '@/misc/create-temp.js';
 import { AiService } from '@/core/AiService.js';
@@ -122,7 +123,7 @@ export class FileInfoService {
 			'image/avif',
 			'image/svg+xml',
 		].includes(type.mime)) {
-			blurhash = await this.getBlurhash(path).catch(e => {
+			blurhash = await this.getBlurhash(path, type.mime).catch(e => {
 				warnings.push(`getBlurhash failed: ${e}`);
 				return undefined;
 			});
@@ -407,9 +408,9 @@ export class FileInfoService {
 	 * Calculate average color of image
 	 */
 	@bindThis
-	private getBlurhash(path: string): Promise<string> {
-		return new Promise((resolve, reject) => {
-			sharp(path)
+	private getBlurhash(path: string, type: string): Promise<string> {
+		return new Promise(async (resolve, reject) => {
+			(await sharpBmp(path, type))
 				.raw()
 				.ensureAlpha()
 				.resize(64, 64, { fit: 'inside' })
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f89f8a417d68..d7b2fb1f2f15 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -111,8 +111,8 @@ importers:
         specifier: 8.2.0
         version: 8.2.0
       '@misskey-dev/sharp-read-bmp':
-        specifier: ^1.1.1
-        version: 1.1.1
+        specifier: ^1.2.0
+        version: 1.2.0
       '@misskey-dev/summaly':
         specifier: ^5.0.3
         version: 5.0.3
@@ -366,8 +366,8 @@ importers:
         specifier: 2.7.0
         version: 2.7.0
       sharp:
-        specifier: 0.32.6
-        version: 0.32.6
+        specifier: 0.33.2
+        version: 0.33.2
       slacc:
         specifier: 0.0.10
         version: 0.0.10
@@ -3370,6 +3370,14 @@ packages:
     engines: {node: '>=10.0.0'}
     dev: true
 
+  /@emnapi/runtime@0.45.0:
+    resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==}
+    requiresBuild: true
+    dependencies:
+      tslib: 2.6.2
+    dev: false
+    optional: true
+
   /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0):
     resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
     peerDependencies:
@@ -4017,6 +4025,194 @@ packages:
     resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
     dev: true
 
+  /@img/sharp-darwin-arm64@0.33.2:
+    resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-darwin-arm64': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-darwin-x64@0.33.2:
+    resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-darwin-x64': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-darwin-arm64@1.0.1:
+    resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==}
+    engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-darwin-x64@1.0.1:
+    resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==}
+    engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-linux-arm64@1.0.1:
+    resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==}
+    engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-linux-arm@1.0.1:
+    resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==}
+    engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-linux-s390x@1.0.1:
+    resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==}
+    engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-linux-x64@1.0.1:
+    resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==}
+    engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-linuxmusl-arm64@1.0.1:
+    resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==}
+    engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-libvips-linuxmusl-x64@1.0.1:
+    resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==}
+    engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-linux-arm64@0.33.2:
+    resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-linux-arm64': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-linux-arm@0.33.2:
+    resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==}
+    engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-linux-arm': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-linux-s390x@0.33.2:
+    resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==}
+    engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-linux-s390x': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-linux-x64@0.33.2:
+    resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-linux-x64': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-linuxmusl-arm64@0.33.2:
+    resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==}
+    engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-linuxmusl-arm64': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-linuxmusl-x64@0.33.2:
+    resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==}
+    engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    optionalDependencies:
+      '@img/sharp-libvips-linuxmusl-x64': 1.0.1
+    dev: false
+    optional: true
+
+  /@img/sharp-wasm32@0.33.2:
+    resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==}
+    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [wasm32]
+    requiresBuild: true
+    dependencies:
+      '@emnapi/runtime': 0.45.0
+    dev: false
+    optional: true
+
+  /@img/sharp-win32-ia32@0.33.2:
+    resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==}
+    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: false
+    optional: true
+
+  /@img/sharp-win32-x64@0.33.2:
+    resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==}
+    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@ioredis/commands@1.2.0:
     resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
     dev: false
@@ -4464,12 +4660,12 @@ packages:
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)
     dev: true
 
-  /@misskey-dev/sharp-read-bmp@1.1.1:
-    resolution: {integrity: sha512-X52BQYL/I9mafypQ+wBhst+BUlYiPWnHhKGcF6ybcYSLl+zhcV0q5mezIXHozhM0Sv0A7xCdrWmR7TCNxHLrtQ==}
+  /@misskey-dev/sharp-read-bmp@1.2.0:
+    resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
     dependencies:
       decode-bmp: 0.2.1
       decode-ico: 0.4.1
-      sharp: 0.32.6
+      sharp: 0.33.2
     dev: false
 
   /@misskey-dev/summaly@5.0.3:
@@ -5076,10 +5272,6 @@ packages:
   /@simplewebauthn/types@9.0.1:
     resolution: {integrity: sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==}
 
-  /@sinclair/typebox@0.24.51:
-    resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==}
-    dev: true
-
   /@sinclair/typebox@0.27.8:
     resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
     dev: true
@@ -8833,6 +9025,7 @@ packages:
       buffer: 5.7.1
       inherits: 2.0.4
       readable-stream: 3.6.0
+    dev: true
 
   /blob-util@2.0.2:
     resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==}
@@ -8981,6 +9174,7 @@ packages:
     dependencies:
       base64-js: 1.5.1
       ieee754: 1.2.1
+    dev: true
 
   /buffer@6.0.3:
     resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
@@ -10197,11 +10391,6 @@ packages:
       which-typed-array: 1.1.11
     dev: true
 
-  /deep-extend@0.6.0:
-    resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
-    engines: {node: '>=4.0.0'}
-    dev: false
-
   /deep-is@0.1.4:
     resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
     dev: true
@@ -11193,11 +11382,6 @@ packages:
     engines: {node: '>= 0.8.0'}
     dev: true
 
-  /expand-template@2.0.3:
-    resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
-    engines: {node: '>=6'}
-    dev: false
-
   /expect@29.7.0:
     resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -11688,6 +11872,7 @@ packages:
 
   /fs-constants@1.0.0:
     resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+    dev: true
 
   /fs-extra@11.1.1:
     resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
@@ -11905,10 +12090,6 @@ packages:
       - supports-color
     dev: true
 
-  /github-from-package@0.0.0:
-    resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
-    dev: false
-
   /github-slugger@2.0.0:
     resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
     dev: true
@@ -12481,6 +12662,7 @@ packages:
 
   /ini@1.3.8:
     resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+    dev: true
 
   /ini@2.0.0:
     resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==}
@@ -14697,6 +14879,7 @@ packages:
 
   /mkdirp-classic@0.5.3:
     resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+    dev: true
 
   /mkdirp@0.5.6:
     resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
@@ -14870,10 +15053,6 @@ packages:
     hasBin: true
     dev: false
 
-  /napi-build-utils@1.0.2:
-    resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
-    dev: false
-
   /natural-compare@1.4.0:
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
     dev: true
@@ -14948,21 +15127,10 @@ packages:
       path-to-regexp: 1.8.0
     dev: true
 
-  /node-abi@3.31.0:
-    resolution: {integrity: sha512-eSKV6s+APenqVh8ubJyiu/YhZgxQpGP66ntzUb3lY1xB9ukSRaGnx0AIxI+IM+1+IVYC1oWobgG5L3Lt9ARykQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      semver: 7.5.4
-    dev: false
-
   /node-abort-controller@3.1.1:
     resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
     dev: false
 
-  /node-addon-api@6.1.0:
-    resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
-    dev: false
-
   /node-bitmap@0.0.1:
     resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==}
     engines: {node: '>=v0.6.5'}
@@ -16237,25 +16405,6 @@ packages:
     resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==}
     dev: true
 
-  /prebuild-install@7.1.1:
-    resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      detect-libc: 2.0.2
-      expand-template: 2.0.3
-      github-from-package: 0.0.0
-      minimist: 1.2.8
-      mkdirp-classic: 0.5.3
-      napi-build-utils: 1.0.2
-      node-abi: 3.31.0
-      pump: 3.0.0
-      rc: 1.2.8
-      simple-get: 4.0.1
-      tar-fs: 2.1.1
-      tunnel-agent: 0.6.0
-    dev: false
-
   /prelude-ls@1.2.1:
     resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
     engines: {node: '>= 0.8.0'}
@@ -16655,16 +16804,6 @@ packages:
       iconv-lite: 0.4.24
       unpipe: 1.0.0
 
-  /rc@1.2.8:
-    resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
-    hasBin: true
-    dependencies:
-      deep-extend: 0.6.0
-      ini: 1.3.8
-      minimist: 1.2.8
-      strip-json-comments: 2.0.1
-    dev: false
-
   /rdf-canonize@3.4.0:
     resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==}
     engines: {node: '>=12'}
@@ -17395,19 +17534,34 @@ packages:
       kind-of: 6.0.3
     dev: true
 
-  /sharp@0.32.6:
-    resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==}
-    engines: {node: '>=14.15.0'}
+  /sharp@0.33.2:
+    resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==}
+    engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     requiresBuild: true
     dependencies:
       color: 4.2.3
       detect-libc: 2.0.2
-      node-addon-api: 6.1.0
-      prebuild-install: 7.1.1
       semver: 7.5.4
-      simple-get: 4.0.1
-      tar-fs: 3.0.4
-      tunnel-agent: 0.6.0
+    optionalDependencies:
+      '@img/sharp-darwin-arm64': 0.33.2
+      '@img/sharp-darwin-x64': 0.33.2
+      '@img/sharp-libvips-darwin-arm64': 1.0.1
+      '@img/sharp-libvips-darwin-x64': 1.0.1
+      '@img/sharp-libvips-linux-arm': 1.0.1
+      '@img/sharp-libvips-linux-arm64': 1.0.1
+      '@img/sharp-libvips-linux-s390x': 1.0.1
+      '@img/sharp-libvips-linux-x64': 1.0.1
+      '@img/sharp-libvips-linuxmusl-arm64': 1.0.1
+      '@img/sharp-libvips-linuxmusl-x64': 1.0.1
+      '@img/sharp-linux-arm': 0.33.2
+      '@img/sharp-linux-arm64': 0.33.2
+      '@img/sharp-linux-s390x': 0.33.2
+      '@img/sharp-linux-x64': 0.33.2
+      '@img/sharp-linuxmusl-arm64': 0.33.2
+      '@img/sharp-linuxmusl-x64': 0.33.2
+      '@img/sharp-wasm32': 0.33.2
+      '@img/sharp-win32-ia32': 0.33.2
+      '@img/sharp-win32-x64': 0.33.2
     dev: false
 
   /shebang-command@1.2.0:
@@ -17456,18 +17610,6 @@ packages:
     resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
     engines: {node: '>=14'}
 
-  /simple-concat@1.0.1:
-    resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
-    dev: false
-
-  /simple-get@4.0.1:
-    resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
-    dependencies:
-      decompress-response: 6.0.0
-      once: 1.4.0
-      simple-concat: 1.0.1
-    dev: false
-
   /simple-oauth2@5.0.0:
     resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==}
     dependencies:
@@ -18042,11 +18184,6 @@ packages:
       min-indent: 1.0.1
     dev: true
 
-  /strip-json-comments@2.0.1:
-    resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
-    engines: {node: '>=0.10.0'}
-    dev: false
-
   /strip-json-comments@3.1.1:
     resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
     engines: {node: '>=8'}
@@ -18154,14 +18291,7 @@ packages:
       mkdirp-classic: 0.5.3
       pump: 3.0.0
       tar-stream: 2.2.0
-
-  /tar-fs@3.0.4:
-    resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==}
-    dependencies:
-      mkdirp-classic: 0.5.3
-      pump: 3.0.0
-      tar-stream: 3.1.6
-    dev: false
+    dev: true
 
   /tar-stream@2.2.0:
     resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
@@ -18172,6 +18302,7 @@ packages:
       fs-constants: 1.0.0
       inherits: 2.0.4
       readable-stream: 3.6.0
+    dev: true
 
   /tar-stream@3.1.6:
     resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==}

From fb0eb5a31fb44cddc5d517fbd5b65b670bc60e4f Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 21 Feb 2024 18:35:05 +0900
Subject: [PATCH 011/266] :art:

---
 packages/frontend/src/ui/deck/channel-column.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index 125c85130ed5..bd3b059497b1 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<template v-if="column.channelId">
 		<div style="padding: 8px; text-align: center;">
-			<MkButton primary gradate rounded inline @click="post"><i class="ti ti-pencil"></i></MkButton>
+			<MkButton primary gradate rounded inline small @click="post"><i class="ti ti-pencil"></i></MkButton>
 		</div>
 		<MkTimeline ref="timeline" src="channel" :channel="column.channelId"/>
 	</template>

From e10ce7204cf54408d533f5f716f4d6a98aed86ed Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Wed, 21 Feb 2024 20:15:04 +0900
Subject: [PATCH 012/266] =?UTF-8?q?fix:=20MkUserPopup=E3=81=8C=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=A6=E3=82=8B=E7=8A=B6=E6=85=8B?=
 =?UTF-8?q?=E3=81=A7v-user-preview=E3=81=8C=E3=81=A4=E3=81=84=E3=81=9F?=
 =?UTF-8?q?=E8=A6=81=E7=B4=A0=E3=81=8Cdetach=E3=81=95=E3=82=8C=E3=82=8B?=
 =?UTF-8?q?=E3=81=A8MkUserPopup=E3=81=8C=E6=B6=88=E3=81=88=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=20(#13349)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: MkUserPopupが表示されてる状態でv-user-previewがついた要素がdetachされるとMkUserPopupが消えない問題

* docs(changelog): previewの中のユーザメンションをホバーした状態で投稿を編集するとユーザの情報popupが消えない問題を修正

* docs(changelog): ユーザの情報のポップアップが消えなくなることがある問題を修正
---
 CHANGELOG.md                                     | 1 +
 packages/frontend/src/directives/user-preview.ts | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1de194da178..027f05c92218 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -96,6 +96,7 @@
 - Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
 - Fix: MkCodeEditorで行がずれていってしまう問題の修正
 - Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
+- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正
 
 ### Server
 - Enhance: 連合先のレートリミットを超過した際にリトライするようになりました
diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts
index 0d6c330da195..7a008a44865e 100644
--- a/packages/frontend/src/directives/user-preview.ts
+++ b/packages/frontend/src/directives/user-preview.ts
@@ -99,7 +99,6 @@ export class UserPreview {
 		this.el.removeEventListener('mouseover', this.onMouseover);
 		this.el.removeEventListener('mouseleave', this.onMouseleave);
 		this.el.removeEventListener('click', this.onClick);
-		window.clearInterval(this.checkTimer);
 	}
 }
 

From b36e6b1a777848ec8553b297e956ada240dcacc9 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 22 Feb 2024 00:59:59 +0900
Subject: [PATCH 013/266] =?UTF-8?q?fix:=20=E7=A6=81=E6=AD=A2=E3=82=AD?=
 =?UTF-8?q?=E3=83=BC=E3=83=AF=E3=83=BC=E3=83=89=E3=82=92=E5=90=AB=E3=82=80?=
 =?UTF-8?q?=E3=83=8E=E3=83=BC=E3=83=88=E3=81=8CDelayed=20Queue=E3=81=AB?=
 =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=95=E3=82=8C=E3=81=A6=E5=86=8D=E5=87=A6?=
 =?UTF-8?q?=E7=90=86=E3=81=95=E3=82=8C=E3=82=8B=E5=95=8F=E9=A1=8C=20(#1342?=
 =?UTF-8?q?8)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: use IdentifiableError instead of NoteCreateService.ContainsProhibitedWordsError

* fix: notes with prohibited words are reprocessed with delay

* docs(changelog): 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題

* lint: fix lint errors

* fix: rethrowするべきなのにrethrowし忘れていたのを修正
---
 CHANGELOG.md                                           |  1 +
 packages/backend/src/core/NoteCreateService.ts         |  5 ++---
 .../src/queue/processors/InboxProcessorService.ts      | 10 +++++++++-
 .../backend/src/server/api/endpoints/notes/create.ts   |  5 +++--
 4 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 027f05c92218..63f5c913f3c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
+- Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
 
 ## 2024.2.0
 
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 9cec614d5c87..2a5fd2e1a6c3 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -59,6 +59,7 @@ import { UtilityService } from '@/core/UtilityService.js';
 import { UserBlockingService } from '@/core/UserBlockingService.js';
 import { isReply } from '@/misc/is-reply.js';
 import { trackPromise } from '@/misc/promise-tracker.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
 
 type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
 
@@ -151,8 +152,6 @@ type Option = {
 export class NoteCreateService implements OnApplicationShutdown {
 	#shutdownController = new AbortController();
 
-	public static ContainsProhibitedWordsError = class extends Error {};
-
 	constructor(
 		@Inject(DI.config)
 		private config: Config,
@@ -264,7 +263,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 
 		if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
-			throw new NoteCreateService.ContainsProhibitedWordsError();
+			throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
 		}
 
 		const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 7adadd799b7a..0a713149e5f1 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -24,6 +24,7 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
 import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js';
 import { ApInboxService } from '@/core/activitypub/ApInboxService.js';
 import { bindThis } from '@/decorators.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
 import { QueueLoggerService } from '../QueueLoggerService.js';
 import type { InboxJobData } from '../types.js';
 
@@ -180,7 +181,14 @@ export class InboxProcessorService {
 		});
 
 		// アクティビティを処理
-		await this.apInboxService.performActivity(authUser.user, activity);
+		try {
+			await this.apInboxService.performActivity(authUser.user, activity);
+		} catch (e) {
+			if (e instanceof IdentifiableError) {
+				if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') return 'blocked notes with prohibited words';
+			}
+			throw e;
+		}
 		return 'ok';
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index e6e4fcc745fd..2fa0bd099fe7 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -19,6 +19,7 @@ import { DI } from '@/di-symbols.js';
 import { isPureRenote } from '@/misc/is-pure-renote.js';
 import { MetaService } from '@/core/MetaService.js';
 import { UtilityService } from '@/core/UtilityService.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -376,8 +377,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				};
 			} catch (e) {
 				// TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい
-				if (e instanceof NoteCreateService.ContainsProhibitedWordsError) {
-					throw new ApiError(meta.errors.containsProhibitedWords);
+				if (e instanceof IdentifiableError) {
+					if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') throw new ApiError(meta.errors.containsProhibitedWords);
 				}
 
 				throw e;

From 26c8b53f701df76a42897af18f0a117a30226662 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 22 Feb 2024 20:59:52 +0900
Subject: [PATCH 014/266] =?UTF-8?q?enhance:=20=E3=82=B5=E3=83=BC=E3=83=90?=
 =?UTF-8?q?=E3=83=BC=E3=81=94=E3=81=A8=E3=81=AB=E3=83=A2=E3=83=87=E3=83=AC?=
 =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=8E=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E3=82=92=E6=AE=8B=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                     |  1 +
 locales/index.d.ts                               |  6 +++++-
 locales/ja-JP.yml                                |  3 ++-
 .../1708399372194-per-instance-mod-note.js       | 16 ++++++++++++++++
 .../src/core/entities/InstanceEntityService.ts   |  9 ++++++++-
 packages/backend/src/models/Instance.ts          |  5 +++++
 .../models/json-schema/federation-instance.ts    |  4 ++++
 .../admin/federation/update-instance.ts          | 15 +++++++++++++--
 .../api/endpoints/federation/show-instance.ts    |  2 +-
 packages/backend/src/types.ts                    |  7 +++++++
 .../frontend/src/pages/admin/modlog.ModLog.vue   |  6 ++++++
 packages/frontend/src/pages/instance-info.vue    | 12 +++++++++++-
 packages/misskey-js/etc/misskey-js.api.md        |  5 ++++-
 packages/misskey-js/src/autogen/types.ts         |  4 +++-
 packages/misskey-js/src/consts.ts                |  7 +++++++
 packages/misskey-js/src/entities.ts              |  3 +++
 16 files changed, 96 insertions(+), 9 deletions(-)
 create mode 100644 packages/backend/migration/1708399372194-per-instance-mod-note.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1de194da178..31850e1d5327 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@
 ## 202x.x.x (unreleased)
 
 ### General
+- Enhance: サーバーごとにモデレーションノートを残せるように
 
 ### Client
 - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 1bc99ab84904..d483fea8378e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -9172,7 +9172,7 @@ export interface Locale extends ILocale {
          */
         "updateServerSettings": string;
         /**
-         * モデレーションノート更新
+         * ユーザーのモデレーションノート更新
          */
         "updateUserNote": string;
         /**
@@ -9219,6 +9219,10 @@ export interface Locale extends ILocale {
          * リモートサーバーを再開
          */
         "unsuspendRemoteInstance": string;
+        /**
+         * リモートサーバーのモデレーションノート更新
+         */
+        "updateRemoteInstanceNote": string;
         /**
          * ファイルをセンシティブ付与
          */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 5993ec80d0ce..7e16619fc78d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2434,7 +2434,7 @@ _moderationLogTypes:
   updateCustomEmoji: "カスタム絵文字更新"
   deleteCustomEmoji: "カスタム絵文字削除"
   updateServerSettings: "サーバー設定更新"
-  updateUserNote: "モデレーションノート更新"
+  updateUserNote: "ユーザーのモデレーションノート更新"
   deleteDriveFile: "ファイルを削除"
   deleteNote: "ノートを削除"
   createGlobalAnnouncement: "全体のお知らせを作成"
@@ -2446,6 +2446,7 @@ _moderationLogTypes:
   resetPassword: "パスワードをリセット"
   suspendRemoteInstance: "リモートサーバーを停止"
   unsuspendRemoteInstance: "リモートサーバーを再開"
+  updateRemoteInstanceNote: "リモートサーバーのモデレーションノート更新"
   markSensitiveDriveFile: "ファイルをセンシティブ付与"
   unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
   resolveAbuseReport: "通報を解決"
diff --git a/packages/backend/migration/1708399372194-per-instance-mod-note.js b/packages/backend/migration/1708399372194-per-instance-mod-note.js
new file mode 100644
index 000000000000..339a4d7af966
--- /dev/null
+++ b/packages/backend/migration/1708399372194-per-instance-mod-note.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class PerInstanceModNote1708399372194 {
+    name = 'PerInstanceModNote1708399372194'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" ADD "moderationNote" character varying(16384) NOT NULL DEFAULT ''`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "moderationNote"`);
+    }
+}
diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts
index 9287c98003a9..e46bd8b9637e 100644
--- a/packages/backend/src/core/entities/InstanceEntityService.ts
+++ b/packages/backend/src/core/entities/InstanceEntityService.ts
@@ -8,12 +8,15 @@ import type { Packed } from '@/misc/json-schema.js';
 import type { MiInstance } from '@/models/Instance.js';
 import { MetaService } from '@/core/MetaService.js';
 import { bindThis } from '@/decorators.js';
-import { UtilityService } from '../UtilityService.js';
+import { UtilityService } from '@/core/UtilityService.js';
+import { RoleService } from '@/core/RoleService.js';
+import { MiUser } from '@/models/User.js';
 
 @Injectable()
 export class InstanceEntityService {
 	constructor(
 		private metaService: MetaService,
+		private roleService: RoleService,
 
 		private utilityService: UtilityService,
 	) {
@@ -22,8 +25,11 @@ export class InstanceEntityService {
 	@bindThis
 	public async pack(
 		instance: MiInstance,
+		me?: { id: MiUser['id']; } | null | undefined,
 	): Promise<Packed<'FederationInstance'>> {
 		const meta = await this.metaService.fetch();
+		const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false;
+
 		return {
 			id: instance.id,
 			firstRetrievedAt: instance.firstRetrievedAt.toISOString(),
@@ -48,6 +54,7 @@ export class InstanceEntityService {
 			themeColor: instance.themeColor,
 			infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
 			latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null,
+			moderationNote: iAmModerator ? instance.moderationNote : null,
 		};
 	}
 
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index 0632ef525b23..9863c9d75da7 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -144,4 +144,9 @@ export class MiInstance {
 		nullable: true,
 	})
 	public infoUpdatedAt: Date | null;
+
+	@Column('varchar', {
+		length: 16384, default: '',
+	})
+	public moderationNote: string;
 }
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index 087a0e69676a..42d98fe5238e 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -107,5 +107,9 @@ export const packedFederationInstanceSchema = {
 			optional: false, nullable: true,
 			format: 'date-time',
 		},
+		moderationNote: {
+			type: 'string',
+			optional: true, nullable: true,
+		},
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
index b989b99e474c..0bcdc2a4b81e 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
@@ -24,8 +24,9 @@ export const paramDef = {
 	properties: {
 		host: { type: 'string' },
 		isSuspended: { type: 'boolean' },
+		moderationNote: { type: 'string' },
 	},
-	required: ['host', 'isSuspended'],
+	required: ['host'],
 } as const;
 
 @Injectable()
@@ -47,9 +48,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			await this.federatedInstanceService.update(instance.id, {
 				isSuspended: ps.isSuspended,
+				moderationNote: ps.moderationNote,
 			});
 
-			if (instance.isSuspended !== ps.isSuspended) {
+			if (ps.isSuspended != null && instance.isSuspended !== ps.isSuspended) {
 				if (ps.isSuspended) {
 					this.moderationLogService.log(me, 'suspendRemoteInstance', {
 						id: instance.id,
@@ -62,6 +64,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					});
 				}
 			}
+
+			if (ps.moderationNote != null && instance.moderationNote !== ps.moderationNote) {
+				this.moderationLogService.log(me, 'updateRemoteInstanceNote', {
+					id: instance.id,
+					host: instance.host,
+					before: instance.moderationNote,
+					after: ps.moderationNote,
+				});
+			}
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts
index e3c598d11025..2972861a4bae 100644
--- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts
+++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts
@@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const instance = await this.instancesRepository
 				.findOneBy({ host: this.utilityService.toPuny(ps.host) });
 
-			return instance ? await this.instanceEntityService.pack(instance) : null;
+			return instance ? await this.instanceEntityService.pack(instance, me) : null;
 		});
 	}
 }
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index fdcd2c062950..506a755cc4c6 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -69,6 +69,7 @@ export const moderationLogTypes = [
 	'resetPassword',
 	'suspendRemoteInstance',
 	'unsuspendRemoteInstance',
+	'updateRemoteInstanceNote',
 	'markSensitiveDriveFile',
 	'unmarkSensitiveDriveFile',
 	'resolveAbuseReport',
@@ -209,6 +210,12 @@ export type ModerationLogPayloads = {
 		id: string;
 		host: string;
 	};
+	updateRemoteInstanceNote: {
+		id: string;
+		host: string;
+		before: string | null;
+		after: string | null;
+	};
 	markSensitiveDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index 21d68331cb71..e33c88272187 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -110,6 +110,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
 			</div>
 		</template>
+		<template v-else-if="log.type === 'updateRemoteInstanceNote'">
+			<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
+			<div :class="$style.diff">
+				<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
+			</div>
+		</template>
 
 		<details>
 			<summary>raw</summary>
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 2f1557182a38..cb7fe2866c2a 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -39,6 +39,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
 						<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
 						<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
+						<MkTextarea v-model="moderationNote" manualSave>
+							<template #label>{{ i18n.ts.moderationNote }}</template>
+						</MkTextarea>
 					</div>
 				</FormSection>
 
@@ -119,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref, computed } from 'vue';
+import { ref, computed, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkChart from '@/components/MkChart.vue';
 import MkObjectView from '@/components/MkObjectView.vue';
@@ -141,6 +144,7 @@ import MkPagination from '@/components/MkPagination.vue';
 import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 import { dateString } from '@/filters/date.js';
+import MkTextarea from '@/components/MkTextarea.vue';
 
 const props = defineProps<{
 	host: string;
@@ -155,6 +159,7 @@ const suspended = ref(false);
 const isBlocked = ref(false);
 const isSilenced = ref(false);
 const faviconUrl = ref<string | null>(null);
+const moderationNote = ref('');
 
 const usersPagination = {
 	endpoint: iAmModerator ? 'admin/show-users' : 'users' as const,
@@ -167,6 +172,10 @@ const usersPagination = {
 	offsetMode: true,
 };
 
+watch(moderationNote, async () => {
+	await misskeyApi('admin/federation/update-instance', { host: instance.value.host, moderationNote: moderationNote.value });
+});
+
 async function fetch(): Promise<void> {
 	if (iAmAdmin) {
 		meta.value = await misskeyApi('admin/meta');
@@ -178,6 +187,7 @@ async function fetch(): Promise<void> {
 	isBlocked.value = instance.value?.isBlocked ?? false;
 	isSilenced.value = instance.value?.isSilenced ?? false;
 	faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
+	moderationNote.value = instance.value?.moderationNote;
 }
 
 async function toggleBlock(): Promise<void> {
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index f362f0b34e2b..c2428910fa5d 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2316,6 +2316,9 @@ type ModerationLog = {
 } | {
     type: 'unsuspendRemoteInstance';
     info: ModerationLogPayloads['unsuspendRemoteInstance'];
+} | {
+    type: 'updateRemoteInstanceNote';
+    info: ModerationLogPayloads['updateRemoteInstanceNote'];
 } | {
     type: 'markSensitiveDriveFile';
     info: ModerationLogPayloads['markSensitiveDriveFile'];
@@ -2355,7 +2358,7 @@ type ModerationLog = {
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
 
 // @public (undocumented)
 type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index d0d8573b407c..9bf79fde7199 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4480,6 +4480,7 @@ export type components = {
       infoUpdatedAt: string | null;
       /** Format: date-time */
       latestRequestReceivedAt: string | null;
+      moderationNote?: string | null;
     };
     GalleryPost: {
       /**
@@ -7213,7 +7214,8 @@ export type operations = {
       content: {
         'application/json': {
           host: string;
-          isSuspended: boolean;
+          isSuspended?: boolean;
+          moderationNote?: string;
         };
       };
     };
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 0e446c121591..b690621e98d9 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -121,6 +121,7 @@ export const moderationLogTypes = [
 	'resetPassword',
 	'suspendRemoteInstance',
 	'unsuspendRemoteInstance',
+	'updateRemoteInstanceNote',
 	'markSensitiveDriveFile',
 	'unmarkSensitiveDriveFile',
 	'resolveAbuseReport',
@@ -261,6 +262,12 @@ export type ModerationLogPayloads = {
 		id: string;
 		host: string;
 	};
+	updateRemoteInstanceNote: {
+		id: string;
+		host: string;
+		before: string | null;
+		after: string | null;
+	};
 	markSensitiveDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 772d2bbfa1e7..35503d6d6fff 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -95,6 +95,9 @@ export type ModerationLog = {
 } | {
 	type: 'unsuspendRemoteInstance';
 	info: ModerationLogPayloads['unsuspendRemoteInstance'];
+} | {
+	type: 'updateRemoteInstanceNote';
+	info: ModerationLogPayloads['updateRemoteInstanceNote'];
 } | {
 	type: 'markSensitiveDriveFile';
 	info: ModerationLogPayloads['markSensitiveDriveFile'];

From 4d6fab06de2af01909cb37a54a407fda7f15f0bf Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 22 Feb 2024 21:10:28 +0900
Subject: [PATCH 015/266] refactor: Refactor NoteReadService.read (#13429)

* refactor: Refactor NoteReadService.read

* clean up

* Update packages/backend/src/core/NoteReadService.ts

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 packages/backend/src/core/NoteReadService.ts  | 63 ++++++++++---------
 .../server/api/endpoints/antennas/notes.ts    |  4 +-
 2 files changed, 33 insertions(+), 34 deletions(-)

diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts
index feef024602ff..181c9f7649d8 100644
--- a/packages/backend/src/core/NoteReadService.ts
+++ b/packages/backend/src/core/NoteReadService.ts
@@ -88,46 +88,47 @@ export class NoteReadService implements OnApplicationShutdown {
 		userId: MiUser['id'],
 		notes: (MiNote | Packed<'Note'>)[],
 	): Promise<void> {
-		const readMentions: (MiNote | Packed<'Note'>)[] = [];
-		const readSpecifiedNotes: (MiNote | Packed<'Note'>)[] = [];
+		if (notes.length === 0) return;
+
+		const noteIds = new Set<MiNote['id']>();
 
 		for (const note of notes) {
 			if (note.mentions && note.mentions.includes(userId)) {
-				readMentions.push(note);
+				noteIds.add(note.id);
 			} else if (note.visibleUserIds && note.visibleUserIds.includes(userId)) {
-				readSpecifiedNotes.push(note);
+				noteIds.add(note.id);
 			}
 		}
 
-		if ((readMentions.length > 0) || (readSpecifiedNotes.length > 0)) {
-			// Remove the record
-			await this.noteUnreadsRepository.delete({
-				userId: userId,
-				noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id)]),
-			});
+		if (noteIds.size === 0) return;
 
-			// TODO: ↓まとめてクエリしたい
+		// Remove the record
+		await this.noteUnreadsRepository.delete({
+			userId: userId,
+			noteId: In(Array.from(noteIds)),
+		});
 
-			trackPromise(this.noteUnreadsRepository.countBy({
-				userId: userId,
-				isMentioned: true,
-			}).then(mentionsCount => {
-				if (mentionsCount === 0) {
-					// 全て既読になったイベントを発行
-					this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions');
-				}
-			}));
-
-			trackPromise(this.noteUnreadsRepository.countBy({
-				userId: userId,
-				isSpecified: true,
-			}).then(specifiedCount => {
-				if (specifiedCount === 0) {
-					// 全て既読になったイベントを発行
-					this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes');
-				}
-			}));
-		}
+		// TODO: ↓まとめてクエリしたい
+
+		trackPromise(this.noteUnreadsRepository.countBy({
+			userId: userId,
+			isMentioned: true,
+		}).then(mentionsCount => {
+			if (mentionsCount === 0) {
+				// 全て既読になったイベントを発行
+				this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions');
+			}
+		}));
+
+		trackPromise(this.noteUnreadsRepository.countBy({
+			userId: userId,
+			isSpecified: true,
+		}).then(specifiedCount => {
+			if (specifiedCount === 0) {
+				// 全て既読になったイベントを発行
+				this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes');
+			}
+		}));
 	}
 
 	@bindThis
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index 39f3fab21ee0..f4dfe1ecc4d7 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -124,9 +124,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				notes.sort((a, b) => a.id > b.id ? -1 : 1);
 			}
 
-			if (notes.length > 0) {
-				this.noteReadService.read(me.id, notes);
-			}
+			this.noteReadService.read(me.id, notes);
 
 			return await this.noteEntityService.packMany(notes, me);
 		});

From bf5952fd63e93894af621828d49ae023ad08ab6a Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Thu, 22 Feb 2024 21:31:57 +0900
Subject: [PATCH 016/266] =?UTF-8?q?flash/update=20=E3=81=A7=E9=83=A8?=
 =?UTF-8?q?=E5=88=86=E7=9A=84=E3=81=AB=E5=A4=89=E6=9B=B4=E3=81=A7=E3=81=8D?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#1339?=
 =?UTF-8?q?6)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* make flash/update params optional

* Update autogen files

pnpm run build-misskey-js-with-types

* Update update.ts

* Update CHANGELOG.md

* hasOwnProperty -> hasOwn

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                         |  1 +
 .../backend/src/server/api/endpoints/flash/update.ts | 12 ++++++------
 packages/misskey-js/src/autogen/types.ts             |  8 ++++----
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31dfb6ce1199..9ff5881df069 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
+- エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
 
 ## 2024.2.0
diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts
index 7d7633daa527..e378669f0a6e 100644
--- a/packages/backend/src/server/api/endpoints/flash/update.ts
+++ b/packages/backend/src/server/api/endpoints/flash/update.ts
@@ -51,7 +51,7 @@ export const paramDef = {
 		} },
 		visibility: { type: 'string', enum: ['public', 'private'] },
 	},
-	required: ['flashId', 'title', 'summary', 'script', 'permissions'],
+	required: ['flashId'],
 } as const;
 
 @Injectable()
@@ -71,11 +71,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			await this.flashsRepository.update(flash.id, {
 				updatedAt: new Date(),
-				title: ps.title,
-				summary: ps.summary,
-				script: ps.script,
-				permissions: ps.permissions,
-				visibility: ps.visibility,
+				...Object.fromEntries(
+					Object.entries(ps).filter(
+						([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key)
+					)
+				),
 			});
 		});
 	}
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 9bf79fde7199..0b2a88b53787 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -22927,10 +22927,10 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           flashId: string;
-          title: string;
-          summary: string;
-          script: string;
-          permissions: string[];
+          title?: string;
+          summary?: string;
+          script?: string;
+          permissions?: string[];
           /** @enum {string} */
           visibility?: 'public' | 'private';
         };

From d20542c495dd3342b23ef9f1c759c2f4f2dce63e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 23 Feb 2024 10:47:17 +0900
Subject: [PATCH 017/266] =?UTF-8?q?enhance:=20`meta`=E3=82=92SSR=20HTML?=
 =?UTF-8?q?=E3=81=AB=E5=9F=8B=E3=82=81=E8=BE=BC=E3=82=80=20(#13436)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: `meta`をSSR HTMLに埋め込む

* HTML Metaの有効時間を指定

* 1時間

* MetaEntityService

* JSONをPackするように

* :v:

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 packages/backend/src/core/CoreModule.ts       |   6 +
 .../src/core/entities/MetaEntityService.ts    | 154 +++++++
 packages/backend/src/misc/json-schema.ts      |   8 +
 .../backend/src/models/json-schema/meta.ts    | 328 ++++++++++++++
 .../backend/src/server/api/endpoints/meta.ts  | 417 +-----------------
 .../src/server/web/ClientServerService.ts     |  24 +-
 .../backend/src/server/web/views/base.pug     |   3 +
 packages/frontend/src/boot/main-boot.ts       |  12 +-
 packages/frontend/src/instance.ts             |  30 +-
 packages/frontend/src/local-storage.ts        |   1 +
 .../src/pages/admin/bot-protection.vue        |   2 +-
 .../frontend/src/pages/admin/branding.vue     |   2 +-
 .../src/pages/admin/email-settings.vue        |   2 +-
 .../src/pages/admin/external-services.vue     |   2 +-
 .../src/pages/admin/instance-block.vue        |   2 +-
 .../frontend/src/pages/admin/moderation.vue   |   2 +-
 .../src/pages/admin/object-storage.vue        |   2 +-
 .../src/pages/admin/other-settings.vue        |   2 +-
 .../src/pages/admin/proxy-account.vue         |   2 +-
 .../frontend/src/pages/admin/security.vue     |   4 +-
 .../frontend/src/pages/admin/server-rules.vue |   2 +-
 .../frontend/src/pages/admin/settings.vue     |   2 +-
 packages/frontend/src/scripts/clear-cache.ts  |   4 +
 packages/misskey-js/etc/misskey-js.api.md     |  14 +-
 packages/misskey-js/src/autogen/models.ts     |   3 +
 packages/misskey-js/src/autogen/types.ts      | 176 ++++----
 26 files changed, 676 insertions(+), 530 deletions(-)
 create mode 100644 packages/backend/src/core/entities/MetaEntityService.ts
 create mode 100644 packages/backend/src/models/json-schema/meta.ts

diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index c31cef36e839..2c27d33c0649 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -116,6 +116,7 @@ import { FlashEntityService } from './entities/FlashEntityService.js';
 import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js';
 import { RoleEntityService } from './entities/RoleEntityService.js';
 import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js';
+import { MetaEntityService } from './entities/MetaEntityService.js';
 
 import { ApAudienceService } from './activitypub/ApAudienceService.js';
 import { ApDbResolverService } from './activitypub/ApDbResolverService.js';
@@ -254,6 +255,7 @@ const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisti
 const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
 const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService };
 const $ReversiGameEntityService: Provider = { provide: 'ReversiGameEntityService', useExisting: ReversiGameEntityService };
+const $MetaEntityService: Provider = { provide: 'MetaEntityService', useExisting: MetaEntityService };
 
 const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService };
 const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService };
@@ -393,6 +395,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		FlashLikeEntityService,
 		RoleEntityService,
 		ReversiGameEntityService,
+		MetaEntityService,
 
 		ApAudienceService,
 		ApDbResolverService,
@@ -528,6 +531,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$FlashLikeEntityService,
 		$RoleEntityService,
 		$ReversiGameEntityService,
+		$MetaEntityService,
 
 		$ApAudienceService,
 		$ApDbResolverService,
@@ -663,6 +667,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		FlashLikeEntityService,
 		RoleEntityService,
 		ReversiGameEntityService,
+		MetaEntityService,
 
 		ApAudienceService,
 		ApDbResolverService,
@@ -797,6 +802,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$FlashLikeEntityService,
 		$RoleEntityService,
 		$ReversiGameEntityService,
+		$MetaEntityService,
 
 		$ApAudienceService,
 		$ApDbResolverService,
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
new file mode 100644
index 000000000000..b50d76288f27
--- /dev/null
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -0,0 +1,154 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Brackets } from 'typeorm';
+import { Inject, Injectable } from '@nestjs/common';
+import JSON5 from 'json5';
+import type { Packed } from '@/misc/json-schema.js';
+import type { MiMeta } from '@/models/Meta.js';
+import type { AdsRepository } from '@/models/_.js';
+import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
+import { MetaService } from '@/core/MetaService.js';
+import { bindThis } from '@/decorators.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { InstanceActorService } from '@/core/InstanceActorService.js';
+import type { Config } from '@/config.js';
+import { DI } from '@/di-symbols.js';
+import { DEFAULT_POLICIES } from '@/core/RoleService.js';
+
+@Injectable()
+export class MetaEntityService {
+	constructor(
+		@Inject(DI.config)
+		private config: Config,
+
+		@Inject(DI.adsRepository)
+		private adsRepository: AdsRepository,
+
+		private userEntityService: UserEntityService,
+		private metaService: MetaService,
+		private instanceActorService: InstanceActorService,
+	) { }
+
+	@bindThis
+	public async pack(meta?: MiMeta): Promise<Packed<'MetaLite'>> {
+		let instance = meta;
+
+		if (!instance) {
+			instance = await this.metaService.fetch();
+		}
+
+		const ads = await this.adsRepository.createQueryBuilder('ads')
+			.where('ads.expiresAt > :now', { now: new Date() })
+			.andWhere('ads.startsAt <= :now', { now: new Date() })
+			.andWhere(new Brackets(qb => {
+				// 曜日のビットフラグを確認する
+				qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() })
+					.orWhere('ads.dayOfWeek = 0');
+			}))
+			.getMany();
+
+		const packed: Packed<'MetaLite'> = {
+			maintainerName: instance.maintainerName,
+			maintainerEmail: instance.maintainerEmail,
+
+			version: this.config.version,
+			providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl,
+
+			name: instance.name,
+			shortName: instance.shortName,
+			uri: this.config.url,
+			description: instance.description,
+			langs: instance.langs,
+			tosUrl: instance.termsOfServiceUrl,
+			repositoryUrl: instance.repositoryUrl,
+			feedbackUrl: instance.feedbackUrl,
+			impressumUrl: instance.impressumUrl,
+			privacyPolicyUrl: instance.privacyPolicyUrl,
+			disableRegistration: instance.disableRegistration,
+			emailRequiredForSignup: instance.emailRequiredForSignup,
+			enableHcaptcha: instance.enableHcaptcha,
+			hcaptchaSiteKey: instance.hcaptchaSiteKey,
+			enableMcaptcha: instance.enableMcaptcha,
+			mcaptchaSiteKey: instance.mcaptchaSitekey,
+			mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl,
+			enableRecaptcha: instance.enableRecaptcha,
+			recaptchaSiteKey: instance.recaptchaSiteKey,
+			enableTurnstile: instance.enableTurnstile,
+			turnstileSiteKey: instance.turnstileSiteKey,
+			swPublickey: instance.swPublicKey,
+			themeColor: instance.themeColor,
+			mascotImageUrl: instance.mascotImageUrl ?? '/assets/ai.png',
+			bannerUrl: instance.bannerUrl,
+			infoImageUrl: instance.infoImageUrl,
+			serverErrorImageUrl: instance.serverErrorImageUrl,
+			notFoundImageUrl: instance.notFoundImageUrl,
+			iconUrl: instance.iconUrl,
+			backgroundImageUrl: instance.backgroundImageUrl,
+			logoImageUrl: instance.logoImageUrl,
+			maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
+			// クライアントの手間を減らすためあらかじめJSONに変換しておく
+			defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null,
+			defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null,
+			ads: ads.map(ad => ({
+				id: ad.id,
+				url: ad.url,
+				place: ad.place,
+				ratio: ad.ratio,
+				imageUrl: ad.imageUrl,
+				dayOfWeek: ad.dayOfWeek,
+			})),
+			notesPerOneAd: instance.notesPerOneAd,
+			enableEmail: instance.enableEmail,
+			enableServiceWorker: instance.enableServiceWorker,
+
+			translatorAvailable: instance.deeplAuthKey != null,
+
+			serverRules: instance.serverRules,
+
+			policies: { ...DEFAULT_POLICIES, ...instance.policies },
+
+			mediaProxy: this.config.mediaProxy,
+		};
+
+		return packed;
+	}
+
+	@bindThis
+	public async packDetailed(meta?: MiMeta): Promise<Packed<'MetaDetailed'>> {
+		let instance = meta;
+
+		if (!instance) {
+			instance = await this.metaService.fetch();
+		}
+
+		const packed = await this.pack(instance);
+
+		const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null;
+
+		const packDetailed: Packed<'MetaDetailed'> = {
+			...packed,
+			cacheRemoteFiles: instance.cacheRemoteFiles,
+			cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
+			requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
+			proxyAccountName: proxyAccount ? proxyAccount.username : null,
+			features: {
+				localTimeline: instance.policies.ltlAvailable,
+				globalTimeline: instance.policies.gtlAvailable,
+				registration: !instance.disableRegistration,
+				emailRequiredForSignup: instance.emailRequiredForSignup,
+				hcaptcha: instance.enableHcaptcha,
+				recaptcha: instance.enableRecaptcha,
+				turnstile: instance.enableTurnstile,
+				objectStorage: instance.useObjectStorage,
+				serviceWorker: instance.enableServiceWorker,
+				miauth: true,
+			},
+		};
+
+		return packDetailed;
+	}
+}
+
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index de38f145b24e..8449e5ff073a 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -50,6 +50,11 @@ import {
 } from '@/models/json-schema/role.js';
 import { packedAdSchema } from '@/models/json-schema/ad.js';
 import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
+import {
+	packedMetaLiteSchema,
+	packedMetaDetailedOnlySchema,
+	packedMetaDetailedSchema,
+} from '@/models/json-schema/meta.js';
 
 export const refs = {
 	UserLite: packedUserLiteSchema,
@@ -99,6 +104,9 @@ export const refs = {
 	RolePolicies: packedRolePoliciesSchema,
 	ReversiGameLite: packedReversiGameLiteSchema,
 	ReversiGameDetailed: packedReversiGameDetailedSchema,
+	MetaLite: packedMetaLiteSchema,
+	MetaDetailedOnly: packedMetaDetailedOnlySchema,
+	MetaDetailed: packedMetaDetailedSchema,
 };
 
 export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
new file mode 100644
index 000000000000..17789f3b46ce
--- /dev/null
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -0,0 +1,328 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const packedMetaLiteSchema = {
+	type: 'object',
+	optional: false, nullable: false,
+	properties: {
+		maintainerName: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		maintainerEmail: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		version: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		providesTarball: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		name: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		shortName: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		uri: {
+			type: 'string',
+			optional: false, nullable: false,
+			format: 'url',
+			example: 'https://misskey.example.com',
+		},
+		description: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		langs: {
+			type: 'array',
+			optional: false, nullable: false,
+			items: {
+				type: 'string',
+				optional: false, nullable: false,
+			},
+		},
+		tosUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		repositoryUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+			default: 'https://github.com/misskey-dev/misskey',
+		},
+		feedbackUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+			default: 'https://github.com/misskey-dev/misskey/issues/new',
+		},
+		defaultDarkTheme: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		defaultLightTheme: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		disableRegistration: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		emailRequiredForSignup: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		enableHcaptcha: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		hcaptchaSiteKey: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		enableMcaptcha: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		mcaptchaSiteKey: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		mcaptchaInstanceUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		enableRecaptcha: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		recaptchaSiteKey: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		enableTurnstile: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		turnstileSiteKey: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		swPublickey: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		mascotImageUrl: {
+			type: 'string',
+			optional: false, nullable: false,
+			default: '/assets/ai.png',
+		},
+		bannerUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		serverErrorImageUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		infoImageUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		notFoundImageUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		iconUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		maxNoteTextLength: {
+			type: 'number',
+			optional: false, nullable: false,
+		},
+		ads: {
+			type: 'array',
+			optional: false, nullable: false,
+			items: {
+				type: 'object',
+				optional: false, nullable: false,
+				properties: {
+					id: {
+						type: 'string',
+						optional: false, nullable: false,
+						format: 'id',
+						example: 'xxxxxxxxxx',
+					},
+					url: {
+						type: 'string',
+						optional: false, nullable: false,
+						format: 'url',
+					},
+					place: {
+						type: 'string',
+						optional: false, nullable: false,
+					},
+					ratio: {
+						type: 'number',
+						optional: false, nullable: false,
+					},
+					imageUrl: {
+						type: 'string',
+						optional: false, nullable: false,
+						format: 'url',
+					},
+					dayOfWeek: {
+						type: 'integer',
+						optional: false, nullable: false,
+					},
+				},
+			},
+		},
+		notesPerOneAd: {
+			type: 'number',
+			optional: false, nullable: false,
+			default: 0,
+		},
+		enableEmail: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		enableServiceWorker: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		translatorAvailable: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		mediaProxy: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		backgroundImageUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		impressumUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		logoImageUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		privacyPolicyUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		serverRules: {
+			type: 'array',
+			optional: false, nullable: false,
+			items: {
+				type: 'string',
+			},
+		},
+		themeColor: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		policies: {
+			type: 'object',
+			optional: false, nullable: false,
+			ref: 'RolePolicies',
+		},
+	},
+} as const;
+
+export const packedMetaDetailedOnlySchema = {
+	type: 'object',
+	optional: false, nullable: false,
+	properties: {
+		features: {
+			type: 'object',
+			optional: true, nullable: false,
+			properties: {
+				registration: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				emailRequiredForSignup: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				localTimeline: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				globalTimeline: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				hcaptcha: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				turnstile: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				recaptcha: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				objectStorage: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				serviceWorker: {
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+				miauth: {
+					type: 'boolean',
+					optional: true, nullable: false,
+					default: true,
+				},
+			},
+		},
+		proxyAccountName: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		requireSetup: {
+			type: 'boolean',
+			optional: false, nullable: false,
+			example: false,
+		},
+		cacheRemoteFiles: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		cacheRemoteSensitiveFiles: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+	},
+} as const;
+
+export const packedMetaDetailedSchema = {
+	type: 'object',
+	allOf: [
+		{
+			type: 'object',
+			ref: 'MetaLite',
+		},
+		{
+			type: 'object',
+			ref: 'MetaDetailedOnly',
+		},
+	],
+} as const;
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 834158baf41a..5460635e1d40 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -3,18 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm';
-import { Inject, Injectable } from '@nestjs/common';
-import JSON5 from 'json5';
-import type { AdsRepository } from '@/models/_.js';
-import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
+import { Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import { MetaService } from '@/core/MetaService.js';
-import { InstanceActorService } from '@/core/InstanceActorService.js';
-import type { Config } from '@/config.js';
-import { DI } from '@/di-symbols.js';
-import { DEFAULT_POLICIES } from '@/core/RoleService.js';
+import { MetaEntityService } from '@/core/entities/MetaEntityService.js';
 
 export const meta = {
 	tags: ['meta'],
@@ -23,297 +14,10 @@ export const meta = {
 
 	res: {
 		type: 'object',
-		optional: false, nullable: false,
-		properties: {
-			maintainerName: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			maintainerEmail: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			version: {
-				type: 'string',
-				optional: false, nullable: false,
-			},
-			providesTarball: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			name: {
-				type: 'string',
-				optional: false, nullable: false,
-			},
-			shortName: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			uri: {
-				type: 'string',
-				optional: false, nullable: false,
-				format: 'url',
-				example: 'https://misskey.example.com',
-			},
-			description: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			langs: {
-				type: 'array',
-				optional: false, nullable: false,
-				items: {
-					type: 'string',
-					optional: false, nullable: false,
-				},
-			},
-			tosUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			repositoryUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-				default: 'https://github.com/misskey-dev/misskey',
-			},
-			feedbackUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-				default: 'https://github.com/misskey-dev/misskey/issues/new',
-			},
-			defaultDarkTheme: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			defaultLightTheme: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			disableRegistration: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			cacheRemoteFiles: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			cacheRemoteSensitiveFiles: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			emailRequiredForSignup: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			enableHcaptcha: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			hcaptchaSiteKey: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			enableMcaptcha: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			mcaptchaSiteKey: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			mcaptchaInstanceUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			enableRecaptcha: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			recaptchaSiteKey: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			enableTurnstile: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			turnstileSiteKey: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			swPublickey: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			mascotImageUrl: {
-				type: 'string',
-				optional: false, nullable: false,
-				default: '/assets/ai.png',
-			},
-			bannerUrl: {
-				type: 'string',
-				optional: false, nullable: false,
-			},
-			serverErrorImageUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			infoImageUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			notFoundImageUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			iconUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			maxNoteTextLength: {
-				type: 'number',
-				optional: false, nullable: false,
-			},
-			ads: {
-				type: 'array',
-				optional: false, nullable: false,
-				items: {
-					type: 'object',
-					optional: false, nullable: false,
-					properties: {
-						id: {
-							type: 'string',
-							optional: false, nullable: false,
-							format: 'id',
-							example: 'xxxxxxxxxx',
-						},
-						url: {
-							type: 'string',
-							optional: false, nullable: false,
-							format: 'url',
-						},
-						place: {
-							type: 'string',
-							optional: false, nullable: false,
-						},
-						ratio: {
-							type: 'number',
-							optional: false, nullable: false,
-						},
-						imageUrl: {
-							type: 'string',
-							optional: false, nullable: false,
-							format: 'url',
-						},
-						dayOfWeek: {
-							type: 'integer',
-							optional: false, nullable: false,
-						},
-					},
-				},
-			},
-			notesPerOneAd: {
-				type: 'number',
-				optional: false, nullable: false,
-				default: 0,
-			},
-			requireSetup: {
-				type: 'boolean',
-				optional: false, nullable: false,
-				example: false,
-			},
-			enableEmail: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			enableServiceWorker: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			translatorAvailable: {
-				type: 'boolean',
-				optional: false, nullable: false,
-			},
-			proxyAccountName: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			mediaProxy: {
-				type: 'string',
-				optional: false, nullable: false,
-			},
-			features: {
-				type: 'object',
-				optional: true, nullable: false,
-				properties: {
-					registration: {
-						type: 'boolean',
-						optional: false, nullable: false,
-					},
-					localTimeline: {
-						type: 'boolean',
-						optional: false, nullable: false,
-					},
-					globalTimeline: {
-						type: 'boolean',
-						optional: false, nullable: false,
-					},
-					hcaptcha: {
-						type: 'boolean',
-						optional: false, nullable: false,
-					},
-					recaptcha: {
-						type: 'boolean',
-						optional: false, nullable: false,
-					},
-					objectStorage: {
-						type: 'boolean',
-						optional: false, nullable: false,
-					},
-					serviceWorker: {
-						type: 'boolean',
-						optional: false, nullable: false,
-					},
-					miauth: {
-						type: 'boolean',
-						optional: true, nullable: false,
-						default: true,
-					},
-				},
-			},
-			backgroundImageUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			impressumUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			logoImageUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			privacyPolicyUrl: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			serverRules: {
-				type: 'array',
-				optional: false, nullable: false,
-				items: {
-					type: 'string',
-				},
-			},
-			themeColor: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
-			policies: {
-				type: 'object',
-				optional: false, nullable: false,
-				ref: 'RolePolicies',
-			},
-		},
+		oneOf: [
+			{ type: 'object', ref: 'MetaLite' },
+			{ type: 'object', ref: 'MetaDetailed' },
+		],
 	},
 } as const;
 
@@ -328,115 +32,10 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
-		@Inject(DI.config)
-		private config: Config,
-
-		@Inject(DI.adsRepository)
-		private adsRepository: AdsRepository,
-
-		private userEntityService: UserEntityService,
-		private metaService: MetaService,
-		private instanceActorService: InstanceActorService,
+		private metaEntityService: MetaEntityService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const instance = await this.metaService.fetch(true);
-
-			const ads = await this.adsRepository.createQueryBuilder('ads')
-				.where('ads.expiresAt > :now', { now: new Date() })
-				.andWhere('ads.startsAt <= :now', { now: new Date() })
-				.andWhere(new Brackets(qb => {
-					// 曜日のビットフラグを確認する
-					qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() })
-						.orWhere('ads.dayOfWeek = 0');
-				}))
-				.getMany();
-
-			const response: any = {
-				maintainerName: instance.maintainerName,
-				maintainerEmail: instance.maintainerEmail,
-
-				version: this.config.version,
-				providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl,
-
-				name: instance.name,
-				shortName: instance.shortName,
-				uri: this.config.url,
-				description: instance.description,
-				langs: instance.langs,
-				tosUrl: instance.termsOfServiceUrl,
-				repositoryUrl: instance.repositoryUrl,
-				feedbackUrl: instance.feedbackUrl,
-				impressumUrl: instance.impressumUrl,
-				privacyPolicyUrl: instance.privacyPolicyUrl,
-				disableRegistration: instance.disableRegistration,
-				emailRequiredForSignup: instance.emailRequiredForSignup,
-				enableHcaptcha: instance.enableHcaptcha,
-				hcaptchaSiteKey: instance.hcaptchaSiteKey,
-				enableMcaptcha: instance.enableMcaptcha,
-				mcaptchaSiteKey: instance.mcaptchaSitekey,
-				mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl,
-				enableRecaptcha: instance.enableRecaptcha,
-				recaptchaSiteKey: instance.recaptchaSiteKey,
-				enableTurnstile: instance.enableTurnstile,
-				turnstileSiteKey: instance.turnstileSiteKey,
-				swPublickey: instance.swPublicKey,
-				themeColor: instance.themeColor,
-				mascotImageUrl: instance.mascotImageUrl,
-				bannerUrl: instance.bannerUrl,
-				infoImageUrl: instance.infoImageUrl,
-				serverErrorImageUrl: instance.serverErrorImageUrl,
-				notFoundImageUrl: instance.notFoundImageUrl,
-				iconUrl: instance.iconUrl,
-				backgroundImageUrl: instance.backgroundImageUrl,
-				logoImageUrl: instance.logoImageUrl,
-				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
-				// クライアントの手間を減らすためあらかじめJSONに変換しておく
-				defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null,
-				defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null,
-				ads: ads.map(ad => ({
-					id: ad.id,
-					url: ad.url,
-					place: ad.place,
-					ratio: ad.ratio,
-					imageUrl: ad.imageUrl,
-					dayOfWeek: ad.dayOfWeek,
-				})),
-				notesPerOneAd: instance.notesPerOneAd,
-				enableEmail: instance.enableEmail,
-				enableServiceWorker: instance.enableServiceWorker,
-
-				translatorAvailable: instance.deeplAuthKey != null,
-
-				serverRules: instance.serverRules,
-
-				policies: { ...DEFAULT_POLICIES, ...instance.policies },
-
-				mediaProxy: this.config.mediaProxy,
-
-				...(ps.detail ? {
-					cacheRemoteFiles: instance.cacheRemoteFiles,
-					cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
-					requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
-				} : {}),
-			};
-
-			if (ps.detail) {
-				const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null;
-
-				response.proxyAccountName = proxyAccount ? proxyAccount.username : null;
-				response.features = {
-					registration: !instance.disableRegistration,
-					emailRequiredForSignup: instance.emailRequiredForSignup,
-					hcaptcha: instance.enableHcaptcha,
-					recaptcha: instance.enableRecaptcha,
-					turnstile: instance.enableTurnstile,
-					objectStorage: instance.useObjectStorage,
-					serviceWorker: instance.enableServiceWorker,
-					miauth: true,
-				};
-			}
-
-			return response;
+			return ps.detail ? await this.metaEntityService.packDetailed() : await this.metaEntityService.pack();
 		});
 	}
 }
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index f255e28fc2b6..e8908f50ec49 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -28,6 +28,7 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { PageEntityService } from '@/core/entities/PageEntityService.js';
+import { MetaEntityService } from '@/core/entities/MetaEntityService.js';
 import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
 import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
 import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
@@ -93,6 +94,7 @@ export class ClientServerService {
 		private userEntityService: UserEntityService,
 		private noteEntityService: NoteEntityService,
 		private pageEntityService: PageEntityService,
+		private metaEntityService: MetaEntityService,
 		private galleryPostEntityService: GalleryPostEntityService,
 		private clipEntityService: ClipEntityService,
 		private channelEntityService: ChannelEntityService,
@@ -173,7 +175,7 @@ export class ClientServerService {
 	}
 
 	@bindThis
-	private generateCommonPugData(meta: MiMeta) {
+	private async generateCommonPugData(meta: MiMeta) {
 		return {
 			instanceName: meta.name ?? 'Misskey',
 			icon: meta.iconUrl,
@@ -183,6 +185,8 @@ export class ClientServerService {
 			infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
 			notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg',
 			instanceUrl: this.config.url,
+			metaJson: JSON.stringify(await this.metaEntityService.packDetailed(meta)),
+			now: Date.now(),
 		};
 	}
 
@@ -433,7 +437,7 @@ export class ClientServerService {
 				url: this.config.url,
 				title: meta.name ?? 'Misskey',
 				desc: meta.description,
-				...this.generateCommonPugData(meta),
+				...await this.generateCommonPugData(meta),
 			});
 		};
 
@@ -520,7 +524,7 @@ export class ClientServerService {
 					user, profile, me,
 					avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
 					sub: request.params.sub,
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				// リモートユーザーなので
@@ -570,7 +574,7 @@ export class ClientServerService {
 					avatarUrl: _note.user.avatarUrl,
 					// TODO: Let locale changeable by instance setting
 					summary: getNoteSummary(_note),
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				return await renderBase(reply);
@@ -609,7 +613,7 @@ export class ClientServerService {
 					page: _page,
 					profile,
 					avatarUrl: _page.user.avatarUrl,
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				return await renderBase(reply);
@@ -635,7 +639,7 @@ export class ClientServerService {
 					flash: _flash,
 					profile,
 					avatarUrl: _flash.user.avatarUrl,
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				return await renderBase(reply);
@@ -661,7 +665,7 @@ export class ClientServerService {
 					clip: _clip,
 					profile,
 					avatarUrl: _clip.user.avatarUrl,
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				return await renderBase(reply);
@@ -685,7 +689,7 @@ export class ClientServerService {
 					post: _post,
 					profile,
 					avatarUrl: _post.user.avatarUrl,
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				return await renderBase(reply);
@@ -704,7 +708,7 @@ export class ClientServerService {
 				reply.header('Cache-Control', 'public, max-age=15');
 				return await reply.view('channel', {
 					channel: _channel,
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				return await renderBase(reply);
@@ -723,7 +727,7 @@ export class ClientServerService {
 				reply.header('Cache-Control', 'public, max-age=3600');
 				return await reply.view('reversi-game', {
 					game: _game,
-					...this.generateCommonPugData(meta),
+					...await this.generateCommonPugData(meta),
 				});
 			} else {
 				return await renderBase(reply);
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index d167afe1e8e9..123336809be0 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -68,6 +68,9 @@ html
 			var VERSION = "#{version}";
 			var CLIENT_ENTRY = "#{clientEntry.file}";
 
+		script(type='application/json' id='misskey_meta' data-generated-at=now)
+			!= metaJson
+
 		script
 			include ../boot.js
 
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index b19d45a35e87..61f04678bff0 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -11,7 +11,7 @@ import { alert, confirm, popup, post, toast } from '@/os.js';
 import { useStream } from '@/stream.js';
 import * as sound from '@/scripts/sound.js';
 import { $i, signout, updateAccount } from '@/account.js';
-import { fetchInstance, instance } from '@/instance.js';
+import { instance } from '@/instance.js';
 import { ColdDeviceStorage, defaultStore } from '@/store.js';
 import { makeHotkey } from '@/scripts/hotkey.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
@@ -235,12 +235,10 @@ export async function mainBoot() {
 			}
 		}
 
-		fetchInstance().then(() => {
-			const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read');
-			if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') {
-				popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed');
-			}
-		});
+		const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read');
+		if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') {
+			popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed');
+		}
 
 		if ('Notification' in window) {
 			// 許可を得ていなかったらリクエスト
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index 205602369298..4232cbcd78d5 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -11,13 +11,24 @@ import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERR
 
 // TODO: 他のタブと永続化されたstateを同期
 
-const cached = miLocalStorage.getItem('instance');
+//#region loader
+const providedMetaEl = document.getElementById('misskey_meta');
+
+let cachedMeta = miLocalStorage.getItem('instance') ? JSON.parse(miLocalStorage.getItem('instance')!) : null;
+let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
+const providedMeta = providedMetaEl && providedMetaEl.textContent ? JSON.parse(providedMetaEl.textContent) : null;
+const providedAt = providedMetaEl && providedMetaEl.dataset.generatedAt ? parseInt(providedMetaEl.dataset.generatedAt) : 0;
+if (providedAt > cachedAt) {
+	miLocalStorage.setItem('instance', JSON.stringify(providedMeta));
+	miLocalStorage.setItem('instanceCachedAt', providedAt.toString());
+	cachedMeta = providedMeta;
+	cachedAt = providedAt;
+}
+//#endregion
 
 // TODO: instanceをリアクティブにするかは再考の余地あり
 
-export const instance: Misskey.entities.MetaResponse = reactive(cached ? JSON.parse(cached) : {
-	// TODO: set default values
-});
+export const instance: Misskey.entities.MetaResponse = reactive(cachedMeta ?? {});
 
 export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL);
 
@@ -25,7 +36,15 @@ export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO
 
 export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
 
-export async function fetchInstance() {
+export async function fetchInstance(force = false): Promise<void> {
+	if (!force) {
+		const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
+
+		if (Date.now() - cachedAt < 1000 * 60 * 60) {
+			return;
+		}
+	}
+
 	const meta = await misskeyApi('meta', {
 		detail: false,
 	});
@@ -35,4 +54,5 @@ export async function fetchInstance() {
 	}
 
 	miLocalStorage.setItem('instance', JSON.stringify(instance));
+	miLocalStorage.setItem('instanceCachedAt', Date.now().toString());
 }
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 3de81c9bb9dc..8029bca68d53 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -7,6 +7,7 @@ type Keys =
 	'v' |
 	'lastVersion' |
 	'instance' |
+	'instanceCachedAt' |
 	'account' |
 	'accounts' |
 	'latestDonationInfoShownAt' |
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index e5e04fdeb8a6..73c5e1919f41 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -142,7 +142,7 @@ function save() {
 		turnstileSiteKey: turnstileSiteKey.value,
 		turnstileSecretKey: turnstileSecretKey.value,
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 </script>
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 2b559f92c9bd..fe1b7c561daf 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -169,7 +169,7 @@ function save() {
 		feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value,
 		manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 839b9bee160e..4a858887f353 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -124,7 +124,7 @@ function save() {
 		smtpUser: smtpUser.value,
 		smtpPass: smtpPass.value,
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index ba3eb05e72ce..e0b82eb02ea0 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -61,7 +61,7 @@ function save() {
 		deeplAuthKey: deeplAuthKey.value,
 		deeplIsPro: deeplIsPro.value,
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index 5167b2e6b219..6b14bd42c2af 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -50,7 +50,7 @@ function save() {
 		silencedHosts: silencedHosts.value.split('\n') || [],
 
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index d6cb1e39a76a..9efb34ac9a01 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -110,7 +110,7 @@ function save() {
 		hiddenTags: hiddenTags.value.split('\n'),
 		preservedUsernames: preservedUsernames.value.split('\n'),
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 4ff5ab09ca48..5fddb715cd5c 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -143,7 +143,7 @@ function save() {
 		objectStorageSetPublicRead: objectStorageSetPublicRead.value,
 		objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue
index 651f0ef93692..345cf333b51f 100644
--- a/packages/frontend/src/pages/admin/other-settings.vue
+++ b/packages/frontend/src/pages/admin/other-settings.vue
@@ -73,7 +73,7 @@ function save() {
 		enableChartsForRemoteUser: enableChartsForRemoteUser.value,
 		enableChartsForFederatedInstances: enableChartsForFederatedInstances.value,
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index 02b506d13dae..81db9f1da9c2 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -56,7 +56,7 @@ function save() {
 	os.apiWithDialog('admin/update-meta', {
 		proxyAccountId: proxyAccountId.value,
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index cadcf5a8cc6d..c4745978dfb6 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -196,7 +196,7 @@ async function init() {
 	enableTruemailApi.value = meta.enableTruemailApi;
 	truemailInstance.value = meta.truemailInstance;
 	truemailAuthKey.value = meta.truemailAuthKey;
-	bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || "";
+	bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || '';
 }
 
 function save() {
@@ -221,7 +221,7 @@ function save() {
 		truemailAuthKey: truemailAuthKey.value,
 		bannedEmailDomains: bannedEmailDomains.value.split('\n'),
 	}).then(() => {
-		fetchInstance();
+		fetchInstance(true);
 	});
 }
 
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 87318bccce00..ff9b8d62993a 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -58,7 +58,7 @@ const save = async () => {
 	await os.apiWithDialog('admin/update-meta', {
 		serverRules: serverRules.value,
 	});
-	fetchInstance();
+	fetchInstance(true);
 };
 
 const remove = (index: number): void => {
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index c505d70aa980..9a198ee8a343 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -243,7 +243,7 @@ async function save(): void {
 		notesPerOneAd: notesPerOneAd.value,
 	});
 
-	fetchInstance();
+	fetchInstance(true);
 }
 
 const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts
index f2db87c4fb06..b20109ec72bf 100644
--- a/packages/frontend/src/scripts/clear-cache.ts
+++ b/packages/frontend/src/scripts/clear-cache.ts
@@ -2,14 +2,18 @@ import { unisonReload } from '@/scripts/unison-reload.js';
 import * as os from '@/os.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { fetchCustomEmojis } from '@/custom-emojis.js';
+import { fetchInstance } from '@/instance.js';
 
 export async function clearCache() {
 	os.waiting();
+	miLocalStorage.removeItem('instance');
+	miLocalStorage.removeItem('instanceCachedAt');
 	miLocalStorage.removeItem('locale');
 	miLocalStorage.removeItem('localeVersion');
 	miLocalStorage.removeItem('theme');
 	miLocalStorage.removeItem('emojis');
 	miLocalStorage.removeItem('lastEmojisFetchedAt');
+	await fetchInstance(true);
 	await fetchCustomEmojis(true);
 	unisonReload();
 }
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index c2428910fa5d..a2d5a4f5144e 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1715,7 +1715,10 @@ declare namespace entities {
         Role,
         RolePolicies,
         ReversiGameLite,
-        ReversiGameDetailed
+        ReversiGameDetailed,
+        MetaLite,
+        MetaDetailedOnly,
+        MetaDetailed
     }
 }
 export { entities }
@@ -2223,6 +2226,15 @@ type MeDetailed = components['schemas']['MeDetailed'];
 // @public (undocumented)
 type MeDetailedOnly = components['schemas']['MeDetailedOnly'];
 
+// @public (undocumented)
+type MetaDetailed = components['schemas']['MetaDetailed'];
+
+// @public (undocumented)
+type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
+
+// @public (undocumented)
+type MetaLite = components['schemas']['MetaLite'];
+
 // @public (undocumented)
 type MetaRequest = operations['meta']['requestBody']['content']['application/json'];
 
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 6400567a2dab..ab49f9478a1e 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -46,3 +46,6 @@ export type Role = components['schemas']['Role'];
 export type RolePolicies = components['schemas']['RolePolicies'];
 export type ReversiGameLite = components['schemas']['ReversiGameLite'];
 export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
+export type MetaLite = components['schemas']['MetaLite'];
+export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
+export type MetaDetailed = components['schemas']['MetaDetailed'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 0b2a88b53787..18bc45b983df 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4724,6 +4724,96 @@ export type components = {
       logs: number[][];
       map: string[];
     };
+    MetaLite: {
+      maintainerName: string | null;
+      maintainerEmail: string | null;
+      version: string;
+      providesTarball: boolean;
+      name: string | null;
+      shortName: string | null;
+      /**
+       * Format: url
+       * @example https://misskey.example.com
+       */
+      uri: string;
+      description: string | null;
+      langs: string[];
+      tosUrl: string | null;
+      /** @default https://github.com/misskey-dev/misskey */
+      repositoryUrl: string | null;
+      /** @default https://github.com/misskey-dev/misskey/issues/new */
+      feedbackUrl: string | null;
+      defaultDarkTheme: string | null;
+      defaultLightTheme: string | null;
+      disableRegistration: boolean;
+      emailRequiredForSignup: boolean;
+      enableHcaptcha: boolean;
+      hcaptchaSiteKey: string | null;
+      enableMcaptcha: boolean;
+      mcaptchaSiteKey: string | null;
+      mcaptchaInstanceUrl: string | null;
+      enableRecaptcha: boolean;
+      recaptchaSiteKey: string | null;
+      enableTurnstile: boolean;
+      turnstileSiteKey: string | null;
+      swPublickey: string | null;
+      /** @default /assets/ai.png */
+      mascotImageUrl: string;
+      bannerUrl: string | null;
+      serverErrorImageUrl: string | null;
+      infoImageUrl: string | null;
+      notFoundImageUrl: string | null;
+      iconUrl: string | null;
+      maxNoteTextLength: number;
+      ads: {
+          /**
+           * Format: id
+           * @example xxxxxxxxxx
+           */
+          id: string;
+          /** Format: url */
+          url: string;
+          place: string;
+          ratio: number;
+          /** Format: url */
+          imageUrl: string;
+          dayOfWeek: number;
+        }[];
+      /** @default 0 */
+      notesPerOneAd: number;
+      enableEmail: boolean;
+      enableServiceWorker: boolean;
+      translatorAvailable: boolean;
+      mediaProxy: string;
+      backgroundImageUrl: string | null;
+      impressumUrl: string | null;
+      logoImageUrl: string | null;
+      privacyPolicyUrl: string | null;
+      serverRules: string[];
+      themeColor: string | null;
+      policies: components['schemas']['RolePolicies'];
+    };
+    MetaDetailedOnly: {
+      features?: {
+        registration: boolean;
+        emailRequiredForSignup: boolean;
+        localTimeline: boolean;
+        globalTimeline: boolean;
+        hcaptcha: boolean;
+        turnstile: boolean;
+        recaptcha: boolean;
+        objectStorage: boolean;
+        serviceWorker: boolean;
+        /** @default true */
+        miauth?: boolean;
+      };
+      proxyAccountName: string | null;
+      /** @example false */
+      requireSetup: boolean;
+      cacheRemoteFiles: boolean;
+      cacheRemoteSensitiveFiles: boolean;
+    };
+    MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly'];
   };
   responses: never;
   parameters: never;
@@ -19448,91 +19538,7 @@ export type operations = {
       /** @description OK (with results) */
       200: {
         content: {
-          'application/json': {
-            maintainerName: string | null;
-            maintainerEmail: string | null;
-            version: string;
-            providesTarball: boolean;
-            name: string;
-            shortName: string | null;
-            /**
-             * Format: url
-             * @example https://misskey.example.com
-             */
-            uri: string;
-            description: string | null;
-            langs: string[];
-            tosUrl: string | null;
-            /** @default https://github.com/misskey-dev/misskey */
-            repositoryUrl: string | null;
-            /** @default https://github.com/misskey-dev/misskey/issues/new */
-            feedbackUrl: string | null;
-            defaultDarkTheme: string | null;
-            defaultLightTheme: string | null;
-            disableRegistration: boolean;
-            cacheRemoteFiles: boolean;
-            cacheRemoteSensitiveFiles: boolean;
-            emailRequiredForSignup: boolean;
-            enableHcaptcha: boolean;
-            hcaptchaSiteKey: string | null;
-            enableMcaptcha: boolean;
-            mcaptchaSiteKey: string | null;
-            mcaptchaInstanceUrl: string | null;
-            enableRecaptcha: boolean;
-            recaptchaSiteKey: string | null;
-            enableTurnstile: boolean;
-            turnstileSiteKey: string | null;
-            swPublickey: string | null;
-            /** @default /assets/ai.png */
-            mascotImageUrl: string;
-            bannerUrl: string;
-            serverErrorImageUrl: string | null;
-            infoImageUrl: string | null;
-            notFoundImageUrl: string | null;
-            iconUrl: string | null;
-            maxNoteTextLength: number;
-            ads: {
-                /**
-                 * Format: id
-                 * @example xxxxxxxxxx
-                 */
-                id: string;
-                /** Format: url */
-                url: string;
-                place: string;
-                ratio: number;
-                /** Format: url */
-                imageUrl: string;
-                dayOfWeek: number;
-              }[];
-            /** @default 0 */
-            notesPerOneAd: number;
-            /** @example false */
-            requireSetup: boolean;
-            enableEmail: boolean;
-            enableServiceWorker: boolean;
-            translatorAvailable: boolean;
-            proxyAccountName: string | null;
-            mediaProxy: string;
-            features?: {
-              registration: boolean;
-              localTimeline: boolean;
-              globalTimeline: boolean;
-              hcaptcha: boolean;
-              recaptcha: boolean;
-              objectStorage: boolean;
-              serviceWorker: boolean;
-              /** @default true */
-              miauth?: boolean;
-            };
-            backgroundImageUrl: string | null;
-            impressumUrl: string | null;
-            logoImageUrl: string | null;
-            privacyPolicyUrl: string | null;
-            serverRules: string[];
-            themeColor: string | null;
-            policies: components['schemas']['RolePolicies'];
-          };
+          'application/json': components['schemas']['MetaLite'] | components['schemas']['MetaDetailed'];
         };
       };
       /** @description Client error */

From 080a3c20bd7f7d6ca7a30fa5a94d8431a6a9c688 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 23 Feb 2024 14:10:13 +0900
Subject: [PATCH 018/266] =?UTF-8?q?fix:=20SSR=E6=99=82=E3=81=AEmeta?=
 =?UTF-8?q?=E3=82=92=E3=82=A8=E3=82=B9=E3=82=B1=E3=83=BC=E3=83=97=E3=81=99?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13440)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: SSR時のmetaをエスケープするように

* エスケープ方法を変更
---
 packages/backend/package.json                     |  2 ++
 .../backend/src/server/web/ClientServerService.ts |  4 ++--
 pnpm-lock.yaml                                    | 15 +++++++++++++++
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 3a3d8e04110b..1745277b4125 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -118,6 +118,7 @@
 		"got": "14.1.0",
 		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
+		"htmlescape": "^1.1.1",
 		"http-link-header": "1.1.1",
 		"ioredis": "5.3.2",
 		"ip-cidr": "3.1.0",
@@ -194,6 +195,7 @@
 		"@types/color-convert": "2.0.3",
 		"@types/content-disposition": "0.5.8",
 		"@types/fluent-ffmpeg": "2.1.24",
+		"@types/htmlescape": "^1.1.3",
 		"@types/http-link-header": "1.0.5",
 		"@types/jest": "29.5.11",
 		"@types/js-yaml": "4.0.9",
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index e8908f50ec49..b1af0c3df652 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -19,6 +19,7 @@ import fastifyView from '@fastify/view';
 import fastifyCookie from '@fastify/cookie';
 import fastifyProxy from '@fastify/http-proxy';
 import vary from 'vary';
+import htmlSafeJsonStringify from 'htmlescape';
 import type { Config } from '@/config.js';
 import { getNoteSummary } from '@/misc/get-note-summary.js';
 import { DI } from '@/di-symbols.js';
@@ -34,7 +35,6 @@ import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
 import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
 import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
 import type Logger from '@/logger.js';
-import { deepClone } from '@/misc/clone.js';
 import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
 import { bindThis } from '@/decorators.js';
 import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
@@ -185,7 +185,7 @@ export class ClientServerService {
 			infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
 			notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg',
 			instanceUrl: this.config.url,
-			metaJson: JSON.stringify(await this.metaEntityService.packDetailed(meta)),
+			metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)),
 			now: Date.now(),
 		};
 	}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d7b2fb1f2f15..ca86ad0445bf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -227,6 +227,9 @@ importers:
       hpagent:
         specifier: 1.2.0
         version: 1.2.0
+      htmlescape:
+        specifier: ^1.1.1
+        version: 1.1.1
       http-link-header:
         specifier: 1.1.1
         version: 1.1.1
@@ -538,6 +541,9 @@ importers:
       '@types/fluent-ffmpeg':
         specifier: 2.1.24
         version: 2.1.24
+      '@types/htmlescape':
+        specifier: ^1.1.3
+        version: 1.1.3
       '@types/http-link-header':
         specifier: 1.0.5
         version: 1.0.5
@@ -7405,6 +7411,10 @@ packages:
       '@types/unist': 3.0.2
     dev: true
 
+  /@types/htmlescape@1.1.3:
+    resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==}
+    dev: true
+
   /@types/http-cache-semantics@4.0.4:
     resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
 
@@ -12456,6 +12466,11 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /htmlescape@1.1.1:
+    resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==}
+    engines: {node: '>=0.10'}
+    dev: false
+
   /htmlparser2@8.0.1:
     resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
     dependencies:

From 64953fadc92abf3fd83186733282bcf4f39bdb49 Mon Sep 17 00:00:00 2001
From: okayurisotto <47853651+okayurisotto@users.noreply.github.com>
Date: Fri, 23 Feb 2024 14:12:57 +0900
Subject: [PATCH 019/266] =?UTF-8?q?refactor(backend):=20`Array.prototype.f?=
 =?UTF-8?q?ilter`=E3=81=A7=E3=81=AE=E9=9D=9Enull=E7=A2=BA=E8=AA=8D?=
 =?UTF-8?q?=E3=81=A7=E3=81=AF`isNotNull`=E9=96=A2=E6=95=B0=E3=82=92?=
 =?UTF-8?q?=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=20(#13442)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* `Array.prototype.filter`での非null確認では`isNotNull`関数を使うように

* `{}` -> `NonNullable<unknown>`
---
 packages/backend/src/core/NoteCreateService.ts                | 3 ++-
 packages/backend/src/core/activitypub/ApAudienceService.ts    | 3 ++-
 packages/backend/src/core/activitypub/ApInboxService.ts       | 3 ++-
 packages/backend/src/core/activitypub/ApRendererService.ts    | 2 +-
 .../backend/src/core/activitypub/models/ApMentionService.ts   | 3 ++-
 packages/backend/src/core/activitypub/models/ApNoteService.ts | 3 ++-
 .../backend/src/core/activitypub/models/ApPersonService.ts    | 3 ++-
 .../backend/src/core/activitypub/models/ApQuestionService.ts  | 3 ++-
 packages/backend/src/core/activitypub/models/tag.ts           | 3 ++-
 packages/backend/src/core/entities/DriveFileEntityService.ts  | 2 +-
 packages/backend/src/core/entities/PageEntityService.ts       | 3 ++-
 packages/backend/src/core/entities/UserEntityService.ts       | 3 ++-
 packages/backend/src/misc/is-not-null.ts                      | 4 +---
 .../backend/src/server/api/endpoints/gallery/posts/create.ts  | 3 ++-
 .../backend/src/server/api/endpoints/gallery/posts/update.ts  | 3 ++-
 packages/backend/src/server/api/endpoints/pinned-users.ts     | 3 ++-
 16 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 2a5fd2e1a6c3..b412d5db113e 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -59,6 +59,7 @@ import { UtilityService } from '@/core/UtilityService.js';
 import { UserBlockingService } from '@/core/UserBlockingService.js';
 import { isReply } from '@/misc/is-reply.js';
 import { trackPromise } from '@/misc/promise-tracker.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { IdentifiableError } from '@/misc/identifiable-error.js';
 
 type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@@ -816,7 +817,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		const mentions = extractMentions(tokens);
 		let mentionedUsers = (await Promise.all(mentions.map(m =>
 			this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null),
-		))).filter(x => x != null) as MiUser[];
+		))).filter(isNotNull);
 
 		// Drop duplicate users
 		mentionedUsers = mentionedUsers.filter((u, i, self) =>
diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts
index d47be79441a1..0fccc7b95086 100644
--- a/packages/backend/src/core/activitypub/ApAudienceService.ts
+++ b/packages/backend/src/core/activitypub/ApAudienceService.ts
@@ -8,6 +8,7 @@ import promiseLimit from 'promise-limit';
 import type { MiRemoteUser, MiUser } from '@/models/User.js';
 import { concat, unique } from '@/misc/prelude/array.js';
 import { bindThis } from '@/decorators.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { getApIds } from './type.js';
 import { ApPersonService } from './models/ApPersonService.js';
 import type { ApObject } from './type.js';
@@ -40,7 +41,7 @@ export class ApAudienceService {
 		const limit = promiseLimit<MiUser | null>(2);
 		const mentionedUsers = (await Promise.all(
 			others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))),
-		)).filter((x): x is MiUser => x != null);
+		)).filter(isNotNull);
 
 		if (toGroups.public.length > 0) {
 			return {
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 1cc54b6ff640..8d9cd74a252d 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -27,6 +27,7 @@ import { QueueService } from '@/core/QueueService.js';
 import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import type { MiRemoteUser } from '@/models/User.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
 import { ApNoteService } from './models/ApNoteService.js';
 import { ApLoggerService } from './ApLoggerService.js';
@@ -521,7 +522,7 @@ export class ApInboxService {
 		const userIds = uris
 			.filter(uri => uri.startsWith(this.config.url + '/users/'))
 			.map(uri => uri.split('/').at(-1))
-			.filter((userId): userId is string => userId !== undefined);
+			.filter(isNotNull);
 		const users = await this.usersRepository.findBy({
 			id: In(userIds),
 		});
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 494622909ade..d7fb977a99d3 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -315,7 +315,7 @@ export class ApRendererService {
 		const getPromisedFiles = async (ids: string[]): Promise<MiDriveFile[]> => {
 			if (ids.length === 0) return [];
 			const items = await this.driveFilesRepository.findBy({ id: In(ids) });
-			return ids.map(id => items.find(item => item.id === id)).filter((item): item is MiDriveFile => item != null);
+			return ids.map(id => items.find(item => item.id === id)).filter(isNotNull);
 		};
 
 		let inReplyTo;
diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts
index 73eea1edf0fb..0ced7e88aff2 100644
--- a/packages/backend/src/core/activitypub/models/ApMentionService.ts
+++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts
@@ -8,6 +8,7 @@ import promiseLimit from 'promise-limit';
 import type { MiUser } from '@/models/_.js';
 import { toArray, unique } from '@/misc/prelude/array.js';
 import { bindThis } from '@/decorators.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { isMention } from '../type.js';
 import { Resolver } from '../ApResolverService.js';
 import { ApPersonService } from './ApPersonService.js';
@@ -27,7 +28,7 @@ export class ApMentionService {
 		const limit = promiseLimit<MiUser | null>(2);
 		const mentionedUsers = (await Promise.all(
 			hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))),
-		)).filter((x): x is MiUser => x != null);
+		)).filter(isNotNull);
 
 		return mentionedUsers;
 	}
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 8da940721636..e201b881733e 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -37,6 +37,7 @@ import { ApQuestionService } from './ApQuestionService.js';
 import { ApImageService } from './ApImageService.js';
 import type { Resolver } from '../ApResolverService.js';
 import type { IObject, IPost } from '../type.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 
 @Injectable()
 export class ApNoteService {
@@ -221,7 +222,7 @@ export class ApNoteService {
 				}
 			};
 
-			const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string'));
+			const uris = unique([note._misskey_quote, note.quoteUrl].filter(isNotNull));
 			const results = await Promise.all(uris.map(tryResolveNote));
 
 			quote = results.filter((x): x is { status: 'ok', res: MiNote } => x.status === 'ok').map(x => x.res).at(0);
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index e80cd34a5682..744b1ea68376 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -38,6 +38,7 @@ import { MetaService } from '@/core/MetaService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
 import type { AccountMoveService } from '@/core/AccountMoveService.js';
 import { checkHttps } from '@/misc/check-https.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js';
 import { extractApHashtags } from './tag.js';
 import type { OnModuleInit } from '@nestjs/common';
@@ -636,7 +637,7 @@ export class ApPersonService implements OnModuleInit {
 
 			// とりあえずidを別の時間で生成して順番を維持
 			let td = 0;
-			for (const note of featuredNotes.filter((note): note is MiNote => note != null)) {
+			for (const note of featuredNotes.filter(isNotNull)) {
 				td -= 1000;
 				transactionalEntityManager.insert(MiUserNotePining, {
 					id: this.idService.gen(Date.now() + td),
diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
index e78b3a35999c..d1936cfe1dff 100644
--- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts
+++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
@@ -10,6 +10,7 @@ import type { Config } from '@/config.js';
 import type { IPoll } from '@/models/Poll.js';
 import type Logger from '@/logger.js';
 import { bindThis } from '@/decorators.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { isQuestion } from '../type.js';
 import { ApLoggerService } from '../ApLoggerService.js';
 import { ApResolverService } from '../ApResolverService.js';
@@ -51,7 +52,7 @@ export class ApQuestionService {
 
 		const choices = question[multiple ? 'anyOf' : 'oneOf']
 			?.map((x) => x.name)
-			.filter((x): x is string => typeof x === 'string')
+			.filter(isNotNull)
 			?? [];
 
 		const votes = question[multiple ? 'anyOf' : 'oneOf']?.map((x) => x.replies?.totalItems ?? x._misskey_votes ?? 0);
diff --git a/packages/backend/src/core/activitypub/models/tag.ts b/packages/backend/src/core/activitypub/models/tag.ts
index ced101b76440..e7ceec3262a3 100644
--- a/packages/backend/src/core/activitypub/models/tag.ts
+++ b/packages/backend/src/core/activitypub/models/tag.ts
@@ -4,6 +4,7 @@
  */
 
 import { toArray } from '@/misc/prelude/array.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { isHashtag } from '../type.js';
 import type { IObject, IApHashtag } from '../type.js';
 
@@ -15,7 +16,7 @@ export function extractApHashtags(tags: IObject | IObject[] | null | undefined):
 	return hashtags.map(tag => {
 		const m = tag.name.match(/^#(.+)/);
 		return m ? m[1] : null;
-	}).filter((x): x is string => x != null);
+	}).filter(isNotNull);
 }
 
 export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] {
diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts
index 50f1c49b4876..8affe2b3bf84 100644
--- a/packages/backend/src/core/entities/DriveFileEntityService.ts
+++ b/packages/backend/src/core/entities/DriveFileEntityService.ts
@@ -259,7 +259,7 @@ export class DriveFileEntityService {
 		options?: PackOptions,
 	): Promise<Packed<'DriveFile'>[]> {
 		const items = await Promise.all(files.map(f => this.packNullable(f, options)));
-		return items.filter((x): x is Packed<'DriveFile'> => x != null);
+		return items.filter(isNotNull);
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts
index fe7b137bd210..65c69a49a7ec 100644
--- a/packages/backend/src/core/entities/PageEntityService.ts
+++ b/packages/backend/src/core/entities/PageEntityService.ts
@@ -14,6 +14,7 @@ import type { MiPage } from '@/models/Page.js';
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { UserEntityService } from './UserEntityService.js';
 import { DriveFileEntityService } from './DriveFileEntityService.js';
 
@@ -102,7 +103,7 @@ export class PageEntityService {
 			script: page.script,
 			eyeCatchingImageId: page.eyeCatchingImageId,
 			eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null,
-			attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null)),
+			attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter(isNotNull)),
 			likedCount: page.likedCount,
 			isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined,
 		});
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index 53df32f210b8..14761357a5a8 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -25,6 +25,7 @@ import { IdService } from '@/core/IdService.js';
 import type { AnnouncementService } from '@/core/AnnouncementService.js';
 import type { CustomEmojiService } from '@/core/CustomEmojiService.js';
 import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import type { OnModuleInit } from '@nestjs/common';
 import type { NoteEntityService } from './NoteEntityService.js';
 import type { DriveFileEntityService } from './DriveFileEntityService.js';
@@ -384,7 +385,7 @@ export class UserEntityService implements OnModuleInit {
 				movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
 				alsoKnownAs: user.alsoKnownAs
 					? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null)))
-						.then(xs => xs.length === 0 ? null : xs.filter(x => x != null) as string[])
+						.then(xs => xs.length === 0 ? null : xs.filter(isNotNull))
 					: null,
 				createdAt: this.idService.parse(user.id).date.toISOString(),
 				updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
diff --git a/packages/backend/src/misc/is-not-null.ts b/packages/backend/src/misc/is-not-null.ts
index 584a09d35aee..8d9dc8bb396e 100644
--- a/packages/backend/src/misc/is-not-null.ts
+++ b/packages/backend/src/misc/is-not-null.ts
@@ -3,8 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-// we are using {} as "any non-nullish value" as expected
-// eslint-disable-next-line @typescript-eslint/ban-types
-export function isNotNull<T extends {}>(input: T | undefined | null): input is T {
+export function isNotNull<T extends NonNullable<unknown>>(input: T | undefined | null): input is T {
 	return input != null;
 }
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
index 784ae5088fd9..b07cdf1ed979 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
@@ -12,6 +12,7 @@ import type { MiDriveFile } from '@/models/DriveFile.js';
 import { IdService } from '@/core/IdService.js';
 import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
 import { DI } from '@/di-symbols.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 
 export const meta = {
 	tags: ['gallery'],
@@ -69,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					id: fileId,
 					userId: me.id,
 				}),
-			))).filter((file): file is MiDriveFile => file != null);
+			))).filter(isNotNull);
 
 			if (files.length === 0) {
 				throw new Error();
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
index 8872b261dd17..8bd83ff5ba0a 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
@@ -10,6 +10,7 @@ import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
 import { DI } from '@/di-symbols.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 
 export const meta = {
 	tags: ['gallery'],
@@ -67,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					id: fileId,
 					userId: me.id,
 				}),
-			))).filter((file): file is MiDriveFile => file != null);
+			))).filter(isNotNull);
 
 			if (files.length === 0) {
 				throw new Error();
diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts
index 1f4509764fa7..784766bcb551 100644
--- a/packages/backend/src/server/api/endpoints/pinned-users.ts
+++ b/packages/backend/src/server/api/endpoints/pinned-users.ts
@@ -12,6 +12,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { MetaService } from '@/core/MetaService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { DI } from '@/di-symbols.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 
 export const meta = {
 	tags: ['users'],
@@ -52,7 +53,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				host: acct.host ?? IsNull(),
 			})));
 
-			return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { schema: 'UserDetailed' });
+			return await this.userEntityService.packMany(users.filter(isNotNull), me, { schema: 'UserDetailed' });
 		});
 	}
 }

From 30fe0726069a90fe1ced88a8e4fbdec19fe13078 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 23 Feb 2024 14:13:46 +0900
Subject: [PATCH 020/266] =?UTF-8?q?fix(test):=20Chromatic=E3=81=8C?=
 =?UTF-8?q?=E8=90=BD=E3=81=A1=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AE=E3=82=92?=
 =?UTF-8?q?=E4=B8=80=E9=83=A8=E4=BF=AE=E6=AD=A3=EF=BC=9F=20(#13435)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(test): Chromaticが落ちているのを修正?

* いらん変更をけす

* 未来過ぎた
---
 .../frontend/src/components/global/MkTime.stories.impl.ts    | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts
index 2b4b1485fdc8..8ddf8e213a4d 100644
--- a/packages/frontend/src/components/global/MkTime.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts
@@ -10,7 +10,7 @@ import MkTime from './MkTime.vue';
 import { i18n } from '@/i18n.js';
 import { dateTimeFormat } from '@/scripts/intl-const.js';
 const now = new Date('2023-04-01T00:00:00.000Z');
-const future = new Date(8640000000000000);
+const future = new Date('3000-04-01T00:00:00.000Z');
 const oneHourAgo = new Date(now.getTime() - 3600000);
 const oneDayAgo = new Date(now.getTime() - 86400000);
 const oneWeekAgo = new Date(now.getTime() - 604800000);
@@ -49,11 +49,12 @@ export const Empty = {
 export const RelativeFuture = {
 	...Empty,
 	async play({ canvasElement }) {
-		await expect(canvasElement).toHaveTextContent(i18n.ts._ago.future);
+		await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 977 }));
 	},
 	args: {
 		...Empty.args,
 		time: future,
+		origin: now,
 	},
 } satisfies StoryObj<typeof MkTime>;
 export const AbsoluteFuture = {

From a85fccaeea93d610d8cdd52def77851166a9391c Mon Sep 17 00:00:00 2001
From: 1Step621 <86859447+1STEP621@users.noreply.github.com>
Date: Fri, 23 Feb 2024 17:01:42 +0900
Subject: [PATCH 021/266] =?UTF-8?q?Fix(frontend):=20=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=82=AA=E3=83=BC=E3=83=88=E3=82=B3=E3=83=B3=E3=83=97?=
 =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=83=88=E3=81=AE=E5=84=AA=E5=85=88=E9=A0=86?=
 =?UTF-8?q?=E4=BD=8D=E3=81=8C=E3=81=8A=E3=81=8B=E3=81=97=E3=81=84=E3=81=AE?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13423)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 絵文字オートコンプリートの優先順位がおかしいのを修正

* update CHANGELOG.md

* テストを追加

* lint fix
---
 CHANGELOG.md                                  |   1 +
 .../src/components/MkAutocomplete.vue         |  96 +----------------
 packages/frontend/src/scripts/search-emoji.ts | 101 ++++++++++++++++++
 packages/frontend/test/autocomplete.test.ts   |  34 ++++++
 4 files changed, 138 insertions(+), 94 deletions(-)
 create mode 100644 packages/frontend/src/scripts/search-emoji.ts
 create mode 100644 packages/frontend/test/autocomplete.test.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ff5881df069..a939fa762169 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@
 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
 - Fix: チャートのラベルが消えている問題を修正
 - Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正
+- Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
 
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index 412325bfee7c..cae6bc711132 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -57,18 +57,7 @@ import { i18n } from '@/i18n.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { customEmojis } from '@/custom-emojis.js';
 import { MFM_TAGS, MFM_PARAMS } from '@/const.js';
-
-type EmojiDef = {
-	emoji: string;
-	name: string;
-	url: string;
-	aliasOf?: string;
-} | {
-	emoji: string;
-	name: string;
-	aliasOf?: string;
-	isCustomEmoji?: true;
-};
+import { searchEmoji, EmojiDef } from '@/scripts/search-emoji.js';
 
 const lib = emojilist.filter(x => x.category !== 'flags');
 
@@ -249,7 +238,7 @@ function exec() {
 			return;
 		}
 
-		emojis.value = emojiAutoComplete(props.q, emojiDb.value);
+		emojis.value = searchEmoji(props.q, emojiDb.value);
 	} else if (props.type === 'mfmTag') {
 		if (!props.q || props.q === '') {
 			mfmTags.value = MFM_TAGS;
@@ -267,87 +256,6 @@ function exec() {
 	}
 }
 
-type EmojiScore = { emoji: EmojiDef, score: number };
-
-function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] {
-	if (!query) {
-		return [];
-	}
-
-	const matched = new Map<string, EmojiScore>();
-	// 完全一致(エイリアス込み)
-	emojiDb.some(x => {
-		if (x.name === query && !matched.has(x.aliasOf ?? x.name)) {
-			matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 });
-		}
-		return matched.size === max;
-	});
-
-	// 前方一致(エイリアスなし)
-	if (matched.size < max) {
-		emojiDb.some(x => {
-			if (x.name.startsWith(query) && !x.aliasOf) {
-				matched.set(x.name, { emoji: x, score: query.length + 1 });
-			}
-			return matched.size === max;
-		});
-	}
-
-	// 前方一致(エイリアス込み)
-	if (matched.size < max) {
-		emojiDb.some(x => {
-			if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) {
-				matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length });
-			}
-			return matched.size === max;
-		});
-	}
-
-	// 部分一致(エイリアス込み)
-	if (matched.size < max) {
-		emojiDb.some(x => {
-			if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) {
-				matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 });
-			}
-			return matched.size === max;
-		});
-	}
-
-	// 簡易あいまい検索(3文字以上)
-	if (matched.size < max && query.length > 3) {
-		const queryChars = [...query];
-		const hitEmojis = new Map<string, EmojiScore>();
-
-		for (const x of emojiDb) {
-			// 文字列の位置を進めながら、クエリの文字を順番に探す
-
-			let pos = 0;
-			let hit = 0;
-			for (const c of queryChars) {
-				pos = x.name.indexOf(c, pos);
-				if (pos <= -1) break;
-				hit++;
-			}
-
-			// 半分以上の文字が含まれていればヒットとする
-			if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) {
-				hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 });
-			}
-		}
-
-		// ヒットしたものを全部追加すると雑多になるので、先頭の6件程度だけにしておく(6件=オートコンプリートのポップアップのサイズ分)
-		[...hitEmojis.values()]
-			.sort((x, y) => y.score - x.score)
-			.slice(0, 6)
-			.forEach(it => matched.set(it.emoji.name, it));
-	}
-
-	return [...matched.values()]
-		.sort((x, y) => y.score - x.score)
-		.slice(0, max)
-		.map(it => it.emoji);
-}
-
 function onMousedown(event: Event) {
 	if (!contains(rootEl.value, event.target) && (rootEl.value !== event.target)) props.close();
 }
diff --git a/packages/frontend/src/scripts/search-emoji.ts b/packages/frontend/src/scripts/search-emoji.ts
new file mode 100644
index 000000000000..07f55e5531d4
--- /dev/null
+++ b/packages/frontend/src/scripts/search-emoji.ts
@@ -0,0 +1,101 @@
+export type EmojiDef = {
+	emoji: string;
+	name: string;
+	url: string;
+	aliasOf?: string;
+} | {
+	emoji: string;
+	name: string;
+	aliasOf?: string;
+	isCustomEmoji?: true;
+};
+type EmojiScore = { emoji: EmojiDef, score: number };
+
+export function searchEmoji(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] {
+	if (!query) {
+		return [];
+	}
+
+	const matched = new Map<string, EmojiScore>();
+	// 完全一致(エイリアスなし)
+	emojiDb.some(x => {
+		if (x.name === query && !x.aliasOf) {
+			matched.set(x.name, { emoji: x, score: query.length + 3 });
+		}
+		return matched.size === max;
+	});
+
+	// 完全一致(エイリアス込み)
+	if (matched.size < max) {
+		emojiDb.some(x => {
+			if (x.name === query && !matched.has(x.aliasOf ?? x.name)) {
+				matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 });
+			}
+			return matched.size === max;
+		});
+	}
+
+	// 前方一致(エイリアスなし)
+	if (matched.size < max) {
+		emojiDb.some(x => {
+			if (x.name.startsWith(query) && !x.aliasOf && !matched.has(x.name)) {
+				matched.set(x.name, { emoji: x, score: query.length + 1 });
+			}
+			return matched.size === max;
+		});
+	}
+
+	// 前方一致(エイリアス込み)
+	if (matched.size < max) {
+		emojiDb.some(x => {
+			if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) {
+				matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length });
+			}
+			return matched.size === max;
+		});
+	}
+
+	// 部分一致(エイリアス込み)
+	if (matched.size < max) {
+		emojiDb.some(x => {
+			if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) {
+				matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 });
+			}
+			return matched.size === max;
+		});
+	}
+
+	// 簡易あいまい検索(3文字以上)
+	if (matched.size < max && query.length > 3) {
+		const queryChars = [...query];
+		const hitEmojis = new Map<string, EmojiScore>();
+
+		for (const x of emojiDb) {
+			// 文字列の位置を進めながら、クエリの文字を順番に探す
+
+			let pos = 0;
+			let hit = 0;
+			for (const c of queryChars) {
+				pos = x.name.indexOf(c, pos);
+				if (pos <= -1) break;
+				hit++;
+			}
+
+			// 半分以上の文字が含まれていればヒットとする
+			if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) {
+				hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 });
+			}
+		}
+
+		// ヒットしたものを全部追加すると雑多になるので、先頭の6件程度だけにしておく(6件=オートコンプリートのポップアップのサイズ分)
+		[...hitEmojis.values()]
+			.sort((x, y) => y.score - x.score)
+			.slice(0, 6)
+			.forEach(it => matched.set(it.emoji.name, it));
+	}
+
+	return [...matched.values()]
+		.sort((x, y) => y.score - x.score)
+		.slice(0, max)
+		.map(it => it.emoji);
+}
diff --git a/packages/frontend/test/autocomplete.test.ts b/packages/frontend/test/autocomplete.test.ts
new file mode 100644
index 000000000000..f6a7ce945009
--- /dev/null
+++ b/packages/frontend/test/autocomplete.test.ts
@@ -0,0 +1,34 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { assert, describe, test } from 'vitest';
+import { searchEmoji } from '@/scripts/search-emoji.js';
+
+describe('emoji autocomplete', () => {
+  test('名前の完全一致は名前の前方一致より優先される', async () => {
+    const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
+    assert.equal(result[0].emoji, ':foooo:');
+  });
+
+  test('名前の前方一致は名前の部分一致より優先される', async () => {
+    const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
+    assert.equal(result[0].emoji, ':baaar:');
+  });
+
+  test('名前の完全一致はタグの完全一致より優先される', async () => {
+    const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
+    assert.equal(result[0].emoji, ':foooo:');
+  });
+
+  test('名前の前方一致はタグの前方一致より優先される', async () => {
+    const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
+    assert.equal(result[0].emoji, ':foooo:');
+  });
+
+  test('名前の部分一致はタグの部分一致より優先される', async () => {
+    const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
+    assert.equal(result[0].emoji, ':foooo:');
+  });
+});

From b8d8b359bc8a6c542d78b86f500e0f45f63f48fb Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Fri, 23 Feb 2024 17:19:08 +0900
Subject: [PATCH 022/266] =?UTF-8?q?fix:=20=E3=83=97=E3=83=83=E3=82=B7?=
 =?UTF-8?q?=E3=83=A5=E9=80=9A=E7=9F=A5=E3=81=AE=E5=A4=89=E6=9B=B4=E3=81=8C?=
 =?UTF-8?q?1=E6=99=82=E9=96=93=E3=81=BB=E3=81=A9=E5=8F=8D=E6=98=A0?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#13407)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: プッシュ通知の変更が1時間ほど反映されない問題を修正

* 410 to refresh

* refreshCache
---
 packages/backend/src/core/PushNotificationService.ts       | 7 +++++++
 packages/backend/src/server/api/endpoints/sw/register.ts   | 4 ++++
 packages/backend/src/server/api/endpoints/sw/unregister.ts | 7 +++++++
 .../src/server/api/endpoints/sw/update-registration.ts     | 5 +++++
 4 files changed, 23 insertions(+)

diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts
index e630539fbc9c..3b706d985433 100644
--- a/packages/backend/src/core/PushNotificationService.ts
+++ b/packages/backend/src/core/PushNotificationService.ts
@@ -115,12 +115,19 @@ export class PushNotificationService implements OnApplicationShutdown {
 						endpoint: subscription.endpoint,
 						auth: subscription.auth,
 						publickey: subscription.publickey,
+					}).then(() => {
+						this.refreshCache(userId);
 					});
 				}
 			});
 		}
 	}
 
+	@bindThis
+	public refreshCache(userId: string): void {
+		this.subscriptionsCache.refresh(userId);
+	}
+
 	@bindThis
 	public dispose(): void {
 		this.subscriptionsCache.dispose();
diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts
index 06c04b3f9adf..a9a33149f95a 100644
--- a/packages/backend/src/server/api/endpoints/sw/register.ts
+++ b/packages/backend/src/server/api/endpoints/sw/register.ts
@@ -9,6 +9,7 @@ import type { SwSubscriptionsRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { MetaService } from '@/core/MetaService.js';
 import { DI } from '@/di-symbols.js';
+import { PushNotificationService } from '@/core/PushNotificationService.js';
 
 export const meta = {
 	tags: ['account'],
@@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		private idService: IdService,
 		private metaService: MetaService,
+		private pushNotificationService: PushNotificationService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			// if already subscribed
@@ -97,6 +99,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				sendReadMessage: ps.sendReadMessage,
 			});
 
+			this.pushNotificationService.refreshCache(me.id);
+
 			return {
 				state: 'subscribed' as const,
 				key: instance.swPublicKey,
diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts
index 2bc91c727881..2edf7fab1b36 100644
--- a/packages/backend/src/server/api/endpoints/sw/unregister.ts
+++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import type { SwSubscriptionsRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { DI } from '@/di-symbols.js';
+import { PushNotificationService } from '@/core/PushNotificationService.js';
 
 export const meta = {
 	tags: ['account'],
@@ -29,12 +30,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	constructor(
 		@Inject(DI.swSubscriptionsRepository)
 		private swSubscriptionsRepository: SwSubscriptionsRepository,
+
+		private pushNotificationService: PushNotificationService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			await this.swSubscriptionsRepository.delete({
 				...(me ? { userId: me.id } : {}),
 				endpoint: ps.endpoint,
 			});
+
+			if (me) {
+				this.pushNotificationService.refreshCache(me.id);
+			}
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/sw/update-registration.ts b/packages/backend/src/server/api/endpoints/sw/update-registration.ts
index b56b07fd0077..839a07c770b0 100644
--- a/packages/backend/src/server/api/endpoints/sw/update-registration.ts
+++ b/packages/backend/src/server/api/endpoints/sw/update-registration.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import type { SwSubscriptionsRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { DI } from '@/di-symbols.js';
+import { PushNotificationService } from '@/core/PushNotificationService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -58,6 +59,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	constructor(
 		@Inject(DI.swSubscriptionsRepository)
 		private swSubscriptionsRepository: SwSubscriptionsRepository,
+
+		private pushNotificationService: PushNotificationService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const swSubscription = await this.swSubscriptionsRepository.findOneBy({
@@ -77,6 +80,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				sendReadMessage: swSubscription.sendReadMessage,
 			});
 
+			this.pushNotificationService.refreshCache(me.id);
+
 			return {
 				userId: swSubscription.userId,
 				endpoint: swSubscription.endpoint,

From a861f913a772841d69d6b19aaa85c3985e3e073c Mon Sep 17 00:00:00 2001
From: okayurisotto <47853651+okayurisotto@users.noreply.github.com>
Date: Fri, 23 Feb 2024 18:02:12 +0900
Subject: [PATCH 023/266] =?UTF-8?q?fix(backend):=20=E3=82=88=E3=82=8A?=
 =?UTF-8?q?=E5=A4=9A=E3=81=8F=E3=81=AE=E4=BA=BA=E3=81=AB=E4=BD=BF=E3=82=8F?=
 =?UTF-8?q?=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E3=83=8F=E3=83=83=E3=82=B7?=
 =?UTF-8?q?=E3=83=A5=E3=82=BF=E3=82=B0=E3=81=8C=E6=A4=9C=E7=B4=A2=E7=B5=90?=
 =?UTF-8?q?=E6=9E=9C=E4=B8=8A=E4=BD=8D=E3=81=AB=E6=9D=A5=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#11498)=20(#13340)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/server/api/endpoints/hashtags/search.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts
index 12d47fa51233..d4eb85105495 100644
--- a/packages/backend/src/server/api/endpoints/hashtags/search.ts
+++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts
@@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		super(meta, paramDef, async (ps, me) => {
 			const hashtags = await this.hashtagsRepository.createQueryBuilder('tag')
 				.where('tag.name like :q', { q: sqlLikeEscape(ps.query.toLowerCase()) + '%' })
-				.orderBy('tag.count', 'DESC')
+				.orderBy('tag.mentionedLocalUsersCount', 'DESC')
 				.groupBy('tag.id')
 				.limit(ps.limit)
 				.offset(ps.offset)

From 600d91beda206fa22cfa1c1a3f94ca9e5a0cac68 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Fri, 23 Feb 2024 18:04:30 +0900
Subject: [PATCH 024/266] =?UTF-8?q?enhance:=20=E3=83=AA=E3=83=A2=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=81=AE=E3=83=95=E3=82=A9=E3=83=AD=E3=83=AF=E3=83=BC?=
 =?UTF-8?q?=E3=81=8B=E3=82=89=E5=86=8D=E5=BA=A6Follow=E3=81=8C=E6=9D=A5?=
 =?UTF-8?q?=E3=81=9F=E5=A0=B4=E5=90=88=E3=80=81accept=E3=82=92=E8=BF=94?=
 =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=82=E3=81=92=E3=82=8B=20(#13388)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: リモートのフォロワーから再度Followが来た場合、acceptを返してあげる

* nanka meccha kaeta

* ブロックチェックの後にフォロー関係の存在チェックをする
---
 .../backend/src/core/UserFollowingService.ts  | 60 +++++++++++++++----
 .../RelationshipProcessorService.ts           |  2 +-
 .../server/api/endpoints/following/create.ts  | 13 +---
 3 files changed, 52 insertions(+), 23 deletions(-)

diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index 8ad85391c652..d87cbacdcb57 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -30,6 +30,7 @@ import type { Config } from '@/config.js';
 import { AccountMoveService } from '@/core/AccountMoveService.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
+import type { ThinUser } from '@/queue/types.js';
 import Logger from '../logger.js';
 
 const logger = new Logger('following/create');
@@ -94,20 +95,43 @@ export class UserFollowingService implements OnModuleInit {
 		this.userBlockingService = this.moduleRef.get('UserBlockingService');
 	}
 
+	@bindThis
+	public async deliverAccept(follower: MiRemoteUser, followee: MiPartialLocalUser, requestId?: string) {
+		const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
+		this.queueService.deliver(followee, content, follower.inbox, false);
+	}
+
+	/**
+	 * ThinUserでなくともユーザーの情報が最新でない場合はこちらを使うべき
+	 */
+	@bindThis
+	public async followByThinUser(
+		_follower: ThinUser,
+		_followee: ThinUser,
+		options: Parameters<typeof this.follow>[2] = {},
+	) {
+		const [follower, followee] = await Promise.all([
+			this.usersRepository.findOneByOrFail({ id: _follower.id }),
+			this.usersRepository.findOneByOrFail({ id: _followee.id }),
+		]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
+
+		await this.follow(follower, followee, options);
+	}
+
 	@bindThis
 	public async follow(
-		_follower: { id: MiUser['id'] },
-		_followee: { id: MiUser['id'] },
+		follower: MiLocalUser | MiRemoteUser,
+		followee: MiLocalUser | MiRemoteUser,
 		{ requestId, silent = false, withReplies }: {
 			requestId?: string,
 			silent?: boolean,
 			withReplies?: boolean,
 		} = {},
 	): Promise<void> {
-		const [follower, followee] = await Promise.all([
-			this.usersRepository.findOneByOrFail({ id: _follower.id }),
-			this.usersRepository.findOneByOrFail({ id: _followee.id }),
-		]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
+		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) {
+			// What?
+			throw new Error('Remote user cannot follow remote user.');
+		}
 
 		// check blocking
 		const [blocking, blocked] = await Promise.all([
@@ -129,6 +153,24 @@ export class UserFollowingService implements OnModuleInit {
 			if (blocked) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked');
 		}
 
+		if (await this.followingsRepository.exists({
+			where: {
+				followerId: follower.id,
+				followeeId: followee.id,
+			},
+		})) {
+			// すでにフォロー関係が存在している場合
+			if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
+				// リモート → ローカル: acceptを送り返しておしまい
+				this.deliverAccept(follower, followee, requestId);
+				return;
+			}
+			if (this.userEntityService.isLocalUser(follower)) {
+				// ローカル → リモート/ローカル: 例外
+				throw new IdentifiableError('ec3f65c0-a9d1-47d9-8791-b2e7b9dcdced', 'already following');
+			}
+		}
+
 		const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id });
 		// フォロー対象が鍵アカウントである or
 		// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
@@ -189,8 +231,7 @@ export class UserFollowingService implements OnModuleInit {
 		await this.insertFollowingDoc(followee, follower, silent, withReplies);
 
 		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
-			const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
-			this.queueService.deliver(followee, content, follower.inbox, false);
+			this.deliverAccept(follower, followee, requestId);
 		}
 	}
 
@@ -571,8 +612,7 @@ export class UserFollowingService implements OnModuleInit {
 		await this.insertFollowingDoc(followee, follower, false, request.withReplies);
 
 		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
-			const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee));
-			this.queueService.deliver(followee, content, follower.inbox, false);
+			this.deliverAccept(follower, followee as MiPartialLocalUser, request.requestId ?? undefined);
 		}
 
 		this.userEntityService.pack(followee.id, followee, {
diff --git a/packages/backend/src/queue/processors/RelationshipProcessorService.ts b/packages/backend/src/queue/processors/RelationshipProcessorService.ts
index 408b02fb380c..53dbb4216905 100644
--- a/packages/backend/src/queue/processors/RelationshipProcessorService.ts
+++ b/packages/backend/src/queue/processors/RelationshipProcessorService.ts
@@ -35,7 +35,7 @@ export class RelationshipProcessorService {
 	@bindThis
 	public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
 		this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`);
-		await this.userFollowingService.follow(job.data.from, job.data.to, {
+		await this.userFollowingService.followByThinUser(job.data.from, job.data.to, {
 			requestId: job.data.requestId,
 			silent: job.data.silent,
 			withReplies: job.data.withReplies,
diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts
index ceaf32ccb276..042d7f119d3a 100644
--- a/packages/backend/src/server/api/endpoints/following/create.ts
+++ b/packages/backend/src/server/api/endpoints/following/create.ts
@@ -100,22 +100,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw err;
 			});
 
-			// Check if already following
-			const exist = await this.followingsRepository.exists({
-				where: {
-					followerId: follower.id,
-					followeeId: followee.id,
-				},
-			});
-
-			if (exist) {
-				throw new ApiError(meta.errors.alreadyFollowing);
-			}
-
 			try {
 				await this.userFollowingService.follow(follower, followee, { withReplies: ps.withReplies });
 			} catch (e) {
 				if (e instanceof IdentifiableError) {
+					if (e.id === 'ec3f65c0-a9d1-47d9-8791-b2e7b9dcdced') throw new ApiError(meta.errors.alreadyFollowing);
 					if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking);
 					if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked);
 				}

From d8342322327924a111a8ece0a6c7eb8c9ac2f378 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 23 Feb 2024 18:07:41 +0900
Subject: [PATCH 025/266] =?UTF-8?q?enhance(games):=20=E6=8A=9C=E3=81=91?=
 =?UTF-8?q?=E3=81=A6=E3=81=84=E3=82=8B=E7=BF=BB=E8=A8=B3=E3=82=92=E8=BF=BD?=
 =?UTF-8?q?=E5=8A=A0=E3=83=BB=E3=82=B9=E3=82=BF=E3=82=A4=E3=83=AB=E5=85=B1?=
 =?UTF-8?q?=E9=80=9A=E5=8C=96=20(#13434)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(games): 抜けている翻訳を追加・スタイル共通化

* frameDivider の使用箇所が見当たらなかったので削除

* ミス

* インナーでもcss変数を使う

* コロンを翻訳から外す

* 一部の翻訳を除去

* p

* revert some text

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 locales/index.d.ts                            |  62 +++++++++
 locales/ja-JP.yml                             |  16 +++
 .../src/pages/drop-and-fusion.game.vue        | 120 ++++++++----------
 .../frontend/src/pages/drop-and-fusion.vue    |  56 ++------
 .../frontend/src/pages/reversi/game.board.vue |  17 +--
 packages/frontend/src/style.scss              |  33 +++++
 6 files changed, 177 insertions(+), 127 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index d483fea8378e..1a2565b06a9c 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4856,6 +4856,14 @@ export interface Locale extends ILocale {
      * リプレイ中
      */
     "replaying": string;
+    /**
+     * リプレイを終了
+     */
+    "endReplay": string;
+    /**
+     * リプレイデータをコピー
+     */
+    "copyReplayData": string;
     /**
      * ランキング
      */
@@ -4884,11 +4892,57 @@ export interface Locale extends ILocale {
      * スワイプしてタブを切り替える
      */
     "enableHorizontalSwipe": string;
+    /**
+     * 読み込み中
+     */
+    "loading": string;
+    /**
+     * やめる
+     */
+    "surrender": string;
+    /**
+     * リトライ
+     */
+    "gameRetry": string;
     "_bubbleGame": {
         /**
          * 遊び方
          */
         "howToPlay": string;
+        /**
+         * ホールド
+         */
+        "hold": string;
+        "_score": {
+            /**
+             * スコア
+             */
+            "score": string;
+            /**
+             * 稼いだ金額
+             */
+            "scoreYen": string;
+            /**
+             * ハイスコア
+             */
+            "highScore": string;
+            /**
+             * 最大チェーン数
+             */
+            "maxChain": string;
+            /**
+             * {yen}円
+             */
+            "yen": ParameterizedString<"yen">;
+            /**
+             * {qty}個分
+             */
+            "estimatedQty": ParameterizedString<"qty">;
+            /**
+             * おにぎり {onigiriQtyWithUnit}
+             */
+            "scoreSweets": ParameterizedString<"onigiriQtyWithUnit">;
+        };
         "_howToPlay": {
             /**
              * 位置を調整してハコにモノを落とします。
@@ -9659,6 +9713,14 @@ export interface Locale extends ILocale {
          * 変則なし
          */
         "disallowIrregularRules": string;
+        /**
+         * 盤面に行・列番号を表示
+         */
+        "showBoardLabels": string;
+        /**
+         * 石をアイコンにする
+         */
+        "useAvatarAsStone": string;
     };
     "_offlineScreen": {
         /**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 7e16619fc78d..61c61b8f96e6 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1210,6 +1210,8 @@ soundWillBePlayed: "サウンドが再生されます"
 showReplay: "リプレイを見る"
 replay: "リプレイ"
 replaying: "リプレイ中"
+endReplay: "リプレイを終了"
+copyReplayData: "リプレイデータをコピー"
 ranking: "ランキング"
 lastNDays: "直近{n}日"
 backToTitle: "タイトルへ"
@@ -1217,9 +1219,21 @@ hemisphere: "お住まいの地域"
 withSensitive: "センシティブなファイルを含むノートを表示"
 userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿"
 enableHorizontalSwipe: "スワイプしてタブを切り替える"
+loading: "読み込み中"
+surrender: "やめる"
+gameRetry: "リトライ"
 
 _bubbleGame:
   howToPlay: "遊び方"
+  hold: "ホールド"
+  _score:
+    score: "スコア"
+    scoreYen: "稼いだ金額"
+    highScore: "ハイスコア"
+    maxChain: "最大チェーン数"
+    yen: "{yen}円"
+    estimatedQty: "{qty}個分"
+    scoreSweets: "おにぎり {onigiriQtyWithUnit}"
   _howToPlay:
     section1: "位置を調整してハコにモノを落とします。"
     section2: "同じ種類のモノがくっつくと別のモノに変化して、スコアが得られます。"
@@ -2572,6 +2586,8 @@ _reversi:
   opponentHasSettingsChanged: "相手が設定を変更しました"
   allowIrregularRules: "変則許可 (完全フリー)"
   disallowIrregularRules: "変則なし"
+  showBoardLabels: "盤面に行・列番号を表示"
+  useAvatarAsStone: "石をアイコンにする"
 
 _offlineScreen:
   title: "オフライン - サーバーに接続できません"
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index d9881cebbf04..eba5b9215463 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -7,9 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkSpacer :contentMax="800">
 	<div :class="$style.root">
 		<div v-if="!gameLoaded" :class="$style.loadingScreen">
-			<div>
-				Loading...
-			</div>
+			<div>{{ i18n.ts.loading }}<MkEllipsis/></div>
 		</div>
 		<!-- ↓に対してTransitionコンポーネントを使うと何故かkeyを指定していてもキャッシュが効かず様々なコンポーネントが都度再評価されてパフォーマンスが低下する -->
 		<div v-show="gameLoaded" class="_gaps_s">
@@ -32,18 +30,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</Transition>
 
 			<div :class="$style.header">
-				<div :class="[$style.frame, $style.headerTitle]">
-					<div :class="$style.frameInner">
-						<b>BUBBLE GAME</b>
-						<div>- {{ gameMode }} -</div>
+				<div class="_woodenFrame" :class="[$style.headerTitle]">
+					<div class="_woodenFrameInner">
+						<b>{{ i18n.ts.bubbleGame }}</b>
+						<div>- {{ gameMode.toUpperCase() }} -</div>
 					</div>
 				</div>
-				<div :class="[$style.frame, $style.frameH]">
-					<div :class="$style.frameInner">
-						<MkButton inline small @click="hold">HOLD</MkButton>
+				<div class="_woodenFrame _woodenFrameH">
+					<div class="_woodenFrameInner">
+						<MkButton inline small @click="hold">{{ i18n.ts._bubbleGame.hold }}</MkButton>
 						<img v-if="holdingStock" :src="getTextureImageUrl(holdingStock.mono)" style="width: 32px; margin-left: 8px; vertical-align: bottom;"/>
 					</div>
-					<div :class="[$style.frameInner, $style.stock]" style="text-align: center;">
+					<div class="_woodenFrameInner" :class="$style.stock" style="text-align: center;">
 						<TransitionGroup
 							:enterActiveClass="$style.transition_stock_enterActive"
 							:leaveActiveClass="$style.transition_stock_leaveActive"
@@ -90,58 +88,74 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<div v-if="isGameOver && !replaying" :class="$style.gameOverLabel">
 					<div class="_gaps_s">
 						<img src="/client-assets/drop-and-fusion/gameover.png" style="width: 200px; max-width: 100%; display: block; margin: auto; margin-bottom: -5px;"/>
-						<div>SCORE: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div>
-						<div>MAX CHAIN: <MkNumber :value="maxCombo"/></div>
-						<div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b><MkNumber :value="yenTotal ?? score"/>円</b></div>
-						<div v-if="gameMode === 'sweets'"><b>おにぎり<MkNumber :value="score / 130"/>個分</b></div>
+						<div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div>
+						<div>{{ i18n.ts._bubbleGame._score.maxChain }}: <MkNumber :value="maxCombo"/></div>
+						<div v-if="gameMode === 'yen'">
+							{{ i18n.ts._bubbleGame._score.scoreYen }}:
+							<I18n :src="i18n.ts._bubbleGame._score.yen" tag="b">
+								<template #yen><MkNumber :value="yenTotal ?? score"/></template>
+							</I18n>
+						</div>
+						<I18n v-if="gameMode === 'sweets'" :src="i18n.ts._bubbleGame._score.scoreSweets" tag="div">
+							<template #onigiriQtyWithUnit>
+								<I18n :src="i18n.ts._bubbleGame._score.estimatedQty" tag="b">
+									<template #qty><MkNumber :value="score / 130"/></template>
+								</I18n>
+							</template>
+						</I18n>
 					</div>
 				</div>
 				<div v-if="replaying" :class="$style.replayIndicator"><span :class="$style.replayIndicatorText"><i class="ti ti-player-play"></i> {{ i18n.ts.replaying }}</span></div>
 			</div>
 
-			<div v-if="replaying" :class="$style.frame">
-				<div :class="$style.frameInner">
+			<div v-if="replaying" class="_woodenFrame">
+				<div class="_woodenFrameInner">
 					<div style="background: #0004;">
 						<div style="height: 10px; background: var(--accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div>
 					</div>
 				</div>
-				<div :class="$style.frameInner">
+				<div class="_woodenFrameInner">
 					<div class="_buttonsCenter">
-						<MkButton @click="endReplay"><i class="ti ti-player-stop"></i> END</MkButton>
+						<MkButton @click="endReplay"><i class="ti ti-player-stop"></i> {{ i18n.ts.endReplay }}</MkButton>
 						<MkButton :primary="replayPlaybackRate === 4" @click="replayPlaybackRate = replayPlaybackRate === 4 ? 1 : 4"><i class="ti ti-player-track-next"></i> x4</MkButton>
 						<MkButton :primary="replayPlaybackRate === 16" @click="replayPlaybackRate = replayPlaybackRate === 16 ? 1 : 16"><i class="ti ti-player-track-next"></i> x16</MkButton>
 					</div>
 				</div>
 			</div>
 
-			<div v-if="isGameOver" :class="$style.frame">
-				<div :class="$style.frameInner">
+			<div v-if="isGameOver" class="_woodenFrame">
+				<div class="_woodenFrameInner">
 					<div class="_buttonsCenter">
 						<MkButton primary rounded @click="backToTitle">{{ i18n.ts.backToTitle }}</MkButton>
 						<MkButton primary rounded @click="replay">{{ i18n.ts.showReplay }}</MkButton>
 						<MkButton primary rounded @click="share">{{ i18n.ts.share }}</MkButton>
-						<MkButton rounded @click="exportLog">Copy replay data</MkButton>
+						<MkButton rounded @click="exportLog">{{ i18n.ts.copyReplayData }}</MkButton>
 					</div>
 				</div>
 			</div>
 
 			<div style="display: flex;">
-				<div :class="$style.frame" style="flex: 1; margin-right: 10px;">
-					<div :class="$style.frameInner">
-						<div>SCORE: <b><MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</b></div>
-						<div>HIGH SCORE: <b v-if="highScore"><MkNumber :value="highScore"/>{{ getScoreUnit(gameMode) }}</b><b v-else>-</b></div>
-						<div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b v-if="yenTotal"><MkNumber :value="yenTotal"/>円</b><b v-else>-</b></div>
+				<div class="_woodenFrame" style="flex: 1; margin-right: 10px;">
+					<div class="_woodenFrameInner">
+						<div>{{ i18n.ts._bubbleGame._score.score }}: <MkNumber :value="score"/>{{ getScoreUnit(gameMode) }}</div>
+						<div>{{ i18n.ts._bubbleGame._score.highScore }}: <b v-if="highScore"><MkNumber :value="highScore"/>{{ getScoreUnit(gameMode) }}</b><b v-else>-</b></div>
+						<div v-if="gameMode === 'yen'">
+							{{ i18n.ts._bubbleGame._score.scoreYen }}:
+							<I18n :src="i18n.ts._bubbleGame._score.yen" tag="b">
+								<template #yen><MkNumber :value="yenTotal ?? score"/></template>
+							</I18n>
+						</div>
 					</div>
 				</div>
-				<div :class="[$style.frame]" style="margin-left: auto;">
-					<div :class="$style.frameInner" style="text-align: center;">
+				<div class="_woodenFrame" style="margin-left: auto;">
+					<div class="_woodenFrameInner" style="text-align: center;">
 						<div @click="showConfig = !showConfig"><i class="ti ti-settings"></i></div>
 					</div>
 				</div>
 			</div>
 
-			<div v-if="showConfig" :class="$style.frame">
-				<div :class="$style.frameInner">
+			<div v-if="showConfig" class="_woodenFrame">
+				<div class="_woodenFrameInner">
 					<div class="_gaps">
 						<MkRange v-model="bgmVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('bgmVolume', v)">
 							<template #label>BGM {{ i18n.ts.volume }}</template>
@@ -153,8 +167,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</div>
 
-			<div :class="$style.frame">
-				<div :class="$style.frameInner">
+			<div class="_woodenFrame">
+				<div class="_woodenFrameInner">
 					<div>FUSION RECIPE</div>
 					<div>
 						<div v-for="(mono, i) in game.monoDefinitions.sort((a, b) => a.level - b.level)" :key="mono.id" style="display: inline-block;">
@@ -165,10 +179,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</div>
 
-			<div :class="$style.frame">
-				<div :class="$style.frameInner">
-					<MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">Surrender</MkButton>
-					<MkButton v-else full @click="restart">Retry</MkButton>
+			<div class="_woodenFrame">
+				<div class="_woodenFrameInner">
+					<MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">{{ i18n.ts.surrender }}</MkButton>
+					<MkButton v-else full @click="restart">{{ i18n.ts.gameRetry }}</MkButton>
 				</div>
 			</div>
 		</div>
@@ -1313,38 +1327,6 @@ definePageMetadata(() => ({
 	max-width: 100%;
 }
 
-.frame {
-	padding: 7px;
-	background: #8C4F26;
-	box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
-	border-radius: 10px;
-}
-
-.frameH {
-	display: flex;
-	gap: 6px;
-}
-
-.frameInner {
-	padding: 8px;
-	margin-top: 8px;
-	background: #F1E8DC;
-	box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
-	border-radius: 6px;
-	color: #693410;
-
-	&:first-child {
-		margin-top: 0;
-	}
-}
-
-.frameDivider {
-	height: 0;
-	border: none;
-	border-top: 1px solid #693410;
-	border-bottom: 1px solid #ce8a5c;
-}
-
 .header {
 	position: relative;
 	z-index: 10;
diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue
index 1b1145798838..54352c9b0d71 100644
--- a/packages/frontend/src/pages/drop-and-fusion.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.vue
@@ -15,13 +15,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkSpacer v-if="!gameStarted" :contentMax="800">
 		<div :class="$style.root">
 			<div class="_gaps">
-				<div :class="$style.frame" style="text-align: center;">
-					<div :class="$style.frameInner">
+				<div class="_woodenFrame" style="text-align: center;">
+					<div class="_woodenFrameInner">
 						<img src="/client-assets/drop-and-fusion/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
 					</div>
 				</div>
-				<div :class="$style.frame" style="text-align: center;">
-					<div :class="$style.frameInner">
+				<div class="_woodenFrame" style="text-align: center;">
+					<div class="_woodenFrameInner">
 						<div class="_gaps" style="padding: 16px;">
 							<MkSelect v-model="gameMode">
 								<option value="normal">NORMAL</option>
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton>
 						</div>
 					</div>
-					<div :class="$style.frameInner">
+					<div class="_woodenFrameInner">
 						<div class="_gaps" style="padding: 16px;">
 							<div style="font-size: 90%;"><i class="ti ti-music"></i> {{ i18n.ts.soundWillBePlayed }}</div>
 							<MkSwitch v-model="mute">
@@ -42,10 +42,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</div>
 					</div>
 				</div>
-				<div :class="$style.frame">
-					<div :class="$style.frameInner">
+				<div class="_woodenFrame">
+					<div class="_woodenFrameInner">
 						<div class="_gaps_s" style="padding: 16px;">
-							<div><b>{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }}</b> ({{ gameMode }})</div>
+							<div><b>{{ i18n.tsx.lastNDays({ n: 7 }) }} {{ i18n.ts.ranking }}</b> ({{ gameMode.toUpperCase() }})</div>
 							<div v-if="ranking" class="_gaps_s">
 								<div v-for="r in ranking" :key="r.id" :class="$style.rankingRecord">
 									<MkAvatar :link="true" style="width: 24px; height: 24px; margin-right: 4px;" :user="r.user"/>
@@ -57,8 +57,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</div>
 					</div>
 				</div>
-				<div :class="$style.frame">
-					<div :class="$style.frameInner" style="padding: 16px;">
+				<div class="_woodenFrame">
+					<div class="_woodenFrameInner" style="padding: 16px;">
 						<div style="font-weight: bold;">{{ i18n.ts._bubbleGame.howToPlay }}</div>
 						<ol>
 							<li>{{ i18n.ts._bubbleGame._howToPlay.section1 }}</li>
@@ -67,8 +67,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</ol>
 					</div>
 				</div>
-				<div :class="$style.frame">
-					<div :class="$style.frameInner">
+				<div class="_woodenFrame">
+					<div class="_woodenFrameInner">
 						<div class="_gaps_s" style="padding: 16px;">
 							<div><b>Credit</b></div>
 							<div>
@@ -149,38 +149,6 @@ definePageMetadata(() => ({
 	}
 }
 
-.frame {
-	padding: 7px;
-	background: #8C4F26;
-	box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
-	border-radius: 10px;
-}
-
-.frameH {
-	display: flex;
-	gap: 6px;
-}
-
-.frameInner {
-	padding: 8px;
-	margin-top: 8px;
-	background: #F1E8DC;
-	box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
-	border-radius: 6px;
-	color: #693410;
-
-	&:first-child {
-		margin-top: 0;
-	}
-}
-
-.frameDivider {
-	height: 0;
-	border: none;
-	border-top: 1px solid #693410;
-	border-bottom: 1px solid #ce8a5c;
-}
-
 .rankingRecord {
 	display: flex;
 	line-height: 24px;
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index 6f7f5b8f3861..5259dfa29a50 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 		</div>
 
-		<div :class="$style.board">
+		<div class="_woodenFrame">
 			<div :class="$style.boardInner">
 				<div v-if="showBoardLabels" :class="$style.labelsX">
 					<span v-for="i in game.map[0].length" :key="i" :class="$style.labelsXLabel">{{ String.fromCharCode(64 + i) }}</span>
@@ -124,8 +124,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkFolder>
 			<template #label>{{ i18n.ts.options }}</template>
 			<div class="_gaps_s" style="text-align: left;">
-				<MkSwitch v-model="showBoardLabels">Show labels</MkSwitch>
-				<MkSwitch v-model="useAvatarAsStone">useAvatarAsStone</MkSwitch>
+				<MkSwitch v-model="showBoardLabels">{{ i18n.ts._reversi.showBoardLabels }}</MkSwitch>
+				<MkSwitch v-model="useAvatarAsStone">{{ i18n.ts._reversi.useAvatarAsStone }}</MkSwitch>
 			</div>
 		</MkFolder>
 
@@ -500,17 +500,6 @@ $gap: 4px;
 	text-align: center;
 }
 
-.board {
-	width: 100%;
-	box-sizing: border-box;
-	margin: 0 auto;
-
-	padding: 7px;
-	background: #8C4F26;
-	box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
-	border-radius: 12px;
-}
-
 .boardInner {
 	padding: 32px;
 
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index cbec37727796..0951a7d98d5d 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -417,6 +417,39 @@ rt {
 	transition-timing-function: cubic-bezier(0,.5,.5,1);
 }
 
+._woodenFrame {
+	padding: 7px;
+	background: #8C4F26;
+	box-shadow: 0 6px 16px #0007, 0 0 1px 1px #693410, inset 0 0 2px 1px #ce8a5c;
+	border-radius: 10px;
+
+	--bg: #F1E8DC;
+	--panel: #fff;
+	--fg: #693410;
+	--switchOffBg: rgba(0, 0, 0, 0.1);
+	--switchOffFg: rgb(255, 255, 255);
+	--switchOnBg: var(--accent);
+	--switchOnFg: rgb(255, 255, 255);
+}
+
+._woodenFrameH {
+	display: flex;
+	gap: 6px;
+}
+
+._woodenFrameInner {
+	padding: 8px;
+	margin-top: 8px;
+	background: var(--bg);
+	box-shadow: 0 0 2px 1px #ce8a5c, inset 0 0 1px 1px #693410;
+	border-radius: 6px;
+	color: var(--fg);
+
+	&:first-child {
+		margin-top: 0;
+	}
+}
+
 ._transition_zoom-enter-active, ._transition_zoom-leave-active {
 	transition: opacity 0.5s, transform 0.5s !important;
 }

From c0156b740b6ce87f2cc55aa85f9d828ef41342ee Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Fri, 23 Feb 2024 18:15:39 +0900
Subject: [PATCH 026/266] =?UTF-8?q?enhance=3F:=20DeleteAccountService?=
 =?UTF-8?q?=E3=81=A7=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92=E5=89=8A?=
 =?UTF-8?q?=E9=99=A4=E3=81=99=E3=82=8B=E9=9A=9B=E3=81=ABuserChangeDeletedS?=
 =?UTF-8?q?tate=E3=82=92=E7=99=BA=E8=A1=8C=E3=81=99=E3=82=8B=20(#13382)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/core/CacheService.ts               | 1 +
 packages/backend/src/core/DeleteAccountService.ts       | 4 ++++
 packages/backend/src/core/GlobalEventService.ts         | 1 +
 packages/backend/src/core/activitypub/ApInboxService.ts | 1 -
 4 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts
index 263df56476bf..0fc47bf8ec83 100644
--- a/packages/backend/src/core/CacheService.ts
+++ b/packages/backend/src/core/CacheService.ts
@@ -128,6 +128,7 @@ export class CacheService implements OnApplicationShutdown {
 			const { type, body } = obj.message as GlobalEvents['internal']['payload'];
 			switch (type) {
 				case 'userChangeSuspendedState':
+				case 'userChangeDeletedState':
 				case 'remoteUserUpdated': {
 					const user = await this.usersRepository.findOneBy({ id: body.id });
 					if (user == null) {
diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts
index fc5d217ae01e..79b614edbaca 100644
--- a/packages/backend/src/core/DeleteAccountService.ts
+++ b/packages/backend/src/core/DeleteAccountService.ts
@@ -9,6 +9,7 @@ import { QueueService } from '@/core/QueueService.js';
 import { UserSuspendService } from '@/core/UserSuspendService.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
 
 @Injectable()
 export class DeleteAccountService {
@@ -18,6 +19,7 @@ export class DeleteAccountService {
 
 		private userSuspendService: UserSuspendService,
 		private queueService: QueueService,
+		private globalEventService: GlobalEventService,
 	) {
 	}
 
@@ -39,5 +41,7 @@ export class DeleteAccountService {
 		await this.usersRepository.update(user.id, {
 			isDeleted: true,
 		});
+
+		this.globalEventService.publishInternalEvent('userChangeDeletedState', { id: user.id, isDeleted: true });
 	}
 }
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index 01dd133eadc4..a127a6df3bff 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -209,6 +209,7 @@ type SerializedAll<T> = {
 
 export interface InternalEventTypes {
 	userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; };
+	userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; };
 	userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; };
 	remoteUserUpdated: { id: MiUser['id']; };
 	follow: { followerId: MiUser['id']; followeeId: MiUser['id']; };
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 8d9cd74a252d..b0f56a5d8284 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -85,7 +85,6 @@ export class ApInboxService {
 		private apPersonService: ApPersonService,
 		private apQuestionService: ApQuestionService,
 		private queueService: QueueService,
-		private cacheService: CacheService,
 		private globalEventService: GlobalEventService,
 	) {
 		this.logger = this.apLoggerService.logger;

From e3dd3f6b63efffde6dd125e8ecef66aa7069c1a0 Mon Sep 17 00:00:00 2001
From: 1Step621 <86859447+1STEP621@users.noreply.github.com>
Date: Sat, 24 Feb 2024 10:22:23 +0900
Subject: [PATCH 027/266] =?UTF-8?q?Enhance(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=94=E3=83=83=E3=82=AB?=
 =?UTF-8?q?=E3=83=BC=E3=82=92=E8=AA=BF=E6=95=B4=20(#13354)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 打てない絵文字を表示しないのではなくグレーアウトするように など

* fix: 今度は検索とピン留めに効いてなかった

* lint fix

* use Map

* 斜めに線を引いてわかりやすく

* 斜め線は右上からのほうが良かったかも

* デザイン調整
---
 .../src/components/MkEmojiPicker.section.vue  |  3 +
 .../frontend/src/components/MkEmojiPicker.vue | 86 ++++++++++++++++---
 .../components/MkReactionsViewer.reaction.vue | 11 ++-
 .../src/scripts/check-reaction-permissions.ts |  6 +-
 packages/frontend/src/scripts/emojilist.ts    |  4 +
 5 files changed, 90 insertions(+), 20 deletions(-)

diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 30ad2bcbbfd3..c295ab6bb7cd 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:key="emoji"
 			:data-emoji="emoji"
 			class="_button item"
+			:disabled="disabledEmojis?.value.includes(emoji)"
 			@pointerenter="computeButtonTitle"
 			@click="emit('chosen', emoji, $event)"
 		>
@@ -48,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:key="emoji"
 			:data-emoji="emoji"
 			class="_button item"
+			:disabled="disabledEmojis?.value.includes(emoji)"
 			@pointerenter="computeButtonTitle"
 			@click="emit('chosen', emoji, $event)"
 		>
@@ -67,6 +69,7 @@ import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue';
 
 const props = defineProps<{
 	emojis: string[] | Ref<string[]>;
+	disabledEmojis?: Ref<string[]>;
 	initialShown?: boolean;
 	hasChildSection?: boolean;
 	customEmojiTree?: CustomEmojiFolderTree[];
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 366273118b47..061afa66ac03 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					v-for="emoji in searchResultCustom"
 					:key="emoji.name"
 					class="_button item"
+					:disabled="!canReact(emoji)"
 					:title="emoji.name"
 					tabindex="0"
 					@click="chosen(emoji, $event)"
@@ -39,16 +40,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<section v-if="showPinned && (pinned && pinned.length > 0)">
 				<div class="body">
 					<button
-						v-for="emoji in pinned"
-						:key="emoji"
-						:data-emoji="emoji"
+						v-for="emoji in pinnedEmojisDef"
+						:key="getKey(emoji)"
+						:data-emoji="getKey(emoji)"
 						class="_button item"
+						:disabled="!canReact(emoji)"
 						tabindex="0"
 						@pointerenter="computeButtonTitle"
 						@click="chosen(emoji, $event)"
 					>
-						<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
-						<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
+						<MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/>
+						<MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/>
 					</button>
 				</div>
 			</section>
@@ -57,15 +59,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header>
 				<div class="body">
 					<button
-						v-for="emoji in recentlyUsedEmojis"
-						:key="emoji"
+						v-for="emoji in recentlyUsedEmojisDef"
+						:key="getKey(emoji)"
 						class="_button item"
-						:data-emoji="emoji"
+						:disabled="!canReact(emoji)"
+						:data-emoji="getKey(emoji)"
 						@pointerenter="computeButtonTitle"
 						@click="chosen(emoji, $event)"
 					>
-						<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
-						<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
+						<MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/>
+						<MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/>
 					</button>
 				</div>
 			</section>
@@ -76,7 +79,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 				v-for="child in customEmojiFolderRoot.children"
 				:key="`custom:${child.value}`"
 				:initialShown="false"
-				:emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))"
+				:emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))"
+				:disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).filter(e => !canReact(e)).map(e => `:${e.name}:`))"
 				:hasChildSection="child.children.length !== 0"
 				:customEmojiTree="child.children"
 				@chosen="chosen"
@@ -104,6 +108,7 @@ import * as Misskey from 'misskey-js';
 import XSection from '@/components/MkEmojiPicker.section.vue';
 import {
 	emojilist,
+	unicodeEmojisMap,
 	emojiCharByCategory,
 	UnicodeEmojiDef,
 	unicodeEmojiCategories as categories,
@@ -146,6 +151,13 @@ const {
 	recentlyUsedEmojis,
 } = defaultStore.reactiveState;
 
+const recentlyUsedEmojisDef = computed(() => {
+	return recentlyUsedEmojis.value.map(getDef);
+});
+const pinnedEmojisDef = computed(() => {
+	return pinned.value?.map(getDef);
+});
+
 const pinned = computed(() => props.pinnedEmojis);
 const size = computed(() => emojiPickerScale.value);
 const width = computed(() => emojiPickerWidth.value);
@@ -337,14 +349,18 @@ watch(q, () => {
 		return matches;
 	};
 
-	searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable);
+	searchResultCustom.value = Array.from(searchCustom());
 	searchResultUnicode.value = Array.from(searchUnicode());
 });
 
-function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean {
+function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
 	return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji);
 }
 
+function filterCategory(emoji: Misskey.entities.EmojiSimple, category: string): boolean {
+	return category === '' ? (emoji.category === 'null' || !emoji.category) : emoji.category === category;
+}
+
 function focus() {
 	if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) {
 		searchEl.value?.focus({
@@ -362,6 +378,14 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef):
 	return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
 }
 
+function getDef(emoji: string) {
+	if (emoji.includes(':')) {
+		return customEmojisMap.get(emoji.replace(/:/g, ''))!;
+	} else {
+		return unicodeEmojisMap.get(emoji)!;
+	}
+}
+
 /** @see MkEmojiPicker.section.vue */
 function computeButtonTitle(ev: MouseEvent): void {
 	const elm = ev.target as HTMLElement;
@@ -526,6 +550,18 @@ defineExpose({
 						width: auto;
 						height: auto;
 						min-width: 0;
+
+						&:disabled {
+							cursor: not-allowed;
+							background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%);
+							opacity: 1;
+
+							> .emoji {
+								filter: grayscale(1);
+								mix-blend-mode: exclusion;
+								opacity: 0.8;
+							}
+						}
 					}
 				}
 			}
@@ -548,6 +584,18 @@ defineExpose({
 						width: auto;
 						height: auto;
 						min-width: 0;
+
+						&:disabled {
+							cursor: not-allowed;
+							background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%);
+							opacity: 1;
+
+							> .emoji {
+								filter: grayscale(1);
+								mix-blend-mode: exclusion;
+								opacity: 0.8;
+							}
+						}
 					}
 				}
 			}
@@ -663,6 +711,18 @@ defineExpose({
 						box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
 					}
 
+					&:disabled {
+						cursor: not-allowed;
+						background: linear-gradient(-45deg, transparent 0% 48%, var(--X6) 48% 52%, transparent 52% 100%);
+						opacity: 1;
+
+						> .emoji {
+							filter: grayscale(1);
+							mix-blend-mode: exclusion;
+							opacity: 0.8;
+						}
+					}
+
 					> .emoji {
 						height: 1.25em;
 						vertical-align: -.25em;
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 0dcd8b0ea25b..bccee5109de9 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -33,7 +33,8 @@ import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import * as sound from '@/scripts/sound.js';
 import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';
-import { customEmojis } from '@/custom-emojis.js';
+import { customEmojisMap } from '@/custom-emojis.js';
+import { unicodeEmojisMap } from '@/scripts/emojilist.js';
 
 const props = defineProps<{
 	reaction: string;
@@ -50,13 +51,11 @@ const emit = defineEmits<{
 
 const buttonEl = shallowRef<HTMLElement>();
 
-const isCustomEmoji = computed(() => props.reaction.includes(':'));
-const emoji = computed(() => isCustomEmoji.value ? customEmojis.value.find(emoji => emoji.name === props.reaction.replace(/:/g, '').replace(/@\./, '')) : null);
+const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
+const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? unicodeEmojisMap.get(props.reaction));
 
 const canToggle = computed(() => {
-	return !props.reaction.match(/@\w/) && $i
-			&& (emoji.value && checkReactionPermissions($i, props.note, emoji.value))
-			|| !isCustomEmoji.value;
+	return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value);
 });
 const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':'));
 
diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts
index c9d2a5bfc6bf..da704717c127 100644
--- a/packages/frontend/src/scripts/check-reaction-permissions.ts
+++ b/packages/frontend/src/scripts/check-reaction-permissions.ts
@@ -1,6 +1,10 @@
 import * as Misskey from 'misskey-js';
+import { UnicodeEmojiDef } from './emojilist.js';
 
-export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple): boolean {
+export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
+  if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
+
+  emoji = emoji as Misskey.entities.EmojiSimple;
   const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
   return !(emoji.localOnly && note.user.host !== me.host)
       && !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 54d45e025f60..2a6120f3bad6 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -20,6 +20,10 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({
 	category: unicodeEmojiCategories[x[2]],
 }));
 
+export const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>(
+	emojilist.map(x => [x.char, x])
+);
+
 const _indexByChar = new Map<string, number>();
 const _charGroupByCategory = new Map<string, string[]>();
 for (let i = 0; i < emojilist.length; i++) {

From 41747b6ee2b2679517ef1f9fb94f333d40673ac5 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 24 Feb 2024 11:50:10 +0900
Subject: [PATCH 028/266] refactor

---
 .../core/entities/NoteReactionEntityService.ts    | 15 +++++++++++++++
 .../src/server/api/endpoints/users/reactions.ts   |  2 +-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts
index 2799f58992c1..3f4fa3cf969d 100644
--- a/packages/backend/src/core/entities/NoteReactionEntityService.ts
+++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts
@@ -69,4 +69,19 @@ export class NoteReactionEntityService implements OnModuleInit {
 			} : {}),
 		};
 	}
+
+	@bindThis
+	public async packMany(
+		reactions: MiNoteReaction[],
+		me?: { id: MiUser['id'] } | null | undefined,
+		options?: {
+			withNote: boolean;
+		},
+	): Promise<Packed<'NoteReaction'>[]> {
+		const opts = Object.assign({
+			withNote: false,
+		}, options);
+
+		return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts)));
+	}
 }
diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts
index e20d89624822..aca883a052bb 100644
--- a/packages/backend/src/server/api/endpoints/users/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/users/reactions.ts
@@ -98,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				.limit(ps.limit)
 				.getMany();
 
-			return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me, { withNote: true })));
+			return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true });
 		});
 	}
 }

From 792168fdfacfd0bc316daadf1e32b953f69e1608 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 24 Feb 2024 18:06:10 +0900
Subject: [PATCH 029/266] =?UTF-8?q?fix(frontend):=20`userActivation`?=
 =?UTF-8?q?=E3=81=8C=E3=81=AA=E3=81=84=E7=92=B0=E5=A2=83=E3=81=AB=E3=81=8A?=
 =?UTF-8?q?=E3=81=84=E3=81=A6=E4=B8=8D=E5=85=B7=E5=90=88=E3=81=8C=E7=94=9F?=
 =?UTF-8?q?=E3=81=98=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#13451)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/scripts/sound.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 67818320b325..fcd59510df18 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -126,7 +126,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
  */
 export function playMisskeySfx(operationType: OperationType) {
 	const sound = defaultStore.state[`sound_${operationType}`];
-	if (sound.type == null || !canPlay || !navigator.userActivation.hasBeenActive) return;
+	if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return;
 
 	canPlay = false;
 	playMisskeySfxFile(sound).finally(() => {

From 2c6f25b710b4f8095458fe88ddd56e6c6a41d006 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 25 Feb 2024 12:36:10 +0900
Subject: [PATCH 030/266] =?UTF-8?q?fix:=20=E5=8F=A4=E3=81=84=E3=82=AD?=
 =?UTF-8?q?=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=82=92=E4=BD=BF=E3=81=86?=
 =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13453)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../backend/src/core/AccountMoveService.ts    |  4 +--
 packages/backend/src/core/CacheService.ts     |  4 ++-
 .../backend/src/core/GlobalEventService.ts    |  1 +
 .../backend/src/core/UserFollowingService.ts  | 27 +++++++------------
 packages/backend/src/misc/cache.ts            |  8 ++++++
 .../RelationshipProcessorService.ts           |  2 +-
 .../server/api/endpoints/following/create.ts  |  2 +-
 .../src/server/api/endpoints/i/update.ts      |  6 ++---
 8 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts
index b7796a518356..5bd885df406b 100644
--- a/packages/backend/src/core/AccountMoveService.ts
+++ b/packages/backend/src/core/AccountMoveService.ts
@@ -20,7 +20,6 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
 import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import { CacheService } from '@/core/CacheService.js';
 import { ProxyAccountService } from '@/core/ProxyAccountService.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -60,7 +59,6 @@ export class AccountMoveService {
 		private instanceChart: InstanceChart,
 		private metaService: MetaService,
 		private relayService: RelayService,
-		private cacheService: CacheService,
 		private queueService: QueueService,
 	) {
 	}
@@ -84,7 +82,7 @@ export class AccountMoveService {
 		Object.assign(src, update);
 
 		// Update cache
-		this.cacheService.uriPersonCache.set(srcUri, src);
+		this.globalEventService.publishInternalEvent('localUserUpdated', src);
 
 		const srcPerson = await this.apRendererService.renderPerson(src);
 		const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src));
diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts
index 0fc47bf8ec83..d008e7ec52a3 100644
--- a/packages/backend/src/core/CacheService.ts
+++ b/packages/backend/src/core/CacheService.ts
@@ -129,10 +129,12 @@ export class CacheService implements OnApplicationShutdown {
 			switch (type) {
 				case 'userChangeSuspendedState':
 				case 'userChangeDeletedState':
-				case 'remoteUserUpdated': {
+				case 'remoteUserUpdated':
+				case 'localUserUpdated': {
 					const user = await this.usersRepository.findOneBy({ id: body.id });
 					if (user == null) {
 						this.userByIdCache.delete(body.id);
+						this.localUserByIdCache.delete(body.id);
 						for (const [k, v] of this.uriPersonCache.cache.entries()) {
 							if (v.value?.id === body.id) {
 								this.uriPersonCache.delete(k);
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index a127a6df3bff..7c1b34da05ea 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -212,6 +212,7 @@ export interface InternalEventTypes {
 	userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; };
 	userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; };
 	remoteUserUpdated: { id: MiUser['id']; };
+	localUserUpdated: { id: MiUser['id']; };
 	follow: { followerId: MiUser['id']; followeeId: MiUser['id']; };
 	unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; };
 	blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; };
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index d87cbacdcb57..0a492c06e4b1 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -101,33 +101,24 @@ export class UserFollowingService implements OnModuleInit {
 		this.queueService.deliver(followee, content, follower.inbox, false);
 	}
 
-	/**
-	 * ThinUserでなくともユーザーの情報が最新でない場合はこちらを使うべき
-	 */
 	@bindThis
-	public async followByThinUser(
+	public async follow(
 		_follower: ThinUser,
 		_followee: ThinUser,
-		options: Parameters<typeof this.follow>[2] = {},
-	) {
-		const [follower, followee] = await Promise.all([
-			this.usersRepository.findOneByOrFail({ id: _follower.id }),
-			this.usersRepository.findOneByOrFail({ id: _followee.id }),
-		]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
-
-		await this.follow(follower, followee, options);
-	}
-
-	@bindThis
-	public async follow(
-		follower: MiLocalUser | MiRemoteUser,
-		followee: MiLocalUser | MiRemoteUser,
 		{ requestId, silent = false, withReplies }: {
 			requestId?: string,
 			silent?: boolean,
 			withReplies?: boolean,
 		} = {},
 	): Promise<void> {
+		/**
+		 * 必ず最新のユーザー情報を取得する
+		 */
+		const [follower, followee] = await Promise.all([
+			this.usersRepository.findOneByOrFail({ id: _follower.id }),
+			this.usersRepository.findOneByOrFail({ id: _followee.id }),
+		]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
+
 		if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) {
 			// What?
 			throw new Error('Remote user cannot follow remote user.');
diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts
index 7f4d1521b5f9..bba64a06eff2 100644
--- a/packages/backend/src/misc/cache.ts
+++ b/packages/backend/src/misc/cache.ts
@@ -187,6 +187,10 @@ export class RedisSingleCache<T> {
 // TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする?
 
 export class MemoryKVCache<T> {
+	/**
+	 * データを持つマップ
+	 * @deprecated これを直接操作するべきではない
+	 */
 	public cache: Map<string, { date: number; value: T; }>;
 	private lifetime: number;
 	private gcIntervalHandle: NodeJS.Timeout;
@@ -201,6 +205,10 @@ export class MemoryKVCache<T> {
 	}
 
 	@bindThis
+	/**
+	 * Mapにキャッシュをセットします
+	 * @deprecated これを直接呼び出すべきではない。InternalEventなどで変更を全てのプロセス/マシンに通知するべき
+	 */
 	public set(key: string, value: T): void {
 		this.cache.set(key, {
 			date: Date.now(),
diff --git a/packages/backend/src/queue/processors/RelationshipProcessorService.ts b/packages/backend/src/queue/processors/RelationshipProcessorService.ts
index 53dbb4216905..408b02fb380c 100644
--- a/packages/backend/src/queue/processors/RelationshipProcessorService.ts
+++ b/packages/backend/src/queue/processors/RelationshipProcessorService.ts
@@ -35,7 +35,7 @@ export class RelationshipProcessorService {
 	@bindThis
 	public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
 		this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`);
-		await this.userFollowingService.followByThinUser(job.data.from, job.data.to, {
+		await this.userFollowingService.follow(job.data.from, job.data.to, {
 			requestId: job.data.requestId,
 			silent: job.data.silent,
 			withReplies: job.data.withReplies,
diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts
index 042d7f119d3a..db320e712981 100644
--- a/packages/backend/src/server/api/endpoints/following/create.ts
+++ b/packages/backend/src/server/api/endpoints/following/create.ts
@@ -71,7 +71,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		userId: { type: 'string', format: 'misskey:id' },
-		withReplies: { type: 'boolean' }
+		withReplies: { type: 'boolean' },
 	},
 	required: ['userId'],
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index bf6c53d8eb09..84a1931a3df5 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -456,9 +456,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			this.hashtagService.updateUsertags(user, tags);
 			//#endregion
 
-			if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates);
-			if (Object.keys(updates).includes('alsoKnownAs')) {
-				this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates });
+			if (Object.keys(updates).length > 0) {
+				await this.usersRepository.update(user.id, updates);
+				this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id });
 			}
 
 			await this.userProfilesRepository.update(user.id, {

From dd48366ed8130617df2563508369e3d4d63ed2a2 Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Sun, 25 Feb 2024 18:06:26 +0900
Subject: [PATCH 031/266] =?UTF-8?q?admin/emoji/update=E3=81=AE=E5=BF=85?=
 =?UTF-8?q?=E9=A0=88=E9=A0=85=E7=9B=AE=E3=82=92=E6=B8=9B=E3=82=89=E3=81=99?=
 =?UTF-8?q?=E3=80=80=E7=AD=89=20(#13449)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* admin/emoji/update enhancement

* add CustomEmojiService.getEmojiByName

* update endpoint

* fix

* Update update.ts

* Update autogen files

* type assertion

* Update CHANGELOG.md
---
 CHANGELOG.md                                  |  4 +++
 .../backend/src/core/CustomEmojiService.ts    |  5 ++++
 .../api/endpoints/admin/emoji/update.ts       | 27 ++++++++++++-------
 packages/misskey-js/src/autogen/types.ts      |  6 ++---
 4 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a939fa762169..ebbe22f2f6d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,10 @@
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
 - エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
+- エンドポイント`admin/emoji/update`の各種修正
+  - 必須パラメータを`id`または`name`のいずれかのみに
+  - `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
+  - `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正
 
 ## 2024.2.0
 
diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index 64a8c1acdf75..edb9335b6ea7 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -393,6 +393,11 @@ export class CustomEmojiService implements OnApplicationShutdown {
 		return this.emojisRepository.findOneBy({ id });
 	}
 
+	@bindThis
+	public getEmojiByName(name: string): Promise<MiEmoji | null> {
+		return this.emojisRepository.findOneBy({ name, host: IsNull() });
+	}
+
 	@bindThis
 	public dispose(): void {
 		this.cache.dispose();
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
index a9ff4236d205..22609a16a39a 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -57,7 +57,10 @@ export const paramDef = {
 			type: 'string',
 		} },
 	},
-	required: ['id', 'name', 'aliases'],
+	anyOf: [
+		{ required: ['id'] },
+		{ required: ['name'] },
+	],
 } as const;
 
 @Injectable()
@@ -70,27 +73,33 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			let driveFile;
-
 			if (ps.fileId) {
 				driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
 				if (driveFile == null) throw new ApiError(meta.errors.noSuchFile);
 			}
-			const emoji = await this.customEmojiService.getEmojiById(ps.id);
-			if (emoji != null) {
-				if (ps.name !== emoji.name) {
+
+			let emojiId;
+			if (ps.id) {
+				emojiId = ps.id;
+				const emoji = await this.customEmojiService.getEmojiById(ps.id);
+				if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
+				if (ps.name && (ps.name !== emoji.name)) {
 					const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name);
 					if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists);
 				}
 			} else {
-				throw new ApiError(meta.errors.noSuchEmoji);
+				if (!ps.name) throw new Error('Invalid Params unexpectedly passed. This is a BUG. Please report it to the development team.');
+				const emoji = await this.customEmojiService.getEmojiByName(ps.name);
+				if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
+				emojiId = emoji.id;
 			}
 
-			await this.customEmojiService.update(ps.id, {
+			await this.customEmojiService.update(emojiId, {
 				driveFile,
 				name: ps.name,
-				category: ps.category ?? null,
+				category: ps.category,
 				aliases: ps.aliases,
-				license: ps.license ?? null,
+				license: ps.license,
 				isSensitive: ps.isSensitive,
 				localOnly: ps.localOnly,
 				roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 18bc45b983df..07edf19c9443 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -7089,13 +7089,13 @@ export type operations = {
       content: {
         'application/json': {
           /** Format: misskey:id */
-          id: string;
-          name: string;
+          id?: string;
+          name?: string;
           /** Format: misskey:id */
           fileId?: string;
           /** @description Use `null` to reset the category. */
           category?: string | null;
-          aliases: string[];
+          aliases?: string[];
           license?: string | null;
           isSensitive?: boolean;
           localOnly?: boolean;

From 0a0af6887a829a45d2982bc61c319e76445f66a9 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 25 Feb 2024 18:06:40 +0900
Subject: [PATCH 032/266] =?UTF-8?q?test(frontend):=20Chromatic=E3=83=86?=
 =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=8C=E8=90=BD=E3=81=A1=E3=82=8B=E3=81=AE?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13448)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* test(frontend): Chromaticテストが落ちるのを修正

* fix: テストケースを修正

* refactor: comment
---
 packages/frontend/.storybook/generate.tsx                     | 3 ++-
 packages/frontend/src/components/global/MkA.stories.impl.ts   | 4 +++-
 .../frontend/src/components/global/MkTime.stories.impl.ts     | 4 ++--
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index 76c5b6be4b5b..1e925aede618 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -401,7 +401,8 @@ function toStories(component: string): Promise<string> {
 // glob('src/{components,pages,ui,widgets}/**/*.vue')
 (async () => {
 	const globs = await Promise.all([
-		glob('src/components/global/*.vue'),
+		glob('src/components/global/Mk*.vue'),
+		glob('src/components/global/RouterView.vue'),
 		glob('src/components/Mk{A,B}*.vue'),
 		glob('src/components/MkDigitalClock.vue'),
 		glob('src/components/MkGalleryPostPreview.vue'),
diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts
index 9d57841f0403..c1d8cf0ca6b2 100644
--- a/packages/frontend/src/components/global/MkA.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkA.stories.impl.ts
@@ -32,7 +32,8 @@ export const Default = {
 	async play({ canvasElement }) {
 		const canvas = within(canvasElement);
 		const a = canvas.getByRole<HTMLAnchorElement>('link');
-		await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
+		// FIXME: 通るけどその後落ちるのでコメントアウト
+		// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
 		await userEvent.pointer({ keys: '[MouseRight]', target: a });
 		await tick();
 		const menu = canvas.getByRole('menu');
@@ -44,6 +45,7 @@ export const Default = {
 	},
 	args: {
 		to: '#test',
+		behavior: 'browser',
 	},
 	parameters: {
 		layout: 'centered',
diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts
index 8ddf8e213a4d..355c83911399 100644
--- a/packages/frontend/src/components/global/MkTime.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts
@@ -10,7 +10,7 @@ import MkTime from './MkTime.vue';
 import { i18n } from '@/i18n.js';
 import { dateTimeFormat } from '@/scripts/intl-const.js';
 const now = new Date('2023-04-01T00:00:00.000Z');
-const future = new Date('3000-04-01T00:00:00.000Z');
+const future = new Date('2024-04-01T00:00:00.000Z');
 const oneHourAgo = new Date(now.getTime() - 3600000);
 const oneDayAgo = new Date(now.getTime() - 86400000);
 const oneWeekAgo = new Date(now.getTime() - 604800000);
@@ -49,7 +49,7 @@ export const Empty = {
 export const RelativeFuture = {
 	...Empty,
 	async play({ canvasElement }) {
-		await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 977 }));
+		await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 1 })); // n (1) = future (2024) - now (2023)
 	},
 	args: {
 		...Empty.args,

From 0fb7b98f96d809de10d5ff12ad57560c1fd7e1f1 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Mon, 26 Feb 2024 19:49:12 +0900
Subject: [PATCH 033/266] fix(backend): fix incorrect schemas (#13458)

---
 packages/backend/src/models/json-schema/user.ts      |  3 +++
 .../src/server/api/endpoints/admin/emoji/add.ts      |  5 ++++-
 packages/misskey-js/etc/misskey-js.api.md            |  4 ++++
 packages/misskey-js/src/autogen/endpoint.ts          |  3 ++-
 packages/misskey-js/src/autogen/entities.ts          |  1 +
 packages/misskey-js/src/autogen/types.ts             | 12 ++++++++----
 6 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index c7f86635da28..952cd6bf8011 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -148,6 +148,9 @@ export const packedUserLiteSchema = {
 		emojis: {
 			type: 'object',
 			nullable: false, optional: false,
+			additionalProperties: {
+				type: 'string',
+			},
 		},
 		onlineStatus: {
 			type: 'string',
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
index e32a19120d15..796f273330fb 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -31,7 +31,10 @@ export const meta = {
 		},
 	},
 
-	ref: 'EmojiDetailed',
+	res: {
+		type: 'object',
+		ref: 'EmojiDetailed',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index a2d5a4f5144e..b5e7ec75485d 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -124,6 +124,9 @@ type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk'
 // @public (undocumented)
 type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
 
@@ -1154,6 +1157,7 @@ declare namespace entities {
         AdminDriveShowFileResponse,
         AdminEmojiAddAliasesBulkRequest,
         AdminEmojiAddRequest,
+        AdminEmojiAddResponse,
         AdminEmojiCopyRequest,
         AdminEmojiCopyResponse,
         AdminEmojiDeleteBulkRequest,
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 595d0d66c0e9..656ac282465f 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -35,6 +35,7 @@ import type {
 	AdminDriveShowFileResponse,
 	AdminEmojiAddAliasesBulkRequest,
 	AdminEmojiAddRequest,
+	AdminEmojiAddResponse,
 	AdminEmojiCopyRequest,
 	AdminEmojiCopyResponse,
 	AdminEmojiDeleteBulkRequest,
@@ -578,7 +579,7 @@ export type Endpoints = {
 	'admin/drive/files': { req: AdminDriveFilesRequest; res: AdminDriveFilesResponse };
 	'admin/drive/show-file': { req: AdminDriveShowFileRequest; res: AdminDriveShowFileResponse };
 	'admin/emoji/add-aliases-bulk': { req: AdminEmojiAddAliasesBulkRequest; res: EmptyResponse };
-	'admin/emoji/add': { req: AdminEmojiAddRequest; res: EmptyResponse };
+	'admin/emoji/add': { req: AdminEmojiAddRequest; res: AdminEmojiAddResponse };
 	'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse };
 	'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse };
 	'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index e7ed146c4349..a936931e99e8 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -37,6 +37,7 @@ export type AdminDriveShowFileRequest = operations['admin/drive/show-file']['req
 export type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json'];
 export type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json'];
 export type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
+export type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json'];
 export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
 export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
 export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 07edf19c9443..733670d704e8 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3588,7 +3588,9 @@ export type components = {
         faviconUrl: string | null;
         themeColor: string | null;
       };
-      emojis: Record<string, never>;
+      emojis: {
+        [key: string]: string;
+      };
       /** @enum {string} */
       onlineStatus: 'unknown' | 'online' | 'active' | 'offline';
       badgeRoles?: ({
@@ -6476,9 +6478,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['EmojiDetailed'];
+        };
       };
       /** @description Client error */
       400: {

From f906ad6ca7044e4c509a5fe01f398f841a44027a Mon Sep 17 00:00:00 2001
From: zawa-ch <satellite.2e1834097@gmail.com>
Date: Tue, 27 Feb 2024 18:45:46 +0900
Subject: [PATCH 034/266] =?UTF-8?q?Enhance:=20=E3=82=B3=E3=83=B3=E3=83=87?=
 =?UTF-8?q?=E3=82=A3=E3=82=B7=E3=83=A7=E3=83=8A=E3=83=AB=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E3=83=AB=E3=81=AE=E6=9D=A1=E4=BB=B6=E3=81=AB=E3=80=8C=E3=83=9E?=
 =?UTF-8?q?=E3=83=8B=E3=83=A5=E3=82=A2=E3=83=AB=E3=83=AD=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=81=B8=E3=81=AE=E3=82=A2=E3=82=B5=E3=82=A4=E3=83=B3=E3=80=8D?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#13463)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加

* コメント修正
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  4 +++
 locales/ja-JP.yml                             |  1 +
 packages/backend/src/core/RoleService.ts      | 19 +++++++------
 packages/backend/src/misc/json-schema.ts      |  2 ++
 packages/backend/src/models/Role.ts           |  6 ++++
 .../backend/src/models/json-schema/role.ts    | 20 +++++++++++++
 packages/backend/test/unit/RoleService.ts     | 28 +++++++++++++++++++
 .../src/pages/admin/RolesEditorFormula.vue    |  9 ++++++
 packages/misskey-js/etc/misskey-js.api.md     |  4 +++
 packages/misskey-js/src/autogen/models.ts     |  1 +
 packages/misskey-js/src/autogen/types.ts      | 11 +++++++-
 12 files changed, 97 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebbe22f2f6d5..513338e6671a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@
 
 ### General
 - Enhance: サーバーごとにモデレーションノートを残せるように
+- Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
 
 ### Client
 - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 1a2565b06a9c..7d5f8ce732ce 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -6528,6 +6528,10 @@ export interface Locale extends ILocale {
             "avatarDecorationLimit": string;
         };
         "_condition": {
+            /**
+             * マニュアルロールにアサイン済み
+             */
+            "roleAssignedTo": string;
             /**
              * ローカルユーザー
              */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 61c61b8f96e6..1bb56738c6ad 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1687,6 +1687,7 @@ _role:
     canUseTranslator: "翻訳機能の利用"
     avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
   _condition:
+    roleAssignedTo: "マニュアルロールにアサイン済み"
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
     createdLessThan: "アカウント作成から~以内"
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index c5baaf3fff42..8312489a78f1 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -200,17 +200,20 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 	}
 
 	@bindThis
-	private evalCond(user: MiUser, value: RoleCondFormulaValue): boolean {
+	private evalCond(user: MiUser, roles: MiRole[], value: RoleCondFormulaValue): boolean {
 		try {
 			switch (value.type) {
 				case 'and': {
-					return value.values.every(v => this.evalCond(user, v));
+					return value.values.every(v => this.evalCond(user, roles, v));
 				}
 				case 'or': {
-					return value.values.some(v => this.evalCond(user, v));
+					return value.values.some(v => this.evalCond(user, roles, v));
 				}
 				case 'not': {
-					return !this.evalCond(user, value.value);
+					return !this.evalCond(user, roles, value.value);
+				}
+				case 'roleAssignedTo': {
+					return roles.some(r => r.id === value.roleId);
 				}
 				case 'isLocal': {
 					return this.userEntityService.isLocalUser(user);
@@ -272,7 +275,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 		const assigns = await this.getUserAssigns(userId);
 		const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
 		const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null;
-		const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula));
+		const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, assignedRoles, r.condFormula));
 		return [...assignedRoles, ...matchedCondRoles];
 	}
 
@@ -285,13 +288,13 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 		let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
 		// 期限切れのロールを除外
 		assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now));
-		const assignedRoleIds = assigns.map(x => x.roleId);
 		const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
-		const assignedBadgeRoles = roles.filter(r => r.asBadge && assignedRoleIds.includes(r.id));
+		const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
+		const assignedBadgeRoles = assignedRoles.filter(r => r.asBadge);
 		const badgeCondRoles = roles.filter(r => r.asBadge && (r.target === 'conditional'));
 		if (badgeCondRoles.length > 0) {
 			const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null;
-			const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, r.condFormula));
+			const matchedBadgeCondRoles = badgeCondRoles.filter(r => this.evalCond(user!, assignedRoles, r.condFormula));
 			return [...assignedBadgeRoles, ...matchedBadgeCondRoles];
 		} else {
 			return assignedBadgeRoles;
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index 8449e5ff073a..46b0bb2fab67 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -44,6 +44,7 @@ import {
 	packedRoleCondFormulaLogicsSchema,
 	packedRoleCondFormulaValueNot,
 	packedRoleCondFormulaValueIsLocalOrRemoteSchema,
+	packedRoleCondFormulaValueAssignedRoleSchema,
 	packedRoleCondFormulaValueCreatedSchema,
 	packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
 	packedRoleCondFormulaValueSchema,
@@ -96,6 +97,7 @@ export const refs = {
 	RoleCondFormulaLogics: packedRoleCondFormulaLogicsSchema,
 	RoleCondFormulaValueNot: packedRoleCondFormulaValueNot,
 	RoleCondFormulaValueIsLocalOrRemote: packedRoleCondFormulaValueIsLocalOrRemoteSchema,
+	RoleCondFormulaValueAssignedRole: packedRoleCondFormulaValueAssignedRoleSchema,
 	RoleCondFormulaValueCreated: packedRoleCondFormulaValueCreatedSchema,
 	RoleCondFormulaFollowersOrFollowingOrNotes: packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
 	RoleCondFormulaValue: packedRoleCondFormulaValueSchema,
diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts
index fa05ea8637b4..058abe311863 100644
--- a/packages/backend/src/models/Role.ts
+++ b/packages/backend/src/models/Role.ts
@@ -29,6 +29,11 @@ type CondFormulaValueIsRemote = {
 	type: 'isRemote';
 };
 
+type CondFormulaValueRoleAssignedTo = {
+	type: 'roleAssignedTo';
+	roleId: string;
+};
+
 type CondFormulaValueCreatedLessThan = {
 	type: 'createdLessThan';
 	sec: number;
@@ -75,6 +80,7 @@ export type RoleCondFormulaValue = { id: string } & (
 	CondFormulaValueNot |
 	CondFormulaValueIsLocal |
 	CondFormulaValueIsRemote |
+	CondFormulaValueRoleAssignedTo |
 	CondFormulaValueCreatedLessThan |
 	CondFormulaValueCreatedMoreThan |
 	CondFormulaValueFollowersLessThanOrEq |
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index ef6b279beef3..9f2b5b17eddf 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -57,6 +57,23 @@ export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = {
 	},
 } as const;
 
+export const packedRoleCondFormulaValueAssignedRoleSchema = {
+	type: 'object',
+	properties: {
+		type: {
+			type: 'string',
+			nullable: false, optional: false,
+			enum: ['roleAssignedTo'],
+		},
+		roleId: {
+			type: 'string',
+			nullable: false, optional: false,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+	},
+} as const;
+
 export const packedRoleCondFormulaValueCreatedSchema = {
 	type: 'object',
 	properties: {
@@ -115,6 +132,9 @@ export const packedRoleCondFormulaValueSchema = {
 		{
 			ref: 'RoleCondFormulaValueIsLocalOrRemote',
 		},
+		{
+			ref: 'RoleCondFormulaValueAssignedRole',
+		},
 		{
 			ref: 'RoleCondFormulaValueCreated',
 		},
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index 5222745b7f03..fe5ad31597ad 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -251,6 +251,34 @@ describe('RoleService', () => {
 			expect(user2Policies.canManageCustomEmojis).toBe(true);
 		});
 
+		test('コンディショナルロール: マニュアルロールにアサイン済み', async () => {
+			const [user1, user2, role1] = await Promise.all([
+				createUser(),
+				createUser(),
+				createRole({
+					name: 'manual role',
+				}),
+			]);
+			const role2 = await createRole({
+				name: 'conditional role',
+				target: 'conditional',
+				condFormula: {
+					// idはバックエンドのロジックに必要ない?
+					id: 'bdc612bd-9d54-4675-ae83-0499c82ea670',
+					type: 'roleAssignedTo',
+					roleId: role1.id,
+				},
+			});
+			await roleService.assign(user2.id, role1.id);
+
+			const [u1role, u2role] = await Promise.all([
+				roleService.getUserRoles(user1.id),
+				roleService.getUserRoles(user2.id),
+			]);
+			expect(u1role.some(r => r.id === role2.id)).toBe(false);
+			expect(u2role.some(r => r.id === role2.id)).toBe(true);
+		});
+
 		test('expired role', async () => {
 			const user = await createUser();
 			const role = await createRole({
diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
index f4a8f44955d7..2f5b4c47d87e 100644
--- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue
+++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
@@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkSelect v-model="type" :class="$style.typeSelect">
 			<option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option>
 			<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
+			<option value="roleAssignedTo">{{ i18n.ts._role._condition.roleAssignedTo }}</option>
 			<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
 			<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
 			<option value="followersLessThanOrEq">{{ i18n.ts._role._condition.followersLessThanOrEq }}</option>
@@ -51,6 +52,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq', 'notesLessThanOrEq', 'notesMoreThanOrEq'].includes(type)" v-model="v.value" type="number">
 	</MkInput>
+
+	<MkSelect v-else-if="type === 'roleAssignedTo'" v-model="v.roleId">
+		<option v-for="role in roles.filter(r => r.target === 'manual')" :key="role.id" :value="role.id">{{ role.name }}</option>
+	</MkSelect>
 </div>
 </template>
 
@@ -62,6 +67,7 @@ import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
 import { deepClone } from '@/scripts/clone.js';
+import { rolesCache } from '@/cache.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
@@ -77,6 +83,8 @@ const props = defineProps<{
 
 const v = ref(deepClone(props.modelValue));
 
+const roles = await rolesCache.fetch();
+
 watch(() => props.modelValue, () => {
 	if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return;
 	v.value = deepClone(props.modelValue);
@@ -92,6 +100,7 @@ const type = computed({
 		if (t === 'and') v.value.values = [];
 		if (t === 'or') v.value.values = [];
 		if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' };
+		if (t === 'roleAssignedTo') v.value.roleId = '';
 		if (t === 'createdLessThan') v.value.sec = 86400;
 		if (t === 'createdMoreThan') v.value.sec = 86400;
 		if (t === 'followersLessThanOrEq') v.value.value = 10;
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index b5e7ec75485d..0e990ffd5a06 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1712,6 +1712,7 @@ declare namespace entities {
         RoleCondFormulaLogics,
         RoleCondFormulaValueNot,
         RoleCondFormulaValueIsLocalOrRemote,
+        RoleCondFormulaValueAssignedRole,
         RoleCondFormulaValueCreated,
         RoleCondFormulaFollowersOrFollowingOrNotes,
         RoleCondFormulaValue,
@@ -2731,6 +2732,9 @@ type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics'];
 // @public (undocumented)
 type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue'];
 
+// @public (undocumented)
+type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole'];
+
 // @public (undocumented)
 type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated'];
 
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index ab49f9478a1e..6f61458600f4 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -38,6 +38,7 @@ export type Signin = components['schemas']['Signin'];
 export type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics'];
 export type RoleCondFormulaValueNot = components['schemas']['RoleCondFormulaValueNot'];
 export type RoleCondFormulaValueIsLocalOrRemote = components['schemas']['RoleCondFormulaValueIsLocalOrRemote'];
+export type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole'];
 export type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated'];
 export type RoleCondFormulaFollowersOrFollowingOrNotes = components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes'];
 export type RoleCondFormulaValue = components['schemas']['RoleCondFormulaValue'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 733670d704e8..8d700fb828fe 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4573,6 +4573,15 @@ export type components = {
       /** @enum {string} */
       type: 'isLocal' | 'isRemote';
     };
+    RoleCondFormulaValueAssignedRole: {
+      /** @enum {string} */
+      type: 'roleAssignedTo';
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      roleId: string;
+    };
     RoleCondFormulaValueCreated: {
       id: string;
       /** @enum {string} */
@@ -4585,7 +4594,7 @@ export type components = {
       type: 'followersLessThanOrEq' | 'followersMoreThanOrEq' | 'followingLessThanOrEq' | 'followingMoreThanOrEq' | 'notesLessThanOrEq' | 'notesMoreThanOrEq';
       value: number;
     };
-    RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes'];
+    RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueAssignedRole'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes'];
     RoleLite: {
       /**
        * Format: id

From 0d47877db1e1012aaba78a2926b165cf9e039d3d Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Wed, 28 Feb 2024 09:49:34 +0900
Subject: [PATCH 035/266] =?UTF-8?q?enhance(backend):=20=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=83=BB=E3=83=95=E3=82=A9=E3=83=AD=E3=83=AF?=
 =?UTF-8?q?=E3=83=BC=E9=96=A2=E9=80=A3=E3=81=AE=E9=80=9A=E7=9F=A5=E3=81=AE?=
 =?UTF-8?q?=E5=8F=97=E4=BF=A1=E8=A8=AD=E5=AE=9A=E3=81=AE=E5=BC=B7=E5=8C=96?=
 =?UTF-8?q?=20(#13468)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(backend): 通知の受信設定に「フォロー中またはフォロワー」を追加

* fix(backend): 通知の受信設定で「相互フォロー」が正しく動作しない問題を修正

* Update CHANGELOG.md
---
 CHANGELOG.md                                  |  2 +
 locales/index.d.ts                            |  4 +
 locales/ja-JP.yml                             |  1 +
 .../backend/src/core/NotificationService.ts   |  8 ++
 packages/backend/src/models/UserProfile.ts    |  2 +
 .../backend/src/models/json-schema/user.ts    |  2 +-
 .../notifications.notification-config.vue     |  1 +
 .../src/pages/settings/notifications.vue      |  1 +
 packages/misskey-js/src/autogen/types.ts      | 84 +++++++++----------
 9 files changed, 62 insertions(+), 43 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 513338e6671a..010d5aed7aab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 ### General
 - Enhance: サーバーごとにモデレーションノートを残せるように
 - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
+- Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加
 
 ### Client
 - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
@@ -33,6 +34,7 @@
   - 必須パラメータを`id`または`name`のいずれかのみに
   - `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
   - `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正
+- Fix: 通知の受信設定で「相互フォロー」が正しく動作しない問題を修正
 
 ## 2024.2.0
 
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 7d5f8ce732ce..3edc9d235ef2 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4656,6 +4656,10 @@ export interface Locale extends ILocale {
      * 相互フォロー
      */
     "mutualFollow": string;
+    /**
+     * フォロー中またはフォロワー
+     */
+    "followingOrFollower": string;
     /**
      * ファイル付きのみ
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 1bb56738c6ad..66ddf6a46d46 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1160,6 +1160,7 @@ showRenotes: "リノートを表示"
 edited: "編集済み"
 notificationRecieveConfig: "通知の受信設定"
 mutualFollow: "相互フォロー"
+followingOrFollower: "フォロー中またはフォロワー"
 fileAttachedOnly: "ファイル付きのみ"
 showRepliesToOthersInTimeline: "TLに他の人への返信を含める"
 hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない"
diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts
index ee16193579fe..7224341991c4 100644
--- a/packages/backend/src/core/NotificationService.ts
+++ b/packages/backend/src/core/NotificationService.ts
@@ -122,6 +122,14 @@ export class NotificationService implements OnApplicationShutdown {
 					return null;
 				}
 			} else if (recieveConfig?.type === 'mutualFollow') {
+				const [isFollowing, isFollower] = await Promise.all([
+					this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)),
+					this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)),
+				]);
+				if (!(isFollowing && isFollower)) {
+					return null;
+				}
+			} else if (recieveConfig?.type === 'followingOrFollower') {
 				const [isFollowing, isFollower] = await Promise.all([
 					this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)),
 					this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)),
diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts
index 1ca2f55850a7..7dbe0b3717c6 100644
--- a/packages/backend/src/models/UserProfile.ts
+++ b/packages/backend/src/models/UserProfile.ts
@@ -249,6 +249,8 @@ export class MiUserProfile {
 			type: 'follower';
 		} | {
 			type: 'mutualFollow';
+		} | {
+			type: 'followingOrFollower';
 		} | {
 			type: 'list';
 			userListId: MiUserList['id'];
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 952cd6bf8011..947a9317d7a6 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -13,7 +13,7 @@ export const notificationRecieveConfig = {
 				type: {
 					type: 'string',
 					nullable: false,
-					enum: ['all', 'following', 'follower', 'mutualFollow', 'never'],
+					enum: ['all', 'following', 'follower', 'mutualFollow', 'followingOrFollower', 'never'],
 				},
 			},
 			required: ['type'],
diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue
index d6aac63674be..a36f036303e4 100644
--- a/packages/frontend/src/pages/settings/notifications.notification-config.vue
+++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<option value="following">{{ i18n.ts.following }}</option>
 		<option value="follower">{{ i18n.ts.followers }}</option>
 		<option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option>
+		<option value="followingOrFollower">{{ i18n.ts.followingOrFollower }}</option>
 		<option value="list">{{ i18n.ts.userList }}</option>
 		<option value="never">{{ i18n.ts.none }}</option>
 	</MkSelect>
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index febcfa32eddc..bbcef652839a 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						$i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following :
 						$i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers :
 						$i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
+						$i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower :
 						$i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList :
 						i18n.ts.all
 					}}
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 8d700fb828fe..a3597e4635b7 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3700,7 +3700,7 @@ export type components = {
       notificationRecieveConfig: {
         note?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3709,7 +3709,7 @@ export type components = {
         }]>;
         follow?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3718,7 +3718,7 @@ export type components = {
         }]>;
         mention?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3727,7 +3727,7 @@ export type components = {
         }]>;
         reply?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3736,7 +3736,7 @@ export type components = {
         }]>;
         renote?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3745,7 +3745,7 @@ export type components = {
         }]>;
         quote?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3754,7 +3754,7 @@ export type components = {
         }]>;
         reaction?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3763,7 +3763,7 @@ export type components = {
         }]>;
         pollEnded?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3772,7 +3772,7 @@ export type components = {
         }]>;
         receiveFollowRequest?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3781,7 +3781,7 @@ export type components = {
         }]>;
         followRequestAccepted?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3790,7 +3790,7 @@ export type components = {
         }]>;
         roleAssigned?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3799,7 +3799,7 @@ export type components = {
         }]>;
         achievementEarned?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3808,7 +3808,7 @@ export type components = {
         }]>;
         app?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -3817,7 +3817,7 @@ export type components = {
         }]>;
         test?: OneOf<[{
           /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
         }, {
           /** @enum {string} */
           type: 'list';
@@ -8436,7 +8436,7 @@ export type operations = {
             notificationRecieveConfig: {
               note?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8445,7 +8445,7 @@ export type operations = {
               }]>;
               follow?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8454,7 +8454,7 @@ export type operations = {
               }]>;
               mention?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8463,7 +8463,7 @@ export type operations = {
               }]>;
               reply?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8472,7 +8472,7 @@ export type operations = {
               }]>;
               renote?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8481,7 +8481,7 @@ export type operations = {
               }]>;
               quote?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8490,7 +8490,7 @@ export type operations = {
               }]>;
               reaction?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8499,7 +8499,7 @@ export type operations = {
               }]>;
               pollEnded?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8508,7 +8508,7 @@ export type operations = {
               }]>;
               receiveFollowRequest?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8517,7 +8517,7 @@ export type operations = {
               }]>;
               followRequestAccepted?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8526,7 +8526,7 @@ export type operations = {
               }]>;
               roleAssigned?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8535,7 +8535,7 @@ export type operations = {
               }]>;
               achievementEarned?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8544,7 +8544,7 @@ export type operations = {
               }]>;
               app?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -8553,7 +8553,7 @@ export type operations = {
               }]>;
               test?: OneOf<[{
                 /** @enum {string} */
-                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+                type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
               }, {
                 /** @enum {string} */
                 type: 'list';
@@ -18787,7 +18787,7 @@ export type operations = {
           notificationRecieveConfig?: {
             note?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18796,7 +18796,7 @@ export type operations = {
             }]>;
             follow?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18805,7 +18805,7 @@ export type operations = {
             }]>;
             mention?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18814,7 +18814,7 @@ export type operations = {
             }]>;
             reply?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18823,7 +18823,7 @@ export type operations = {
             }]>;
             renote?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18832,7 +18832,7 @@ export type operations = {
             }]>;
             quote?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18841,7 +18841,7 @@ export type operations = {
             }]>;
             reaction?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18850,7 +18850,7 @@ export type operations = {
             }]>;
             pollEnded?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18859,7 +18859,7 @@ export type operations = {
             }]>;
             receiveFollowRequest?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18868,7 +18868,7 @@ export type operations = {
             }]>;
             followRequestAccepted?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18877,7 +18877,7 @@ export type operations = {
             }]>;
             roleAssigned?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18886,7 +18886,7 @@ export type operations = {
             }]>;
             achievementEarned?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18895,7 +18895,7 @@ export type operations = {
             }]>;
             app?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';
@@ -18904,7 +18904,7 @@ export type operations = {
             }]>;
             test?: OneOf<[{
               /** @enum {string} */
-              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'never';
+              type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
             }, {
               /** @enum {string} */
               type: 'list';

From b7d9d1620161a728e34ab20d8ea99160b3eb4196 Mon Sep 17 00:00:00 2001
From: okayurisotto <47853651+okayurisotto@users.noreply.github.com>
Date: Wed, 28 Feb 2024 15:34:58 +0900
Subject: [PATCH 036/266] =?UTF-8?q?refactor(backend):=20=E3=83=8E=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=81=AE=E3=82=A8=E3=82=AF=E3=82=B9=E3=83=9D=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E5=87=A6=E7=90=86=E3=81=A7Streams=20API=E3=82=92?=
 =?UTF-8?q?=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=20(#13465)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(backend): ノートのエクスポート処理でStreams APIを使うように

* fixup! refactor(backend): ノートのエクスポート処理でStreams APIを使うように

`await`忘れにより、ジョブがすぐに完了したことになり削除されてしまっていた。
それによって、`NoteStream`内での`updateProgress`メソッドの呼び出しで、`Missing key for job`のエラーが発生することがあった。

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 packages/backend/src/misc/FileWriterStream.ts |  31 ++++
 packages/backend/src/misc/JsonArrayStream.ts  |  30 ++++
 .../processors/ExportNotesProcessorService.ts | 164 +++++++++---------
 3 files changed, 146 insertions(+), 79 deletions(-)
 create mode 100644 packages/backend/src/misc/FileWriterStream.ts
 create mode 100644 packages/backend/src/misc/JsonArrayStream.ts

diff --git a/packages/backend/src/misc/FileWriterStream.ts b/packages/backend/src/misc/FileWriterStream.ts
new file mode 100644
index 000000000000..828851df0e9e
--- /dev/null
+++ b/packages/backend/src/misc/FileWriterStream.ts
@@ -0,0 +1,31 @@
+import * as fs from 'node:fs/promises';
+import type { PathLike } from 'node:fs';
+
+/**
+ * `fs.createWriteStream()`相当のことを行う`WritableStream` (Web標準)
+ */
+export class FileWriterStream extends WritableStream<Uint8Array> {
+	constructor(path: PathLike) {
+		let file: fs.FileHandle | null = null;
+
+		super({
+			start: async () => {
+				file = await fs.open(path, 'a');
+			},
+			write: async (chunk, controller) => {
+				if (file === null) {
+					controller.error();
+					throw new Error();
+				}
+
+				await file.write(chunk);
+			},
+			close: async () => {
+				await file?.close();
+			},
+			abort: async () => {
+				await file?.close();
+			},
+		});
+	}
+}
diff --git a/packages/backend/src/misc/JsonArrayStream.ts b/packages/backend/src/misc/JsonArrayStream.ts
new file mode 100644
index 000000000000..ad35bb3a7905
--- /dev/null
+++ b/packages/backend/src/misc/JsonArrayStream.ts
@@ -0,0 +1,30 @@
+import { TransformStream } from 'node:stream/web';
+
+/**
+ * ストリームに流れてきた各データについて`JSON.stringify()`した上で、それらを一つの配列にまとめる
+ */
+export class JsonArrayStream extends TransformStream<unknown, string> {
+	constructor() {
+		/** 最初の要素かどうかを変数に記録 */
+		let isFirst = true;
+
+		super({
+			start(controller) {
+				controller.enqueue('[');
+			},
+			flush(controller) {
+				controller.enqueue(']');
+			},
+			transform(chunk, controller) {
+				if (isFirst) {
+					isFirst = false;
+				} else {
+					// 妥当なJSON配列にするためには最初以外の要素の前に`,`を挿入しなければならない
+					controller.enqueue(',\n');
+				}
+
+				controller.enqueue(JSON.stringify(chunk));
+			},
+		});
+	}
+}
diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts
index f2ae0ce4b4b4..c7611012d74b 100644
--- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as fs from 'node:fs';
+import { ReadableStream, TextEncoderStream } from 'node:stream/web';
 import { Inject, Injectable } from '@nestjs/common';
 import { MoreThan } from 'typeorm';
 import { format as dateFormat } from 'date-fns';
@@ -18,10 +18,82 @@ import { bindThis } from '@/decorators.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
 import { Packed } from '@/misc/json-schema.js';
 import { IdService } from '@/core/IdService.js';
+import { JsonArrayStream } from '@/misc/JsonArrayStream.js';
+import { FileWriterStream } from '@/misc/FileWriterStream.js';
 import { QueueLoggerService } from '../QueueLoggerService.js';
 import type * as Bull from 'bullmq';
 import type { DbJobDataWithUser } from '../types.js';
 
+class NoteStream extends ReadableStream<Record<string, unknown>> {
+	constructor(
+		job: Bull.Job,
+		notesRepository: NotesRepository,
+		pollsRepository: PollsRepository,
+		driveFileEntityService: DriveFileEntityService,
+		idService: IdService,
+		userId: string,
+	) {
+		let exportedNotesCount = 0;
+		let cursor: MiNote['id'] | null = null;
+
+		const serialize = (
+			note: MiNote,
+			poll: MiPoll | null,
+			files: Packed<'DriveFile'>[],
+		): Record<string, unknown> => {
+			return {
+				id: note.id,
+				text: note.text,
+				createdAt: idService.parse(note.id).date.toISOString(),
+				fileIds: note.fileIds,
+				files: files,
+				replyId: note.replyId,
+				renoteId: note.renoteId,
+				poll: poll,
+				cw: note.cw,
+				visibility: note.visibility,
+				visibleUserIds: note.visibleUserIds,
+				localOnly: note.localOnly,
+				reactionAcceptance: note.reactionAcceptance,
+			};
+		};
+
+		super({
+			async pull(controller): Promise<void> {
+				const notes = await notesRepository.find({
+					where: {
+						userId,
+						...(cursor !== null ? { id: MoreThan(cursor) } : {}),
+					},
+					take: 100, // 100件ずつ取得
+					order: { id: 1 },
+				});
+
+				if (notes.length === 0) {
+					job.updateProgress(100);
+					controller.close();
+				}
+
+				cursor = notes.at(-1)?.id ?? null;
+
+				for (const note of notes) {
+					const poll = note.hasPoll
+						? await pollsRepository.findOneByOrFail({ noteId: note.id }) // N+1
+						: null;
+					const files = await driveFileEntityService.packManyByIds(note.fileIds); // N+1
+					const content = serialize(note, poll, files);
+
+					controller.enqueue(content);
+					exportedNotesCount++;
+				}
+
+				const total = await notesRepository.countBy({ userId });
+				job.updateProgress(exportedNotesCount / total);
+			},
+		});
+	}
+}
+
 @Injectable()
 export class ExportNotesProcessorService {
 	private logger: Logger;
@@ -59,67 +131,19 @@ export class ExportNotesProcessorService {
 		this.logger.info(`Temp file is ${path}`);
 
 		try {
-			const stream = fs.createWriteStream(path, { flags: 'a' });
-
-			const write = (text: string): Promise<void> => {
-				return new Promise<void>((res, rej) => {
-					stream.write(text, err => {
-						if (err) {
-							this.logger.error(err);
-							rej(err);
-						} else {
-							res();
-						}
-					});
-				});
-			};
-
-			await write('[');
-
-			let exportedNotesCount = 0;
-			let cursor: MiNote['id'] | null = null;
-
-			while (true) {
-				const notes = await this.notesRepository.find({
-					where: {
-						userId: user.id,
-						...(cursor ? { id: MoreThan(cursor) } : {}),
-					},
-					take: 100,
-					order: {
-						id: 1,
-					},
-				}) as MiNote[];
-
-				if (notes.length === 0) {
-					job.updateProgress(100);
-					break;
-				}
-
-				cursor = notes.at(-1)?.id ?? null;
+			// メモリが足りなくならないようにストリームで処理する
+			await new NoteStream(
+				job,
+				this.notesRepository,
+				this.pollsRepository,
+				this.driveFileEntityService,
+				this.idService,
+				user.id,
+			)
+				.pipeThrough(new JsonArrayStream())
+				.pipeThrough(new TextEncoderStream())
+				.pipeTo(new FileWriterStream(path));
 
-				for (const note of notes) {
-					let poll: MiPoll | undefined;
-					if (note.hasPoll) {
-						poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id });
-					}
-					const files = await this.driveFileEntityService.packManyByIds(note.fileIds);
-					const content = JSON.stringify(this.serialize(note, poll, files));
-					const isFirst = exportedNotesCount === 0;
-					await write(isFirst ? content : ',\n' + content);
-					exportedNotesCount++;
-				}
-
-				const total = await this.notesRepository.countBy({
-					userId: user.id,
-				});
-
-				job.updateProgress(exportedNotesCount / total);
-			}
-
-			await write(']');
-
-			stream.end();
 			this.logger.succ(`Exported to: ${path}`);
 
 			const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
@@ -130,22 +154,4 @@ export class ExportNotesProcessorService {
 			cleanup();
 		}
 	}
-
-	private serialize(note: MiNote, poll: MiPoll | null = null, files: Packed<'DriveFile'>[]): Record<string, unknown> {
-		return {
-			id: note.id,
-			text: note.text,
-			createdAt: this.idService.parse(note.id).date.toISOString(),
-			fileIds: note.fileIds,
-			files: files,
-			replyId: note.replyId,
-			renoteId: note.renoteId,
-			poll: poll,
-			cw: note.cw,
-			visibility: note.visibility,
-			visibleUserIds: note.visibleUserIds,
-			localOnly: note.localOnly,
-			reactionAcceptance: note.reactionAcceptance,
-		};
-	}
 }

From 664aeb3ced65f3911c8a21c2d5ffbd1035aec31a Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Wed, 28 Feb 2024 17:43:17 +0900
Subject: [PATCH 037/266] =?UTF-8?q?fix(backend):=20=E3=83=AA=E3=83=8E?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E6=99=82=E3=81=AEHTL=E3=81=B8=E3=81=AE?=
 =?UTF-8?q?=E3=82=B9=E3=83=88=E3=83=AA=E3=83=BC=E3=83=9F=E3=83=B3=E3=82=B0?=
 =?UTF-8?q?=E3=81=AE=E6=84=8F=E5=9B=B3=E3=81=97=E3=81=AA=E3=81=84=E6=8C=99?=
 =?UTF-8?q?=E5=8B=95=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13425)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): リノート時のストリーミングの意図しない挙動を修正

* Update CHANGELOG.md

* fix: 不要な返り値

* fix: 不適切な条件分岐を修正

* test(backend): add htl tests

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  2 +
 .../api/stream/channels/home-timeline.ts      | 10 ++-
 packages/backend/test/e2e/streaming.ts        | 65 +++++++++++++++++--
 packages/backend/test/utils.ts                |  6 +-
 4 files changed, 75 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 010d5aed7aab..f52226a635e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,8 @@
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
 - エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
+- Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
+- Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正
 - エンドポイント`admin/emoji/update`の各種修正
   - 必須パラメータを`id`または`name`のいずれかのみに
   - `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index ce9d7f56472e..f45bf8622eb2 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -71,7 +71,15 @@ class HomeTimelineChannel extends Channel {
 			}
 		}
 
-		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
+		// 純粋なリノート(引用リノートでないリノート)の場合
+		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && note.poll == null) {
+			if (!this.withRenotes) return;
+			if (note.renote.reply) {
+				const reply = note.renote.reply;
+				// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
+				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId)) return;
+			}
+		}
 
 		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
 		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index 071daa275f52..13d5a683ba6f 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -40,9 +40,9 @@ describe('Streaming', () => {
 		let chinatsu: misskey.entities.SignupResponse;
 		let takumi: misskey.entities.SignupResponse;
 
-		let kyokoNote: any;
-		let kanakoNote: any;
-		let takumiNote: any;
+		let kyokoNote: misskey.entities.Note;
+		let kanakoNote: misskey.entities.Note;
+		let takumiNote: misskey.entities.Note;
 		let list: any;
 
 		beforeAll(async () => {
@@ -68,6 +68,9 @@ describe('Streaming', () => {
 			// Follow: ayano => akari
 			await follow(ayano, akari);
 
+			// Follow: kyoko => chitose
+			await api('following/create', { userId: chitose.id }, kyoko);
+
 			// Mute: chitose => kanako
 			await api('mute/create', { userId: kanako.id }, chitose);
 
@@ -170,7 +173,28 @@ describe('Streaming', () => {
 			*/
 
 			test('フォローしているユーザーのフォローしていないユーザーの visibility: followers な投稿への返信が流れない', async () => {
-				// TODO
+				const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' });
+
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { text: 'reply to chitose\'s followers-only post', replyId: chitoseNote.id }, kyoko),	// kyoko's reply to chitose's followers-only post
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,	// wait kyoko
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			test('フォローしているユーザーのフォローしていないユーザーの visibility: followers な投稿への返信のリノートが流れない', async () => {
+				const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' });
+				const kyokoReply = await post(kyoko, { text: 'reply to followers-only post', replyId: chitoseNote.id });
+
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { renoteId: kyokoReply.id }, kyoko),	// kyoko's renote of kyoko's reply to chitose's followers-only post
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,	// wait kyoko
+				);
+
+				assert.strictEqual(fired, false);
 			});
 
 			test('フォローしていないユーザーの投稿は流れない', async () => {
@@ -202,6 +226,39 @@ describe('Streaming', () => {
 
 				assert.strictEqual(fired, false);
 			});
+
+			test('withRenotes: false のときリノートが流れない', async () => {
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { renoteId: kyokoNote.id }, kyoko),	// kyoko renote
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,	// wait kyoko
+					{ withRenotes: false },
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			test('withRenotes: false のとき引用リノートが流れる', async () => {
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { text: 'quote', renoteId: kyokoNote.id }, kyoko),	// kyoko quote
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,	// wait kyoko
+					{ withRenotes: false },
+				);
+
+				assert.strictEqual(fired, true);
+			});
+
+			test('withRenotes: false のとき投票のみのリノートが流れる', async () => {
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { poll: { choices: ['kinoko', 'takenoko'] }, renoteId: kyokoNote.id }, kyoko),	// kyoko renote with poll
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,	// wait kyoko
+					{ withRenotes: false },
+				);
+
+				assert.strictEqual(fired, true);
+			});
 		});	// Home
 
 		describe('Local Timeline', () => {
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index a2220ffae656..cd5dddd68d8e 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -355,7 +355,7 @@ export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'D
 	return catcher;
 };
 
-export function connectStream(user: UserToken, channel: string, listener: (message: Record<string, any>) => any, params?: any): Promise<WebSocket> {
+export function connectStream<C extends keyof misskey.Channels>(user: UserToken, channel: C, listener: (message: Record<string, any>) => any, params?: misskey.Channels[C]['params']): Promise<WebSocket> {
 	return new Promise((res, rej) => {
 		const url = new URL(`ws://127.0.0.1:${port}/streaming`);
 		const options: ClientOptions = {};
@@ -390,7 +390,7 @@ export function connectStream(user: UserToken, channel: string, listener: (messa
 	});
 }
 
-export const waitFire = async (user: UserToken, channel: string, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: any) => {
+export const waitFire = async <C extends keyof misskey.Channels>(user: UserToken, channel: C, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: misskey.Channels[C]['params']) => {
 	return new Promise<boolean>(async (res, rej) => {
 		let timer: NodeJS.Timeout | null = null;
 
@@ -435,7 +435,7 @@ export const waitFire = async (user: UserToken, channel: string, trgr: () => any
  */
 export function makeStreamCatcher<T>(
 	user: UserToken,
-	channel: string,
+	channel: keyof misskey.Channels,
 	cond: (message: Record<string, any>) => boolean,
 	extractor: (message: Record<string, any>) => T,
 	timeout = 60 * 1000): Promise<T> {

From 29350c9f334f426567e71eed479ae60ab4dea690 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Wed, 28 Feb 2024 18:26:38 +0900
Subject: [PATCH 038/266] =?UTF-8?q?refactor(frontend):=20`os.ts`=E5=91=A8?=
 =?UTF-8?q?=E3=82=8A=E3=81=AE=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?=
 =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=B0=20(#13186)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(frontend): `os.ts`周りのリファクタリング

* refactor: apiWithDialogのdataの型付け

* refactor: 不要なas anyを除去

* refactor: 返り値の型を明記、`selectDriveFolder`は`File`のほうに合わせるよう返り値を変更

* refactor: 返り値の型を改善

* refactor: フォームの型を改善

* refactor: 良い感じのimportに修正

* refactor: フォームの返り値の型を改善

* refactor: `popup()`の`props`に`ref`な値を入れるのを許可するように

* fix: `os.input`系と`os.select`の返り値の型がおかしい問題とそれによるバグを修正

* Update CHANGELOG.md

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   2 +
 packages/frontend/src/account.ts              |   2 +-
 packages/frontend/src/components/MkDialog.vue |  29 +-
 .../src/components/MkDriveSelectDialog.vue    |   8 +-
 .../src/components/MkEmojiPickerDialog.vue    |   4 +-
 .../src/components/MkEmojiPickerWindow.vue    |  49 ---
 .../frontend/src/components/MkFormDialog.vue  |  56 ++--
 packages/frontend/src/os.ts                   | 302 ++++++++++--------
 .../frontend/src/pages/emoji-edit-dialog.vue  |   2 +-
 packages/frontend/src/pages/notifications.vue |   4 +-
 .../frontend/src/pages/settings/drive.vue     |   2 +-
 .../src/pages/settings/emoji-picker.vue       |   2 +-
 .../pages/settings/preferences-backups.vue    |   2 +
 packages/frontend/src/scripts/form.ts         |  17 +-
 packages/frontend/src/ui/deck.vue             |  20 +-
 .../frontend/src/widgets/WidgetSlideshow.vue  |   4 +-
 16 files changed, 256 insertions(+), 249 deletions(-)
 delete mode 100644 packages/frontend/src/components/MkEmojiPickerWindow.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f52226a635e6..bd356918715b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,8 @@
 - Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
 - Fix: チャートのラベルが消えている問題を修正
 - Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正
+- Fix: 設定のバックアップ作成時に名前を入力しなかった場合、ローカライゼーションがおかしくなる問題を修正
+- Fix: ページ`/admin/emojis`の絵文字編集ダイアログで「リアクションとして使えるロール」を追加する際に何も選択せずOKを押下すると画面が固まる問題を修正
 - Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
 
 ### Server
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index e606fe368cd9..7f20e0b1a275 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -290,7 +290,7 @@ export async function openAccountMenu(opts: {
 			text: i18n.ts.profile,
 			to: `/@${ $i.username }`,
 			avatar: $i,
-		}, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
+		}, { type: 'divider' as const }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
 			type: 'parent' as const,
 			icon: 'ti ti-plus',
 			text: i18n.ts.addAccount,
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 4b7584faaa10..4577d37c0837 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -38,11 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<template v-if="select.items">
 				<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
 			</template>
-			<template v-else>
-				<optgroup v-for="groupedItem in select.groupedItems" :label="groupedItem.label">
-					<option v-for="item in groupedItem.items" :value="item.value">{{ item.text }}</option>
-				</optgroup>
-			</template>
 		</MkSelect>
 		<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
 			<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
@@ -64,7 +59,7 @@ import MkSelect from '@/components/MkSelect.vue';
 import { i18n } from '@/i18n.js';
 
 type Input = {
-	type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';
+	type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';
 	placeholder?: string | null;
 	autocomplete?: string;
 	default: string | number | null;
@@ -74,22 +69,17 @@ type Input = {
 
 type Select = {
 	items: {
-		value: string;
+		value: any;
 		text: string;
 	}[];
-	groupedItems: {
-		label: string;
-		items: {
-			value: string;
-			text: string;
-		}[];
-	}[];
 	default: string | null;
 };
 
+type Result = string | number | true | null;
+
 const props = withDefaults(defineProps<{
 	type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting';
-	title: string;
+	title?: string;
 	text?: string;
 	input?: Input;
 	select?: Select;
@@ -113,7 +103,7 @@ const props = withDefaults(defineProps<{
 });
 
 const emit = defineEmits<{
-	(ev: 'done', v: { canceled: boolean; result: any }): void;
+	(ev: 'done', v: { canceled: true } | { canceled: false, result: Result }): void;
 	(ev: 'closed'): void;
 }>();
 
@@ -139,8 +129,11 @@ const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'character
 	return null;
 });
 
-function done(canceled: boolean, result?) {
-	emit('done', { canceled, result });
+// overload function を使いたいので lint エラーを無視する
+function done(canceled: true): void;
+function done(canceled: false, result: Result): void; // eslint-disable-line no-redeclare
+function done(canceled: boolean, result?: Result): void { // eslint-disable-line no-redeclare
+	emit('done', { canceled, result } as { canceled: true } | { canceled: false, result: Result });
 	modal.value?.close();
 }
 
diff --git a/packages/frontend/src/components/MkDriveSelectDialog.vue b/packages/frontend/src/components/MkDriveSelectDialog.vue
index 77b5532f79cd..f1ecc27123b3 100644
--- a/packages/frontend/src/components/MkDriveSelectDialog.vue
+++ b/packages/frontend/src/components/MkDriveSelectDialog.vue
@@ -39,13 +39,13 @@ withDefaults(defineProps<{
 });
 
 const emit = defineEmits<{
-	(ev: 'done', r?: Misskey.entities.DriveFile[]): void;
+	(ev: 'done', r?: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void;
 	(ev: 'closed'): void;
 }>();
 
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 
-const selected = ref<Misskey.entities.DriveFile[]>([]);
+const selected = ref<Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]>([]);
 
 function ok() {
 	emit('done', selected.value);
@@ -57,7 +57,7 @@ function cancel() {
 	dialog.value?.close();
 }
 
-function onChangeSelection(files: Misskey.entities.DriveFile[]) {
-	selected.value = files;
+function onChangeSelection(v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]) {
+	selected.value = v;
 }
 </script>
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index 59f4b515225c..adcea839ee5b 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{
 });
 
 const emit = defineEmits<{
-	(ev: 'done', v: any): void;
+	(ev: 'done', v: string): void;
 	(ev: 'close'): void;
 	(ev: 'closed'): void;
 }>();
@@ -64,7 +64,7 @@ const emit = defineEmits<{
 const modal = shallowRef<InstanceType<typeof MkModal>>();
 const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>();
 
-function chosen(emoji: any) {
+function chosen(emoji: string) {
 	emit('done', emoji);
 	if (props.choseAndClose) {
 		modal.value?.close();
diff --git a/packages/frontend/src/components/MkEmojiPickerWindow.vue b/packages/frontend/src/components/MkEmojiPickerWindow.vue
deleted file mode 100644
index 69529433459a..000000000000
--- a/packages/frontend/src/components/MkEmojiPickerWindow.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and misskey-project
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<MkWindow
-	ref="window"
-	:initialWidth="300"
-	:initialHeight="290"
-	:canResize="true"
-	:mini="true"
-	:front="true"
-	@closed="emit('closed')"
->
-	<MkEmojiPicker :showPinned="showPinned" :asReactionPicker="asReactionPicker" :targetNote="targetNote" asWindow :class="$style.picker" @chosen="chosen"/>
-</MkWindow>
-</template>
-
-<script lang="ts" setup>
-import { } from 'vue';
-import * as Misskey from 'misskey-js';
-import MkWindow from '@/components/MkWindow.vue';
-import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
-
-withDefaults(defineProps<{
-	src?: HTMLElement;
-	showPinned?: boolean;
-	asReactionPicker?: boolean;
-	targetNote?: Misskey.entities.Note
-}>(), {
-	showPinned: true,
-});
-
-const emit = defineEmits<{
-	(ev: 'chosen', v: any): void;
-	(ev: 'closed'): void;
-}>();
-
-function chosen(emoji: any) {
-	emit('chosen', emoji);
-}
-</script>
-
-<style lang="scss" module>
-.picker {
-	height: 100%;
-}
-</style>
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index 0d8734799c33..deedc5badb1b 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -21,37 +21,37 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<MkSpacer :marginMin="20" :marginMax="32">
 		<div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
-			<template v-for="item in Object.keys(form).filter(item => !form[item].hidden)">
-				<MkInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
-					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
+			<template v-for="(v, k) in Object.fromEntries(Object.entries(form).filter(([_, v]) => !('hidden' in v) || 'hidden' in v && !v.hidden))">
+				<MkInput v-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1">
+					<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+					<template v-if="v.description" #caption>{{ v.description }}</template>
 				</MkInput>
-				<MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" :mfmAutocomplete="form[item].treatAsMfm">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
-					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
+				<MkInput v-else-if="v.type === 'string' && !v.multiline" v-model="values[k]" type="text" :mfmAutocomplete="v.treatAsMfm">
+					<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+					<template v-if="v.description" #caption>{{ v.description }}</template>
 				</MkInput>
-				<MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" :mfmAutocomplete="form[item].treatAsMfm" :mfmPreview="form[item].treatAsMfm">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
-					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
+				<MkTextarea v-else-if="v.type === 'string' && v.multiline" v-model="values[k]" :mfmAutocomplete="v.treatAsMfm" :mfmPreview="v.treatAsMfm">
+					<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+					<template v-if="v.description" #caption>{{ v.description }}</template>
 				</MkTextarea>
-				<MkSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]">
-					<span v-text="form[item].label || item"></span>
-					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
+				<MkSwitch v-else-if="v.type === 'boolean'" v-model="values[k]">
+					<span v-text="v.label || k"></span>
+					<template v-if="v.description" #caption>{{ v.description }}</template>
 				</MkSwitch>
-				<MkSelect v-else-if="form[item].type === 'enum'" v-model="values[item]">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
-					<option v-for="option in form[item].enum" :key="option.value" :value="option.value">{{ option.label }}</option>
+				<MkSelect v-else-if="v.type === 'enum'" v-model="values[k]">
+					<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+					<option v-for="option in v.enum" :key="option.value" :value="option.value">{{ option.label }}</option>
 				</MkSelect>
-				<MkRadios v-else-if="form[item].type === 'radio'" v-model="values[item]">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
-					<option v-for="option in form[item].options" :key="option.value" :value="option.value">{{ option.label }}</option>
+				<MkRadios v-else-if="v.type === 'radio'" v-model="values[k]">
+					<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+					<option v-for="option in v.options" :key="option.value" :value="option.value">{{ option.label }}</option>
 				</MkRadios>
-				<MkRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :textConverter="form[item].textConverter">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
-					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
+				<MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter">
+					<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+					<template v-if="v.description" #caption>{{ v.description }}</template>
 				</MkRange>
-				<MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)">
-					<span v-text="form[item].content || item"></span>
+				<MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)">
+					<span v-text="v.content || k"></span>
 				</MkButton>
 			</template>
 		</div>
@@ -72,19 +72,21 @@ import MkSelect from './MkSelect.vue';
 import MkRange from './MkRange.vue';
 import MkButton from './MkButton.vue';
 import MkRadios from './MkRadios.vue';
+import type { Form } from '@/scripts/form.js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { i18n } from '@/i18n.js';
 import { infoImageUrl } from '@/instance.js';
 
 const props = defineProps<{
 	title: string;
-	form: any;
+	form: Form;
 }>();
 
 const emit = defineEmits<{
 	(ev: 'done', v: {
-		canceled?: boolean;
-		result?: any;
+		canceled: true;
+	} | {
+		result: Record<string, any>;
 	}): void;
 	(ev: 'closed'): void;
 }>();
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index a4fde6b70170..c561e84a23b5 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -7,9 +7,9 @@
 
 import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
 import { EventEmitter } from 'eventemitter3';
-import insertTextAtCursor from 'insert-text-at-cursor';
 import * as Misskey from 'misskey-js';
-import type { ComponentProps } from 'vue-component-type-helpers';
+import type { ComponentProps as CP } from 'vue-component-type-helpers';
+import type { Form, GetFormResultType } from '@/scripts/form.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import MkPostFormDialog from '@/components/MkPostFormDialog.vue';
@@ -19,7 +19,6 @@ import MkToast from '@/components/MkToast.vue';
 import MkDialog from '@/components/MkDialog.vue';
 import MkPasswordDialog from '@/components/MkPasswordDialog.vue';
 import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
-import MkEmojiPickerWindow from '@/components/MkEmojiPickerWindow.vue';
 import MkPopupMenu from '@/components/MkPopupMenu.vue';
 import MkContextMenu from '@/components/MkContextMenu.vue';
 import { MenuItem } from '@/types/menu.js';
@@ -28,15 +27,15 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 
 export const openingWindowsCount = ref(0);
 
-export const apiWithDialog = ((
-	endpoint: string,
-	data: Record<string, any> = {},
+export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req']>(
+	endpoint: E,
+	data: P = {} as any,
 	token?: string | null | undefined,
 ) => {
 	const promise = misskeyApi(endpoint, data, token);
 	promiseDialog(promise, null, async (err) => {
-		let title = null;
-		let text = err.message + '\n' + (err as any).id;
+		let title: string | undefined;
+		let text = err.message + '\n' + err.id;
 		if (err.code === 'INTERNAL_ERROR') {
 			title = i18n.ts.internalServerError;
 			text = i18n.ts.internalServerErrorDescription;
@@ -88,7 +87,7 @@ export const apiWithDialog = ((
 export function promiseDialog<T extends Promise<any>>(
 	promise: T,
 	onSuccess?: ((res: any) => void) | null,
-	onFailure?: ((err: Error) => void) | null,
+	onFailure?: ((err: Misskey.api.APIError) => void) | null,
 	text?: string,
 ): T {
 	const showing = ref(true);
@@ -149,14 +148,30 @@ export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number {
 // 使い物にならないので、代わりに ['$props'] から色々省くことで emit の型を生成する
 // FIXME: 何故か *.ts ファイルからだと型がうまく取れない?ことがあるのをなんとかしたい
 type ComponentEmit<T> = T extends new () => { $props: infer Props }
-	? EmitsExtractor<Props>
-	: never;
+	? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never]
+		? Record<string, unknown> // *.ts ファイルから型がうまく取れないとき用(これがないと {} になって型エラーがうるさい)
+		: EmitsExtractor<Props>
+	: T extends (...args: any) => any
+		? ReturnType<T> extends { [x: string]: any; __ctx?: { [x: string]: any; props: infer Props } }
+			? [keyof Pick<T, Extract<keyof T, `on${string}`>>] extends [never]
+				? Record<string, unknown>
+				: EmitsExtractor<Props>
+			: never
+		: never;
+
+// props に ref を許可するようにする
+type ComponentProps<T extends Component> = { [K in keyof CP<T>]: CP<T>[K] | Ref<CP<T>[K]> };
 
 type EmitsExtractor<T> = {
 	[K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K];
 };
 
-export async function popup<T extends Component>(component: T, props: ComponentProps<T>, events: ComponentEmit<T> = {} as ComponentEmit<T>, disposeEvent?: keyof ComponentEmit<T>) {
+export async function popup<T extends Component>(
+	component: T,
+	props: ComponentProps<T>,
+	events: ComponentEmit<T> = {} as ComponentEmit<T>,
+	disposeEvent?: keyof ComponentEmit<T>,
+): Promise<{ dispose: () => void }> {
 	markRaw(component);
 
 	const id = ++popupIdCount;
@@ -197,12 +212,12 @@ export function toast(message: string) {
 
 export function alert(props: {
 	type?: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
-	title?: string | null;
-	text?: string | null;
+	title?: string;
+	text?: string;
 }): Promise<void> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkDialog, props, {
-			done: result => {
+			done: () => {
 				resolve();
 			},
 		}, 'closed');
@@ -211,12 +226,12 @@ export function alert(props: {
 
 export function confirm(props: {
 	type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
-	title?: string | null;
-	text?: string | null;
+	title?: string;
+	text?: string;
 	okText?: string;
 	cancelText?: string;
 }): Promise<{ canceled: boolean }> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkDialog, {
 			...props,
 			showCancelButton: true,
@@ -237,13 +252,15 @@ export function actions<T extends {
 	danger?: boolean,
 }[]>(props: {
 	type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
-	title?: string | null;
-	text?: string | null;
+	title?: string;
+	text?: string;
 	actions: T;
-}): Promise<{ canceled: true; result: undefined; } | {
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
 	canceled: false; result: T[number]['value'];
 }> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkDialog, {
 			...props,
 			actions: props.actions.map(a => ({
@@ -262,19 +279,50 @@ export function actions<T extends {
 	});
 }
 
+// default が指定されていたら result は null になり得ないことを保証する overload function
 export function inputText(props: {
 	type?: 'text' | 'email' | 'password' | 'url';
-	title?: string | null;
-	text?: string | null;
+	title?: string;
+	text?: string;
 	placeholder?: string | null;
 	autocomplete?: string;
-	default?: string | null;
+	default: string;
 	minLength?: number;
 	maxLength?: number;
-}): Promise<{ canceled: true; result: undefined; } | {
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
 	canceled: false; result: string;
+}>;
+export function inputText(props: {
+	type?: 'text' | 'email' | 'password' | 'url';
+	title?: string;
+	text?: string;
+	placeholder?: string | null;
+	autocomplete?: string;
+	default?: string | null;
+	minLength?: number;
+	maxLength?: number;
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
+	canceled: false; result: string | null;
+}>;
+export function inputText(props: {
+	type?: 'text' | 'email' | 'password' | 'url';
+	title?: string;
+	text?: string;
+	placeholder?: string | null;
+	autocomplete?: string;
+	default?: string | null;
+	minLength?: number;
+	maxLength?: number;
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
+	canceled: false; result: string | null;
 }> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkDialog, {
 			title: props.title,
 			text: props.text,
@@ -282,7 +330,7 @@ export function inputText(props: {
 				type: props.type,
 				placeholder: props.placeholder,
 				autocomplete: props.autocomplete,
-				default: props.default,
+				default: props.default ?? null,
 				minLength: props.minLength,
 				maxLength: props.maxLength,
 			},
@@ -294,16 +342,41 @@ export function inputText(props: {
 	});
 }
 
+// default が指定されていたら result は null になり得ないことを保証する overload function
 export function inputNumber(props: {
-	title?: string | null;
-	text?: string | null;
+	title?: string;
+	text?: string;
 	placeholder?: string | null;
 	autocomplete?: string;
-	default?: number | null;
-}): Promise<{ canceled: true; result: undefined; } | {
+	default: number;
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
 	canceled: false; result: number;
+}>;
+export function inputNumber(props: {
+	title?: string;
+	text?: string;
+	placeholder?: string | null;
+	autocomplete?: string;
+	default?: number | null;
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
+	canceled: false; result: number | null;
+}>;
+export function inputNumber(props: {
+	title?: string;
+	text?: string;
+	placeholder?: string | null;
+	autocomplete?: string;
+	default?: number | null;
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
+	canceled: false; result: number | null;
 }> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkDialog, {
 			title: props.title,
 			text: props.text,
@@ -311,7 +384,7 @@ export function inputNumber(props: {
 				type: 'number',
 				placeholder: props.placeholder,
 				autocomplete: props.autocomplete,
-				default: props.default,
+				default: props.default ?? null,
 			},
 		}, {
 			done: result => {
@@ -322,34 +395,38 @@ export function inputNumber(props: {
 }
 
 export function inputDate(props: {
-	title?: string | null;
-	text?: string | null;
+	title?: string;
+	text?: string;
 	placeholder?: string | null;
-	default?: Date | null;
-}): Promise<{ canceled: true; result: undefined; } | {
+	default?: string | null;
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
 	canceled: false; result: Date;
 }> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkDialog, {
 			title: props.title,
 			text: props.text,
 			input: {
 				type: 'date',
 				placeholder: props.placeholder,
-				default: props.default,
+				default: props.default ?? null,
 			},
 		}, {
 			done: result => {
-				resolve(result ? { result: new Date(result.result), canceled: false } : { canceled: true });
+				resolve(result ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true });
 			},
 		}, 'closed');
 	});
 }
 
-export function authenticateDialog(): Promise<{ canceled: true; result: undefined; } | {
+export function authenticateDialog(): Promise<{
+	canceled: true; result: undefined;
+} | {
 	canceled: false; result: { password: string; token: string | null; };
 }> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkPasswordDialog, {}, {
 			done: result => {
 				resolve(result ? { canceled: false, result } : { canceled: true, result: undefined });
@@ -358,34 +435,53 @@ export function authenticateDialog(): Promise<{ canceled: true; result: undefine
 	});
 }
 
+// default が指定されていたら result は null になり得ないことを保証する overload function
+export function select<C = any>(props: {
+	title?: string;
+	text?: string;
+	default: string;
+	items: {
+		value: C;
+		text: string;
+	}[];
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
+	canceled: false; result: C;
+}>;
 export function select<C = any>(props: {
-	title?: string | null;
-	text?: string | null;
+	title?: string;
+	text?: string;
 	default?: string | null;
-} & ({
 	items: {
 		value: C;
 		text: string;
 	}[];
+}): Promise<{
+	canceled: true; result: undefined;
 } | {
-	groupedItems: {
-		label: string;
-		items: {
-			value: C;
-			text: string;
-		}[];
+	canceled: false; result: C | null;
+}>;
+export function select<C = any>(props: {
+	title?: string;
+	text?: string;
+	default?: string | null;
+	items: {
+		value: C;
+		text: string;
 	}[];
-})): Promise<{ canceled: true; result: undefined; } | {
-	canceled: false; result: C;
+}): Promise<{
+	canceled: true; result: undefined;
+} | {
+	canceled: false; result: C | null;
 }> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(MkDialog, {
 			title: props.title,
 			text: props.text,
 			select: {
 				items: props.items,
-				groupedItems: props.groupedItems,
-				default: props.default,
+				default: props.default ?? null,
 			},
 		}, {
 			done: result => {
@@ -396,7 +492,7 @@ export function select<C = any>(props: {
 }
 
 export function success(): Promise<void> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		const showing = ref(true);
 		window.setTimeout(() => {
 			showing.value = false;
@@ -411,7 +507,7 @@ export function success(): Promise<void> {
 }
 
 export function waiting(): Promise<void> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		const showing = ref(true);
 		popup(MkWaitingDialog, {
 			success: false,
@@ -422,9 +518,9 @@ export function waiting(): Promise<void> {
 	});
 }
 
-export function form(title, form) {
-	return new Promise((resolve, reject) => {
-		popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form }, {
+export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true } | { result: GetFormResultType<F> }> {
+	return new Promise(resolve => {
+		popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, {
 			done: result => {
 				resolve(result);
 			},
@@ -433,7 +529,7 @@ export function form(title, form) {
 }
 
 export async function selectUser(opts: { includeSelf?: boolean; localOnly?: boolean; } = {}): Promise<Misskey.entities.UserDetailed> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), {
 			includeSelf: opts.includeSelf,
 			localOnly: opts.localOnly,
@@ -446,7 +542,7 @@ export async function selectUser(opts: { includeSelf?: boolean; localOnly?: bool
 }
 
 export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
 			type: 'file',
 			multiple,
@@ -460,23 +556,23 @@ export async function selectDriveFile(multiple: boolean): Promise<Misskey.entiti
 	});
 }
 
-export async function selectDriveFolder(multiple: boolean) {
-	return new Promise((resolve, reject) => {
+export async function selectDriveFolder(multiple: boolean): Promise<Misskey.entities.DriveFolder[]> {
+	return new Promise(resolve => {
 		popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
 			type: 'folder',
 			multiple,
 		}, {
 			done: folders => {
 				if (folders) {
-					resolve(multiple ? folders : folders[0]);
+					resolve(folders);
 				}
 			},
 		}, 'closed');
 	});
 }
 
-export async function pickEmoji(src: HTMLElement | null, opts) {
-	return new Promise((resolve, reject) => {
+export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof MkEmojiPickerDialog>): Promise<string> {
+	return new Promise(resolve => {
 		popup(MkEmojiPickerDialog, {
 			src,
 			...opts,
@@ -492,7 +588,7 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: {
 	aspectRatio: number;
 	uploadFolder?: string | null;
 }): Promise<Misskey.entities.DriveFile> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), {
 			file: image,
 			aspectRatio: options.aspectRatio,
@@ -505,67 +601,13 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: {
 	});
 }
 
-type AwaitType<T> =
-	T extends Promise<infer U> ? U :
-	T extends (...args: any[]) => Promise<infer V> ? V :
-	T;
-let openingEmojiPicker: AwaitType<ReturnType<typeof popup>> | null = null;
-let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null;
-export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea: typeof activeTextarea) {
-	if (openingEmojiPicker) return;
-
-	activeTextarea = initialTextarea;
-
-	const textareas = document.querySelectorAll('textarea, input');
-	for (const textarea of Array.from(textareas)) {
-		textarea.addEventListener('focus', () => {
-			activeTextarea = textarea;
-		});
-	}
-
-	const observer = new MutationObserver(records => {
-		for (const record of records) {
-			for (const node of Array.from(record.addedNodes).filter(node => node instanceof HTMLElement) as HTMLElement[]) {
-				const textareas = node.querySelectorAll('textarea, input') as NodeListOf<NonNullable<typeof activeTextarea>>;
-				for (const textarea of Array.from(textareas).filter(textarea => textarea.dataset.preventEmojiInsert == null)) {
-					if (document.activeElement === textarea) activeTextarea = textarea;
-					textarea.addEventListener('focus', () => {
-						activeTextarea = textarea;
-					});
-				}
-			}
-		}
-	});
-
-	observer.observe(document.body, {
-		childList: true,
-		subtree: true,
-		attributes: false,
-		characterData: false,
-	});
-
-	openingEmojiPicker = await popup(MkEmojiPickerWindow, {
-		src,
-		...opts,
-	}, {
-		chosen: emoji => {
-			insertTextAtCursor(activeTextarea, emoji);
-		},
-		closed: () => {
-			openingEmojiPicker!.dispose();
-			openingEmojiPicker = null;
-			observer.disconnect();
-		},
-	});
-}
-
-export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement | EventTarget | null, options?: {
+export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | null, options?: {
 	align?: string;
 	width?: number;
 	viaKeyboard?: boolean;
 	onClosing?: () => void;
 }): Promise<void> {
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		let dispose;
 		popup(MkPopupMenu, {
 			items,
@@ -587,9 +629,9 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement
 	});
 }
 
-export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent): Promise<void> {
+export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
 	ev.preventDefault();
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		let dispose;
 		popup(MkContextMenu, {
 			items,
@@ -608,7 +650,7 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent)
 export function post(props: Record<string, any> = {}): Promise<void> {
 	showMovedDialog();
 
-	return new Promise((resolve, reject) => {
+	return new Promise(resolve => {
 		// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない
 		// NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、
 		//       Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 12e9416f7243..16769ef36007 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -135,7 +135,7 @@ async function addRole() {
 	const { canceled, result: role } = await os.select({
 		items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })),
 	});
-	if (canceled) return;
+	if (canceled || role == null) return;
 
 	rolesThatCanBeUsedThisEmojiAsReaction.value.push(role);
 }
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 7db6fa539550..28f583829653 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -52,7 +52,7 @@ const directNotesPagination = {
 function setFilter(ev) {
 	const typeItems = notificationTypes.map(t => ({
 		text: i18n.ts._notification._types[t],
-		active: includeTypes.value && includeTypes.value.includes(t),
+		active: (includeTypes.value && includeTypes.value.includes(t)) ?? false,
 		action: () => {
 			includeTypes.value = [t];
 		},
@@ -63,7 +63,7 @@ function setFilter(ev) {
 		action: () => {
 			includeTypes.value = null;
 		},
-	}, { type: 'divider' }, ...typeItems] : typeItems;
+	}, { type: 'divider' as const }, ...typeItems] : typeItems;
 	os.popupMenu(items, ev.currentTarget ?? ev.target);
 }
 
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index cd38f9850f18..1919f808640f 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -113,7 +113,7 @@ if (defaultStore.state.uploadFolder) {
 
 function chooseUploadFolder() {
 	os.selectDriveFolder(false).then(async folder => {
-		defaultStore.set('uploadFolder', folder ? folder.id : null);
+		defaultStore.set('uploadFolder', folder[0] ? folder[0].id : null);
 		os.success();
 		if (defaultStore.state.uploadFolder) {
 			uploadFolder.value = await misskeyApi('drive/folders/show', {
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index 79969427ec3c..ce296ec183da 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -213,7 +213,7 @@ async function pickEmoji(itemsRef: Ref<string[]>, ev: MouseEvent) {
 	os.pickEmoji(getHTMLElement(ev), {
 		showPinned: false,
 	}).then(it => {
-		const emoji = it as string;
+		const emoji = it;
 		if (!itemsRef.value.includes(emoji)) {
 			itemsRef.value.push(emoji);
 		}
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 676159d1b5fb..942de19d82af 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -203,6 +203,7 @@ async function saveNew(): Promise<void> {
 
 	const { canceled, result: name } = await os.inputText({
 		title: ts._preferencesBackups.inputName,
+		default: '',
 	});
 	if (canceled) return;
 
@@ -371,6 +372,7 @@ async function rename(id: string): Promise<void> {
 
 	const { canceled: cancel1, result: name } = await os.inputText({
 		title: ts._preferencesBackups.inputName,
+		default: '',
 	});
 	if (cancel1 || profiles.value[id].name === name) return;
 
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index 26a027f4616f..b0db404f2818 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -12,29 +12,37 @@ export type FormItem = {
 	label?: string;
 	type: 'string';
 	default: string | null;
+	description?: string;
+	required?: boolean;
 	hidden?: boolean;
 	multiline?: boolean;
+	treatAsMfm?: boolean;
 } | {
 	label?: string;
 	type: 'number';
 	default: number | null;
+	description?: string;
+	required?: boolean;
 	hidden?: boolean;
 	step?: number;
 } | {
 	label?: string;
 	type: 'boolean';
 	default: boolean | null;
+	description?: string;
 	hidden?: boolean;
 } | {
 	label?: string;
 	type: 'enum';
 	default: string | null;
+	required?: boolean;
 	hidden?: boolean;
 	enum: EnumItem[];
 } | {
 	label?: string;
 	type: 'radio';
 	default: unknown | null;
+	required?: boolean;
 	hidden?: boolean;
 	options: {
 		label: string;
@@ -44,9 +52,12 @@ export type FormItem = {
 	label?: string;
 	type: 'range';
 	default: number | null;
-	step: number;
+	description?: string;
+	required?: boolean;
+	step?: number;
 	min: number;
 	max: number;
+	textConverter?: (value: number) => string;
 } | {
 	label?: string;
 	type: 'object';
@@ -57,6 +68,10 @@ export type FormItem = {
 	type: 'array';
 	default: unknown[] | null;
 	hidden: boolean;
+} | {
+	type: 'button';
+	content?: string;
+	action: (ev: MouseEvent, v: any) => void;
 };
 
 export type Form = Record<string, FormItem>;
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 92d2e23d9b06..bdb62dca15b5 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -117,6 +117,7 @@ import XMentionsColumn from '@/ui/deck/mentions-column.vue';
 import XDirectColumn from '@/ui/deck/direct-column.vue';
 import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
 import { mainRouter } from '@/router/main.js';
+import { MenuItem } from '@/types/menu.js';
 const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
 const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
 
@@ -221,21 +222,19 @@ document.documentElement.style.scrollBehavior = 'auto';
 loadDeck();
 
 function changeProfile(ev: MouseEvent) {
-	const items = ref([{
+	let items: MenuItem[] = [{
 		text: deckStore.state.profile,
-		active: true.valueOf,
-	}]);
+		active: true,
+		action: () => {},
+	}];
 	getProfiles().then(profiles => {
-		items.value = [{
-			text: deckStore.state.profile,
-			active: true.valueOf,
-		}, ...(profiles.filter(k => k !== deckStore.state.profile).map(k => ({
+		items.push(...(profiles.filter(k => k !== deckStore.state.profile).map(k => ({
 			text: k,
 			action: () => {
 				deckStore.set('profile', k);
 				unisonReload();
 			},
-		}))), { type: 'divider' }, {
+		}))), { type: 'divider' as const }, {
 			text: i18n.ts._deck.newProfile,
 			icon: 'ti ti-plus',
 			action: async () => {
@@ -248,9 +247,10 @@ function changeProfile(ev: MouseEvent) {
 				deckStore.set('profile', name);
 				unisonReload();
 			},
-		}];
+		});
+	}).then(() => {
+		os.popupMenu(items, ev.currentTarget ?? ev.target);
 	});
-	os.popupMenu(items, ev.currentTarget ?? ev.target);
 }
 
 async function deleteProfile() {
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index 7a3671a2404f..b8efd3bda9fc 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -93,10 +93,10 @@ const fetch = () => {
 
 const choose = () => {
 	os.selectDriveFolder(false).then(folder => {
-		if (folder == null) {
+		if (folder[0] == null) {
 			return;
 		}
-		widgetProps.folderId = folder.id;
+		widgetProps.folderId = folder[0].id;
 		save();
 		fetch();
 	});

From 5f43c2faa2fae3866a9921d81ab43c3b9e8bd222 Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Wed, 28 Feb 2024 21:26:26 +0900
Subject: [PATCH 039/266] =?UTF-8?q?enhance(backend):=20=E9=80=9A=E7=9F=A5?=
 =?UTF-8?q?=E3=81=8C=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E3=83=BB=E5=87=8D?=
 =?UTF-8?q?=E7=B5=90=E3=82=92=E8=80=83=E6=85=AE=E3=81=99=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#13412)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Never return broken notifications #409

Since notifications are stored in Redis, we can't expect relational
integrity: deleting a user will *not* delete notifications that
mention it.

But if we return notifications with missing bits (a `follow` without a
`user`, for example), the frontend will get very confused and throw an
exception while trying to render them.

This change makes sure we never expose those broken notifications. For
uniformity, I've applied the same logic to notes and roles mentioned
in notifications, even if nobody reported breakage in those cases.

Tested by creating a few types of notifications with a `notifierId`,
then deleting their user.

(cherry picked from commit 421f8d49e5d7a8dc3a798cc54716c767df8be3cb)

* Update Changelog

* Update CHANGELOG.md

* enhance: 通知がミュートを考慮するようにする

* enhance: 通知が凍結も考慮するようにする

* fix: notifierIdがない通知が消えてしまう問題

* Add tests (通知がミュートを考慮しているかどうか)

* fix: notifierIdがない通知が消えてしまう問題 (grouped)

* Remove unused import

* Fix: typo

* Revert "enhance: 通知が凍結も考慮するようにする"

This reverts commit b1e57e571dfd9a7d8b2430294473c2053cc3ea33.

* Revert API handling

* Remove unused imports

* enhance: Check if notifierId is valid in NotificationEntityService

* 通知作成時にpackしてnullになったらあとの処理をやめる

* Remove duplication of valid notifier check

* add filter notification is not null

* Revert "Remove duplication of valid notifier check"

This reverts commit 239a6952f717add53d52c3e701e7362eb1987645.

* Improve performance

* Fix packGrouped

* Refactor: 判定部分を共通化

* Fix condition

* use isNotNull

* Update CHANGELOG.md

* filterの改善

* Refactor: DONT REPEAT YOURSELF
Note: GroupedNotificationはNotificationの拡張なのでその例外だけ書けば基本的に共通の処理になり複雑な個別の処理は増えにくいと思われる

* Add groupedNotificationTypes

* Update misskey-js typedef

* Refactor: less sql calls

* refactor

* clean up

* filter notes to mark as read

* packed noteがmapなのでそちらを使う

* if (notesToRead.size > 0)

* if (notes.length === 0) return;

* fix

* Revert "if (notes.length === 0) return;"

This reverts commit 22e2324f9633bddba50769ef838bc5ddb4564c88.

* :art:

* console.error

* err

* remove try-catch

* 不要なジェネリクスを除去

* Revert  (既読処理をpack内で行うものを元に戻す)

* Clean

* Update packages/backend/src/core/entities/NotificationEntityService.ts

* Update packages/backend/src/core/entities/NotificationEntityService.ts

* Update packages/backend/src/core/entities/NotificationEntityService.ts

* Update packages/backend/src/core/entities/NotificationEntityService.ts

* Update packages/backend/src/core/NotificationService.ts

* Clean

---------

Co-authored-by: dakkar <dakkar@thenautilus.net>
Co-authored-by: kakkokari-gtyih <daisho7308+f@gmail.com>
Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   3 +
 .../backend/src/core/NotificationService.ts   |   2 +
 .../entities/NotificationEntityService.ts     | 269 ++++++++++--------
 .../api/endpoints/i/notifications-grouped.ts  |  15 +-
 .../server/api/endpoints/i/notifications.ts   |   2 +-
 packages/backend/src/types.ts                 |  11 +-
 packages/backend/test/e2e/mute.ts             | 179 ++++++++++++
 packages/misskey-js/src/autogen/types.ts      |   4 +-
 8 files changed, 356 insertions(+), 129 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd356918715b..bb339ff26f2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@
 ## 202x.x.x (unreleased)
 
 ### General
+- 通知がミュート、凍結を考慮するようになりました
 - Enhance: サーバーごとにモデレーションノートを残せるように
 - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
 - Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加
@@ -30,6 +31,8 @@
 
 ### Server
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
+- Fix: 破損した通知をクライアントに送信しないように
+	* 通知欄が無限にリロードされる問題が改善する可能性があります
 - エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
 - Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts
index 7224341991c4..af5755f88bd7 100644
--- a/packages/backend/src/core/NotificationService.ts
+++ b/packages/backend/src/core/NotificationService.ts
@@ -163,6 +163,8 @@ export class NotificationService implements OnApplicationShutdown {
 
 		const packed = await this.notificationEntityService.pack(notification, notifieeId, {});
 
+		if (packed == null) return null;
+
 		// Publish notification event
 		this.globalEventService.publishMainStream(notifieeId, 'notification', packed);
 
diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts
index 0663898edbd5..94d56c883bfb 100644
--- a/packages/backend/src/core/entities/NotificationEntityService.ts
+++ b/packages/backend/src/core/entities/NotificationEntityService.ts
@@ -14,14 +14,14 @@ import type { MiNote } from '@/models/Note.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { bindThis } from '@/decorators.js';
 import { isNotNull } from '@/misc/is-not-null.js';
-import { FilterUnionByProperty, notificationTypes } from '@/types.js';
+import { FilterUnionByProperty, groupedNotificationTypes } from '@/types.js';
+import { CacheService } from '@/core/CacheService.js';
 import { RoleEntityService } from './RoleEntityService.js';
 import type { OnModuleInit } from '@nestjs/common';
 import type { UserEntityService } from './UserEntityService.js';
 import type { NoteEntityService } from './NoteEntityService.js';
 
-const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded'] as (typeof notificationTypes[number])[]);
-const NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded']);
+const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded'] as (typeof groupedNotificationTypes[number])[]);
 
 @Injectable()
 export class NotificationEntityService implements OnModuleInit {
@@ -41,6 +41,8 @@ export class NotificationEntityService implements OnModuleInit {
 		@Inject(DI.followRequestsRepository)
 		private followRequestsRepository: FollowRequestsRepository,
 
+		private cacheService: CacheService,
+
 		//private userEntityService: UserEntityService,
 		//private noteEntityService: NoteEntityService,
 	) {
@@ -52,130 +54,48 @@ export class NotificationEntityService implements OnModuleInit {
 		this.roleEntityService = this.moduleRef.get('RoleEntityService');
 	}
 
-	@bindThis
-	public async pack(
-		src: MiNotification,
+	/**
+	 * 通知をパックする共通処理
+	*/
+	async #packInternal <T extends MiNotification | MiGroupedNotification> (
+		src: T,
 		meId: MiUser['id'],
 		// eslint-disable-next-line @typescript-eslint/ban-types
 		options: {
-
+			checkValidNotifier?: boolean;
 		},
 		hint?: {
 			packedNotes: Map<MiNote['id'], Packed<'Note'>>;
 			packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
 		},
-	): Promise<Packed<'Notification'>> {
+	): Promise<Packed<'Notification'> | null> {
 		const notification = src;
-		const noteIfNeed = NOTE_REQUIRED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification ? (
-			hint?.packedNotes != null
-				? hint.packedNotes.get(notification.noteId)
-				: this.noteEntityService.pack(notification.noteId, { id: meId }, {
-					detail: true,
-				})
-		) : undefined;
-		const userIfNeed = 'notifierId' in notification ? (
-			hint?.packedUsers != null
-				? hint.packedUsers.get(notification.notifierId)
-				: this.userEntityService.pack(notification.notifierId, { id: meId })
-		) : undefined;
-		const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
-
-		return await awaitAll({
-			id: notification.id,
-			createdAt: new Date(notification.createdAt).toISOString(),
-			type: notification.type,
-			userId: 'notifierId' in notification ? notification.notifierId : undefined,
-			...(userIfNeed != null ? { user: userIfNeed } : {}),
-			...(noteIfNeed != null ? { note: noteIfNeed } : {}),
-			...(notification.type === 'reaction' ? {
-				reaction: notification.reaction,
-			} : {}),
-			...(notification.type === 'roleAssigned' ? {
-				role: role,
-			} : {}),
-			...(notification.type === 'achievementEarned' ? {
-				achievement: notification.achievement,
-			} : {}),
-			...(notification.type === 'app' ? {
-				body: notification.customBody,
-				header: notification.customHeader,
-				icon: notification.customIcon,
-			} : {}),
-		});
-	}
-
-	@bindThis
-	public async packMany(
-		notifications: MiNotification[],
-		meId: MiUser['id'],
-	) {
-		if (notifications.length === 0) return [];
-
-		let validNotifications = notifications;
-
-		const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(isNotNull);
-		const notes = noteIds.length > 0 ? await this.notesRepository.find({
-			where: { id: In(noteIds) },
-			relations: ['user', 'reply', 'reply.user', 'renote', 'renote.user'],
-		}) : [];
-		const packedNotesArray = await this.noteEntityService.packMany(notes, { id: meId }, {
-			detail: true,
-		});
-		const packedNotes = new Map(packedNotesArray.map(p => [p.id, p]));
 
-		validNotifications = validNotifications.filter(x => !('noteId' in x) || packedNotes.has(x.noteId));
+		if (options.checkValidNotifier !== false && !(await this.#isValidNotifier(notification, meId))) return null;
 
-		const userIds = validNotifications.map(x => 'notifierId' in x ? x.notifierId : null).filter(isNotNull);
-		const users = userIds.length > 0 ? await this.usersRepository.find({
-			where: { id: In(userIds) },
-		}) : [];
-		const packedUsersArray = await this.userEntityService.packMany(users, { id: meId });
-		const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
-
-		// 既に解決されたフォローリクエストの通知を除外
-		const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest');
-		if (followRequestNotifications.length > 0) {
-			const reqs = await this.followRequestsRepository.find({
-				where: { followerId: In(followRequestNotifications.map(x => x.notifierId)) },
-			});
-			validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId));
-		}
-
-		return await Promise.all(validNotifications.map(x => this.pack(x, meId, {}, {
-			packedNotes,
-			packedUsers,
-		})));
-	}
-
-	@bindThis
-	public async packGrouped(
-		src: MiGroupedNotification,
-		meId: MiUser['id'],
-		// eslint-disable-next-line @typescript-eslint/ban-types
-		options: {
-
-		},
-		hint?: {
-			packedNotes: Map<MiNote['id'], Packed<'Note'>>;
-			packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
-		},
-	): Promise<Packed<'Notification'>> {
-		const notification = src;
-		const noteIfNeed = NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification ? (
+		const needsNote = NOTE_REQUIRED_NOTIFICATION_TYPES.has(notification.type) && 'noteId' in notification;
+		const noteIfNeed = needsNote ? (
 			hint?.packedNotes != null
 				? hint.packedNotes.get(notification.noteId)
 				: this.noteEntityService.pack(notification.noteId, { id: meId }, {
 					detail: true,
 				})
 		) : undefined;
-		const userIfNeed = 'notifierId' in notification ? (
+		// if the note has been deleted, don't show this notification
+		if (needsNote && !noteIfNeed) return null;
+
+		const needsUser = 'notifierId' in notification;
+		const userIfNeed = needsUser ? (
 			hint?.packedUsers != null
 				? hint.packedUsers.get(notification.notifierId)
 				: this.userEntityService.pack(notification.notifierId, { id: meId })
 		) : undefined;
+		// if the user has been deleted, don't show this notification
+		if (needsUser && !userIfNeed) return null;
 
+		// #region Grouped notifications
 		if (notification.type === 'reaction:grouped') {
-			const reactions = await Promise.all(notification.reactions.map(async reaction => {
+			const reactions = (await Promise.all(notification.reactions.map(async reaction => {
 				const user = hint?.packedUsers != null
 					? hint.packedUsers.get(reaction.userId)!
 					: await this.userEntityService.pack(reaction.userId, { id: meId });
@@ -183,7 +103,12 @@ export class NotificationEntityService implements OnModuleInit {
 					user,
 					reaction: reaction.reaction,
 				};
-			}));
+			}))).filter(r => isNotNull(r.user));
+			// if all users have been deleted, don't show this notification
+			if (reactions.length === 0) {
+				return null;
+			}
+
 			return await awaitAll({
 				id: notification.id,
 				createdAt: new Date(notification.createdAt).toISOString(),
@@ -192,14 +117,19 @@ export class NotificationEntityService implements OnModuleInit {
 				reactions,
 			});
 		} else if (notification.type === 'renote:grouped') {
-			const users = await Promise.all(notification.userIds.map(userId => {
+			const users = (await Promise.all(notification.userIds.map(userId => {
 				const packedUser = hint?.packedUsers != null ? hint.packedUsers.get(userId) : null;
 				if (packedUser) {
 					return packedUser;
 				}
 
 				return this.userEntityService.pack(userId, { id: meId });
-			}));
+			}))).filter(isNotNull);
+			// if all users have been deleted, don't show this notification
+			if (users.length === 0) {
+				return null;
+			}
+
 			return await awaitAll({
 				id: notification.id,
 				createdAt: new Date(notification.createdAt).toISOString(),
@@ -208,8 +138,14 @@ export class NotificationEntityService implements OnModuleInit {
 				users,
 			});
 		}
+		// #endregion
 
-		const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
+		const needsRole = notification.type === 'roleAssigned';
+		const role = needsRole ? await this.roleEntityService.pack(notification.roleId) : undefined;
+		// if the role has been deleted, don't show this notification
+		if (needsRole && !role) {
+			return null;
+		}
 
 		return await awaitAll({
 			id: notification.id,
@@ -235,15 +171,16 @@ export class NotificationEntityService implements OnModuleInit {
 		});
 	}
 
-	@bindThis
-	public async packGroupedMany(
-		notifications: MiGroupedNotification[],
+	async #packManyInternal <T extends MiNotification | MiGroupedNotification>	(
+		notifications: T[],
 		meId: MiUser['id'],
-	) {
+	): Promise<T[]> {
 		if (notifications.length === 0) return [];
 
 		let validNotifications = notifications;
 
+		validNotifications = await this.#filterValidNotifier(validNotifications, meId);
+
 		const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(isNotNull);
 		const notes = noteIds.length > 0 ? await this.notesRepository.find({
 			where: { id: In(noteIds) },
@@ -269,7 +206,7 @@ export class NotificationEntityService implements OnModuleInit {
 		const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
 
 		// 既に解決されたフォローリクエストの通知を除外
-		const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest');
+		const followRequestNotifications = validNotifications.filter((x): x is FilterUnionByProperty<T, 'type', 'receiveFollowRequest'> => x.type === 'receiveFollowRequest');
 		if (followRequestNotifications.length > 0) {
 			const reqs = await this.followRequestsRepository.find({
 				where: { followerId: In(followRequestNotifications.map(x => x.notifierId)) },
@@ -277,9 +214,107 @@ export class NotificationEntityService implements OnModuleInit {
 			validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId));
 		}
 
-		return await Promise.all(validNotifications.map(x => this.packGrouped(x, meId, {}, {
-			packedNotes,
-			packedUsers,
-		})));
+		const packPromises = validNotifications.map(x => {
+			return this.pack(
+				x,
+				meId,
+				{ checkValidNotifier: false },
+				{ packedNotes, packedUsers },
+			);
+		});
+
+		return (await Promise.all(packPromises)).filter(isNotNull);
+	}
+
+	@bindThis
+	public async pack(
+		src: MiNotification | MiGroupedNotification,
+		meId: MiUser['id'],
+		// eslint-disable-next-line @typescript-eslint/ban-types
+		options: {
+			checkValidNotifier?: boolean;
+		},
+		hint?: {
+			packedNotes: Map<MiNote['id'], Packed<'Note'>>;
+			packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
+		},
+	): Promise<Packed<'Notification'> | null> {
+		return await this.#packInternal(src, meId, options, hint);
+	}
+
+	@bindThis
+	public async packMany(
+		notifications: MiNotification[],
+		meId: MiUser['id'],
+	): Promise<MiNotification[]> {
+		return await this.#packManyInternal(notifications, meId);
+	}
+
+	@bindThis
+	public async packGroupedMany(
+		notifications: MiGroupedNotification[],
+		meId: MiUser['id'],
+	): Promise<MiGroupedNotification[]> {
+		return await this.#packManyInternal(notifications, meId);
+	}
+
+	/**
+	 * notifierが存在するか、ミュートされていないか、サスペンドされていないかを確認するvalidator
+	 */
+	#validateNotifier <T extends MiNotification | MiGroupedNotification> (
+		notification: T,
+		userIdsWhoMeMuting: Set<MiUser['id']>,
+		userMutedInstances: Set<string>,
+		notifiers: MiUser[],
+	): boolean {
+		if (!('notifierId' in notification)) return true;
+		if (userIdsWhoMeMuting.has(notification.notifierId)) return false;
+
+		const notifier = notifiers.find(x => x.id === notification.notifierId) ?? null;
+
+		if (notifier == null) return false;
+		if (notifier.host && userMutedInstances.has(notifier.host)) return false;
+
+		if (notifier.isSuspended) return false;
+
+		return true;
+	}
+
+	/**
+	 * notifierが存在するか、ミュートされていないか、サスペンドされていないかを実際に確認する
+	 */
+	async #isValidNotifier(
+		notification: MiNotification | MiGroupedNotification,
+		meId: MiUser['id'],
+	): Promise<boolean> {
+		return (await this.#filterValidNotifier([notification], meId)).length === 1;
+	}
+
+	/**
+	 * notifierが存在するか、ミュートされていないか、サスペンドされていないかを実際に複数確認する
+	 */
+	async #filterValidNotifier <T extends MiNotification | MiGroupedNotification> (
+		notifications: T[],
+		meId: MiUser['id'],
+	): Promise<T[]> {
+		const [
+			userIdsWhoMeMuting,
+			userMutedInstances,
+		] = await Promise.all([
+			this.cacheService.userMutingsCache.fetch(meId),
+			this.cacheService.userProfileCache.fetch(meId).then(p => new Set(p.mutedInstances)),
+		]);
+
+		const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(isNotNull);
+		const notifiers = notifierIds.length > 0 ? await this.usersRepository.find({
+			where: { id: In(notifierIds) },
+		}) : [];
+
+		const filteredNotifications = ((await Promise.all(notifications.map(async (notification) => {
+			const isValid = this.#validateNotifier(notification, userIdsWhoMeMuting, userMutedInstances, notifiers);
+			return isValid ? notification : null;
+		}))) as [T | null] ).filter(isNotNull);
+
+		return filteredNotifications;
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts
index 703808d2792a..dc6ffd3e02e2 100644
--- a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts
+++ b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts
@@ -3,11 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Brackets, In } from 'typeorm';
+import { In } from 'typeorm';
 import * as Redis from 'ioredis';
 import { Inject, Injectable } from '@nestjs/common';
 import type { NotesRepository } from '@/models/_.js';
-import { obsoleteNotificationTypes, notificationTypes, FilterUnionByProperty } from '@/types.js';
+import { obsoleteNotificationTypes, groupedNotificationTypes, FilterUnionByProperty } from '@/types.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteReadService } from '@/core/NoteReadService.js';
 import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js';
@@ -48,10 +48,10 @@ export const paramDef = {
 		markAsRead: { type: 'boolean', default: true },
 		// 後方互換のため、廃止された通知タイプも受け付ける
 		includeTypes: { type: 'array', items: {
-			type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes],
+			type: 'string', enum: [...groupedNotificationTypes, ...obsoleteNotificationTypes],
 		} },
 		excludeTypes: { type: 'array', items: {
-			type: 'string', enum: [...notificationTypes, ...obsoleteNotificationTypes],
+			type: 'string', enum: [...groupedNotificationTypes, ...obsoleteNotificationTypes],
 		} },
 	},
 	required: [],
@@ -79,12 +79,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return [];
 			}
 			// excludeTypes に全指定されている場合はクエリしない
-			if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) {
+			if (groupedNotificationTypes.every(type => ps.excludeTypes?.includes(type))) {
 				return [];
 			}
 
-			const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][];
-			const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][];
+			const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof groupedNotificationTypes[number][];
+			const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof groupedNotificationTypes[number][];
 
 			const limit = (ps.limit + EXTRA_LIMIT) + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1
 			const notificationsRes = await this.redisClient.xrevrange(
@@ -162,7 +162,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			groupedNotifications = groupedNotifications.slice(0, ps.limit);
-
 			const noteIds = groupedNotifications
 				.filter((notification): notification is FilterUnionByProperty<MiNotification, 'type', 'mention' | 'reply' | 'quote'> => ['mention', 'reply', 'quote'].includes(notification.type))
 				.map(notification => notification.noteId!);
diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts
index 52b6749e3fa0..320d9fdb0039 100644
--- a/packages/backend/src/server/api/endpoints/i/notifications.ts
+++ b/packages/backend/src/server/api/endpoints/i/notifications.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Brackets, In } from 'typeorm';
+import { In } from 'typeorm';
 import * as Redis from 'ioredis';
 import { Inject, Injectable } from '@nestjs/common';
 import type { NotesRepository } from '@/models/_.js';
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 506a755cc4c6..d894ef730e25 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -18,6 +18,7 @@
  * achievementEarned - 実績を獲得
  * app - アプリ通知
  * test - テスト通知(サーバー側)
+ *
  */
 export const notificationTypes = [
 	'note',
@@ -33,7 +34,15 @@ export const notificationTypes = [
 	'roleAssigned',
 	'achievementEarned',
 	'app',
-	'test'] as const;
+	'test',
+] as const;
+
+export const groupedNotificationTypes = [
+	...notificationTypes,
+	'reaction:grouped',
+	'renote:grouped',
+] as const;
+
 export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
 
 export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts
index e63067cd62ad..1e4225184af4 100644
--- a/packages/backend/test/e2e/mute.ts
+++ b/packages/backend/test/e2e/mute.ts
@@ -117,5 +117,184 @@ describe('Mute', () => {
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
 		});
+		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
+			const aliceNote = await post(alice, { text: 'hi' });
+			await post(bob, { text: '@alice hi', replyId: aliceNote.id });
+			await post(carol, { text: '@alice hi', replyId: aliceNote.id });
+
+			const res = await api('/i/notifications', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
+			await post(alice, { text: 'hi' });
+			await post(bob, { text: '@alice hi' });
+			await post(carol, { text: '@alice hi' });
+
+			const res = await api('/i/notifications', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
+			const aliceNote = await post(alice, { text: 'hi' });
+			await post(bob, { text: 'hi', renoteId: aliceNote.id });
+			await post(carol, { text: 'hi', renoteId: aliceNote.id });
+
+			const res = await api('/i/notifications', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
+			const aliceNote = await post(alice, { text: 'hi' });
+			await post(bob, { renoteId: aliceNote.id });
+			await post(carol, { renoteId: aliceNote.id });
+
+			const res = await api('/i/notifications', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
+			await api('/i/follow', { userId: alice.id }, bob);
+			await api('/i/follow', { userId: alice.id }, carol);
+
+			const res = await api('/i/notifications', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
+			await api('/i/update/', { isLocked: true }, alice);
+			await api('/following/create', { userId: alice.id }, bob);
+			await api('/following/create', { userId: alice.id }, carol);
+
+			const res = await api('/i/notifications', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+	});
+
+	describe('Notification (Grouped)', () => {
+		test('通知にミュートしているユーザーの通知が含まれない(リアクション)', async () => {
+			const aliceNote = await post(alice, { text: 'hi' });
+			await react(bob, aliceNote, 'like');
+			await react(carol, aliceNote, 'like');
+
+			const res = await api('/i/notifications-grouped', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
+			const aliceNote = await post(alice, { text: 'hi' });
+			await post(bob, { text: '@alice hi', replyId: aliceNote.id });
+			await post(carol, { text: '@alice hi', replyId: aliceNote.id });
+
+			const res = await api('/i/notifications-grouped', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
+			await post(alice, { text: 'hi' });
+			await post(bob, { text: '@alice hi' });
+			await post(carol, { text: '@alice hi' });
+
+			const res = await api('/i/notifications-grouped', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
+			const aliceNote = await post(alice, { text: 'hi' });
+			await post(bob, { text: 'hi', renoteId: aliceNote.id });
+			await post(carol, { text: 'hi', renoteId: aliceNote.id });
+
+			const res = await api('/i/notifications-grouped', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
+			const aliceNote = await post(alice, { text: 'hi' });
+			await post(bob, { renoteId: aliceNote.id });
+			await post(carol, { renoteId: aliceNote.id });
+
+			const res = await api('/i/notifications-grouped', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
+			await api('/i/follow', { userId: alice.id }, bob);
+			await api('/i/follow', { userId: alice.id }, carol);
+
+			const res = await api('/i/notifications-grouped', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
+
+		test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
+			await api('/i/update/', { isLocked: true }, alice);
+			await api('/following/create', { userId: alice.id }, bob);
+			await api('/following/create', { userId: alice.id }, carol);
+
+			const res = await api('/i/notifications-grouped', {}, alice);
+
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(Array.isArray(res.body), true);
+
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+		});
 	});
 });
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index a3597e4635b7..9a2ff7487fdf 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -17687,8 +17687,8 @@ export type operations = {
           untilId?: string;
           /** @default true */
           markAsRead?: boolean;
-          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
-          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
+          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
         };
       };
     };

From 797bb493ab9d52c0a9b87980c8f17b5323354f88 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 10:20:37 +0900
Subject: [PATCH 040/266] Update CHANGELOG.md

---
 CHANGELOG.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bb339ff26f2f..5160228696b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,7 +14,7 @@
 ## 202x.x.x (unreleased)
 
 ### General
-- 通知がミュート、凍結を考慮するようになりました
+- Enhance: 通知がミュート、凍結を考慮するようになりました
 - Enhance: サーバーごとにモデレーションノートを残せるように
 - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
 - Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加
@@ -37,7 +37,7 @@
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
 - Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
 - Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正
-- エンドポイント`admin/emoji/update`の各種修正
+- Fix: エンドポイント`admin/emoji/update`の各種修正
   - 必須パラメータを`id`または`name`のいずれかのみに
   - `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
   - `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正

From 920c3be75059546a4f5e52d4378eae7aa67680aa Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 11:10:03 +0900
Subject: [PATCH 041/266] update deps

---
 package.json                     |    8 +-
 packages/backend/package.json    |   46 +-
 packages/frontend/package.json   |   74 +-
 packages/misskey-js/package.json |    8 +-
 packages/sw/package.json         |    2 +-
 pnpm-lock.yaml                   | 1983 ++++++++++++++++--------------
 6 files changed, 1098 insertions(+), 1023 deletions(-)

diff --git a/package.json b/package.json
index 3f94448db7ce..ad77a08d18db 100644
--- a/package.json
+++ b/package.json
@@ -48,21 +48,21 @@
 		"lodash": "4.17.21"
 	},
 	"dependencies": {
-		"cssnano": "6.0.3",
+		"cssnano": "6.0.5",
 		"execa": "8.0.1",
 		"fast-glob": "3.3.2",
 		"ignore-walk": "6.0.4",
 		"js-yaml": "4.1.0",
-		"postcss": "8.4.33",
+		"postcss": "8.4.35",
 		"tar": "6.2.0",
-		"terser": "5.27.0",
+		"terser": "5.28.1",
 		"typescript": "5.3.3"
 	},
 	"devDependencies": {
 		"@typescript-eslint/eslint-plugin": "6.18.1",
 		"@typescript-eslint/parser": "6.18.1",
 		"cross-env": "7.0.3",
-		"cypress": "13.6.3",
+		"cypress": "13.6.6",
 		"eslint": "8.56.0",
 		"ncp": "2.0.0",
 		"start-server-and-test": "2.0.3"
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 1745277b4125..9b38fd6228b6 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -67,9 +67,9 @@
 	"dependencies": {
 		"@aws-sdk/client-s3": "3.412.0",
 		"@aws-sdk/lib-storage": "3.412.0",
-		"@bull-board/api": "5.14.0",
-		"@bull-board/fastify": "5.14.0",
-		"@bull-board/ui": "5.14.0",
+		"@bull-board/api": "5.14.2",
+		"@bull-board/fastify": "5.14.2",
+		"@bull-board/ui": "5.14.2",
 		"@discordapp/twemoji": "15.0.2",
 		"@fastify/accepts": "4.3.0",
 		"@fastify/cookie": "9.3.1",
@@ -79,13 +79,13 @@
 		"@fastify/multipart": "8.1.0",
 		"@fastify/static": "6.12.0",
 		"@fastify/view": "8.2.0",
-		"@misskey-dev/sharp-read-bmp": "^1.2.0",
-		"@misskey-dev/summaly": "^5.0.3",
+		"@misskey-dev/sharp-read-bmp": "1.2.0",
+		"@misskey-dev/summaly": "5.0.3",
 		"@nestjs/common": "10.2.10",
 		"@nestjs/core": "10.2.10",
 		"@nestjs/testing": "10.2.10",
 		"@peertube/http-signature": "1.7.0",
-		"@simplewebauthn/server": "9.0.2",
+		"@simplewebauthn/server": "9.0.3",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.1.10",
 		"@swc/cli": "0.1.63",
@@ -98,7 +98,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "5.1.9",
+		"bullmq": "5.4.0",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.2",
 		"chalk": "5.3.0",
@@ -115,11 +115,11 @@
 		"file-type": "19.0.0",
 		"fluent-ffmpeg": "2.1.2",
 		"form-data": "4.0.0",
-		"got": "14.1.0",
+		"got": "14.2.0",
 		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
-		"htmlescape": "^1.1.1",
-		"http-link-header": "1.1.1",
+		"htmlescape": "1.1.1",
+		"http-link-header": "1.1.2",
 		"ioredis": "5.3.2",
 		"ip-cidr": "3.1.0",
 		"ipaddr.js": "2.1.0",
@@ -128,7 +128,7 @@
 		"jsdom": "23.2.0",
 		"json5": "2.2.3",
 		"jsonld": "8.3.2",
-		"jsrsasign": "11.0.0",
+		"jsrsasign": "11.1.0",
 		"meilisearch": "0.37.0",
 		"mfm-js": "0.24.0",
 		"microformats-parser": "2.0.2",
@@ -136,10 +136,10 @@
 		"misskey-js": "workspace:*",
 		"misskey-reversi": "workspace:*",
 		"ms": "3.0.0-canary.1",
-		"nanoid": "5.0.4",
+		"nanoid": "5.0.6",
 		"nested-property": "4.0.0",
 		"node-fetch": "3.3.2",
-		"nodemailer": "6.9.8",
+		"nodemailer": "6.9.10",
 		"nsfwjs": "2.4.2",
 		"oauth": "0.10.0",
 		"oauth2orize": "1.12.0",
@@ -163,15 +163,15 @@
 		"rename": "1.0.4",
 		"rss-parser": "3.13.0",
 		"rxjs": "7.8.1",
-		"sanitize-html": "2.11.0",
+		"sanitize-html": "2.12.1",
 		"secure-json-parse": "2.7.0",
 		"sharp": "0.33.2",
 		"slacc": "0.0.10",
 		"strict-event-emitter-types": "2.0.0",
 		"stringz": "2.1.0",
-		"systeminformation": "5.21.24",
+		"systeminformation": "5.22.0",
 		"tinycolor2": "1.6.0",
-		"tmp": "0.2.1",
+		"tmp": "0.2.2",
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
 		"typeorm": "0.3.20",
@@ -185,7 +185,7 @@
 	"devDependencies": {
 		"@jest/globals": "29.7.0",
 		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@nestjs/platform-express": "10.3.1",
+		"@nestjs/platform-express": "10.3.3",
 		"@simplewebauthn/types": "9.0.1",
 		"@swc/jest": "0.2.31",
 		"@types/accepts": "1.3.7",
@@ -204,21 +204,21 @@
 		"@types/jsrsasign": "10.5.12",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.11.17",
+		"@types/node": "20.11.22",
 		"@types/node-fetch": "3.0.3",
 		"@types/nodemailer": "6.4.14",
 		"@types/oauth": "0.9.4",
 		"@types/oauth2orize": "1.11.3",
 		"@types/oauth2orize-pkce": "0.1.2",
-		"@types/pg": "8.11.0",
+		"@types/pg": "8.11.2",
 		"@types/pug": "2.0.10",
-		"@types/punycode": "2.1.3",
+		"@types/punycode": "2.1.4",
 		"@types/qrcode": "1.5.5",
 		"@types/random-seed": "0.3.5",
 		"@types/ratelimiter": "3.4.6",
 		"@types/rename": "1.0.7",
-		"@types/sanitize-html": "2.9.5",
-		"@types/semver": "7.5.6",
+		"@types/sanitize-html": "2.11.0",
+		"@types/semver": "7.5.8",
 		"@types/simple-oauth2": "5.0.7",
 		"@types/sinonjs__fake-timers": "8.1.5",
 		"@types/tinycolor2": "1.4.6",
@@ -236,7 +236,7 @@
 		"fkill": "^9.0.0",
 		"jest": "29.7.0",
 		"jest-mock": "29.7.0",
-		"nodemon": "3.0.3",
+		"nodemon": "3.1.0",
 		"pid-port": "1.0.0",
 		"simple-oauth2": "5.0.0"
 	}
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 91a391ac080a..b4b486c97994 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -27,19 +27,19 @@
 		"@syuilo/aiscript": "0.17.0",
 		"@tabler/icons-webfont": "2.44.0",
 		"@twemoji/parser": "15.0.0",
-		"@vitejs/plugin-vue": "5.0.3",
-		"@vue/compiler-sfc": "3.4.18",
+		"@vitejs/plugin-vue": "5.0.4",
+		"@vue/compiler-sfc": "3.4.21",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
 		"buraha": "0.0.1",
 		"canvas-confetti": "1.6.1",
-		"chart.js": "4.4.1",
+		"chart.js": "4.4.2",
 		"chartjs-adapter-date-fns": "3.0.0",
 		"chartjs-chart-matrix": "2.0.1",
 		"chartjs-plugin-gradient": "0.6.1",
 		"chartjs-plugin-zoom": "2.0.1",
-		"chromatic": "10.6.1",
+		"chromatic": "11.0.0",
 		"compare-versions": "6.1.0",
 		"cropperjs": "2.0.0-beta.4",
 		"date-fns": "2.30.0",
@@ -57,13 +57,13 @@
 		"misskey-reversi": "workspace:*",
 		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
-		"rollup": "4.9.6",
-		"sanitize-html": "2.11.0",
-		"sass": "1.70.0",
+		"rollup": "4.12.0",
+		"sanitize-html": "2.12.1",
+		"sass": "1.71.1",
 		"shiki": "1.0.0-beta.3",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
-		"three": "0.160.1",
+		"three": "0.161.0",
 		"throttle-debounce": "5.0.0",
 		"tinycolor2": "1.6.0",
 		"tsc-alias": "1.8.8",
@@ -71,39 +71,39 @@
 		"typescript": "5.3.3",
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
-		"vite": "5.1.0",
-		"vue": "3.4.18",
+		"vite": "5.1.4",
+		"vue": "3.4.21",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@misskey-dev/summaly": "5.0.3",
-		"@storybook/addon-actions": "8.0.0-beta.2",
-		"@storybook/addon-essentials": "8.0.0-beta.2",
-		"@storybook/addon-interactions": "8.0.0-beta.2",
-		"@storybook/addon-links": "8.0.0-beta.2",
-		"@storybook/addon-mdx-gfm": "8.0.0-beta.2",
-		"@storybook/addon-storysource": "8.0.0-beta.2",
-		"@storybook/blocks": "8.0.0-beta.2",
-		"@storybook/components": "8.0.0-beta.2",
-		"@storybook/core-events": "8.0.0-beta.2",
-		"@storybook/manager-api": "8.0.0-beta.2",
-		"@storybook/preview-api": "8.0.0-beta.2",
-		"@storybook/react": "8.0.0-beta.2",
-		"@storybook/react-vite": "8.0.0-beta.2",
-		"@storybook/test": "8.0.0-beta.2",
-		"@storybook/theming": "8.0.0-beta.2",
-		"@storybook/types": "8.0.0-beta.2",
-		"@storybook/vue3": "8.0.0-beta.2",
-		"@storybook/vue3-vite": "8.0.0-beta.2",
+		"@storybook/addon-actions": "8.0.0-beta.6",
+		"@storybook/addon-essentials": "8.0.0-beta.6",
+		"@storybook/addon-interactions": "8.0.0-beta.6",
+		"@storybook/addon-links": "8.0.0-beta.6",
+		"@storybook/addon-mdx-gfm": "8.0.0-beta.6",
+		"@storybook/addon-storysource": "8.0.0-beta.6",
+		"@storybook/blocks": "8.0.0-beta.6",
+		"@storybook/components": "8.0.0-beta.6",
+		"@storybook/core-events": "8.0.0-beta.6",
+		"@storybook/manager-api": "8.0.0-beta.6",
+		"@storybook/preview-api": "8.0.0-beta.6",
+		"@storybook/react": "8.0.0-beta.6",
+		"@storybook/react-vite": "8.0.0-beta.6",
+		"@storybook/test": "8.0.0-beta.6",
+		"@storybook/theming": "8.0.0-beta.6",
+		"@storybook/types": "8.0.0-beta.6",
+		"@storybook/vue3": "8.0.0-beta.6",
+		"@storybook/vue3-vite": "8.0.0-beta.6",
 		"@testing-library/vue": "8.0.2",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
 		"@types/matter-js": "0.19.6",
 		"@types/micromatch": "4.0.6",
-		"@types/node": "20.11.17",
-		"@types/punycode": "2.1.3",
-		"@types/sanitize-html": "2.9.5",
+		"@types/node": "20.11.22",
+		"@types/punycode": "2.1.4",
+		"@types/sanitize-html": "2.11.0",
 		"@types/throttle-debounce": "5.0.2",
 		"@types/tinycolor2": "1.4.6",
 		"@types/uuid": "9.0.8",
@@ -111,25 +111,25 @@
 		"@typescript-eslint/eslint-plugin": "6.18.1",
 		"@typescript-eslint/parser": "6.18.1",
 		"@vitest/coverage-v8": "0.34.6",
-		"@vue/runtime-core": "3.4.18",
+		"@vue/runtime-core": "3.4.21",
 		"acorn": "8.11.3",
 		"cross-env": "7.0.3",
-		"cypress": "13.6.4",
+		"cypress": "13.6.6",
 		"eslint": "8.56.0",
 		"eslint-plugin-import": "2.29.1",
-		"eslint-plugin-vue": "9.20.1",
+		"eslint-plugin-vue": "9.22.0",
 		"fast-glob": "3.3.2",
-		"happy-dom": "10.0.3",
+		"happy-dom": "13.6.2",
 		"intersection-observer": "0.12.2",
 		"micromatch": "4.0.5",
 		"msw": "2.1.7",
 		"msw-storybook-addon": "2.0.0-beta.1",
-		"nodemon": "3.0.3",
+		"nodemon": "3.1.0",
 		"prettier": "3.2.5",
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
 		"start-server-and-test": "2.0.3",
-		"storybook": "8.0.0-beta.2",
+		"storybook": "8.0.0-beta.6",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
 		"vite-plugin-turbosnap": "1.0.3",
 		"vitest": "0.34.6",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 051c63cbe179..d45c24a01773 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -38,8 +38,8 @@
 		"@microsoft/api-extractor": "7.39.1",
 		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@swc/jest": "0.2.31",
-		"@types/jest": "29.5.11",
-		"@types/node": "20.11.17",
+		"@types/jest": "29.5.12",
+		"@types/node": "20.11.22",
 		"@typescript-eslint/eslint-plugin": "6.18.1",
 		"@typescript-eslint/parser": "6.18.1",
 		"eslint": "8.56.0",
@@ -48,8 +48,8 @@
 		"jest-websocket-mock": "2.5.0",
 		"mock-socket": "9.3.1",
 		"ncp": "2.0.0",
-		"nodemon": "3.0.3",
-		"tsd": "0.30.4",
+		"nodemon": "3.1.0",
+		"tsd": "0.30.7",
 		"typescript": "5.3.3"
 	},
 	"files": [
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 244a676e8641..de38a3d5fd9a 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -19,7 +19,7 @@
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
 		"eslint": "8.56.0",
 		"eslint-plugin-import": "2.29.1",
-		"nodemon": "3.0.3",
+		"nodemon": "3.1.0",
 		"typescript": "5.3.3"
 	},
 	"type": "module"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ca86ad0445bf..26add9a11e24 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,8 +13,8 @@ importers:
   .:
     dependencies:
       cssnano:
-        specifier: 6.0.3
-        version: 6.0.3(postcss@8.4.33)
+        specifier: 6.0.5
+        version: 6.0.5(postcss@8.4.35)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -28,14 +28,14 @@ importers:
         specifier: 4.1.0
         version: 4.1.0
       postcss:
-        specifier: 8.4.33
-        version: 8.4.33
+        specifier: 8.4.35
+        version: 8.4.35
       tar:
         specifier: 6.2.0
         version: 6.2.0
       terser:
-        specifier: 5.27.0
-        version: 5.27.0
+        specifier: 5.28.1
+        version: 5.28.1
       typescript:
         specifier: 5.3.3
         version: 5.3.3
@@ -54,8 +54,8 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.6.3
-        version: 13.6.3
+        specifier: 13.6.6
+        version: 13.6.6
       eslint:
         specifier: 8.56.0
         version: 8.56.0
@@ -75,14 +75,14 @@ importers:
         specifier: 3.412.0
         version: 3.412.0(@aws-sdk/client-s3@3.412.0)
       '@bull-board/api':
-        specifier: 5.14.0
-        version: 5.14.0(@bull-board/ui@5.14.0)
+        specifier: 5.14.2
+        version: 5.14.2(@bull-board/ui@5.14.2)
       '@bull-board/fastify':
-        specifier: 5.14.0
-        version: 5.14.0
+        specifier: 5.14.2
+        version: 5.14.2
       '@bull-board/ui':
-        specifier: 5.14.0
-        version: 5.14.0
+        specifier: 5.14.2
+        version: 5.14.2
       '@discordapp/twemoji':
         specifier: 15.0.2
         version: 15.0.2
@@ -111,26 +111,26 @@ importers:
         specifier: 8.2.0
         version: 8.2.0
       '@misskey-dev/sharp-read-bmp':
-        specifier: ^1.2.0
+        specifier: 1.2.0
         version: 1.2.0
       '@misskey-dev/summaly':
-        specifier: ^5.0.3
+        specifier: 5.0.3
         version: 5.0.3
       '@nestjs/common':
         specifier: 10.2.10
         version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/core':
         specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/testing':
         specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.1)
+        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.3)
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
       '@simplewebauthn/server':
-        specifier: 9.0.2
-        version: 9.0.2
+        specifier: 9.0.3
+        version: 9.0.3
       '@sinonjs/fake-timers':
         specifier: 11.2.2
         version: 11.2.2
@@ -168,8 +168,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 5.1.9
-        version: 5.1.9
+        specifier: 5.4.0
+        version: 5.4.0
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -219,8 +219,8 @@ importers:
         specifier: 4.0.0
         version: 4.0.0
       got:
-        specifier: 14.1.0
-        version: 14.1.0
+        specifier: 14.2.0
+        version: 14.2.0
       happy-dom:
         specifier: 10.0.3
         version: 10.0.3
@@ -228,11 +228,11 @@ importers:
         specifier: 1.2.0
         version: 1.2.0
       htmlescape:
-        specifier: ^1.1.1
-        version: 1.1.1
-      http-link-header:
         specifier: 1.1.1
         version: 1.1.1
+      http-link-header:
+        specifier: 1.1.2
+        version: 1.1.2
       ioredis:
         specifier: 5.3.2
         version: 5.3.2
@@ -258,8 +258,8 @@ importers:
         specifier: 8.3.2
         version: 8.3.2
       jsrsasign:
-        specifier: 11.0.0
-        version: 11.0.0
+        specifier: 11.1.0
+        version: 11.1.0
       meilisearch:
         specifier: 0.37.0
         version: 0.37.0
@@ -282,8 +282,8 @@ importers:
         specifier: 3.0.0-canary.1
         version: 3.0.0-canary.1
       nanoid:
-        specifier: 5.0.4
-        version: 5.0.4
+        specifier: 5.0.6
+        version: 5.0.6
       nested-property:
         specifier: 4.0.0
         version: 4.0.0
@@ -291,8 +291,8 @@ importers:
         specifier: 3.3.2
         version: 3.3.2
       nodemailer:
-        specifier: 6.9.8
-        version: 6.9.8
+        specifier: 6.9.10
+        version: 6.9.10
       nsfwjs:
         specifier: 2.4.2
         version: 2.4.2(@tensorflow/tfjs@4.4.0)
@@ -363,8 +363,8 @@ importers:
         specifier: 7.8.1
         version: 7.8.1
       sanitize-html:
-        specifier: 2.11.0
-        version: 2.11.0
+        specifier: 2.12.1
+        version: 2.12.1
       secure-json-parse:
         specifier: 2.7.0
         version: 2.7.0
@@ -381,14 +381,14 @@ importers:
         specifier: 2.1.0
         version: 2.1.0
       systeminformation:
-        specifier: 5.21.24
-        version: 5.21.24
+        specifier: 5.22.0
+        version: 5.22.0
       tinycolor2:
         specifier: 1.6.0
         version: 1.6.0
       tmp:
-        specifier: 0.2.1
-        version: 0.2.1
+        specifier: 0.2.2
+        version: 0.2.2
       tsc-alias:
         specifier: 1.8.8
         version: 1.8.8
@@ -512,8 +512,8 @@ importers:
         specifier: 1.0.0
         version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
       '@nestjs/platform-express':
-        specifier: 10.3.1
-        version: 10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
+        specifier: 10.3.3
+        version: 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
       '@simplewebauthn/types':
         specifier: 9.0.1
         version: 9.0.1
@@ -569,8 +569,8 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.11.17
-        version: 20.11.17
+        specifier: 20.11.22
+        version: 20.11.22
       '@types/node-fetch':
         specifier: 3.0.3
         version: 3.0.3
@@ -587,14 +587,14 @@ importers:
         specifier: 0.1.2
         version: 0.1.2
       '@types/pg':
-        specifier: 8.11.0
-        version: 8.11.0
+        specifier: 8.11.2
+        version: 8.11.2
       '@types/pug':
         specifier: 2.0.10
         version: 2.0.10
       '@types/punycode':
-        specifier: 2.1.3
-        version: 2.1.3
+        specifier: 2.1.4
+        version: 2.1.4
       '@types/qrcode':
         specifier: 1.5.5
         version: 1.5.5
@@ -608,11 +608,11 @@ importers:
         specifier: 1.0.7
         version: 1.0.7
       '@types/sanitize-html':
-        specifier: 2.9.5
-        version: 2.9.5
+        specifier: 2.11.0
+        version: 2.11.0
       '@types/semver':
-        specifier: 7.5.6
-        version: 7.5.6
+        specifier: 7.5.8
+        version: 7.5.8
       '@types/simple-oauth2':
         specifier: 5.0.7
         version: 5.0.7
@@ -660,13 +660,13 @@ importers:
         version: 9.0.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.11.17)
+        version: 29.7.0(@types/node@20.11.22)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
       nodemon:
-        specifier: 3.0.3
-        version: 3.0.3
+        specifier: 3.1.0
+        version: 3.1.0
       pid-port:
         specifier: 1.0.0
         version: 1.0.0
@@ -690,13 +690,13 @@ importers:
         version: 2024.1.0
       '@rollup/plugin-json':
         specifier: 6.1.0
-        version: 6.1.0(rollup@4.9.6)
+        version: 6.1.0(rollup@4.12.0)
       '@rollup/plugin-replace':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.9.6)
+        version: 5.0.5(rollup@4.12.0)
       '@rollup/pluginutils':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.9.6)
+        version: 5.1.0(rollup@4.12.0)
       '@syuilo/aiscript':
         specifier: 0.17.0
         version: 0.17.0
@@ -707,11 +707,11 @@ importers:
         specifier: 15.0.0
         version: 15.0.0
       '@vitejs/plugin-vue':
-        specifier: 5.0.3
-        version: 5.0.3(vite@5.1.0)(vue@3.4.18)
+        specifier: 5.0.4
+        version: 5.0.4(vite@5.1.4)(vue@3.4.21)
       '@vue/compiler-sfc':
-        specifier: 3.4.18
-        version: 3.4.18
+        specifier: 3.4.21
+        version: 3.4.21
       aiscript-vscode:
         specifier: github:aiscript-dev/aiscript-vscode#v0.1.2
         version: github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07
@@ -728,23 +728,23 @@ importers:
         specifier: 1.6.1
         version: 1.6.1
       chart.js:
-        specifier: 4.4.1
-        version: 4.4.1
+        specifier: 4.4.2
+        version: 4.4.2
       chartjs-adapter-date-fns:
         specifier: 3.0.0
-        version: 3.0.0(chart.js@4.4.1)(date-fns@2.30.0)
+        version: 3.0.0(chart.js@4.4.2)(date-fns@2.30.0)
       chartjs-chart-matrix:
         specifier: 2.0.1
-        version: 2.0.1(chart.js@4.4.1)
+        version: 2.0.1(chart.js@4.4.2)
       chartjs-plugin-gradient:
         specifier: 0.6.1
-        version: 0.6.1(chart.js@4.4.1)
+        version: 0.6.1(chart.js@4.4.2)
       chartjs-plugin-zoom:
         specifier: 2.0.1
-        version: 2.0.1(chart.js@4.4.1)
+        version: 2.0.1(chart.js@4.4.2)
       chromatic:
-        specifier: 10.6.1
-        version: 10.6.1
+        specifier: 11.0.0
+        version: 11.0.0
       compare-versions:
         specifier: 6.1.0
         version: 6.1.0
@@ -797,14 +797,14 @@ importers:
         specifier: 2.3.1
         version: 2.3.1
       rollup:
-        specifier: 4.9.6
-        version: 4.9.6
+        specifier: 4.12.0
+        version: 4.12.0
       sanitize-html:
-        specifier: 2.11.0
-        version: 2.11.0
+        specifier: 2.12.1
+        version: 2.12.1
       sass:
-        specifier: 1.70.0
-        version: 1.70.0
+        specifier: 1.71.1
+        version: 1.71.1
       shiki:
         specifier: 1.0.0-beta.3
         version: 1.0.0-beta.3
@@ -815,8 +815,8 @@ importers:
         specifier: 3.1.0
         version: 3.1.0
       three:
-        specifier: 0.160.1
-        version: 0.160.1
+        specifier: 0.161.0
+        version: 0.161.0
       throttle-debounce:
         specifier: 5.0.0
         version: 5.0.0
@@ -837,16 +837,16 @@ importers:
         version: 9.0.1
       v-code-diff:
         specifier: 1.7.2
-        version: 1.7.2(vue@3.4.18)
+        version: 1.7.2(vue@3.4.21)
       vite:
-        specifier: 5.1.0
-        version: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
+        specifier: 5.1.4
+        version: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
       vue:
-        specifier: 3.4.18
-        version: 3.4.18(typescript@5.3.3)
+        specifier: 3.4.21
+        version: 3.4.21(typescript@5.3.3)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.4.18)
+        version: 4.1.0(vue@3.4.21)
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
@@ -855,62 +855,62 @@ importers:
         specifier: 5.0.3
         version: 5.0.3
       '@storybook/addon-actions':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6
       '@storybook/addon-essentials':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-interactions':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6
       '@storybook/addon-links':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(react@18.2.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(react@18.2.0)
       '@storybook/addon-mdx-gfm':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6
       '@storybook/addon-storysource':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6
       '@storybook/blocks':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       '@storybook/components':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6
       '@storybook/manager-api':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6
       '@storybook/react':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       '@storybook/react-vite':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.1.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4)
       '@storybook/test':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(vitest@0.34.6)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(vitest@0.34.6)
       '@storybook/theming':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
       '@storybook/types':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6
       '@storybook/vue3':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(vue@3.4.18)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(vue@3.4.21)
       '@storybook/vue3-vite':
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)(vite@5.1.0)(vue@3.4.18)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21)
       '@testing-library/vue':
         specifier: 8.0.2
-        version: 8.0.2(@vue/compiler-sfc@3.4.18)(vue@3.4.18)
+        version: 8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21)
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -924,14 +924,14 @@ importers:
         specifier: 4.0.6
         version: 4.0.6
       '@types/node':
-        specifier: 20.11.17
-        version: 20.11.17
+        specifier: 20.11.22
+        version: 20.11.22
       '@types/punycode':
-        specifier: 2.1.3
-        version: 2.1.3
+        specifier: 2.1.4
+        version: 2.1.4
       '@types/sanitize-html':
-        specifier: 2.9.5
-        version: 2.9.5
+        specifier: 2.11.0
+        version: 2.11.0
       '@types/throttle-debounce':
         specifier: 5.0.2
         version: 5.0.2
@@ -954,8 +954,8 @@ importers:
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
       '@vue/runtime-core':
-        specifier: 3.4.18
-        version: 3.4.18
+        specifier: 3.4.21
+        version: 3.4.21
       acorn:
         specifier: 8.11.3
         version: 8.11.3
@@ -963,8 +963,8 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.6.4
-        version: 13.6.4
+        specifier: 13.6.6
+        version: 13.6.6
       eslint:
         specifier: 8.56.0
         version: 8.56.0
@@ -972,14 +972,14 @@ importers:
         specifier: 2.29.1
         version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)
       eslint-plugin-vue:
-        specifier: 9.20.1
-        version: 9.20.1(eslint@8.56.0)
+        specifier: 9.22.0
+        version: 9.22.0(eslint@8.56.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
       happy-dom:
-        specifier: 10.0.3
-        version: 10.0.3
+        specifier: 13.6.2
+        version: 13.6.2
       intersection-observer:
         specifier: 0.12.2
         version: 0.12.2
@@ -993,8 +993,8 @@ importers:
         specifier: 2.0.0-beta.1
         version: 2.0.0-beta.1(msw@2.1.7)
       nodemon:
-        specifier: 3.0.3
-        version: 3.0.3
+        specifier: 3.1.0
+        version: 3.1.0
       prettier:
         specifier: 3.2.5
         version: 3.2.5
@@ -1008,17 +1008,17 @@ importers:
         specifier: 2.0.3
         version: 2.0.3
       storybook:
-        specifier: 8.0.0-beta.2
-        version: 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.0-beta.6
+        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
-        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.2)(@storybook/components@8.0.0-beta.2)(@storybook/core-events@8.0.0-beta.2)(@storybook/manager-api@8.0.0-beta.2)(@storybook/preview-api@8.0.0-beta.2)(@storybook/theming@8.0.0-beta.2)(@storybook/types@8.0.0-beta.2)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0)
       vite-plugin-turbosnap:
         specifier: 1.0.3
         version: 1.0.3
       vitest:
         specifier: 0.34.6
-        version: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
+        version: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
       vitest-fetch-mock:
         specifier: 0.2.2
         version: 0.2.2(vitest@0.34.6)
@@ -1095,7 +1095,7 @@ importers:
     devDependencies:
       '@microsoft/api-extractor':
         specifier: 7.39.1
-        version: 7.39.1(@types/node@20.11.17)
+        version: 7.39.1(@types/node@20.11.22)
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
         version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
@@ -1103,11 +1103,11 @@ importers:
         specifier: 0.2.31
         version: 0.2.31(@swc/core@1.3.105)
       '@types/jest':
-        specifier: 29.5.11
-        version: 29.5.11
+        specifier: 29.5.12
+        version: 29.5.12
       '@types/node':
-        specifier: 20.11.17
-        version: 20.11.17
+        specifier: 20.11.22
+        version: 20.11.22
       '@typescript-eslint/eslint-plugin':
         specifier: 6.18.1
         version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
@@ -1119,7 +1119,7 @@ importers:
         version: 8.56.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.11.17)
+        version: 29.7.0(@types/node@20.11.22)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3
@@ -1133,11 +1133,11 @@ importers:
         specifier: 2.0.0
         version: 2.0.0
       nodemon:
-        specifier: 3.0.3
-        version: 3.0.3
+        specifier: 3.1.0
+        version: 3.1.0
       tsd:
-        specifier: 0.30.4
-        version: 0.30.4
+        specifier: 0.30.7
+        version: 0.30.7
       typescript:
         specifier: 5.3.3
         version: 5.3.3
@@ -1240,8 +1240,8 @@ importers:
         specifier: 2.29.1
         version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)
       nodemon:
-        specifier: 3.0.3
-        version: 3.0.3
+        specifier: 3.1.0
+        version: 3.1.0
       typescript:
         specifier: 5.3.3
         version: 5.3.3
@@ -1899,6 +1899,29 @@ packages:
       - supports-color
     dev: true
 
+  /@babel/core@7.24.0:
+    resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@ampproject/remapping': 2.2.1
+      '@babel/code-frame': 7.23.5
+      '@babel/generator': 7.23.6
+      '@babel/helper-compilation-targets': 7.23.6
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
+      '@babel/helpers': 7.24.0
+      '@babel/parser': 7.24.0
+      '@babel/template': 7.24.0
+      '@babel/traverse': 7.24.0
+      '@babel/types': 7.24.0
+      convert-source-map: 2.0.0
+      debug: 4.3.4(supports-color@8.1.1)
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@babel/generator@7.23.5:
     resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==}
     engines: {node: '>=6.9.0'}
@@ -1909,6 +1932,16 @@ packages:
       jsesc: 2.5.2
     dev: true
 
+  /@babel/generator@7.23.6:
+    resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.24.0
+      '@jridgewell/gen-mapping': 0.3.2
+      '@jridgewell/trace-mapping': 0.3.18
+      jsesc: 2.5.2
+    dev: true
+
   /@babel/helper-annotate-as-pure@7.22.5:
     resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
     engines: {node: '>=6.9.0'}
@@ -1934,6 +1967,17 @@ packages:
       semver: 6.3.1
     dev: true
 
+  /@babel/helper-compilation-targets@7.23.6:
+    resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/compat-data': 7.23.5
+      '@babel/helper-validator-option': 7.23.5
+      browserslist: 4.22.2
+      lru-cache: 5.1.1
+      semver: 6.3.1
+    dev: true
+
   /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5):
     resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
     engines: {node: '>=6.9.0'}
@@ -2027,6 +2071,20 @@ packages:
       '@babel/helper-validator-identifier': 7.22.20
     dev: true
 
+  /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-module-imports': 7.22.15
+      '@babel/helper-simple-access': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/helper-validator-identifier': 7.22.20
+    dev: true
+
   /@babel/helper-optimise-call-expression@7.22.5:
     resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
     engines: {node: '>=6.9.0'}
@@ -2117,6 +2175,17 @@ packages:
       - supports-color
     dev: true
 
+  /@babel/helpers@7.24.0:
+    resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.24.0
+      '@babel/traverse': 7.24.0
+      '@babel/types': 7.24.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@babel/highlight@7.23.4:
     resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
     engines: {node: '>=6.9.0'}
@@ -2133,6 +2202,14 @@ packages:
     dependencies:
       '@babel/types': 7.23.5
 
+  /@babel/parser@7.24.0:
+    resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.24.0
+    dev: true
+
   /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
     engines: {node: '>=6.9.0'}
@@ -3100,6 +3177,15 @@ packages:
       '@babel/types': 7.23.5
     dev: true
 
+  /@babel/template@7.24.0:
+    resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.23.5
+      '@babel/parser': 7.24.0
+      '@babel/types': 7.24.0
+    dev: true
+
   /@babel/traverse@7.23.5:
     resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
     engines: {node: '>=6.9.0'}
@@ -3118,6 +3204,24 @@ packages:
       - supports-color
     dev: true
 
+  /@babel/traverse@7.24.0:
+    resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.23.5
+      '@babel/generator': 7.23.6
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-function-name': 7.23.0
+      '@babel/helper-hoist-variables': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/parser': 7.24.0
+      '@babel/types': 7.24.0
+      debug: 4.3.4(supports-color@8.1.1)
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@babel/types@7.23.5:
     resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
     engines: {node: '>=6.9.0'}
@@ -3126,6 +3230,15 @@ packages:
       '@babel/helper-validator-identifier': 7.22.20
       to-fast-properties: 2.0.0
 
+  /@babel/types@7.24.0:
+    resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.23.4
+      '@babel/helper-validator-identifier': 7.22.20
+      to-fast-properties: 2.0.0
+    dev: true
+
   /@base2/pretty-print-object@1.0.1:
     resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==}
     dev: true
@@ -3134,29 +3247,29 @@ packages:
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
     dev: true
 
-  /@bull-board/api@5.14.0(@bull-board/ui@5.14.0):
-    resolution: {integrity: sha512-ppN9GeCH8QmCzs47CpDFwVb4Q5W2nK2QvcnbxKpjktCTonZ+5PnoWyXQvLStbcKU9SbMKAM0/OXhj4xOcSRllQ==}
+  /@bull-board/api@5.14.2(@bull-board/ui@5.14.2):
+    resolution: {integrity: sha512-0wppAGPU7ZMwWMpzkmtrlmm7ySI5immymyaRS1cVNJ54rUiGOZP5tnm+Sj7MwPdf63rxqIM843un8+PvQyARGg==}
     peerDependencies:
-      '@bull-board/ui': 5.14.0
+      '@bull-board/ui': 5.14.2
     dependencies:
-      '@bull-board/ui': 5.14.0
+      '@bull-board/ui': 5.14.2
       redis-info: 3.1.0
     dev: false
 
-  /@bull-board/fastify@5.14.0:
-    resolution: {integrity: sha512-MEZbfUY74wL2dc9OJZGgYABZADlohp62MP1ZMOlC+6ZF4i7X95yxTQ9DmtIV6kkva7+abJgFGNUhtKi7Mq15Fg==}
+  /@bull-board/fastify@5.14.2:
+    resolution: {integrity: sha512-GQMK70tKOu2gjBi2pjWXMXcftzWRvQNSm+deLmGlJUgqUUbNlzIGRyvaTk7giT4CFzgKcP+hT+lphcAsGTKBQw==}
     dependencies:
-      '@bull-board/api': 5.14.0(@bull-board/ui@5.14.0)
-      '@bull-board/ui': 5.14.0
+      '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
+      '@bull-board/ui': 5.14.2
       '@fastify/static': 6.12.0
       '@fastify/view': 8.2.0
       ejs: 3.1.9
     dev: false
 
-  /@bull-board/ui@5.14.0:
-    resolution: {integrity: sha512-quustWmLsLbqdbCQd4Mud9Eo/2BQzfJSNSiyJt9OrtYT4AXHMgGtbFUy2Ycyda7iQjC4ScKl8f+WdFs4y+KUJA==}
+  /@bull-board/ui@5.14.2:
+    resolution: {integrity: sha512-NiyKWLjKjy29I6ySCnSYbzGX4ZJyPE4xlS5/Z5dVsF2bJLoAV+yD1obflxteJMt60FiEgLV7tfs6tMSVa+Htew==}
     dependencies:
-      '@bull-board/api': 5.14.0(@bull-board/ui@5.14.0)
+      '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
     dev: false
 
   /@bundled-es-modules/cookie@2.0.0:
@@ -3175,54 +3288,6 @@ packages:
     resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==}
     dev: false
 
-  /@cbor-extract/cbor-extract-darwin-arm64@2.1.1:
-    resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
-    optional: true
-
-  /@cbor-extract/cbor-extract-darwin-x64@2.1.1:
-    resolution: {integrity: sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
-    optional: true
-
-  /@cbor-extract/cbor-extract-linux-arm64@2.1.1:
-    resolution: {integrity: sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
-    optional: true
-
-  /@cbor-extract/cbor-extract-linux-arm@2.1.1:
-    resolution: {integrity: sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: false
-    optional: true
-
-  /@cbor-extract/cbor-extract-linux-x64@2.1.1:
-    resolution: {integrity: sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
-    optional: true
-
-  /@cbor-extract/cbor-extract-win32-x64@2.1.1:
-    resolution: {integrity: sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: false
-    optional: true
-
   /@colors/colors@1.5.0:
     resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
     engines: {node: '>=0.1.90'}
@@ -4255,7 +4320,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -4276,14 +4341,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.7.1
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.11.17)
+      jest-config: 29.7.0(@types/node@20.11.22)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -4318,7 +4383,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       jest-mock: 29.7.0
     dev: true
 
@@ -4345,7 +4410,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -4378,7 +4443,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -4465,7 +4530,7 @@ packages:
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       '@types/yargs': 16.0.5
       chalk: 4.1.2
     dev: true
@@ -4477,12 +4542,12 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       '@types/yargs': 17.0.19
       chalk: 4.1.2
     dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.1.0):
+  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.1.4):
     resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
     peerDependencies:
       typescript: '>= 4.3.x'
@@ -4496,7 +4561,7 @@ packages:
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.3.3)
       typescript: 5.3.3
-      vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
+      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4541,6 +4606,10 @@ packages:
     resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
     dev: false
 
+  /@levischuck/tiny-cbor@0.2.2:
+    resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==}
+    dev: false
+
   /@lukeed/csprng@1.0.1:
     resolution: {integrity: sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==}
     engines: {node: '>=8'}
@@ -4591,24 +4660,24 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@microsoft/api-extractor-model@7.28.4(@types/node@20.11.17):
+  /@microsoft/api-extractor-model@7.28.4(@types/node@20.11.22):
     resolution: {integrity: sha512-vucgyPmgHrJ/D4/xQywAmjTmSfxAx2/aDmD6TkIoLu51FdsAfuWRbijWA48AePy60OO+l+mmy9p2P/CEeBZqig==}
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.63.0(@types/node@20.11.17)
+      '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22)
     transitivePeerDependencies:
       - '@types/node'
     dev: true
 
-  /@microsoft/api-extractor@7.39.1(@types/node@20.11.17):
+  /@microsoft/api-extractor@7.39.1(@types/node@20.11.22):
     resolution: {integrity: sha512-V0HtCufWa8hZZvSmlEzQZfINcJkHAU/bmpyJQj6w+zpI87EkR8DuBOW6RWrO9c7mUYFZoDaNgUTyKo83ytv+QQ==}
     hasBin: true
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.4(@types/node@20.11.17)
+      '@microsoft/api-extractor-model': 7.28.4(@types/node@20.11.22)
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.63.0(@types/node@20.11.17)
+      '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22)
       '@rushstack/rig-package': 0.5.1
       '@rushstack/ts-command-line': 4.17.1
       colors: 1.2.5
@@ -4792,7 +4861,7 @@ packages:
       tslib: 2.6.2
       uid: 2.0.2
 
-  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1):
+  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1):
     resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
     requiresBuild: true
     peerDependencies:
@@ -4811,7 +4880,7 @@ packages:
         optional: true
     dependencies:
       '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/platform-express': 10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
+      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
@@ -4823,14 +4892,14 @@ packages:
     transitivePeerDependencies:
       - encoding
 
-  /@nestjs/platform-express@10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10):
-    resolution: {integrity: sha512-Rj21quI5h4Lry7q9an+nO4ADQiQUy9A6XK74o5aTUHo3Ysm25ujqh2NgU4XbT3M2oXU9qzhE59OfhkQ7ZUvTAg==}
+  /@nestjs/platform-express@10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10):
+    resolution: {integrity: sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/core': ^10.0.0
     dependencies:
       '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
       body-parser: 1.20.2
       cors: 2.8.5
       express: 4.18.2
@@ -4839,7 +4908,7 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.1):
+  /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.3):
     resolution: {integrity: sha512-IVLUnPz/+fkBtPATYfqTIP+phN9yjkXejmj+JyhmcfPJZpxBmD1i9VSMqa4u54l37j0xkGPscQ0IXpbhqMYUKw==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
@@ -4853,8 +4922,8 @@ packages:
         optional: true
     dependencies:
       '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/platform-express': 10.3.1(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
       tslib: 2.6.2
     dev: false
 
@@ -5074,7 +5143,7 @@ packages:
       openapi-types: 12.1.3
     dev: true
 
-  /@rollup/plugin-json@6.1.0(rollup@4.9.6):
+  /@rollup/plugin-json@6.1.0(rollup@4.12.0):
     resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5083,11 +5152,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.9.6)
-      rollup: 4.9.6
+      '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
+      rollup: 4.12.0
     dev: false
 
-  /@rollup/plugin-replace@5.0.5(rollup@4.9.6):
+  /@rollup/plugin-replace@5.0.5(rollup@4.12.0):
     resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5096,12 +5165,12 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.9.6)
+      '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
       magic-string: 0.30.7
-      rollup: 4.9.6
+      rollup: 4.12.0
     dev: false
 
-  /@rollup/pluginutils@5.1.0(rollup@4.9.6):
+  /@rollup/pluginutils@5.1.0(rollup@4.12.0):
     resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5113,100 +5182,100 @@ packages:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.9.6
+      rollup: 4.12.0
 
-  /@rollup/rollup-android-arm-eabi@4.9.6:
-    resolution: {integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==}
+  /@rollup/rollup-android-arm-eabi@4.12.0:
+    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.9.6:
-    resolution: {integrity: sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==}
+  /@rollup/rollup-android-arm64@4.12.0:
+    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.9.6:
-    resolution: {integrity: sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==}
+  /@rollup/rollup-darwin-arm64@4.12.0:
+    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.9.6:
-    resolution: {integrity: sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==}
+  /@rollup/rollup-darwin-x64@4.12.0:
+    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.9.6:
-    resolution: {integrity: sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==}
+  /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
+    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.9.6:
-    resolution: {integrity: sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==}
+  /@rollup/rollup-linux-arm64-gnu@4.12.0:
+    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.9.6:
-    resolution: {integrity: sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==}
+  /@rollup/rollup-linux-arm64-musl@4.12.0:
+    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-riscv64-gnu@4.9.6:
-    resolution: {integrity: sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==}
+  /@rollup/rollup-linux-riscv64-gnu@4.12.0:
+    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.9.6:
-    resolution: {integrity: sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==}
+  /@rollup/rollup-linux-x64-gnu@4.12.0:
+    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.9.6:
-    resolution: {integrity: sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==}
+  /@rollup/rollup-linux-x64-musl@4.12.0:
+    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.9.6:
-    resolution: {integrity: sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==}
+  /@rollup/rollup-win32-arm64-msvc@4.12.0:
+    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.9.6:
-    resolution: {integrity: sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==}
+  /@rollup/rollup-win32-ia32-msvc@4.12.0:
+    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.9.6:
-    resolution: {integrity: sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==}
+  /@rollup/rollup-win32-x64-msvc@4.12.0:
+    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rushstack/node-core-library@3.63.0(@types/node@20.11.17):
+  /@rushstack/node-core-library@3.63.0(@types/node@20.11.22):
     resolution: {integrity: sha512-Q7B3dVpBQF1v+mUfxNcNZh5uHVR8ntcnkN5GYjbBLrxUYHBGKbnCM+OdcN+hzCpFlLBH6Ob0dEHhZ0spQwf24A==}
     peerDependencies:
       '@types/node': '*'
@@ -5214,7 +5283,7 @@ packages:
       '@types/node':
         optional: true
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       colors: 1.2.5
       fs-extra: 7.0.1
       import-lazy: 4.0.0
@@ -5258,18 +5327,18 @@ packages:
     resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
     dev: true
 
-  /@simplewebauthn/server@9.0.2:
-    resolution: {integrity: sha512-aaWA+qVOU4byk5IDb/l+M1+7dmrAJhTb4ISJHucpsgRQcMMEes76tbGIqO2JQuA7N50tc/OBrnGKBjoKYG1kSw==}
+  /@simplewebauthn/server@9.0.3:
+    resolution: {integrity: sha512-FMZieoBosrVLFxCnxPFD9Enhd1U7D8nidVDT4MsHc6l4fdVcjoeHjDueeXCloO1k5O/fZg1fsSXXPKbY2XTzDA==}
     engines: {node: '>=16.0.0'}
     dependencies:
       '@hexagon/base64': 1.1.27
+      '@levischuck/tiny-cbor': 0.2.2
       '@peculiar/asn1-android': 2.3.10
       '@peculiar/asn1-ecc': 2.3.8
       '@peculiar/asn1-rsa': 2.3.8
       '@peculiar/asn1-schema': 2.3.8
       '@peculiar/asn1-x509': 2.3.8
       '@simplewebauthn/types': 9.0.1
-      cbor-x: 1.5.4
       cross-fetch: 4.0.0
     transitivePeerDependencies:
       - encoding
@@ -5762,10 +5831,10 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@storybook/addon-actions@8.0.0-beta.2:
-    resolution: {integrity: sha512-sw51iot8E4aZP+z96fWLG7idrpCj/LqTV5lOcE06MU3T6/mW0OqoS7nFF+ncOtjcDsOjValmLiVQCL8m759mTQ==}
+  /@storybook/addon-actions@8.0.0-beta.6:
+    resolution: {integrity: sha512-g+X2M6Awg21vkXzRP7hWBYCdbXnxJ3BJWsP7BblYmPo2J7eJDzhQascNyTmSr0pb1/7nv+tworGviXThgvlUgw==}
     dependencies:
-      '@storybook/core-events': 8.0.0-beta.2
+      '@storybook/core-events': 8.0.0-beta.6
       '@storybook/global': 5.0.0
       '@types/uuid': 9.0.8
       dequal: 2.0.3
@@ -5773,18 +5842,18 @@ packages:
       uuid: 9.0.1
     dev: true
 
-  /@storybook/addon-backgrounds@8.0.0-beta.2:
-    resolution: {integrity: sha512-cyDbV7srhuh/qaEMCvfz4dTLwnJV0VjHMivLtqSZgzhU24kekc7145KnLOOpDKzEQiAl1mVXb/7HBrykQcbKtg==}
+  /@storybook/addon-backgrounds@8.0.0-beta.6:
+    resolution: {integrity: sha512-C8MS635knAOSat5JbkpZXOiAqkDm1bKWvuVqiQfbX2into45/aAuyN3mYxveGIRTRjPJCv/UpostkLSNvfH/NQ==}
     dependencies:
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-controls@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-9rvjv4Er7WJkSeXPvCJ78GnKeUqbc7NFGZVlWl2gS3gFeLrXRgtrA5raOR+XneI51UtvAPZX89Mdeg/bQueUvQ==}
+  /@storybook/addon-controls@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-G96MH7yU/KShq3lTrkgtU1IbNQXLVc3BG7miaLqzQgWFN8SSAivlu3vk1Vffui3+3Dv52WZhMKi3hueNfnM1Xw==}
     dependencies:
-      '@storybook/blocks': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       lodash: 4.17.21
       ts-dedent: 2.2.0
     transitivePeerDependencies:
@@ -5795,22 +5864,22 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-docs@8.0.0-beta.2:
-    resolution: {integrity: sha512-ax9Nto8pXGmNh13IfYalBoQ/6YLYjlQkhURM5eGDqhz6lZdMLQZF/GMz3gMwSXTD8edcfamXtmMOfzWc8qR1kw==}
+  /@storybook/addon-docs@8.0.0-beta.6:
+    resolution: {integrity: sha512-VLys4EuL8XVhmu1QxUiUG5keID8v/FsC5L71Y0Wcf5D+ll6ZD8vCqEtbMY3TiJJ9NqqNIcmcG3bG6JVXOYcD8g==}
     dependencies:
       '@babel/core': 7.23.5
       '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.2.0)
-      '@storybook/blocks': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/components': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-plugin': 8.0.0-beta.2
-      '@storybook/csf-tools': 8.0.0-beta.2
+      '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-plugin': 8.0.0-beta.6
+      '@storybook/csf-tools': 8.0.0-beta.6
       '@storybook/global': 5.0.0
-      '@storybook/node-logger': 8.0.0-beta.2
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/react-dom-shim': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.0-beta.6
       '@types/react': 18.0.28
       fs-extra: 11.1.1
       react: 18.2.0
@@ -5823,22 +5892,22 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-essentials@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-zB1sRf/ynxddBcWkzxZ55YVN5trbh2sMh9iPA+MLmKwz/tWK+f8/EoV8jfevu1ou2MS/2Jkjyk90jyZEXloVjg==}
-    dependencies:
-      '@storybook/addon-actions': 8.0.0-beta.2
-      '@storybook/addon-backgrounds': 8.0.0-beta.2
-      '@storybook/addon-controls': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-docs': 8.0.0-beta.2
-      '@storybook/addon-highlight': 8.0.0-beta.2
-      '@storybook/addon-measure': 8.0.0-beta.2
-      '@storybook/addon-outline': 8.0.0-beta.2
-      '@storybook/addon-toolbars': 8.0.0-beta.2
-      '@storybook/addon-viewport': 8.0.0-beta.2
-      '@storybook/core-common': 8.0.0-beta.2
-      '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 8.0.0-beta.2
-      '@storybook/preview-api': 8.0.0-beta.2
+  /@storybook/addon-essentials@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-6Vjf03c0oIavXqOK9DIN0UeH0iJFmBoVrFt1mTwydMxchyJBSP785MSd9DuFhLdYZPQTMHaR4/JhOIjdDV8mbA==}
+    dependencies:
+      '@storybook/addon-actions': 8.0.0-beta.6
+      '@storybook/addon-backgrounds': 8.0.0-beta.6
+      '@storybook/addon-controls': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-docs': 8.0.0-beta.6
+      '@storybook/addon-highlight': 8.0.0-beta.6
+      '@storybook/addon-measure': 8.0.0-beta.6
+      '@storybook/addon-outline': 8.0.0-beta.6
+      '@storybook/addon-toolbars': 8.0.0-beta.6
+      '@storybook/addon-viewport': 8.0.0-beta.6
+      '@storybook/core-common': 8.0.0-beta.6
+      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/preview-api': 8.0.0-beta.6
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@types/react'
@@ -5848,24 +5917,24 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-highlight@8.0.0-beta.2:
-    resolution: {integrity: sha512-Y5/I4WkhcwiE6/p3kaWz+wN1IMr6GNK8ytxsVnIQHOCUfpu1lArGuHzU4E6nN7/bmXahDO+Hz3dWGdnS5YeLXw==}
+  /@storybook/addon-highlight@8.0.0-beta.6:
+    resolution: {integrity: sha512-U+qz4TNLrw24t1eZ2Zmhl2FZKZKiwHbibq4qR5ruAFe9W5/aMHqPuBB0POroaGu3P+tyDP2G46dckMNXVraiWA==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/addon-interactions@8.0.0-beta.2:
-    resolution: {integrity: sha512-L4XLTkF8z3f6V9Z61N+t/8i1d0tECyHkaeexsRjWgXaiJst+9iSdDFCApalxemLzI6mA8tIiOkRH0+DqewvpNQ==}
+  /@storybook/addon-interactions@8.0.0-beta.6:
+    resolution: {integrity: sha512-KSigq+7vCA1tnj31MjhM7xaqickR1guZdjyXVRx7gi7qbdhSuCQv52gAkVpDapwlEuvGFCCYxzt7tmcn6dkLZQ==}
     dependencies:
       '@storybook/global': 5.0.0
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/types': 8.0.0-beta.6
       jest-mock: 27.5.1
       polished: 4.2.2
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-links@8.0.0-beta.2(react@18.2.0):
-    resolution: {integrity: sha512-hP1sBcG7/yVz6s81xW3mMS39G4rGcBiw4PWLKmILCqpBhAyog9EGXJrKrYMTdDlX9EcPd11fHbdLgRNw+UPIDg==}
+  /@storybook/addon-links@8.0.0-beta.6(react@18.2.0):
+    resolution: {integrity: sha512-+5knw5CHEb23n6Bm9Xp9nmoLRqWZ3QVGb1gNI3mGwmkpLwesohFR4fW7OrdRmzYHpS0PyYToZyfTCMYrmjBDvg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
     peerDependenciesMeta:
@@ -5878,50 +5947,50 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-mdx-gfm@8.0.0-beta.2:
-    resolution: {integrity: sha512-yFRBEoJzeGsLcXKQmDfiT+tr1EjLJ1ktsFDjS3ymVK9DzxSbnZa1u+wIA8spAn6F5qC9uSSAng64UrVyU9JbWQ==}
+  /@storybook/addon-mdx-gfm@8.0.0-beta.6:
+    resolution: {integrity: sha512-b4pb59rrX+C/oYFeEiHb8jJn0h9WZSkHVkLIgaj0G64Nd9OpyKZXMbGpDxwMq4LTi1w65Wddi1UUQbUVVDNHRw==}
     dependencies:
-      '@storybook/node-logger': 8.0.0-beta.2
+      '@storybook/node-logger': 8.0.0-beta.6
       remark-gfm: 4.0.0
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/addon-measure@8.0.0-beta.2:
-    resolution: {integrity: sha512-V0kVtV9EihgsBHZ698QtH0tPr6bwFpwjLvK/Oz/PYh97jBfjzYI1A0qfAV1ixFAr12W/Aco1BBsw+ascI+0AjA==}
+  /@storybook/addon-measure@8.0.0-beta.6:
+    resolution: {integrity: sha512-D+KzWRULcbwR8/ysD7Qbw4uWBn9gwNm9s3IeVuhupawUb3u+H4XfVCOW2rA5qry/x8aroKOhAmyKd9v4i+l3pg==}
     dependencies:
       '@storybook/global': 5.0.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-outline@8.0.0-beta.2:
-    resolution: {integrity: sha512-0FNcGgUvftiML5c5j9nRbKlaYcsXKISAdHxvku/dFBd16HctX/krf4neHVcSBpP1VfU2wT/782s3BXQcRwC/4Q==}
+  /@storybook/addon-outline@8.0.0-beta.6:
+    resolution: {integrity: sha512-U+5TFTj+gtkIiIJCk6h7zbrP588CUipzVVsiDTSLl4pc+H3ylGTGncq3ZGtOyl+DCoBsQCgKxy2YWQtKHrESOw==}
     dependencies:
       '@storybook/global': 5.0.0
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-storysource@8.0.0-beta.2:
-    resolution: {integrity: sha512-t3Nsr8MvcWlS+OONimYQ01CI3pPM5CKb+spS3BI7g89gnt7nz/OdrvbTZjOVLp6AqUo0lYnmVgEcjsOL09Zdfg==}
+  /@storybook/addon-storysource@8.0.0-beta.6:
+    resolution: {integrity: sha512-J9sCZ5/KQW2hbfKsom8LmgSWJxw+Kp/7LjIHGevFfov/i9DR8i9xbh5htUwC9fx+vWGR87tez03b+oUJbyHPog==}
     dependencies:
-      '@storybook/source-loader': 8.0.0-beta.2
+      '@storybook/source-loader': 8.0.0-beta.6
       estraverse: 5.3.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-toolbars@8.0.0-beta.2:
-    resolution: {integrity: sha512-VoTZeLZo156QE4ZkymIH2OFHaZvfDWNBG2YdG/2vcBz3XG5xqlBtM+8IIAwIQik4vHIGVqFVDwPpjzWayQFr2A==}
+  /@storybook/addon-toolbars@8.0.0-beta.6:
+    resolution: {integrity: sha512-ClT5spwh6S1rUvyFEIFQndE3VK6tpwI2cyIW4E20LajtfUmj3dOfJQX/ZbnhEH3sDBsCm97ysZ/mNR0mbBHZrg==}
     dev: true
 
-  /@storybook/addon-viewport@8.0.0-beta.2:
-    resolution: {integrity: sha512-OZzMtkOSIvLGXbODGd5UZb3KXvJNAuXfqkcrrtkSnC+8baJi+3xscVDTU5Tn8gfLz7wsGInrWchxNnXX+DKfmg==}
+  /@storybook/addon-viewport@8.0.0-beta.6:
+    resolution: {integrity: sha512-KNYGM6nVrz/Ej25W3lcpaxxJDYVXBYeGl60FWN/WlqRnjo4c4Fyufl6Xev2plQ3eI8jIvWEdGNC/Z/NQnDx1+Q==}
     dependencies:
       memoizerific: 1.11.3
     dev: true
 
-  /@storybook/blocks@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-IH8hEfPtR5N81PGydrPQdpBWGqOf6l1mXFjRjWwp1BkWvrvWv4lLk4bQ9JqpMF0zH2soKl5BUa5aP0yiufFtlg==}
+  /@storybook/blocks@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-QkrWT0BELNv3UGv/dtNuB/ROZn0f9VpERbadhXLE/oNXMJLalyjEbRGM635l0lDeoqjYnWHl+tuM6DTe1Xpk2w==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5931,18 +6000,18 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/channels': 8.0.0-beta.2
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/components': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 8.0.0-beta.2
+      '@storybook/channels': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 8.0.0-beta.6
       '@storybook/csf': 0.1.2
-      '@storybook/docs-tools': 8.0.0-beta.2
+      '@storybook/docs-tools': 8.0.0-beta.6
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.0-beta.6
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
@@ -5963,13 +6032,13 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-manager@8.0.0-beta.2:
-    resolution: {integrity: sha512-YC9UFESllCLmo69R8xktieWcesCbJiDxeAhMdn9mosQLSOvPlZ/ElivTx423Ombrs3saXAxtLVY5rQJQKSUHEw==}
+  /@storybook/builder-manager@8.0.0-beta.6:
+    resolution: {integrity: sha512-bB/gSsPIpU22Tc6YTjPZdw1RM6nrsuJJ9aYXGqEJTqA4l4lBUN7fwIZQ1x/pS+5LbeUO0J9lAhGXurS+m8rI2A==}
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 8.0.0-beta.2
-      '@storybook/manager': 8.0.0-beta.2
-      '@storybook/node-logger': 8.0.0-beta.2
+      '@storybook/core-common': 8.0.0-beta.6
+      '@storybook/manager': 8.0.0-beta.6
+      '@storybook/node-logger': 8.0.0-beta.6
       '@types/ejs': 3.1.2
       '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.20)
       browser-assert: 1.2.1
@@ -5985,8 +6054,8 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@8.0.0-beta.2(typescript@5.3.3)(vite@5.1.0):
-    resolution: {integrity: sha512-dtkEef/pZMRkv3f+byj6rNlotXK3L+93q1kZRPkICq3V46F4D8EhPZmN/KYi8LHoyKHP/8zE9aI3Mi7GBjQZiA==}
+  /@storybook/builder-vite@8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4):
+    resolution: {integrity: sha512-3P5uTZqwwcUW64Hep/VtJXpQYi5vTkmqAjwZvr8gmzr37NYq3YT/PiSGn4CaZswSx5Z/lSYq3In8oIwmj/a1/g==}
     peerDependencies:
       '@preact/preset-vite': '*'
       typescript: '>= 4.3.x'
@@ -6000,14 +6069,15 @@ packages:
       vite-plugin-glimmerx:
         optional: true
     dependencies:
-      '@storybook/channels': 8.0.0-beta.2
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/core-common': 8.0.0-beta.2
-      '@storybook/csf-plugin': 8.0.0-beta.2
-      '@storybook/node-logger': 8.0.0-beta.2
-      '@storybook/preview': 8.0.0-beta.2
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/channels': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/core-common': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/csf-plugin': 8.0.0-beta.6
+      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/preview': 8.0.0-beta.6
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/types': 8.0.0-beta.6
       '@types/find-cache-dir': 3.2.1
       browser-assert: 1.2.1
       es-module-lexer: 0.9.3
@@ -6017,38 +6087,39 @@ packages:
       magic-string: 0.30.7
       ts-dedent: 2.2.0
       typescript: 5.3.3
-      vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
+      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@storybook/channels@8.0.0-beta.2:
-    resolution: {integrity: sha512-6PoOkce/T3g5pf5wA/tE9JRo9ZoyhdjzZqS2gVsxKza1Ie3gICVKWA+Cu3IM7s05+fX5syHmTvzOLykwfMh9QQ==}
+  /@storybook/channels@8.0.0-beta.6:
+    resolution: {integrity: sha512-DjwJhty45gQifo+TvGqddLX+NX1iGTmZyGLxlqPMpdp+x/yq8WwVZ316Q7tLt6z6fyAmsroc3ma5p1iLhqpV7g==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/core-events': 8.0.0-beta.2
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
       '@storybook/global': 5.0.0
       qs: 6.11.1
       telejson: 7.2.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/cli@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-IfCYCpkOZvMQnf3i+AIdTZ4x45lfuEYNRWZYAZT8Nmnuz2gc0AKui3So4IgNB276Zmbru+OCudf05xHgoxxu3A==}
+  /@storybook/cli@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-sREQYnPds2bwQS7FLbRy7oaxGvOmYhPEYVf93pWKyo/qwSWyXEXbqGCGT6bNhSl/xzqXX7VryLDmuOoHmVTh1g==}
     hasBin: true
     dependencies:
+      '@babel/core': 7.23.5
       '@babel/types': 7.23.5
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 8.0.0-beta.2
-      '@storybook/core-common': 8.0.0-beta.2
-      '@storybook/core-events': 8.0.0-beta.2
-      '@storybook/core-server': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-tools': 8.0.0-beta.2
-      '@storybook/node-logger': 8.0.0-beta.2
-      '@storybook/telemetry': 8.0.0-beta.2
-      '@storybook/types': 8.0.0-beta.2
-      '@types/semver': 7.5.6
+      '@storybook/codemod': 8.0.0-beta.6
+      '@storybook/core-common': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-tools': 8.0.0-beta.6
+      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/telemetry': 8.0.0-beta.6
+      '@storybook/types': 8.0.0-beta.6
+      '@types/semver': 7.5.8
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
       chalk: 4.1.2
@@ -6083,22 +6154,22 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@8.0.0-beta.2:
-    resolution: {integrity: sha512-Sp2tRQO7NmwUjFgN7WTptzJhcyT75rJ+PV9TeSi5BxJXSPTKvA/e6VKFA5k83MS5AI3VBzKV//rFsqyd5+EVkg==}
+  /@storybook/client-logger@8.0.0-beta.6:
+    resolution: {integrity: sha512-XX9CSWt9NDO/1K8tTYV+yuj0ur4HznM1Vc5mY5AwT5xh0RP5HtWZ+VoJfrWYXlBoRXaj0gf8si+FO+lSW82DcQ==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/codemod@8.0.0-beta.2:
-    resolution: {integrity: sha512-s0QcLCdFsMjQmMYRfLQwPaVaYwBmT+CYp0p43xLJ9EVMydSj+So9zs2L0Tp4BN+w9yMz+QvjSq0UZvexuFmC9Q==}
+  /@storybook/codemod@8.0.0-beta.6:
+    resolution: {integrity: sha512-ttQYDkhKmtU6Qbg+Kgn4K2XXf8XMpa2euuC6PmYffBD7/qLiGfABfBc4FHKRv4yScnvKK7Ehy7K0lvipfg6tXw==}
     dependencies:
       '@babel/core': 7.23.5
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
       '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 8.0.0-beta.2
-      '@storybook/node-logger': 8.0.0-beta.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/csf-tools': 8.0.0-beta.6
+      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/types': 8.0.0-beta.6
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
@@ -6111,19 +6182,19 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/components@8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-FsY+Sk6i/62RSPRTupUkBJEBb02Ry5Cg9XEfAa7eH5MpaxxLLIBBDxJ8y1FPepvr0Hkzqo0sBa8w3KMbTfo2ow==}
+  /@storybook/components@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-J3aJtPgaSco0sefvRMBLFsWbslhKMhaS3U+5baRqlV5bjPLZN+d4P18gP1RMaw/coh6DiKEQJZuHRoPIOdt4CA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
       '@radix-ui/react-slot': 1.0.2(@types/react@18.0.28)(react@18.2.0)
-      '@storybook/client-logger': 8.0.0-beta.2
+      '@storybook/client-logger': 8.0.0-beta.6
       '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.0-beta.6
       memoizerific: 1.11.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -6132,13 +6203,13 @@ packages:
       - '@types/react'
     dev: true
 
-  /@storybook/core-common@8.0.0-beta.2:
-    resolution: {integrity: sha512-il2D+GpFg0MdVrQ04f2g5dopynleY9SbkDIfd28RCwTuMefy2exe9DEQoGFiEgBx9inJPS7L3WR0h0p6OMO9KA==}
+  /@storybook/core-common@8.0.0-beta.6:
+    resolution: {integrity: sha512-Mah4Kx/VBNhHaX6neYHTiVwfD93yf3LVVfLTS9WcJFOpek74EAAqbARV3vzOn/utOI75N7yu2PCVoKi5KkDoVw==}
     dependencies:
-      '@storybook/core-events': 8.0.0-beta.2
-      '@storybook/csf-tools': 8.0.0-beta.2
-      '@storybook/node-logger': 8.0.0-beta.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/csf-tools': 8.0.0-beta.6
+      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/types': 8.0.0-beta.6
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
       chalk: 4.1.2
@@ -6168,35 +6239,36 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@8.0.0-beta.2:
-    resolution: {integrity: sha512-C2o0ShpfIFvSDyqaNaXwEfvJaaFlR0rRfvD1a65FMEFM6YAttA/es6z2yjUySUR2vfJ/vwnEtJxs7eGmuQuBmA==}
+  /@storybook/core-events@8.0.0-beta.6:
+    resolution: {integrity: sha512-ZyEVkOJ5gGGTfHjyasyeZgNGoeVJwVkLFRpV6cUl8hzOT29R5iDsf5PbJdrpF1x2pm1oLumeRckYQ7sYhr+R/w==}
     dependencies:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/core-server@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-J96aic++0180m6KrIerWxNAbDxjUnUor7smbVWFWcvvZAM2cW77Th2tIXxs5gcyJ6LEEAea/jYV0P+/I+afdoA==}
+  /@storybook/core-server@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-0ciJTWZs+mCnQOUzB3WuSfkhwXKpO033M5iYK92PKu9A6KSrwdc/WCwIJHeBNnIpmxC0GEh9j6/CgIsWehwJvg==}
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
+      '@babel/core': 7.24.0
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 8.0.0-beta.2
-      '@storybook/channels': 8.0.0-beta.2
-      '@storybook/core-common': 8.0.0-beta.2
-      '@storybook/core-events': 8.0.0-beta.2
+      '@storybook/builder-manager': 8.0.0-beta.6
+      '@storybook/channels': 8.0.0-beta.6
+      '@storybook/core-common': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
       '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 8.0.0-beta.2
+      '@storybook/csf-tools': 8.0.0-beta.6
       '@storybook/docs-mdx': 3.0.0
       '@storybook/global': 5.0.0
-      '@storybook/manager': 8.0.0-beta.2
-      '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 8.0.0-beta.2
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/telemetry': 8.0.0-beta.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/manager': 8.0.0-beta.6
+      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/telemetry': 8.0.0-beta.6
+      '@storybook/types': 8.0.0-beta.6
       '@types/detect-port': 1.3.2
       '@types/node': 18.17.15
       '@types/pretty-hrtime': 1.0.1
-      '@types/semver': 7.5.6
+      '@types/semver': 7.5.8
       better-opn: 3.0.2
       chalk: 4.1.2
       cli-table3: 0.6.3
@@ -6205,7 +6277,7 @@ packages:
       express: 4.18.2
       fs-extra: 11.1.1
       globby: 11.1.0
-      ip: 2.0.0
+      ip: 2.0.1
       lodash: 4.17.21
       open: 8.4.2
       pretty-hrtime: 1.0.3
@@ -6228,24 +6300,24 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/csf-plugin@8.0.0-beta.2:
-    resolution: {integrity: sha512-gdOiI57mkMwgPXnONE1bY4myX2dkol2UdzHYB12QEp9rxE+DHFudCYhxHIj4uyTybTXCjffXAgjlFyT8vBfYUA==}
+  /@storybook/csf-plugin@8.0.0-beta.6:
+    resolution: {integrity: sha512-cYI/4OndODf0utV0DxJs8AOKbmjCG+pEgxQGcmPtGnkSmEuieUwpQpN7v+fEIN7IPUQLYvs0wspR0njZQAIzyA==}
     dependencies:
-      '@storybook/csf-tools': 8.0.0-beta.2
+      '@storybook/csf-tools': 8.0.0-beta.6
       unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf-tools@8.0.0-beta.2:
-    resolution: {integrity: sha512-vujr640EkjkCj8h9r579wugSuKdc3Hbd8GWWiWnCQCRMYW6j9Axj79W8lNOz+u3yWSy6FhqWXqUxr0eMcAv1NQ==}
+  /@storybook/csf-tools@8.0.0-beta.6:
+    resolution: {integrity: sha512-wwzbE6f8ykrvIeZlXYTba0IA8D5GPSyZ4L0+PqRAYHm3ozu0DXqtm4USDHKrjYAzuD+W+fG/6qIOQmsWYbNmpA==}
     dependencies:
       '@babel/generator': 7.23.5
       '@babel/parser': 7.23.9
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/types': 8.0.0-beta.6
       fs-extra: 11.1.1
       recast: 0.23.4
       ts-dedent: 2.2.0
@@ -6263,12 +6335,12 @@ packages:
     resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==}
     dev: true
 
-  /@storybook/docs-tools@8.0.0-beta.2:
-    resolution: {integrity: sha512-uw2F9bhbotZ/v6+FFFv2jj+Oflfd+7gVj5vQttAVQ4o+f6hSsOQkvLeRc5pbs9/ANhB4OVKp23CZBcuySfDtTg==}
+  /@storybook/docs-tools@8.0.0-beta.6:
+    resolution: {integrity: sha512-fSKXEu0vegzqC2HT1RaOKqi0+W/vIn+qa5D+dZHkj2BnceYxWAGYsX9ZZPHW6DUvvwp0WZp1vz57nPUhsLvcQg==}
     dependencies:
-      '@storybook/core-common': 8.0.0-beta.2
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/core-common': 8.0.0-beta.6
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/types': 8.0.0-beta.6
       '@types/doctrine': 0.0.3
       assert: 2.1.0
       doctrine: 3.0.0
@@ -6293,29 +6365,29 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/instrumenter@8.0.0-beta.2:
-    resolution: {integrity: sha512-44W0krseJHhJ4u8auD2QB6civNjBWdAuc7pxJ/IYgIO7Hd3yGnJpDrcOUpaUpPT3WhLFpbbASjQIlauED0DiXw==}
+  /@storybook/instrumenter@8.0.0-beta.6:
+    resolution: {integrity: sha512-xJ3qkvj8dce7nJEa6hmp4PDDZJMBuP5UlSKPidiMAfEsB0MeUbDulTFNDb0t1DwcH9ywinDl8TilSzG4+r1kDA==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.2
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/core-events': 8.0.0-beta.2
+      '@storybook/channels': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.0-beta.2
+      '@storybook/preview-api': 8.0.0-beta.6
       '@vitest/utils': 0.34.6
       util: 0.12.5
     dev: true
 
-  /@storybook/manager-api@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-LMgxSXqpB8Zdtmvs0rthityGE77rVgQ82gq+LBMaBEEFlwdSfYhoLLnlNLHrc3m8A2mTXJ4/aDhvTTjh9PPG5w==}
+  /@storybook/manager-api@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-kOGOT/yGFgKzld9IL1HREouFwZ0LpFuXZZOHBih5ydK8XT+bkWF6e3SiqthB3qtqpd0eVLAbNiPfY9R8t3qfWg==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.2
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/core-events': 8.0.0-beta.2
+      '@storybook/channels': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
       '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/router': 8.0.0-beta.2
-      '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/router': 8.0.0-beta.6
+      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.0-beta.6
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
@@ -6327,23 +6399,23 @@ packages:
       - react-dom
     dev: true
 
-  /@storybook/manager@8.0.0-beta.2:
-    resolution: {integrity: sha512-eho+n+gUjuNlX5HQYoOKKvWjTcL96kwp9KdAmrhUn3KMNLkG8kax9iEUSFKG2p+tR+YAxsJJzDlC9gV4XzbBGA==}
+  /@storybook/manager@8.0.0-beta.6:
+    resolution: {integrity: sha512-FeQ2/CIasSOgcTMEE3QYMFa92KeMnfEMyUVO4hHEmPh3SqPsz6OOv8p0bQvN0SWWBgZarbhFR0dKC3W10yYrXg==}
     dev: true
 
-  /@storybook/node-logger@8.0.0-beta.2:
-    resolution: {integrity: sha512-bBTayxV0B87FPL+suMGxpMfPzUhAwu/yO8c6glLJ4xVHJlUNn+tVQpLDehU6NeqgYTdAg9oh0fi9ufZoROVfMw==}
+  /@storybook/node-logger@8.0.0-beta.6:
+    resolution: {integrity: sha512-nmBlmZ8wzJiU1/ubhUmFeWQaJPBv6l6s0Cndk04omPSjROa+O1whoPhDTVGvWC28zm17tmAYVcQRujkdoi+YBA==}
     dev: true
 
-  /@storybook/preview-api@8.0.0-beta.2:
-    resolution: {integrity: sha512-eekdhIwSOI3RnLDHJViLBBoTuSmQUo7Oa1FGU/gDx7ZEofNF+k2N4FdFPRc72Dkv4SI7hGmoJJWbPA6BR2ZHww==}
+  /@storybook/preview-api@8.0.0-beta.6:
+    resolution: {integrity: sha512-V07MF1ArjBGi2EPSjrEW8pjCoW/TIwxNDilcO9cD12LHrDQGXuo/iKyR47TGUYmcJ/u1I2Eu9cjyVj9DVyppag==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.2
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/core-events': 8.0.0-beta.2
+      '@storybook/channels': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
       '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/types': 8.0.0-beta.6
       '@types/qs': 6.9.7
       dequal: 2.0.3
       lodash: 4.17.21
@@ -6354,12 +6426,12 @@ packages:
       util-deprecate: 1.0.2
     dev: true
 
-  /@storybook/preview@8.0.0-beta.2:
-    resolution: {integrity: sha512-n9OqS5KRdUGD3oImCG5NzUIZabcV/A3LifD2YOYCyJHS4U9yg4Wse2o6Px8niklAstAdFFOP2iyMQxjt0iQ0DA==}
+  /@storybook/preview@8.0.0-beta.6:
+    resolution: {integrity: sha512-tp3Wyvjsbf5r5RhbCQSafArQWJAir1bmIJWGG2S4o2E3YT6TlHFpR078tNJtgXqsPyG0yhF9vhRRkDczrPX/Gw==}
     dev: true
 
-  /@storybook/react-dom-shim@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-z372LCk+5WbSX/nWpnYaxP4oWoeKTtmq7CHqK7pWrdtZJRwZJbsIKZdZragzO4yyfZLEuAybw7kR9qgCqz1ToA==}
+  /@storybook/react-dom-shim@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-l14oDKAW2jyrXynHKP6SoNGal78gXcWCgj0zLwSDWpKgAFWC7SuIneuxLv6weU1D4+f9Y9FBrz+K3CCaMgMtOA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6368,23 +6440,23 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.6)(typescript@5.3.3)(vite@5.1.0):
-    resolution: {integrity: sha512-PKbOW0JP03e0x9cndFkCXoCUUQ+P7C7JzUnURRz/uRGXl/DUiR1IcjxL0aNT7uv9Gm+whpnGCP977RHFrvMlqA==}
+  /@storybook/react-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4):
+    resolution: {integrity: sha512-Tvz25pTXmhncDxprjIYsnXc68Lfa9idDybpRTRRbtvjsJyVpZogUdgz2/kddGNTuX3mqz6vmTMWiLiIVh+ytQA==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
       vite: ^4.0.0 || ^5.0.0
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.0)
-      '@rollup/pluginutils': 5.1.0(rollup@4.9.6)
-      '@storybook/builder-vite': 8.0.0-beta.2(typescript@5.3.3)(vite@5.1.0)
-      '@storybook/react': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.4)
+      '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
+      '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4)
+      '@storybook/react': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       magic-string: 0.30.7
       react: 18.2.0
       react-docgen: 7.0.1
       react-dom: 18.2.0(react@18.2.0)
-      vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
+      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -6394,8 +6466,8 @@ packages:
       - vite-plugin-glimmerx
     dev: true
 
-  /@storybook/react@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-6Snd+u9UQHrzYkmEYi/BxJMl1FNnJI+3aXdopiwdslze9bosZg0glK4TKBvhqGwCBYLnKgzKHXhttMECWQApFA==}
+  /@storybook/react@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-69B0c08HDYHEgZRRnkB+3z4dY/HO/GMSiRzRCNpzI0SBQzk1YwDzG9MOtkNgGqzdLK3e3DveSXb5Uyy1cB0ZiQ==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6405,12 +6477,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/docs-tools': 8.0.0-beta.2
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/docs-tools': 8.0.0-beta.6
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/react-dom-shim': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.0-beta.6
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 18.17.15
@@ -6434,30 +6506,30 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/router@8.0.0-beta.2:
-    resolution: {integrity: sha512-bnNEayh3g4KZUVmH7zlJZ0tdSE6lJZZhUDE61bjPHjweLx3xYLlev0A8EwzAjz1BBpr4H8UtyYa32C7+G1Urxw==}
+  /@storybook/router@8.0.0-beta.6:
+    resolution: {integrity: sha512-JjLyDaVzCH3kmNsOkuJ8/U2bPIoReZZ/QsgHJdfvm22T2wKNjQ+lfNrQptBgNybfi1o/Tmn9VbCdRqurSlh9Dw==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.2
+      '@storybook/client-logger': 8.0.0-beta.6
       memoizerific: 1.11.3
       qs: 6.11.1
     dev: true
 
-  /@storybook/source-loader@8.0.0-beta.2:
-    resolution: {integrity: sha512-nSGtn7y/o4aaHVI2mw2Xi/GIElfqMUah9uHFtEzl+vT0JIOs7jImR9dblpR5jGFRZM/2Waod8aXJ+hYkQ7MaQA==}
+  /@storybook/source-loader@8.0.0-beta.6:
+    resolution: {integrity: sha512-cYtjnuJZgm8MS9SsNsbuhuFz2d7j6BKRLZByBUqELrK+ftup0qqOWM+78w26qn3nPgA8myZXWxGa+V/Pjxio5w==}
     dependencies:
       '@storybook/csf': 0.1.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/types': 8.0.0-beta.6
       estraverse: 5.3.0
       lodash: 4.17.21
       prettier: 3.2.5
     dev: true
 
-  /@storybook/telemetry@8.0.0-beta.2:
-    resolution: {integrity: sha512-jc2w//1ZYn0EuDOjtBx27uTpThjFj2+0fNf0G83+BmZR8yJciBj+YXrzQB6FWdiKyT4WLaH4la1ZlaiJP+ZdYQ==}
+  /@storybook/telemetry@8.0.0-beta.6:
+    resolution: {integrity: sha512-3CU5Sdj8eVm0tb35GriMkDrxJyTpdGcfU/hgUnsuw+I4eHYdZsc4Boh9uXWTVNsaBaoqbD/MP1aqbfxkElqPxQ==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/core-common': 8.0.0-beta.2
-      '@storybook/csf-tools': 8.0.0-beta.2
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/core-common': 8.0.0-beta.6
+      '@storybook/csf-tools': 8.0.0-beta.6
       chalk: 4.1.2
       detect-package-manager: 2.0.1
       fetch-retry: 5.0.4
@@ -6468,16 +6540,16 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/test@8.0.0-beta.2(vitest@0.34.6):
-    resolution: {integrity: sha512-sMo5mUKMOLoPMOWvAvK++V9Db6qO5bgGr1JLmnX+1YTh3mzZzOlsKe5nEKdEBjT1iI1OstypDg+oKHAEfg5Oag==}
+  /@storybook/test@8.0.0-beta.6(vitest@0.34.6):
+    resolution: {integrity: sha512-GcV76EX3U77G+k8+0V+jAa/sJQZEuNb/4W+g/RaqGLRCEG73UADzkgRuFm60UQUBGtltvvRZU9sIPVbFTJFxuA==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.2
-      '@storybook/core-events': 8.0.0-beta.2
-      '@storybook/instrumenter': 8.0.0-beta.2
-      '@storybook/preview-api': 8.0.0-beta.2
+      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/instrumenter': 8.0.0-beta.6
+      '@storybook/preview-api': 8.0.0-beta.6
       '@testing-library/dom': 9.3.3
       '@testing-library/jest-dom': 6.4.2(vitest@0.34.6)
-      '@testing-library/user-event': 14.3.0(@testing-library/dom@9.3.3)
+      '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.3)
       '@vitest/expect': 1.1.3
       '@vitest/spy': 1.2.2
       chai: 4.3.10
@@ -6490,8 +6562,8 @@ packages:
       - vitest
     dev: true
 
-  /@storybook/theming@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Nl3eCdsBVjh98sghb7YF/v+65bUWEyJfvhU7aQHxRcYoBw+UKJIX5FKSd+PFnC/BOkH0So2ngDU2XFoOdA6BPg==}
+  /@storybook/theming@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-WXvDbV257fKbHM5jHd7hOHefRSBnyZec08NGpcVOG6muJjLu8nPjazcYgISqFc97MkFmxvEDPFfX8CvBEeefzA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6502,33 +6574,36 @@ packages:
         optional: true
     dependencies:
       '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
-      '@storybook/client-logger': 8.0.0-beta.2
+      '@storybook/client-logger': 8.0.0-beta.6
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@8.0.0-beta.2:
-    resolution: {integrity: sha512-MK6QFpMGWxu+sLCw8VrXmL0gOJ3g6XPpV85T5s+CEMsfMhSH5wCMdhtWkCRbHGfVEPKa5fEtA0SaGLqJhnSpQw==}
+  /@storybook/types@8.0.0-beta.6:
+    resolution: {integrity: sha512-w3jq8mBcxir4P0RK3gQePeUJ0rXbnUbCKg91YBOKeitmU0+4jSr4e1EwTWOYgsyz7KtikzSNr8JXtMQn2TJD5A==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.2
+      '@storybook/channels': 8.0.0-beta.6
       '@types/express': 4.17.17
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)(vite@5.1.0)(vue@3.4.18):
-    resolution: {integrity: sha512-Pzth2PEEmLyI2hW827x+Cd4nYO6xayAsWk46JdXjfHOVnHDgp6CZPSi1zr79J+bbjvQtHHb+9BV4TljdiM0zxw==}
+  /@storybook/vue3-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21):
+    resolution: {integrity: sha512-Pf9W7hcHjx1FE3JmhY1iSxGq9k/Tp5n/obOCd4FJGUdIttPYFclG9km49DrCJtNfhK7M6+d2QTZ6Uds4ORWZPg==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 8.0.0-beta.2(typescript@5.3.3)(vite@5.1.0)
-      '@storybook/core-server': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/vue3': 8.0.0-beta.2(vue@3.4.18)
+      '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4)
+      '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/vue3': 8.0.0-beta.6(vue@3.4.21)
+      find-package-json: 1.2.0
       magic-string: 0.30.7
-      vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
-      vue-docgen-api: 4.64.1(vue@3.4.18)
+      typescript: 5.3.3
+      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+      vue-component-meta: 1.8.27(typescript@5.3.3)
+      vue-docgen-api: 4.75.1(vue@3.4.21)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - bufferutil
@@ -6536,27 +6611,26 @@ packages:
       - react
       - react-dom
       - supports-color
-      - typescript
       - utf-8-validate
       - vite-plugin-glimmerx
       - vue
     dev: true
 
-  /@storybook/vue3@8.0.0-beta.2(vue@3.4.18):
-    resolution: {integrity: sha512-lGupXqWl+/gx5in8jJEzxCHvtTfFHCemYFVMXUqNhJ+Chudwx7LRyy3frQ+0AE4FzGIIYWM/tJjkaKvtBQvEDg==}
+  /@storybook/vue3@8.0.0-beta.6(vue@3.4.21):
+    resolution: {integrity: sha512-027KDM1f6y0XzMK1yE5W4JKY/VsbGpr1kj0mvEKxaPUYgBJV9wTHADWgmluiJS/e/MWrCCZql5mE+D9lVJUjoA==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       vue: ^3.0.0
     dependencies:
-      '@storybook/docs-tools': 8.0.0-beta.2
+      '@storybook/docs-tools': 8.0.0-beta.6
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/types': 8.0.0-beta.6
       '@vue/compiler-core': 3.4.18
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.4.18(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.3.3)
       vue-component-type-helpers: 1.8.27
     transitivePeerDependencies:
       - encoding
@@ -7151,11 +7225,11 @@ packages:
       dom-accessibility-api: 0.6.3
       lodash: 4.17.21
       redent: 3.0.0
-      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
+      vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
     dev: true
 
-  /@testing-library/user-event@14.3.0(@testing-library/dom@9.3.3):
-    resolution: {integrity: sha512-P02xtBBa8yMaLhK8CzJCIns8rqwnF6FxhR9zs810flHOBXUYCFjLd8Io1rQrAkQRWEmW2PGdZIEdMxf/KLsqFA==}
+  /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.3):
+    resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==}
     engines: {node: '>=12', npm: '>=6'}
     peerDependencies:
       '@testing-library/dom': '>=7.21.4'
@@ -7163,7 +7237,7 @@ packages:
       '@testing-library/dom': 9.3.3
     dev: true
 
-  /@testing-library/vue@8.0.2(@vue/compiler-sfc@3.4.18)(vue@3.4.18):
+  /@testing-library/vue@8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21):
     resolution: {integrity: sha512-A8wWX+qQn0o0izpQWnGCpwQt8wAdpsVP8vPP2h5Q/jcGhZ5yKXz9PPUqhQv+45LTFaWlyRf8bArTVaB/KFFd5A==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -7175,9 +7249,9 @@ packages:
     dependencies:
       '@babel/runtime': 7.23.4
       '@testing-library/dom': 9.3.3
-      '@vue/compiler-sfc': 3.4.18
-      '@vue/test-utils': 2.4.1(vue@3.4.18)
-      vue: 3.4.18(typescript@5.3.3)
+      '@vue/compiler-sfc': 3.4.21
+      '@vue/test-utils': 2.4.1(vue@3.4.21)
+      vue: 3.4.21(typescript@5.3.3)
     transitivePeerDependencies:
       - '@vue/server-renderer'
     dev: true
@@ -7203,7 +7277,7 @@ packages:
   /@types/accepts@1.3.7:
     resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/archiver@6.0.2:
@@ -7257,7 +7331,7 @@ packages:
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/braces@3.0.1:
@@ -7269,7 +7343,7 @@ packages:
     dependencies:
       '@types/http-cache-semantics': 4.0.4
       '@types/keyv': 3.1.4
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       '@types/responselike': 1.0.0
     dev: false
 
@@ -7296,7 +7370,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/content-disposition@0.5.8:
@@ -7310,7 +7384,7 @@ packages:
   /@types/cross-spawn@6.0.2:
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/debug@4.1.12:
@@ -7368,7 +7442,7 @@ packages:
   /@types/express-serve-static-core@4.17.33:
     resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
     dev: true
@@ -7389,20 +7463,20 @@ packages:
   /@types/fluent-ffmpeg@2.1.24:
     resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/glob@7.2.0:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/hast@3.0.4:
@@ -7421,7 +7495,7 @@ packages:
   /@types/http-link-header@1.0.5:
     resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/istanbul-lib-coverage@2.0.4:
@@ -7447,6 +7521,13 @@ packages:
       pretty-format: 29.7.0
     dev: true
 
+  /@types/jest@29.5.12:
+    resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==}
+    dependencies:
+      expect: 29.7.0
+      pretty-format: 29.7.0
+    dev: true
+
   /@types/js-yaml@4.0.9:
     resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
     dev: true
@@ -7454,7 +7535,7 @@ packages:
   /@types/jsdom@21.1.6:
     resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
     dev: true
@@ -7478,7 +7559,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: false
 
   /@types/lodash@4.14.191:
@@ -7534,7 +7615,7 @@ packages:
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       form-data: 3.0.1
     dev: false
 
@@ -7549,8 +7630,8 @@ packages:
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
     dev: true
 
-  /@types/node@20.11.17:
-    resolution: {integrity: sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==}
+  /@types/node@20.11.22:
+    resolution: {integrity: sha512-/G+IxWxma6V3E+pqK1tSl2Fo1kl41pK1yeCyDsgkF9WlVAme4j5ISYM2zR11bgLFJGLN5sVK40T4RJNuiZbEjA==}
     dependencies:
       undici-types: 5.26.5
 
@@ -7569,7 +7650,7 @@ packages:
   /@types/nodemailer@6.4.14:
     resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/normalize-package-data@2.4.1:
@@ -7586,13 +7667,13 @@ packages:
     resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==}
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/oauth@0.9.4:
     resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/offscreencanvas@2019.3.0:
@@ -7605,10 +7686,10 @@ packages:
     requiresBuild: true
     dev: false
 
-  /@types/pg@8.11.0:
-    resolution: {integrity: sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==}
+  /@types/pg@8.11.2:
+    resolution: {integrity: sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       pg-protocol: 1.6.0
       pg-types: 4.0.1
     dev: true
@@ -7625,14 +7706,14 @@ packages:
     resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
     dev: true
 
-  /@types/punycode@2.1.3:
-    resolution: {integrity: sha512-dFkH9Mz0yY5UfQVSrpj1grQyqRwe4TohTLlHFx4Gli8/fsaNyoOVUAsiEBZk5JBwbEJVZ49W6st8D5g6dRJb/w==}
+  /@types/punycode@2.1.4:
+    resolution: {integrity: sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ==}
     dev: true
 
   /@types/qrcode@1.5.5:
     resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/qs@6.9.7:
@@ -7662,7 +7743,7 @@ packages:
   /@types/readdir-glob@1.1.1:
     resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/rename@1.0.7:
@@ -7676,11 +7757,11 @@ packages:
   /@types/responselike@1.0.0:
     resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: false
 
-  /@types/sanitize-html@2.9.5:
-    resolution: {integrity: sha512-2Sr1vd8Dw+ypsg/oDDfZ57OMSG2Befs+l2CMyCC5bVSK3CpE7lTB2aNlbbWzazgVA+Qqfuholwom6x/mWd1qmw==}
+  /@types/sanitize-html@2.11.0:
+    resolution: {integrity: sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==}
     dependencies:
       htmlparser2: 8.0.1
     dev: true
@@ -7698,15 +7779,15 @@ packages:
     resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==}
     dev: true
 
-  /@types/semver@7.5.6:
-    resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==}
+  /@types/semver@7.5.8:
+    resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
     dev: true
 
   /@types/serve-static@1.15.1:
     resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/serviceworker@0.0.67:
@@ -7770,13 +7851,13 @@ packages:
   /@types/vary@1.1.3:
     resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/web-push@3.6.3:
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/webgl-ext@0.0.30:
@@ -7787,7 +7868,7 @@ packages:
   /@types/ws@8.5.10:
     resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /@types/yargs-parser@21.0.0:
@@ -7810,7 +7891,7 @@ packages:
     resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
     optional: true
 
@@ -8031,7 +8112,7 @@ packages:
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
       '@types/json-schema': 7.0.12
-      '@types/semver': 7.5.6
+      '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
@@ -8050,7 +8131,7 @@ packages:
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
       '@types/json-schema': 7.0.12
-      '@types/semver': 7.5.6
+      '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 6.18.1
       '@typescript-eslint/types': 6.18.1
       '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
@@ -8081,15 +8162,15 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@vitejs/plugin-vue@5.0.3(vite@5.1.0)(vue@3.4.18):
-    resolution: {integrity: sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==}
+  /@vitejs/plugin-vue@5.0.4(vite@5.1.4)(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.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
-      vue: 3.4.18(typescript@5.3.3)
+      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+      vue: 3.4.21(typescript@5.3.3)
     dev: false
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
@@ -8108,7 +8189,7 @@ packages:
       std-env: 3.7.0
       test-exclude: 6.0.0
       v8-to-istanbul: 9.2.0
-      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
+      vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -8207,31 +8288,48 @@ packages:
       entities: 4.5.0
       estree-walker: 2.0.2
       source-map-js: 1.0.2
+    dev: true
+
+  /@vue/compiler-core@3.4.21:
+    resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
+    dependencies:
+      '@babel/parser': 7.23.9
+      '@vue/shared': 3.4.21
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.0.2
 
   /@vue/compiler-dom@3.4.18:
     resolution: {integrity: sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==}
     dependencies:
       '@vue/compiler-core': 3.4.18
       '@vue/shared': 3.4.18
+    dev: true
+
+  /@vue/compiler-dom@3.4.21:
+    resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
+    dependencies:
+      '@vue/compiler-core': 3.4.21
+      '@vue/shared': 3.4.21
 
-  /@vue/compiler-sfc@3.4.18:
-    resolution: {integrity: sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==}
+  /@vue/compiler-sfc@3.4.21:
+    resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
     dependencies:
       '@babel/parser': 7.23.9
-      '@vue/compiler-core': 3.4.18
-      '@vue/compiler-dom': 3.4.18
-      '@vue/compiler-ssr': 3.4.18
-      '@vue/shared': 3.4.18
+      '@vue/compiler-core': 3.4.21
+      '@vue/compiler-dom': 3.4.21
+      '@vue/compiler-ssr': 3.4.21
+      '@vue/shared': 3.4.21
       estree-walker: 2.0.2
       magic-string: 0.30.7
-      postcss: 8.4.33
+      postcss: 8.4.35
       source-map-js: 1.0.2
 
-  /@vue/compiler-ssr@3.4.18:
-    resolution: {integrity: sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==}
+  /@vue/compiler-ssr@3.4.21:
+    resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
     dependencies:
-      '@vue/compiler-dom': 3.4.18
-      '@vue/shared': 3.4.18
+      '@vue/compiler-dom': 3.4.21
+      '@vue/shared': 3.4.21
 
   /@vue/language-core@1.8.27(typescript@5.3.3):
     resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==}
@@ -8253,32 +8351,32 @@ packages:
       vue-template-compiler: 2.7.14
     dev: true
 
-  /@vue/reactivity@3.4.18:
-    resolution: {integrity: sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==}
+  /@vue/reactivity@3.4.21:
+    resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
     dependencies:
-      '@vue/shared': 3.4.18
+      '@vue/shared': 3.4.21
 
-  /@vue/runtime-core@3.4.18:
-    resolution: {integrity: sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==}
+  /@vue/runtime-core@3.4.21:
+    resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==}
     dependencies:
-      '@vue/reactivity': 3.4.18
-      '@vue/shared': 3.4.18
+      '@vue/reactivity': 3.4.21
+      '@vue/shared': 3.4.21
 
-  /@vue/runtime-dom@3.4.18:
-    resolution: {integrity: sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==}
+  /@vue/runtime-dom@3.4.21:
+    resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==}
     dependencies:
-      '@vue/runtime-core': 3.4.18
-      '@vue/shared': 3.4.18
+      '@vue/runtime-core': 3.4.21
+      '@vue/shared': 3.4.21
       csstype: 3.1.3
 
-  /@vue/server-renderer@3.4.18(vue@3.4.18):
-    resolution: {integrity: sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==}
+  /@vue/server-renderer@3.4.21(vue@3.4.21):
+    resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
     peerDependencies:
-      vue: 3.4.18
+      vue: 3.4.21
     dependencies:
-      '@vue/compiler-ssr': 3.4.18
-      '@vue/shared': 3.4.18
-      vue: 3.4.18(typescript@5.3.3)
+      '@vue/compiler-ssr': 3.4.21
+      '@vue/shared': 3.4.21
+      vue: 3.4.21(typescript@5.3.3)
 
   /@vue/shared@3.3.12:
     resolution: {integrity: sha512-6p0Yin0pclvnER7BLNOQuod9Z+cxSYh8pSh7CzHnWNjAIP6zrTlCdHRvSCb1aYEx6i3Q3kvfuWU7nG16CgG1ag==}
@@ -8286,8 +8384,12 @@ packages:
 
   /@vue/shared@3.4.18:
     resolution: {integrity: sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==}
+    dev: true
 
-  /@vue/test-utils@2.4.1(vue@3.4.18):
+  /@vue/shared@3.4.21:
+    resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
+
+  /@vue/test-utils@2.4.1(vue@3.4.21):
     resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
     peerDependencies:
       '@vue/server-renderer': ^3.0.1
@@ -8297,7 +8399,7 @@ packages:
         optional: true
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.4.18(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.3.3)
       vue-component-type-helpers: 1.8.4
     dev: true
 
@@ -8737,20 +8839,6 @@ packages:
     resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
     dev: true
 
-  /ast-types@0.14.2:
-    resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==}
-    engines: {node: '>=4'}
-    dependencies:
-      tslib: 2.6.2
-    dev: true
-
-  /ast-types@0.15.2:
-    resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==}
-    engines: {node: '>=4'}
-    dependencies:
-      tslib: 2.6.2
-    dev: true
-
   /ast-types@0.16.1:
     resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
     engines: {node: '>=4'}
@@ -9150,6 +9238,18 @@ packages:
       electron-to-chromium: 1.4.601
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.22.2)
+    dev: true
+
+  /browserslist@4.23.0:
+    resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      caniuse-lite: 1.0.30001591
+      electron-to-chromium: 1.4.686
+      node-releases: 2.0.14
+      update-browserslist-db: 1.0.13(browserslist@4.23.0)
+    dev: false
 
   /bser@2.1.1:
     resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
@@ -9200,13 +9300,14 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@5.1.9:
-    resolution: {integrity: sha512-9MfcQxYyfkG8kxpIxRsRXWYlTRQ1o8xWqgdoFR5pLClVTjtMI8qeDO5basRQLZPfp/uiPtv+gpzJ3OTNrm2ZNg==}
+  /bullmq@5.4.0:
+    resolution: {integrity: sha512-QNOT+Vp/OFctwKa1/LYvrfIMXqb6Hu8a1VwXxQpa/JXoFAQ9E4ZcqW4fyEjx9iYrXakpV6cAGPbmdgWaKTGXOQ==}
     dependencies:
       cron-parser: 4.8.1
-      glob: 8.1.0
+      fast-glob: 3.3.2
       ioredis: 5.3.2
       lodash: 4.17.21
+      minimatch: 9.0.3
       msgpackr: 1.10.1
       node-abort-controller: 3.1.1
       semver: 7.5.4
@@ -9333,7 +9434,7 @@ packages:
   /caniuse-api@3.0.0:
     resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
     dependencies:
-      browserslist: 4.22.2
+      browserslist: 4.23.0
       caniuse-lite: 1.0.30001566
       lodash.memoize: 4.1.2
       lodash.uniq: 4.5.0
@@ -9342,6 +9443,10 @@ packages:
   /caniuse-lite@1.0.30001566:
     resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==}
 
+  /caniuse-lite@1.0.30001591:
+    resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==}
+    dev: false
+
   /canonicalize@1.0.8:
     resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
     dev: false
@@ -9353,28 +9458,6 @@ packages:
   /caseless@0.12.0:
     resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
 
-  /cbor-extract@2.1.1:
-    resolution: {integrity: sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==}
-    hasBin: true
-    requiresBuild: true
-    dependencies:
-      node-gyp-build-optional-packages: 5.0.3
-    optionalDependencies:
-      '@cbor-extract/cbor-extract-darwin-arm64': 2.1.1
-      '@cbor-extract/cbor-extract-darwin-x64': 2.1.1
-      '@cbor-extract/cbor-extract-linux-arm': 2.1.1
-      '@cbor-extract/cbor-extract-linux-arm64': 2.1.1
-      '@cbor-extract/cbor-extract-linux-x64': 2.1.1
-      '@cbor-extract/cbor-extract-win32-x64': 2.1.1
-    dev: false
-    optional: true
-
-  /cbor-x@1.5.4:
-    resolution: {integrity: sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==}
-    optionalDependencies:
-      cbor-extract: 2.1.1
-    dev: false
-
   /cbor@9.0.2:
     resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==}
     engines: {node: '>=16'}
@@ -9452,45 +9535,45 @@ packages:
     resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
     dev: true
 
-  /chart.js@4.4.1:
-    resolution: {integrity: sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==}
-    engines: {pnpm: '>=7'}
+  /chart.js@4.4.2:
+    resolution: {integrity: sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==}
+    engines: {pnpm: '>=8'}
     dependencies:
       '@kurkle/color': 0.3.2
     dev: false
 
-  /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.1)(date-fns@2.30.0):
+  /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@2.30.0):
     resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
     peerDependencies:
       chart.js: '>=2.8.0'
       date-fns: '>=2.0.0'
     dependencies:
-      chart.js: 4.4.1
+      chart.js: 4.4.2
       date-fns: 2.30.0
     dev: false
 
-  /chartjs-chart-matrix@2.0.1(chart.js@4.4.1):
+  /chartjs-chart-matrix@2.0.1(chart.js@4.4.2):
     resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==}
     peerDependencies:
       chart.js: '>=3.0.0'
     dependencies:
-      chart.js: 4.4.1
+      chart.js: 4.4.2
     dev: false
 
-  /chartjs-plugin-gradient@0.6.1(chart.js@4.4.1):
+  /chartjs-plugin-gradient@0.6.1(chart.js@4.4.2):
     resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==}
     peerDependencies:
       chart.js: '>=2.6.0'
     dependencies:
-      chart.js: 4.4.1
+      chart.js: 4.4.2
     dev: false
 
-  /chartjs-plugin-zoom@2.0.1(chart.js@4.4.1):
+  /chartjs-plugin-zoom@2.0.1(chart.js@4.4.2):
     resolution: {integrity: sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==}
     peerDependencies:
       chart.js: '>=3.2.0'
     dependencies:
-      chart.js: 4.4.1
+      chart.js: 4.4.2
       hammerjs: 2.0.8
     dev: false
 
@@ -9549,16 +9632,16 @@ packages:
     engines: {node: '>=10'}
     requiresBuild: true
 
-  /chromatic@10.6.1:
-    resolution: {integrity: sha512-bd4C5sEEtN83uUmbc4Fu+x7+lJIPdMUdu4D6HRDQEIDl/Tatc8+By4bZluH1pzg/MbP9vllkL6Ua9vF4EEA7VA==}
+  /chromatic@11.0.0:
+    resolution: {integrity: sha512-utzRVqdMrpzYwZNf7dHWU0z0/rx6SH/FUVNozQxYHDtQfYUdEj+Ro4OSch5+Wsk2FoUmznJyLkaC2J839z1N7A==}
     hasBin: true
     peerDependencies:
-      chromatic-cypress: ^0.4.0 || ^1.0.0
-      chromatic-playwright: ^0.4.0 || ^1.0.0
+      '@chromatic-com/cypress': ^0.5.2 || ^1.0.0
+      '@chromatic-com/playwright': ^0.5.2 || ^1.0.0
     peerDependenciesMeta:
-      chromatic-cypress:
+      '@chromatic-com/cypress':
         optional: true
-      chromatic-playwright:
+      '@chromatic-com/playwright':
         optional: true
     dev: false
 
@@ -9921,7 +10004,7 @@ packages:
       readable-stream: 3.6.0
     dev: false
 
-  /create-jest@29.7.0(@types/node@20.11.17):
+  /create-jest@29.7.0(@types/node@20.11.22):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -9930,7 +10013,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.11.17)
+      jest-config: 29.7.0(@types/node@20.11.22)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -9998,13 +10081,13 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /css-declaration-sorter@7.1.1(postcss@8.4.33):
+  /css-declaration-sorter@7.1.1(postcss@8.4.35):
     resolution: {integrity: sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==}
     engines: {node: ^14 || ^16 || >=18}
     peerDependencies:
       postcss: ^8.0.9
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
   /css-select@5.1.0:
@@ -10044,62 +10127,62 @@ packages:
     engines: {node: '>=4'}
     hasBin: true
 
-  /cssnano-preset-default@6.0.3(postcss@8.4.33):
-    resolution: {integrity: sha512-4y3H370aZCkT9Ev8P4SO4bZbt+AExeKhh8wTbms/X7OLDo5E7AYUUy6YPxa/uF5Grf+AJwNcCnxKhZynJ6luBA==}
+  /cssnano-preset-default@6.0.5(postcss@8.4.35):
+    resolution: {integrity: sha512-M+qRDEr5QZrfNl0B2ySdbTLGyNb8kBcSjuwR7WBamYBOEREH9t2efnB/nblekqhdGLZdkf4oZNetykG2JWRdZQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      css-declaration-sorter: 7.1.1(postcss@8.4.33)
-      cssnano-utils: 4.0.1(postcss@8.4.33)
-      postcss: 8.4.33
-      postcss-calc: 9.0.1(postcss@8.4.33)
-      postcss-colormin: 6.0.2(postcss@8.4.33)
-      postcss-convert-values: 6.0.2(postcss@8.4.33)
-      postcss-discard-comments: 6.0.1(postcss@8.4.33)
-      postcss-discard-duplicates: 6.0.1(postcss@8.4.33)
-      postcss-discard-empty: 6.0.1(postcss@8.4.33)
-      postcss-discard-overridden: 6.0.1(postcss@8.4.33)
-      postcss-merge-longhand: 6.0.2(postcss@8.4.33)
-      postcss-merge-rules: 6.0.3(postcss@8.4.33)
-      postcss-minify-font-values: 6.0.1(postcss@8.4.33)
-      postcss-minify-gradients: 6.0.1(postcss@8.4.33)
-      postcss-minify-params: 6.0.2(postcss@8.4.33)
-      postcss-minify-selectors: 6.0.2(postcss@8.4.33)
-      postcss-normalize-charset: 6.0.1(postcss@8.4.33)
-      postcss-normalize-display-values: 6.0.1(postcss@8.4.33)
-      postcss-normalize-positions: 6.0.1(postcss@8.4.33)
-      postcss-normalize-repeat-style: 6.0.1(postcss@8.4.33)
-      postcss-normalize-string: 6.0.1(postcss@8.4.33)
-      postcss-normalize-timing-functions: 6.0.1(postcss@8.4.33)
-      postcss-normalize-unicode: 6.0.2(postcss@8.4.33)
-      postcss-normalize-url: 6.0.1(postcss@8.4.33)
-      postcss-normalize-whitespace: 6.0.1(postcss@8.4.33)
-      postcss-ordered-values: 6.0.1(postcss@8.4.33)
-      postcss-reduce-initial: 6.0.2(postcss@8.4.33)
-      postcss-reduce-transforms: 6.0.1(postcss@8.4.33)
-      postcss-svgo: 6.0.2(postcss@8.4.33)
-      postcss-unique-selectors: 6.0.2(postcss@8.4.33)
-    dev: false
-
-  /cssnano-utils@4.0.1(postcss@8.4.33):
+      css-declaration-sorter: 7.1.1(postcss@8.4.35)
+      cssnano-utils: 4.0.1(postcss@8.4.35)
+      postcss: 8.4.35
+      postcss-calc: 9.0.1(postcss@8.4.35)
+      postcss-colormin: 6.0.3(postcss@8.4.35)
+      postcss-convert-values: 6.0.4(postcss@8.4.35)
+      postcss-discard-comments: 6.0.1(postcss@8.4.35)
+      postcss-discard-duplicates: 6.0.2(postcss@8.4.35)
+      postcss-discard-empty: 6.0.2(postcss@8.4.35)
+      postcss-discard-overridden: 6.0.1(postcss@8.4.35)
+      postcss-merge-longhand: 6.0.3(postcss@8.4.35)
+      postcss-merge-rules: 6.0.4(postcss@8.4.35)
+      postcss-minify-font-values: 6.0.2(postcss@8.4.35)
+      postcss-minify-gradients: 6.0.2(postcss@8.4.35)
+      postcss-minify-params: 6.0.3(postcss@8.4.35)
+      postcss-minify-selectors: 6.0.2(postcss@8.4.35)
+      postcss-normalize-charset: 6.0.1(postcss@8.4.35)
+      postcss-normalize-display-values: 6.0.1(postcss@8.4.35)
+      postcss-normalize-positions: 6.0.1(postcss@8.4.35)
+      postcss-normalize-repeat-style: 6.0.1(postcss@8.4.35)
+      postcss-normalize-string: 6.0.1(postcss@8.4.35)
+      postcss-normalize-timing-functions: 6.0.1(postcss@8.4.35)
+      postcss-normalize-unicode: 6.0.3(postcss@8.4.35)
+      postcss-normalize-url: 6.0.1(postcss@8.4.35)
+      postcss-normalize-whitespace: 6.0.1(postcss@8.4.35)
+      postcss-ordered-values: 6.0.1(postcss@8.4.35)
+      postcss-reduce-initial: 6.0.3(postcss@8.4.35)
+      postcss-reduce-transforms: 6.0.1(postcss@8.4.35)
+      postcss-svgo: 6.0.2(postcss@8.4.35)
+      postcss-unique-selectors: 6.0.2(postcss@8.4.35)
+    dev: false
+
+  /cssnano-utils@4.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /cssnano@6.0.3(postcss@8.4.33):
-    resolution: {integrity: sha512-MRq4CIj8pnyZpcI2qs6wswoYoDD1t0aL28n+41c1Ukcpm56m1h6mCexIHBGjfZfnTqtGSSCP4/fB1ovxgjBOiw==}
+  /cssnano@6.0.5(postcss@8.4.35):
+    resolution: {integrity: sha512-tpTp/ukgrElwu3ESFY4IvWnGn8eTt8cJhC2aAbtA3lvUlxp6t6UPv8YCLjNnEGiFreT1O0LiOM1U3QyTBVFl2A==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      cssnano-preset-default: 6.0.3(postcss@8.4.33)
-      lilconfig: 3.0.0
-      postcss: 8.4.33
+      cssnano-preset-default: 6.0.5(postcss@8.4.35)
+      lilconfig: 3.1.1
+      postcss: 8.4.35
     dev: false
 
   /csso@5.0.5:
@@ -10125,58 +10208,8 @@ packages:
       uniq: 1.0.1
     dev: false
 
-  /cypress@13.6.3:
-    resolution: {integrity: sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA==}
-    engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
-    hasBin: true
-    requiresBuild: true
-    dependencies:
-      '@cypress/request': 3.0.0
-      '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
-      '@types/sinonjs__fake-timers': 8.1.1
-      '@types/sizzle': 2.3.3
-      arch: 2.2.0
-      blob-util: 2.0.2
-      bluebird: 3.7.2
-      buffer: 5.7.1
-      cachedir: 2.3.0
-      chalk: 4.1.2
-      check-more-types: 2.24.0
-      cli-cursor: 3.1.0
-      cli-table3: 0.6.3
-      commander: 6.2.1
-      common-tags: 1.8.2
-      dayjs: 1.11.10
-      debug: 4.3.4(supports-color@8.1.1)
-      enquirer: 2.3.6
-      eventemitter2: 6.4.7
-      execa: 4.1.0
-      executable: 4.1.1
-      extract-zip: 2.0.1(supports-color@8.1.1)
-      figures: 3.2.0
-      fs-extra: 9.1.0
-      getos: 3.2.1
-      is-ci: 3.0.1
-      is-installed-globally: 0.4.0
-      lazy-ass: 1.6.0
-      listr2: 3.14.0(enquirer@2.3.6)
-      lodash: 4.17.21
-      log-symbols: 4.1.0
-      minimist: 1.2.8
-      ospath: 1.2.2
-      pretty-bytes: 5.6.0
-      process: 0.11.10
-      proxy-from-env: 1.0.0
-      request-progress: 3.0.0
-      semver: 7.5.4
-      supports-color: 8.1.1
-      tmp: 0.2.1
-      untildify: 4.0.0
-      yauzl: 2.10.0
-    dev: true
-
-  /cypress@13.6.4:
-    resolution: {integrity: sha512-pYJjCfDYB+hoOoZuhysbbYhEmNW7DEDsqn+ToCLwuVowxUXppIWRr7qk4TVRIU471ksfzyZcH+mkoF0CQUKnpw==}
+  /cypress@13.6.6:
+    resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==}
     engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
     hasBin: true
     requiresBuild: true
@@ -10220,7 +10253,7 @@ packages:
       request-progress: 3.0.0
       semver: 7.5.4
       supports-color: 8.1.1
-      tmp: 0.2.1
+      tmp: 0.2.2
       untildify: 4.0.0
       yauzl: 2.10.0
     dev: true
@@ -10659,6 +10692,11 @@ packages:
 
   /electron-to-chromium@1.4.601:
     resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==}
+    dev: true
+
+  /electron-to-chromium@1.4.686:
+    resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==}
+    dev: false
 
   /emittery@0.13.1:
     resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@@ -11096,8 +11134,8 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-vue@9.20.1(eslint@8.56.0):
-    resolution: {integrity: sha512-GyCs8K3lkEvoyC1VV97GJhP1SvqsKCiWGHnbn0gVUYiUhaH2+nB+Dv1uekv1THFMPbBfYxukrzQdltw950k+LQ==}
+  /eslint-plugin-vue@9.22.0(eslint@8.56.0):
+    resolution: {integrity: sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
@@ -11107,7 +11145,7 @@ packages:
       natural-compare: 1.4.0
       nth-check: 2.1.1
       postcss-selector-parser: 6.0.15
-      semver: 7.5.4
+      semver: 7.6.0
       vue-eslint-parser: 9.4.2(eslint@8.56.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
@@ -11734,6 +11772,10 @@ packages:
       safe-regex2: 2.0.0
     dev: false
 
+  /find-package-json@1.2.0:
+    resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==}
+    dev: true
+
   /find-up@3.0.0:
     resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
     engines: {node: '>=6'}
@@ -12244,8 +12286,8 @@ packages:
       p-cancelable: 3.0.0
       responselike: 3.0.0
 
-  /got@14.1.0:
-    resolution: {integrity: sha512-jGmSBfxa7jOGg464azcsf/cUlJBZldU8edFpiVebIJrVBE4vqVx0t3Z2f1kz1WrcMvLgQREoC/l2ttDmSHwyRg==}
+  /got@14.2.0:
+    resolution: {integrity: sha512-dBq2KkHcQl3AwPoIWsLsQScCPpUgRulz1qZVthjPYKYOPmYfBnekR3vxecjZbm91Vc3JUGnV9mqFX7B+Fe2quw==}
     engines: {node: '>=20'}
     dependencies:
       '@sindresorhus/is': 6.1.0
@@ -12316,6 +12358,16 @@ packages:
       webidl-conversions: 7.0.0
       whatwg-encoding: 2.0.0
       whatwg-mimetype: 3.0.0
+    dev: false
+
+  /happy-dom@13.6.2:
+    resolution: {integrity: sha512-Ku+wDqcF/KwFA0dI+xIMZd9Jn020RXjuSil/Vz7gu2yhDC3FsDYZ55qqV9k+SGC4opwb4acisXqVSRxUJMlPbQ==}
+    engines: {node: '>=16.0.0'}
+    dependencies:
+      entities: 4.5.0
+      webidl-conversions: 7.0.0
+      whatwg-mimetype: 3.0.0
+    dev: true
 
   /har-schema@2.0.0:
     resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
@@ -12492,8 +12544,8 @@ packages:
       statuses: 2.0.1
       toidentifier: 1.0.1
 
-  /http-link-header@1.1.1:
-    resolution: {integrity: sha512-mW3N/rTYpCn99s1do0zx6nzFZSwLH9HGfUM4ZqLWJ16ylmYaC2v5eYGqrNTQlByx8AzUgGI+V/32gXPugs1+Sw==}
+  /http-link-header@1.1.2:
+    resolution: {integrity: sha512-6qz1XhMq/ryde52SZGzVhzi3jcG2KqO16KITkupyQxvW6u7iylm0Fq7r3OpCYsc0S0ELlCiFpuxDcccUwjbEqA==}
     engines: {node: '>=6.0.0'}
     dev: false
 
@@ -12770,6 +12822,11 @@ packages:
 
   /ip@2.0.0:
     resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
+    dev: false
+
+  /ip@2.0.1:
+    resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==}
+    dev: true
 
   /ipaddr.js@1.9.1:
     resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
@@ -13222,7 +13279,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -13243,7 +13300,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.11.17):
+  /jest-cli@29.7.0(@types/node@20.11.22):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13257,10 +13314,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.17)
+      create-jest: 29.7.0(@types/node@20.11.22)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.11.17)
+      jest-config: 29.7.0(@types/node@20.11.22)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -13271,7 +13328,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.11.17):
+  /jest-config@29.7.0(@types/node@20.11.22):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -13286,7 +13343,7 @@ packages:
       '@babel/core': 7.23.5
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       babel-jest: 29.7.0(@babel/core@7.23.5)
       chalk: 4.1.2
       ci-info: 3.7.1
@@ -13346,7 +13403,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -13371,7 +13428,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -13422,7 +13479,7 @@ packages:
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
       '@jest/types': 27.5.1
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
     dev: true
 
   /jest-mock@29.7.0:
@@ -13430,7 +13487,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       jest-util: 29.7.0
     dev: true
 
@@ -13485,7 +13542,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -13516,7 +13573,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -13568,7 +13625,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -13593,7 +13650,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -13612,13 +13669,13 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
 
-  /jest@29.7.0(@types/node@20.11.17):
+  /jest@29.7.0(@types/node@20.11.22):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13631,7 +13688,7 @@ packages:
       '@jest/core': 29.7.0
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.11.17)
+      jest-cli: 29.7.0(@types/node@20.11.22)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -13890,8 +13947,8 @@ packages:
       verror: 1.10.0
     dev: true
 
-  /jsrsasign@11.0.0:
-    resolution: {integrity: sha512-BtRwVKS+5dsgPpAtzJcpo5OoWjSs1/zllSBG0+8o8/aV0Ki76m6iZwHnwnsqoTdhfFZDN1XIdcaZr5ZkP+H2gg==}
+  /jsrsasign@11.1.0:
+    resolution: {integrity: sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==}
     dev: false
 
   /jssha@3.3.1:
@@ -14000,8 +14057,8 @@ packages:
       set-cookie-parser: 2.6.0
     dev: false
 
-  /lilconfig@3.0.0:
-    resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==}
+  /lilconfig@3.1.1:
+    resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
     engines: {node: '>=14'}
     dev: false
 
@@ -15062,8 +15119,8 @@ packages:
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
 
-  /nanoid@5.0.4:
-    resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
+  /nanoid@5.0.6:
+    resolution: {integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==}
     engines: {node: ^18 || >=20}
     hasBin: true
     dev: false
@@ -15198,13 +15255,6 @@ packages:
       fetch-blob: 3.2.0
       formdata-polyfill: 4.0.10
 
-  /node-gyp-build-optional-packages@5.0.3:
-    resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==}
-    hasBin: true
-    requiresBuild: true
-    dev: false
-    optional: true
-
   /node-gyp-build-optional-packages@5.0.7:
     resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==}
     hasBin: true
@@ -15243,8 +15293,8 @@ packages:
   /node-releases@2.0.14:
     resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
 
-  /nodemailer@6.9.8:
-    resolution: {integrity: sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ==}
+  /nodemailer@6.9.10:
+    resolution: {integrity: sha512-qtoKfGFhvIFW5kLfrkw2R6Nm6Ur4LNUMykyqu6n9BRKJuyQrqEGwdXXUAbwWEKt33dlWUGXb7rzmJP/p4+O+CA==}
     engines: {node: '>=6.0.0'}
     dev: false
 
@@ -15265,8 +15315,8 @@ packages:
       undefsafe: 2.0.5
     dev: true
 
-  /nodemon@3.0.3:
-    resolution: {integrity: sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==}
+  /nodemon@3.1.0:
+    resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
@@ -16063,264 +16113,264 @@ packages:
       '@babel/runtime': 7.23.4
     dev: true
 
-  /postcss-calc@9.0.1(postcss@8.4.33):
+  /postcss-calc@9.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.2
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-selector-parser: 6.0.15
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-colormin@6.0.2(postcss@8.4.33):
-    resolution: {integrity: sha512-TXKOxs9LWcdYo5cgmcSHPkyrLAh86hX1ijmyy6J8SbOhyv6ua053M3ZAM/0j44UsnQNIWdl8gb5L7xX2htKeLw==}
+  /postcss-colormin@6.0.3(postcss@8.4.35):
+    resolution: {integrity: sha512-ECpkS+UZRyAtu/kjive2/1mihP+GNtgC8kcdU8ueWZi1ZVxMNnRziCLdhrWECJhEtSWijfX2Cl9XTTCK/hjGaA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      browserslist: 4.22.2
+      browserslist: 4.23.0
       caniuse-api: 3.0.0
       colord: 2.9.3
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-convert-values@6.0.2(postcss@8.4.33):
-    resolution: {integrity: sha512-aeBmaTnGQ+NUSVQT8aY0sKyAD/BaLJenEKZ03YK0JnDE1w1Rr8XShoxdal2V2H26xTJKr3v5haByOhJuyT4UYw==}
+  /postcss-convert-values@6.0.4(postcss@8.4.35):
+    resolution: {integrity: sha512-YT2yrGzPXoQD3YeA2kBo/696qNwn7vI+15AOS2puXWEvSWqdCqlOyDWRy5GNnOc9ACRGOkuQ4ESQEqPJBWt/GA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      browserslist: 4.22.2
-      postcss: 8.4.33
+      browserslist: 4.23.0
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-discard-comments@6.0.1(postcss@8.4.33):
+  /postcss-discard-comments@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /postcss-discard-duplicates@6.0.1(postcss@8.4.33):
-    resolution: {integrity: sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg==}
+  /postcss-discard-duplicates@6.0.2(postcss@8.4.35):
+    resolution: {integrity: sha512-U2rsj4w6pAGROCCcD13LP2eBIi1whUsXs4kgE6xkIuGfkbxCBSKhkCTWyowFd66WdVlLv0uM1euJKIgmdmZObg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /postcss-discard-empty@6.0.1(postcss@8.4.33):
-    resolution: {integrity: sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg==}
+  /postcss-discard-empty@6.0.2(postcss@8.4.35):
+    resolution: {integrity: sha512-rj6pVC2dVCJrP0Y2RkYTQEbYaCf4HEm+R/2StQgJqGHxAa3+KcYslNQhcRqjLHtl/4wpzipJluaJLqBj6d5eDQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /postcss-discard-overridden@6.0.1(postcss@8.4.33):
+  /postcss-discard-overridden@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /postcss-merge-longhand@6.0.2(postcss@8.4.33):
-    resolution: {integrity: sha512-+yfVB7gEM8SrCo9w2lCApKIEzrTKl5yS1F4yGhV3kSim6JzbfLGJyhR1B6X+6vOT0U33Mgx7iv4X9MVWuaSAfw==}
+  /postcss-merge-longhand@6.0.3(postcss@8.4.35):
+    resolution: {integrity: sha512-kF/y3DU8CRt+SX3tP/aG+2gkZI2Z7OXDsPU7FgxIJmuyhQQ1EHceIYcsp/alvzCm2P4c37Sfdu8nNrHc+YeyLg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
-      stylehacks: 6.0.2(postcss@8.4.33)
+      stylehacks: 6.0.3(postcss@8.4.35)
     dev: false
 
-  /postcss-merge-rules@6.0.3(postcss@8.4.33):
-    resolution: {integrity: sha512-yfkDqSHGohy8sGYIJwBmIGDv4K4/WrJPX355XrxQb/CSsT4Kc/RxDi6akqn5s9bap85AWgv21ArcUWwWdGNSHA==}
+  /postcss-merge-rules@6.0.4(postcss@8.4.35):
+    resolution: {integrity: sha512-97iF3UJ5v8N1BWy38y+0l+Z8o5/9uGlEgtWic2PJPzoRrLB6Gxg8TVG93O0EK52jcLeMsywre26AUlX1YAYeHA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      browserslist: 4.22.2
+      browserslist: 4.23.0
       caniuse-api: 3.0.0
-      cssnano-utils: 4.0.1(postcss@8.4.33)
-      postcss: 8.4.33
+      cssnano-utils: 4.0.1(postcss@8.4.35)
+      postcss: 8.4.35
       postcss-selector-parser: 6.0.15
     dev: false
 
-  /postcss-minify-font-values@6.0.1(postcss@8.4.33):
-    resolution: {integrity: sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w==}
+  /postcss-minify-font-values@6.0.2(postcss@8.4.35):
+    resolution: {integrity: sha512-IedzbVMoX0a7VZWjSYr5qJ6C37rws8kl8diPBeMZLJfWKkgXuMFY5R/OxPegn/q9tK9ztd0XRH3aR0u2t+A7uQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-gradients@6.0.1(postcss@8.4.33):
-    resolution: {integrity: sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w==}
+  /postcss-minify-gradients@6.0.2(postcss@8.4.35):
+    resolution: {integrity: sha512-vP5mF7iI6/5fcpv+rSfwWQekOE+8I1i7/7RjZPGuIjj6eUaZVeG4XZYZrroFuw1WQd51u2V32wyQFZ+oYdE7CA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
       colord: 2.9.3
-      cssnano-utils: 4.0.1(postcss@8.4.33)
-      postcss: 8.4.33
+      cssnano-utils: 4.0.1(postcss@8.4.35)
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-params@6.0.2(postcss@8.4.33):
-    resolution: {integrity: sha512-zwQtbrPEBDj+ApELZ6QylLf2/c5zmASoOuA4DzolyVGdV38iR2I5QRMsZcHkcdkZzxpN8RS4cN7LPskOkTwTZw==}
+  /postcss-minify-params@6.0.3(postcss@8.4.35):
+    resolution: {integrity: sha512-j4S74d3AAeCK5eGdQndXSrkxusV2ekOxbXGnlnZthMyZBBvSDiU34CihTASbJxuVB3bugudmwolS7+Dgs5OyOQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      browserslist: 4.22.2
-      cssnano-utils: 4.0.1(postcss@8.4.33)
-      postcss: 8.4.33
+      browserslist: 4.23.0
+      cssnano-utils: 4.0.1(postcss@8.4.35)
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-selectors@6.0.2(postcss@8.4.33):
+  /postcss-minify-selectors@6.0.2(postcss@8.4.35):
     resolution: {integrity: sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-selector-parser: 6.0.15
     dev: false
 
-  /postcss-normalize-charset@6.0.1(postcss@8.4.33):
+  /postcss-normalize-charset@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /postcss-normalize-display-values@6.0.1(postcss@8.4.33):
+  /postcss-normalize-display-values@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-positions@6.0.1(postcss@8.4.33):
+  /postcss-normalize-positions@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-repeat-style@6.0.1(postcss@8.4.33):
+  /postcss-normalize-repeat-style@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-string@6.0.1(postcss@8.4.33):
+  /postcss-normalize-string@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-timing-functions@6.0.1(postcss@8.4.33):
+  /postcss-normalize-timing-functions@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-unicode@6.0.2(postcss@8.4.33):
-    resolution: {integrity: sha512-Ff2VdAYCTGyMUwpevTZPZ4w0+mPjbZzLLyoLh/RMpqUqeQKZ+xMm31hkxBavDcGKcxm6ACzGk0nBfZ8LZkStKA==}
+  /postcss-normalize-unicode@6.0.3(postcss@8.4.35):
+    resolution: {integrity: sha512-T2Bb3gXz0ASgc3ori2dzjv6j/P2IantreaC6fT8tWjqYUiqMAh5jGIkdPwEV2FaucjQlCLeFJDJh2BeSugE1ig==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      browserslist: 4.22.2
-      postcss: 8.4.33
+      browserslist: 4.23.0
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-url@6.0.1(postcss@8.4.33):
+  /postcss-normalize-url@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-whitespace@6.0.1(postcss@8.4.33):
+  /postcss-normalize-whitespace@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-ordered-values@6.0.1(postcss@8.4.33):
+  /postcss-ordered-values@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      cssnano-utils: 4.0.1(postcss@8.4.33)
-      postcss: 8.4.33
+      cssnano-utils: 4.0.1(postcss@8.4.35)
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-reduce-initial@6.0.2(postcss@8.4.33):
-    resolution: {integrity: sha512-YGKalhNlCLcjcLvjU5nF8FyeCTkCO5UtvJEt0hrPZVCTtRLSOH4z00T1UntQPj4dUmIYZgMj8qK77JbSX95hSw==}
+  /postcss-reduce-initial@6.0.3(postcss@8.4.35):
+    resolution: {integrity: sha512-w4QIR9pEa1N4xMx3k30T1vLZl6udVK2RmNqrDXhBXX9L0mBj2a8ADs8zkbaEH7eUy1m30Wyr5EBgHN31Yq1JvA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      browserslist: 4.22.2
+      browserslist: 4.23.0
       caniuse-api: 3.0.0
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /postcss-reduce-transforms@6.0.1(postcss@8.4.33):
+  /postcss-reduce-transforms@6.0.1(postcss@8.4.35):
     resolution: {integrity: sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
     dev: false
 
@@ -16331,24 +16381,24 @@ packages:
       cssesc: 3.0.0
       util-deprecate: 1.0.2
 
-  /postcss-svgo@6.0.2(postcss@8.4.33):
+  /postcss-svgo@6.0.2(postcss@8.4.35):
     resolution: {integrity: sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ==}
     engines: {node: ^14 || ^16 || >= 18}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-value-parser: 4.2.0
       svgo: 3.2.0
     dev: false
 
-  /postcss-unique-selectors@6.0.2(postcss@8.4.33):
+  /postcss-unique-selectors@6.0.2(postcss@8.4.35):
     resolution: {integrity: sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      postcss: 8.4.33
+      postcss: 8.4.35
       postcss-selector-parser: 6.0.15
     dev: false
 
@@ -16356,14 +16406,6 @@ packages:
     resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
     dev: false
 
-  /postcss@8.4.33:
-    resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==}
-    engines: {node: ^10 || ^12 || >=14}
-    dependencies:
-      nanoid: 3.3.7
-      picocolors: 1.0.0
-      source-map-js: 1.0.2
-
   /postcss@8.4.35:
     resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
     engines: {node: ^10 || ^12 || >=14}
@@ -17000,17 +17042,6 @@ packages:
     engines: {node: '>= 12.13.0'}
     dev: false
 
-  /recast@0.22.0:
-    resolution: {integrity: sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==}
-    engines: {node: '>= 4'}
-    dependencies:
-      assert: 2.1.0
-      ast-types: 0.15.2
-      esprima: 4.0.1
-      source-map: 0.6.1
-      tslib: 2.6.2
-    dev: true
-
   /recast@0.23.4:
     resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==}
     engines: {node: '>= 4'}
@@ -17329,26 +17360,33 @@ packages:
     dependencies:
       glob: 7.2.3
 
-  /rollup@4.9.6:
-    resolution: {integrity: sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==}
+  /rimraf@5.0.5:
+    resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
+    engines: {node: '>=14'}
+    hasBin: true
+    dependencies:
+      glob: 10.3.10
+
+  /rollup@4.12.0:
+    resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     dependencies:
       '@types/estree': 1.0.5
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.9.6
-      '@rollup/rollup-android-arm64': 4.9.6
-      '@rollup/rollup-darwin-arm64': 4.9.6
-      '@rollup/rollup-darwin-x64': 4.9.6
-      '@rollup/rollup-linux-arm-gnueabihf': 4.9.6
-      '@rollup/rollup-linux-arm64-gnu': 4.9.6
-      '@rollup/rollup-linux-arm64-musl': 4.9.6
-      '@rollup/rollup-linux-riscv64-gnu': 4.9.6
-      '@rollup/rollup-linux-x64-gnu': 4.9.6
-      '@rollup/rollup-linux-x64-musl': 4.9.6
-      '@rollup/rollup-win32-arm64-msvc': 4.9.6
-      '@rollup/rollup-win32-ia32-msvc': 4.9.6
-      '@rollup/rollup-win32-x64-msvc': 4.9.6
+      '@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
       fsevents: 2.3.3
 
   /rrweb-cssom@0.6.0:
@@ -17415,19 +17453,19 @@ packages:
   /safer-buffer@2.1.2:
     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
 
-  /sanitize-html@2.11.0:
-    resolution: {integrity: sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==}
+  /sanitize-html@2.12.1:
+    resolution: {integrity: sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==}
     dependencies:
       deepmerge: 4.2.2
       escape-string-regexp: 4.0.0
       htmlparser2: 8.0.1
       is-plain-object: 5.0.0
       parse-srcset: 1.0.2
-      postcss: 8.4.33
+      postcss: 8.4.35
     dev: false
 
-  /sass@1.70.0:
-    resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==}
+  /sass@1.71.1:
+    resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==}
     engines: {node: '>=14.0.0'}
     hasBin: true
     dependencies:
@@ -17488,6 +17526,14 @@ packages:
     dependencies:
       lru-cache: 6.0.0
 
+  /semver@7.6.0:
+    resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      lru-cache: 6.0.0
+    dev: true
+
   /send@0.18.0:
     resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
     engines: {node: '>= 0.8.0'}
@@ -18013,11 +18059,11 @@ packages:
     resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
     dev: true
 
-  /storybook@8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-iXPJck+USEAp9JIBgPvkHNOGzgbfcRoyrk18JMtypwoEXeMZgf6gPw9uKqH2rAoQ0opEYHKbU8FsJ2v+GX01yQ==}
+  /storybook@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-8d9gpKPDY9Ix64f0560rXIifmnuoswDdvSdTz4NXHGvPt7WrKNmaDTvWGyt1/fbTbv2dvvVp7bsWPgq1KGbrcg==}
     hasBin: true
     dependencies:
-      '@storybook/cli': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/cli': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
     transitivePeerDependencies:
       - '@babel/preset-env'
       - bufferutil
@@ -18227,14 +18273,14 @@ packages:
       peek-readable: 5.0.0
     dev: false
 
-  /stylehacks@6.0.2(postcss@8.4.33):
-    resolution: {integrity: sha512-00zvJGnCu64EpMjX8b5iCZ3us2Ptyw8+toEkb92VdmkEaRaSGBNKAoK6aWZckhXxmQP8zWiTaFaiMGIU8Ve8sg==}
+  /stylehacks@6.0.3(postcss@8.4.35):
+    resolution: {integrity: sha512-KzBqjnqktc8/I0ERCb+lGq06giF/JxDbw2r9kEVhen9noHeIDRtMWUp9r62sOk+/2bbX6sFG1GhsS7ToXG0PEg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.4.31
     dependencies:
-      browserslist: 4.22.2
-      postcss: 8.4.33
+      browserslist: 4.23.0
+      postcss: 8.4.35
       postcss-selector-parser: 6.0.15
     dev: false
 
@@ -18292,8 +18338,8 @@ packages:
     resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
     dev: false
 
-  /systeminformation@5.21.24:
-    resolution: {integrity: sha512-xQada8ByGGFoRXJaUptGgddn3i7IjtSdqNdCKzB8xkzsM7pHnfLYBWxkPdGzhZ0Z/l+W1yo+aZQZ74d2isj8kw==}
+  /systeminformation@5.22.0:
+    resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==}
     engines: {node: '>=8.0.0'}
     os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
     hasBin: true
@@ -18389,8 +18435,8 @@ packages:
       unique-string: 2.0.0
     dev: true
 
-  /terser@5.27.0:
-    resolution: {integrity: sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==}
+  /terser@5.28.1:
+    resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
@@ -18439,8 +18485,8 @@ packages:
       real-require: 0.2.0
     dev: false
 
-  /three@0.160.1:
-    resolution: {integrity: sha512-Bgl2wPJypDOZ1stAxwfWAcJ0WQf7QzlptsxkjYiURPz+n5k4RBDLsq+6f9Y75TYxn6aHLcWz+JNmwTOXWrQTBQ==}
+  /three@0.161.0:
+    resolution: {integrity: sha512-LC28VFtjbOyEu5b93K0bNRLw1rQlMJ85lilKsYj6dgTu+7i17W+JCCEbvrpmNHF1F3NAUqDSWq50UD7w9H2xQw==}
     dev: false
 
   /throttle-debounce@5.0.0:
@@ -18500,11 +18546,11 @@ packages:
       os-tmpdir: 1.0.2
     dev: true
 
-  /tmp@0.2.1:
-    resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
-    engines: {node: '>=8.17.0'}
+  /tmp@0.2.2:
+    resolution: {integrity: sha512-ETcvHhaIc9J2MDEAH6N67j9bvBvu/3Gb764qaGhwtFvjtvhegqoqSpofgeyq1Sc24mW5pdyUDs9HP5j3ehkxRw==}
+    engines: {node: '>=14'}
     dependencies:
-      rimraf: 3.0.2
+      rimraf: 5.0.5
 
   /tmpl@1.0.5:
     resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
@@ -18655,8 +18701,8 @@ packages:
       strip-bom: 3.0.0
     dev: false
 
-  /tsd@0.30.4:
-    resolution: {integrity: sha512-ncC4SwAeUk0OTcXt5h8l0/gOLHJSp9ogosvOADT6QYzrl0ITm398B3wkz8YESqefIsEEwvYAU8bvo7/rcN/M0Q==}
+  /tsd@0.30.7:
+    resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==}
     engines: {node: '>=14.16'}
     hasBin: true
     dependencies:
@@ -19057,6 +19103,18 @@ packages:
       browserslist: 4.22.2
       escalade: 3.1.1
       picocolors: 1.0.0
+    dev: true
+
+  /update-browserslist-db@1.0.13(browserslist@4.23.0):
+    resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+    dependencies:
+      browserslist: 4.23.0
+      escalade: 3.1.1
+      picocolors: 1.0.0
+    dev: false
 
   /uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -19107,7 +19165,7 @@ packages:
     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
     hasBin: true
 
-  /v-code-diff@1.7.2(vue@3.4.18):
+  /v-code-diff@1.7.2(vue@3.4.21):
     resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==}
     requiresBuild: true
     peerDependencies:
@@ -19120,8 +19178,8 @@ packages:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.8.0
-      vue: 3.4.18(typescript@5.3.3)
-      vue-demi: 0.13.11(vue@3.4.18)
+      vue: 3.4.21(typescript@5.3.3)
+      vue-demi: 0.13.11(vue@3.4.21)
     dev: false
 
   /v8-to-istanbul@9.2.0:
@@ -19172,7 +19230,7 @@ packages:
       vfile-message: 4.0.2
     dev: true
 
-  /vite-node@0.34.6(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0):
+  /vite-node@0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1):
     resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -19182,7 +19240,7 @@ packages:
       mlly: 1.5.0
       pathe: 1.1.2
       picocolors: 1.0.0
-      vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
+      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19198,8 +19256,8 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0):
-    resolution: {integrity: sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==}
+  /vite@5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1):
+    resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -19226,12 +19284,12 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       esbuild: 0.19.11
       postcss: 8.4.35
-      rollup: 4.9.6
-      sass: 1.70.0
-      terser: 5.27.0
+      rollup: 4.12.0
+      sass: 1.71.1
+      terser: 5.28.1
     optionalDependencies:
       fsevents: 2.3.3
 
@@ -19242,12 +19300,12 @@ packages:
       vitest: '>=0.16.0'
     dependencies:
       cross-fetch: 3.1.6
-      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0)
+      vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
     transitivePeerDependencies:
       - encoding
     dev: true
 
-  /vitest@0.34.6(happy-dom@10.0.3)(sass@1.70.0)(terser@5.27.0):
+  /vitest@0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1):
     resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -19280,7 +19338,7 @@ packages:
     dependencies:
       '@types/chai': 4.3.11
       '@types/chai-subset': 1.3.5
-      '@types/node': 20.11.17
+      '@types/node': 20.11.22
       '@vitest/expect': 0.34.6
       '@vitest/runner': 0.34.6
       '@vitest/snapshot': 0.34.6
@@ -19291,7 +19349,7 @@ packages:
       cac: 6.7.14
       chai: 4.3.10
       debug: 4.3.4(supports-color@8.1.1)
-      happy-dom: 10.0.3
+      happy-dom: 13.6.2
       local-pkg: 0.4.3
       magic-string: 0.30.7
       pathe: 1.1.2
@@ -19300,8 +19358,8 @@ packages:
       strip-literal: 1.3.0
       tinybench: 2.6.0
       tinypool: 0.7.0
-      vite: 5.1.0(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
-      vite-node: 0.34.6(@types/node@20.11.17)(sass@1.70.0)(terser@5.27.0)
+      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+      vite-node: 0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19353,6 +19411,21 @@ packages:
       vscode-languageserver-protocol: 3.17.5
     dev: false
 
+  /vue-component-meta@1.8.27(typescript@5.3.3):
+    resolution: {integrity: sha512-j3WJsyQHP4TDlvnjHc/eseo0/eVkf0FaCpkqGwez5zD+Tj31onBzWZEXTnWKs8xRj0n3dMNYdy3SpiS6NubSvg==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@volar/typescript': 1.11.1
+      '@vue/language-core': 1.8.27(typescript@5.3.3)
+      path-browserify: 1.0.1
+      typescript: 5.3.3
+      vue-component-type-helpers: 1.8.27
+    dev: true
+
   /vue-component-type-helpers@1.8.27:
     resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
     dev: true
@@ -19361,7 +19434,7 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-demi@0.13.11(vue@3.4.18):
+  /vue-demi@0.13.11(vue@3.4.21):
     resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
     engines: {node: '>=12'}
     hasBin: true
@@ -19373,25 +19446,26 @@ packages:
       '@vue/composition-api':
         optional: true
     dependencies:
-      vue: 3.4.18(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.3.3)
     dev: false
 
-  /vue-docgen-api@4.64.1(vue@3.4.18):
-    resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
+  /vue-docgen-api@4.75.1(vue@3.4.21):
+    resolution: {integrity: sha512-MECZ3uExz+ssmhD/2XrFoQQs93y17IVO1KDYTp8nr6i9GNrk67AAto6QAtilW1H/pTDPMkQxJ7w/25ZIqVtfAA==}
+    peerDependencies:
+      vue: '>=2'
     dependencies:
       '@babel/parser': 7.23.9
       '@babel/types': 7.23.5
       '@vue/compiler-dom': 3.4.18
-      '@vue/compiler-sfc': 3.4.18
-      ast-types: 0.14.2
+      '@vue/compiler-sfc': 3.4.21
+      ast-types: 0.16.1
       hash-sum: 2.0.0
       lru-cache: 8.0.4
       pug: 3.0.2
-      recast: 0.22.0
+      recast: 0.23.4
       ts-map: 1.0.3
-      vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.4.18)
-    transitivePeerDependencies:
-      - vue
+      vue: 3.4.21(typescript@5.3.3)
+      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.21)
     dev: true
 
   /vue-eslint-parser@9.4.2(eslint@8.56.0):
@@ -19412,12 +19486,12 @@ packages:
       - supports-color
     dev: true
 
-  /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.4.18):
-    resolution: {integrity: sha512-Hn32n07XZ8j9W8+fmOXPQL+i+W2e/8i6mkH4Ju3H6nR0+cfvmWM95GhczYi5B27+Y8JlCKgAo04IUiYce4mKAw==}
+  /vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.21):
+    resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==}
     peerDependencies:
       vue: '>=2'
     dependencies:
-      vue: 3.4.18(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.3.3)
     dev: true
 
   /vue-template-compiler@2.7.14:
@@ -19439,28 +19513,28 @@ packages:
       typescript: 5.3.3
     dev: true
 
-  /vue@3.4.18(typescript@5.3.3):
-    resolution: {integrity: sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==}
+  /vue@3.4.21(typescript@5.3.3):
+    resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@vue/compiler-dom': 3.4.18
-      '@vue/compiler-sfc': 3.4.18
-      '@vue/runtime-dom': 3.4.18
-      '@vue/server-renderer': 3.4.18(vue@3.4.18)
-      '@vue/shared': 3.4.18
+      '@vue/compiler-dom': 3.4.21
+      '@vue/compiler-sfc': 3.4.21
+      '@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
 
-  /vuedraggable@4.1.0(vue@3.4.18):
+  /vuedraggable@4.1.0(vue@3.4.21):
     resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
     peerDependencies:
       vue: ^3.0.1
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.4.18(typescript@5.3.3)
+      vue: 3.4.21(typescript@5.3.3)
     dev: false
 
   /w3c-xmlserializer@5.0.0:
@@ -19544,6 +19618,7 @@ packages:
     engines: {node: '>=12'}
     dependencies:
       iconv-lite: 0.6.3
+    dev: false
 
   /whatwg-encoding@3.1.1:
     resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
@@ -19901,7 +19976,7 @@ packages:
       vscode-languageclient: 9.0.1
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.2)(@storybook/components@8.0.0-beta.2)(@storybook/core-events@8.0.0-beta.2)(@storybook/manager-api@8.0.0-beta.2)(@storybook/preview-api@8.0.0-beta.2)(@storybook/theming@8.0.0-beta.2)(@storybook/types@8.0.0-beta.2)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0):
     resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
     id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640
     name: storybook-addon-misskey-theme
@@ -19922,13 +19997,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/blocks': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 8.0.0-beta.2(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 8.0.0-beta.2
-      '@storybook/manager-api': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 8.0.0-beta.2
-      '@storybook/theming': 8.0.0-beta.2(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.2
+      '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 8.0.0-beta.6
+      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.0-beta.6
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true

From 98934b6738d04703fd2df6af4aa38ab739fb565e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 17:54:32 +0900
Subject: [PATCH 042/266] fix type

---
 packages/backend/src/core/WebAuthnService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/WebAuthnService.ts b/packages/backend/src/core/WebAuthnService.ts
index 4d1186590618..42fbed21107a 100644
--- a/packages/backend/src/core/WebAuthnService.ts
+++ b/packages/backend/src/core/WebAuthnService.ts
@@ -191,7 +191,7 @@ export class WebAuthnService {
 			if (cert[0] === 0x04) { // 前の実装ではいつも 0x04 で始まっていた
 				const halfLength = (cert.length - 1) / 2;
 
-				const cborMap = new Map<number, number | ArrayBufferLike>();
+				const cborMap = new Map<number, number | Uint8Array>();
 				cborMap.set(1, 2); // kty, EC2
 				cborMap.set(3, -7); // alg, ES256
 				cborMap.set(-1, 1); // crv, P256

From 9d0fc96d1a1241bb29b8b2b64e65cc9da4ba9a13 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 18:04:03 +0900
Subject: [PATCH 043/266] fix test

---
 packages/frontend/test/scroll.test.ts | 2 --
 1 file changed, 2 deletions(-)

diff --git a/packages/frontend/test/scroll.test.ts b/packages/frontend/test/scroll.test.ts
index e49ec270d547..c5e91eef0996 100644
--- a/packages/frontend/test/scroll.test.ts
+++ b/packages/frontend/test/scroll.test.ts
@@ -39,7 +39,6 @@ describe('Scroll', () => {
 			const { document } = new Window();
 			const div = document.createElement('div');
 			assert.strictEqual(div.scrollTop, 0);
-			(div as any).scrollHeight = 100; // happy-dom has no scrollHeight
 
 			document.body.append(div);
 
@@ -53,7 +52,6 @@ describe('Scroll', () => {
 			const { document } = new Window();
 			const div = document.createElement('div');
 			assert.strictEqual(div.scrollTop, 0);
-			(div as any).scrollHeight = 100; // happy-dom has no scrollHeight
 
 			let called = false;
 			onScrollBottom(div as any as HTMLElement, () => called = true);

From ec18991328dc3ecba79cc37b3a24c20c49da51aa Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 19:44:00 +0900
Subject: [PATCH 044/266] Update scroll.test.ts

---
 packages/frontend/test/scroll.test.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/packages/frontend/test/scroll.test.ts b/packages/frontend/test/scroll.test.ts
index c5e91eef0996..a0b56b7221bb 100644
--- a/packages/frontend/test/scroll.test.ts
+++ b/packages/frontend/test/scroll.test.ts
@@ -9,6 +9,7 @@ import { onScrollBottom, onScrollTop } from '@/scripts/scroll.js';
 
 describe('Scroll', () => {
 	describe('onScrollTop', () => {
+		/* 動作しない(happy-domのバグ?)
 		test('Initial onScrollTop callback for connected elements', () => {
 			const { document } = new Window();
 			const div = document.createElement('div');
@@ -21,6 +22,7 @@ describe('Scroll', () => {
 
 			assert.ok(called);
 		});
+		*/
 
 		test('No onScrollTop callback for disconnected elements', () => {
 			const { document } = new Window();
@@ -35,6 +37,7 @@ describe('Scroll', () => {
 	});
 
 	describe('onScrollBottom', () => {
+		/* 動作しない(happy-domのバグ?)
 		test('Initial onScrollBottom callback for connected elements', () => {
 			const { document } = new Window();
 			const div = document.createElement('div');
@@ -47,6 +50,7 @@ describe('Scroll', () => {
 
 			assert.ok(called);
 		});
+		*/
 
 		test('No onScrollBottom callback for disconnected elements', () => {
 			const { document } = new Window();

From 39d6af135f43c2521bd7688fcb1c46bcce546b73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 29 Feb 2024 20:03:30 +0900
Subject: [PATCH 045/266] =?UTF-8?q?enhance:=20=E9=80=9A=E7=9F=A5=E3=81=AE?=
 =?UTF-8?q?=E5=B1=A5=E6=AD=B4=E3=82=92=E3=83=AA=E3=82=BB=E3=83=83=E3=83=88?=
 =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1333?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: 通知の履歴をリセットできるように

* Update Changelog

* 通知欄も連動して更新するように

* revert some changes

* Update CHANGELOG.md

* Remove unused part

* fix
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  4 ++
 locales/ja-JP.yml                             |  1 +
 .../backend/src/core/GlobalEventService.ts    |  1 +
 .../backend/src/core/NotificationService.ts   |  9 ++++
 .../backend/src/server/api/EndpointsModule.ts |  5 ++
 packages/backend/src/server/api/endpoints.ts  |  2 +
 .../api/endpoints/notifications/flush.ts      | 33 ++++++++++++
 .../src/components/MkNotifications.vue        |  5 +-
 .../src/pages/settings/notifications.vue      | 12 +++++
 packages/misskey-js/etc/misskey-js.api.md     |  1 +
 .../misskey-js/src/autogen/apiClientJSDoc.ts  | 11 ++++
 packages/misskey-js/src/autogen/endpoint.ts   |  1 +
 packages/misskey-js/src/autogen/types.ts      | 53 +++++++++++++++++++
 packages/misskey-js/src/streaming.types.ts    |  1 +
 15 files changed, 139 insertions(+), 1 deletion(-)
 create mode 100644 packages/backend/src/server/api/endpoints/notifications/flush.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5160228696b7..ae611875dc85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@
 - Enhance: サーバーごとにモデレーションノートを残せるように
 - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
 - Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加
+- Enhance: 通知の履歴をリセットできるように
 
 ### Client
 - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 3edc9d235ef2..0883749a3358 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -8913,6 +8913,10 @@ export interface Locale extends ILocale {
          * {n}人にフォローされました
          */
         "followedBySomeUsers": ParameterizedString<"n">;
+        /**
+         * 通知の履歴をリセットする
+         */
+        "flushNotification": string;
         "_types": {
             /**
              * すべて
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 66ddf6a46d46..dc91b9f21022 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2356,6 +2356,7 @@ _notification:
   reactedBySomeUsers: "{n}人がリアクションしました"
   renotedBySomeUsers: "{n}人がリノートしました"
   followedBySomeUsers: "{n}人にフォローされました"
+  flushNotification: "通知の履歴をリセットする"
 
   _types:
     all: "すべて"
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index 7c1b34da05ea..90efd63f3a05 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -69,6 +69,7 @@ export interface MainEventTypes {
 		file: Packed<'DriveFile'>;
 	};
 	readAllNotifications: undefined;
+	notificationFlushed: undefined;
 	unreadNotification: Packed<'Notification'>;
 	unreadMention: MiNote['id'];
 	readAllUnreadMentions: undefined;
diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts
index af5755f88bd7..68ad92f39690 100644
--- a/packages/backend/src/core/NotificationService.ts
+++ b/packages/backend/src/core/NotificationService.ts
@@ -214,6 +214,15 @@ export class NotificationService implements OnApplicationShutdown {
 		*/
 	}
 
+	@bindThis
+	public async flushAllNotifications(userId: MiUser['id']) {
+		await Promise.all([
+			this.redisClient.del(`notificationTimeline:${userId}`),
+			this.redisClient.del(`latestReadNotification:${userId}`),
+		]);
+		this.globalEventService.publishMainStream(userId, 'notificationFlushed');
+	}
+
 	@bindThis
 	public dispose(): void {
 		this.#shutdownController.abort();
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 8a003725cd5b..88d3999eb0b1 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js';
 import * as ep___notes_unrenote from './endpoints/notes/unrenote.js';
 import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js';
 import * as ep___notifications_create from './endpoints/notifications/create.js';
+import * as ep___notifications_flush from './endpoints/notifications/flush.js';
 import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js';
 import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js';
 import * as ep___pagePush from './endpoints/page-push.js';
@@ -664,6 +665,7 @@ const $notes_translate: Provider = { provide: 'ep:notes/translate', useClass: ep
 const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default };
 const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default };
 const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default };
+const $notifications_flush: Provider = { provide: 'ep:notifications/flush', useClass: ep___notifications_flush.default };
 const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default };
 const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default };
 const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default };
@@ -1039,6 +1041,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$notes_unrenote,
 		$notes_userListTimeline,
 		$notifications_create,
+		$notifications_flush,
 		$notifications_markAllAsRead,
 		$notifications_testNotification,
 		$pagePush,
@@ -1408,7 +1411,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$notes_unrenote,
 		$notes_userListTimeline,
 		$notifications_create,
+		$notifications_flush,
 		$notifications_markAllAsRead,
+		$notifications_testNotification,
 		$pagePush,
 		$pages_create,
 		$pages_delete,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index e1c8be727e47..f7e64a7356d4 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js';
 import * as ep___notes_unrenote from './endpoints/notes/unrenote.js';
 import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js';
 import * as ep___notifications_create from './endpoints/notifications/create.js';
+import * as ep___notifications_flush from './endpoints/notifications/flush.js';
 import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js';
 import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js';
 import * as ep___pagePush from './endpoints/page-push.js';
@@ -662,6 +663,7 @@ const eps = [
 	['notes/unrenote', ep___notes_unrenote],
 	['notes/user-list-timeline', ep___notes_userListTimeline],
 	['notifications/create', ep___notifications_create],
+	['notifications/flush', ep___notifications_flush],
 	['notifications/mark-all-as-read', ep___notifications_markAllAsRead],
 	['notifications/test-notification', ep___notifications_testNotification],
 	['page-push', ep___pagePush],
diff --git a/packages/backend/src/server/api/endpoints/notifications/flush.ts b/packages/backend/src/server/api/endpoints/notifications/flush.ts
new file mode 100644
index 000000000000..47c0642fd15d
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/notifications/flush.ts
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { NotificationService } from '@/core/NotificationService.js';
+
+export const meta = {
+	tags: ['notifications', 'account'],
+
+	requireCredential: true,
+
+	kind: 'write:notifications',
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {},
+	required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private notificationService: NotificationService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			this.notificationService.flushAllNotifications(me.id);
+		});
+	}
+}
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index a9f019dd9cc6..389987338d0e 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -35,6 +35,7 @@ import { notificationTypes } from '@/const.js';
 import { infoImageUrl } from '@/instance.js';
 import { defaultStore } from '@/store.js';
 import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
+import * as Misskey from 'misskey-js';
 
 const props = defineProps<{
 	excludeTypes?: typeof notificationTypes[number][];
@@ -75,17 +76,19 @@ function reload() {
 	});
 }
 
-let connection;
+let connection: Misskey.ChannelConnection<Misskey.Channels['main']>;
 
 onMounted(() => {
 	connection = useStream().useChannel('main');
 	connection.on('notification', onNotification);
+	connection.on('notificationFlushed', reload);
 });
 
 onActivated(() => {
 	pagingComponent.value?.reload();
 	connection = useStream().useChannel('main');
 	connection.on('notification', onNotification);
+	connection.on('notificationFlushed', reload);
 });
 
 onUnmounted(() => {
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index bbcef652839a..70db6a510964 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<FormSection>
 		<div class="_gaps_m">
 			<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
+			<FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink>
 		</div>
 	</FormSection>
 	<FormSection>
@@ -114,6 +115,17 @@ function testNotification(): void {
 	misskeyApi('notifications/test-notification');
 }
 
+async function flushNotification() {
+	const { canceled } = await os.confirm({
+		type: 'warning',
+		text: i18n.ts.resetAreYouSure,
+	});
+
+	if (canceled) return;
+
+	os.apiWithDialog('notifications/flush');
+}
+
 const headerActions = computed(() => []);
 
 const headerTabs = computed(() => []);
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 0e990ffd5a06..2237d278f46f 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -530,6 +530,7 @@ export type Channels = {
             unreadNotification: (payload: Notification_2) => void;
             unreadMention: (payload: Note['id']) => void;
             readAllUnreadMentions: () => void;
+            notificationFlushed: () => void;
             unreadSpecifiedNote: (payload: Note['id']) => void;
             readAllUnreadSpecifiedNotes: () => void;
             readAllAntennas: () => void;
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index d27413810c7d..53093501008c 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -3195,6 +3195,17 @@ declare module '../api.js' {
       credential?: string | null,
     ): Promise<SwitchCaseResponseType<E, P>>;
 
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    request<E extends 'notifications/flush', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
     /**
      * No description provided.
      * 
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 656ac282465f..b0982e1e55d0 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -841,6 +841,7 @@ export type Endpoints = {
 	'notes/unrenote': { req: NotesUnrenoteRequest; res: EmptyResponse };
 	'notes/user-list-timeline': { req: NotesUserListTimelineRequest; res: NotesUserListTimelineResponse };
 	'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse };
+	'notifications/flush': { req: EmptyRequest; res: EmptyResponse };
 	'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse };
 	'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse };
 	'page-push': { req: PagePushRequest; res: EmptyResponse };
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 9a2ff7487fdf..a89e18ea76ac 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -2770,6 +2770,15 @@ export type paths = {
      */
     post: operations['notifications/create'];
   };
+  '/notifications/flush': {
+    /**
+     * notifications/flush
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    post: operations['notifications/flush'];
+  };
   '/notifications/mark-all-as-read': {
     /**
      * notifications/mark-all-as-read
@@ -22056,6 +22065,50 @@ export type operations = {
       };
     };
   };
+  /**
+   * notifications/flush
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:notifications*
+   */
+  'notifications/flush': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * notifications/mark-all-as-read
    * @description No description provided.
diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts
index 0ba5715d689a..9a86e03d69f6 100644
--- a/packages/misskey-js/src/streaming.types.ts
+++ b/packages/misskey-js/src/streaming.types.ts
@@ -40,6 +40,7 @@ export type Channels = {
 			unreadNotification: (payload: Notification) => void;
 			unreadMention: (payload: Note['id']) => void;
 			readAllUnreadMentions: () => void;
+			notificationFlushed: () => void;
 			unreadSpecifiedNote: (payload: Note['id']) => void;
 			readAllUnreadSpecifiedNotes: () => void;
 			readAllAntennas: () => void;

From 16f16e6b0879199a78f0f9ef2da7e1e44ee8d355 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 29 Feb 2024 20:42:02 +0900
Subject: [PATCH 046/266] =?UTF-8?q?fix(backend):=20=E3=83=80=E3=82=A4?=
 =?UTF-8?q?=E3=83=AC=E3=82=AF=E3=83=88=E3=81=AA=E3=83=8E=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E3=81=AB=E5=AF=BE=E3=81=97=E3=81=A6=E3=81=AF=E3=83=80=E3=82=A4?=
 =?UTF-8?q?=E3=83=AC=E3=82=AF=E3=83=88=E3=81=A7=E3=81=97=E3=81=8B=E8=BF=94?=
 =?UTF-8?q?=E4=BF=A1=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#13477)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): ダイレクトなノートに対してはダイレクトでしか返信できないように

* Update CHANGELOG.md

* test(backend): `notes/create`とWebSocket関連のテストを追加
---
 CHANGELOG.md                                  |  1 +
 .../src/server/api/endpoints/notes/create.ts  |  8 ++
 packages/backend/test/e2e/note.ts             | 81 +++++++++++++++++++
 packages/backend/test/e2e/streaming.ts        | 40 +++++++++
 .../frontend/src/components/MkPostForm.vue    |  3 +-
 .../src/components/MkVisibilityPicker.vue     |  7 +-
 6 files changed, 136 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae611875dc85..995b37f24a33 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
 - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
 - Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加
 - Enhance: 通知の履歴をリセットできるように
+- Fix: ダイレクトなノートに対してはダイレクトでしか返信できないように
 
 ### Client
 - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 2fa0bd099fe7..27463577fe27 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -85,6 +85,12 @@ export const meta = {
 			id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15',
 		},
 
+		cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility: {
+			message: 'You cannot reply to a specified visibility note with extended visibility.',
+			code: 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY',
+			id: 'ed940410-535c-4d5e-bfa3-af798671e93c',
+		},
+
 		cannotCreateAlreadyExpiredPoll: {
 			message: 'Poll is already expired.',
 			code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
@@ -313,6 +319,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					throw new ApiError(meta.errors.cannotReplyToPureRenote);
 				} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) {
 					throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
+				} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
+					throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
 				}
 
 				// Check blocking
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index a5742d6e77d7..23de94889d36 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -176,6 +176,87 @@ describe('Note', () => {
 		assert.strictEqual(deleteRes.status, 204);
 	});
 
+	test('visibility: followersなノートに対してフォロワーはリプライできる', async () => {
+		await api('/following/create', {
+			userId: alice.id,
+		}, bob);
+
+		const aliceNote = await api('/notes/create', {
+			text: 'direct note to bob',
+			visibility: 'followers',
+		}, alice);
+
+		assert.strictEqual(aliceNote.status, 200);
+
+		const replyId = aliceNote.body.createdNote.id;
+		const bobReply = await api('/notes/create', {
+			text: 'reply to alice note',
+			replyId,
+		}, bob);
+
+		assert.strictEqual(bobReply.status, 200);
+		assert.strictEqual(bobReply.body.createdNote.replyId, replyId);
+
+		await api('/following/delete', {
+			userId: alice.id,
+		}, bob);
+	});
+
+	test('visibility: followersなノートに対してフォロワーでないユーザーがリプライしようとすると怒られる', async () => {
+		const aliceNote = await api('/notes/create', {
+			text: 'direct note to bob',
+			visibility: 'followers',
+		}, alice);
+
+		assert.strictEqual(aliceNote.status, 200);
+
+		const bobReply = await api('/notes/create', {
+			text: 'reply to alice note',
+			replyId: aliceNote.body.createdNote.id,
+		}, bob);
+
+		assert.strictEqual(bobReply.status, 400);
+		assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE');
+	});
+
+	test('visibility: specifiedなノートに対してvisibility: specifiedで返信できる', async () => {
+		const aliceNote = await api('/notes/create', {
+			text: 'direct note to bob',
+			visibility: 'specified',
+			visibleUserIds: [bob.id],
+		}, alice);
+
+		assert.strictEqual(aliceNote.status, 200);
+
+		const bobReply = await api('/notes/create', {
+			text: 'reply to alice note',
+			replyId: aliceNote.body.createdNote.id,
+			visibility: 'specified',
+			visibleUserIds: [alice.id],
+		}, bob);
+
+		assert.strictEqual(bobReply.status, 200);
+	});
+
+	test('visibility: specifiedなノートに対してvisibility: follwersで返信しようとすると怒られる', async () => {
+		const aliceNote = await api('/notes/create', {
+			text: 'direct note to bob',
+			visibility: 'specified',
+			visibleUserIds: [bob.id],
+		}, alice);
+
+		assert.strictEqual(aliceNote.status, 200);
+
+		const bobReply = await api('/notes/create', {
+			text: 'reply to alice note with visibility: followers',
+			replyId: aliceNote.body.createdNote.id,
+			visibility: 'followers',
+		}, bob);
+
+		assert.strictEqual(bobReply.status, 400);
+		assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY');
+	});
+
 	test('文字数ぎりぎりで怒られない', async () => {
 		const post = {
 			text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000文字
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index 13d5a683ba6f..57ce73ba603b 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -227,6 +227,46 @@ describe('Streaming', () => {
 				assert.strictEqual(fired, false);
 			});
 
+			/**
+			 * TODO: 落ちる
+			 * @see https://github.com/misskey-dev/misskey/issues/13474
+			test('visibility: specified なノートで visibleUserIds に自分が含まれているときそのノートへのリプライが流れてくる', async () => {
+				const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] });
+
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+				);
+
+				assert.strictEqual(fired, true);
+			});
+			 */
+
+			test('visibility: specified な投稿に対するリプライで visibleUserIds が拡張されたとき、その拡張されたユーザーの HTL にはそのリプライが流れない', async () => {
+				const chitoseToKyoko = await post(chitose, { text: 'direct note from chitose to kyoko', visibility: 'specified', visibleUserIds: [kyoko.id] });
+
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyoko.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			test('visibility: specified な投稿に対するリプライで visibleUserIds が収縮されたとき、その収縮されたユーザーの HTL にはそのリプライが流れない', async () => {
+				const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] });
+
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { text: 'direct reply from kyoko to chitose', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
 			test('withRenotes: false のときリノートが流れない', async () => {
 				const fired = await waitFire(
 					ayano, 'homeTimeline',	// ayano:home
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 819f0f692ce5..e03faeaf555f 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -172,7 +172,7 @@ const emit = defineEmits<{
 const textareaEl = shallowRef<HTMLTextAreaElement | null>(null);
 const cwInputEl = shallowRef<HTMLInputElement | null>(null);
 const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null);
-const visibilityButton = shallowRef<HTMLElement | null>(null);
+const visibilityButton = shallowRef<HTMLElement>();
 
 const posting = ref(false);
 const posted = ref(false);
@@ -461,6 +461,7 @@ function setVisibility() {
 		isSilenced: $i.isSilenced,
 		localOnly: localOnly.value,
 		src: visibilityButton.value,
+		...(props.reply ? { isReplyVisibilitySpecified: props.reply.visibility === 'specified' } : {}),
 	}, {
 		changeVisibility: v => {
 			visibility.value = v;
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index 3439a751a050..5ecd41bfdfc6 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -9,21 +9,21 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="[$style.label, $style.item]">
 			{{ i18n.ts.visibility }}
 		</div>
-		<button key="public" :disabled="isSilenced" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')">
+		<button key="public" :disabled="isSilenced || isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')">
 			<div :class="$style.icon"><i class="ti ti-world"></i></div>
 			<div :class="$style.body">
 				<span :class="$style.itemTitle">{{ i18n.ts._visibility.public }}</span>
 				<span :class="$style.itemDescription">{{ i18n.ts._visibility.publicDescription }}</span>
 			</div>
 		</button>
-		<button key="home" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')">
+		<button key="home" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')">
 			<div :class="$style.icon"><i class="ti ti-home"></i></div>
 			<div :class="$style.body">
 				<span :class="$style.itemTitle">{{ i18n.ts._visibility.home }}</span>
 				<span :class="$style.itemDescription">{{ i18n.ts._visibility.homeDescription }}</span>
 			</div>
 		</button>
-		<button key="followers" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')">
+		<button key="followers" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')">
 			<div :class="$style.icon"><i class="ti ti-lock"></i></div>
 			<div :class="$style.body">
 				<span :class="$style.itemTitle">{{ i18n.ts._visibility.followers }}</span>
@@ -54,6 +54,7 @@ const props = withDefaults(defineProps<{
 	isSilenced: boolean;
 	localOnly: boolean;
 	src?: HTMLElement;
+	isReplyVisibilitySpecified?: boolean;
 }>(), {
 });
 

From 1205d306576e57c9ad1fd8d40808d7e303c7227e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 20:42:58 +0900
Subject: [PATCH 047/266] Update CHANGELOG.md

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 995b37f24a33..aa976939d52b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,10 +32,10 @@
 - Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
 
 ### Server
+- Enhance: エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
 - Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
 - Fix: 破損した通知をクライアントに送信しないように
 	* 通知欄が無限にリロードされる問題が改善する可能性があります
-- エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
 - Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
 - Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正

From 6365805687b3f83df1900ad6631dc66bdebe021a Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 20:44:32 +0900
Subject: [PATCH 048/266] New Crowdin updates (#13359)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Norwegian)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)
---
 locales/ar-SA.yml |   1 +
 locales/ca-ES.yml |   1 +
 locales/cs-CZ.yml |   1 +
 locales/de-DE.yml |   1 +
 locales/en-US.yml |  28 ++++
 locales/es-ES.yml |   1 +
 locales/fr-FR.yml |  10 ++
 locales/id-ID.yml |   1 +
 locales/it-IT.yml |  18 ++-
 locales/ja-KS.yml |   2 +
 locales/ko-GS.yml |   1 +
 locales/ko-KR.yml |  11 ++
 locales/no-NO.yml |   1 +
 locales/pt-PT.yml |   1 +
 locales/ru-RU.yml |   1 +
 locales/th-TH.yml | 361 +++++++++++++++++++++++++---------------------
 locales/vi-VN.yml |   1 +
 locales/zh-CN.yml |  49 +++++--
 locales/zh-TW.yml |  32 +++-
 19 files changed, 340 insertions(+), 182 deletions(-)

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index b0f7408587e3..17c8f24fa548 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -1014,6 +1014,7 @@ renotes: "أعد النشر"
 sourceCode: "الشفرة المصدرية"
 flip: "اقلب"
 lastNDays: "آخر {n} أيام"
+surrender: "ألغِ"
 _initialAccountSetting:
   accountCreated: "نجح إنشاء حسابك!"
   letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي."
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index af5329dc7e9b..2ea6bd9309d5 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -1210,6 +1210,7 @@ hemisphere: "Geolocalització"
 withSensitive: "Incloure notes amb fitxers sensibles"
 userSaysSomethingSensitive: "La publicació de {name} conte material sensible"
 enableHorizontalSwipe: "Lliscar per canviar de pestanya"
+surrender: "Cancel·lar "
 _bubbleGame:
   howToPlay: "Com es juga"
   _howToPlay:
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 3161ff275a57..cbf5c33c18ee 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1098,6 +1098,7 @@ renotes: "Přeposlat"
 sourceCode: "Zdrojový kód"
 flip: "Otočit"
 lastNDays: "Posledních {n} dnů"
+surrender: "Zrušit"
 _initialAccountSetting:
   accountCreated: "Váš účet byl úspěšně vytvořen!"
   letsStartAccountSetup: "Pro začátek si nastavte svůj profil."
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index f733fa1ee90d..9a22a7b4451d 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1184,6 +1184,7 @@ decorate: "Dekorieren"
 addMfmFunction: "MFM hinzufügen"
 sfx: "Soundeffekte"
 lastNDays: "Letzten {n} Tage"
+surrender: "Abbrechen"
 _announcement:
   forExistingUsers: "Nur für existierende Nutzer"
   forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 084f6b23b94b..0ed30bc3de0f 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -991,6 +991,7 @@ neverShow: "Don't show again"
 remindMeLater: "Maybe later"
 didYouLikeMisskey: "Have you taken a liking to Misskey?"
 pleaseDonate: "{host} uses the free software, Misskey. We would highly appreciate your donations so development of Misskey can continue!"
+correspondingSourceIsAvailable: "The corresponding source code is available at {anchor}"
 roles: "Roles"
 role: "Role"
 noRole: "Role not found"
@@ -1041,6 +1042,8 @@ resetPasswordConfirm: "Really reset your password?"
 sensitiveWords: "Sensitive words"
 sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks."
 sensitiveWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
+prohibitedWords: "Prohibited words"
+prohibitedWordsDescription: "Enables an error when attempting to post a note containing the set word(s). Multiple words can be set, separated by a new line."
 prohibitedWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
 hiddenTags: "Hidden hashtags"
 hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines."
@@ -1166,6 +1169,12 @@ confirmShowRepliesAll: "This operation is irreversible. Would you really like to
 confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?"
 externalServices: "External Services"
 sourceCode: "Source code"
+sourceCodeIsNotYetProvided: "Source code is not yet available. Contact the administrator to fix this problem."
+repositoryUrl: "Repository URL"
+repositoryUrlDescription: "If you are using Misskey as is (without any changes to the source code), enter https://github.com/misskey-dev/misskey"
+repositoryUrlOrTarballRequired: "If you have not published a repository, you must provide a tarball instead. See .config/example.yml for more information."
+feedback: "Feedback"
+feedbackUrl: "Feedback URL"
 impressum: "Impressum"
 impressumUrl: "Impressum URL"
 impressumDescription: "In some countries, like germany, the inclusion of operator contact information (an Impressum) is legally required for commercial websites."
@@ -1201,6 +1210,8 @@ soundWillBePlayed: "Sound will be played"
 showReplay: "View Replay"
 replay: "Replay"
 replaying: "Showing replay"
+endReplay: "Exit Replay"
+copyReplayData: "Copy replay data"
 ranking: "Ranking"
 lastNDays: "Last {n} days"
 backToTitle: "Go back to title"
@@ -1208,8 +1219,20 @@ hemisphere: "Where are you located"
 withSensitive: "Include notes with sensitive files"
 userSaysSomethingSensitive: "Post by {name} contains sensitive content"
 enableHorizontalSwipe: "Swipe to switch tabs"
+loading: "Loading"
+surrender: "Cancel"
+gameRetry: "Retry"
 _bubbleGame:
   howToPlay: "How to play"
+  hold: "Hold"
+  _score:
+    score: "Score"
+    scoreYen: "Amount of money earned"
+    highScore: "High score"
+    maxChain: "Maximum number of chains"
+    yen: "{yen} Yen"
+    estimatedQty: "{qty} Pieces"
+    scoreSweets: "{onigiriQtyWithUnit} Onigiri"
   _howToPlay:
     section1: "Adjust the position and drop the object into the box."
     section2: "When two objects of the same type touch each other, they will change into a different object and you score points."
@@ -1754,6 +1777,8 @@ _aboutMisskey:
   contributors: "Main contributors"
   allContributors: "All contributors"
   source: "Source code"
+  original: "Original"
+  thisIsModifiedVersion: "{name} uses a modified version of the original Misskey."
   translation: "Translate Misskey"
   donate: "Donate to Misskey"
   morePatrons: "We also appreciate the support of many other helpers not listed here. Thank you! 🥰"
@@ -2369,6 +2394,7 @@ _moderationLogTypes:
   resetPassword: "Password reset"
   suspendRemoteInstance: "Remote instance suspended"
   unsuspendRemoteInstance: "Remote instance unsuspended"
+  updateRemoteInstanceNote: "Moderation note updated for remote instance."
   markSensitiveDriveFile: "File marked as sensitive"
   unmarkSensitiveDriveFile: "File unmarked as sensitive"
   resolveAbuseReport: "Report resolved"
@@ -2489,6 +2515,8 @@ _reversi:
   opponentHasSettingsChanged: "The opponent has changed their settings."
   allowIrregularRules: "Irregular rules (completely free)"
   disallowIrregularRules: "No irregular rules"
+  showBoardLabels: "Display row and column numbering on the board"
+  useAvatarAsStone: "Turn stones into user avatars"
 _offlineScreen:
   title: "Offline - cannot connect to the server"
   header: "Unable to connect to the server"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 2952e89f8396..246ec2360427 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -1209,6 +1209,7 @@ hemisphere: "Región"
 withSensitive: "Mostrar notas que contengan material sensible"
 userSaysSomethingSensitive: "La publicación de {name} contiene material sensible"
 enableHorizontalSwipe: "Deslice para cambiar de pestaña"
+surrender: "detener"
 _bubbleGame:
   howToPlay: "Cómo jugar"
   _howToPlay:
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 35fac49cddde..b7ab4d37c450 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1164,7 +1164,11 @@ remainingN: "Restants : {n}"
 overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?"
 seasonalScreenEffect: "Effet d'écran saisonnier"
 decorate: "Décorer"
+sfx: "Effets sonores"
+showReplay: "Voir le replay"
+ranking: "Classement"
 lastNDays: "Derniers {n} jours"
+surrender: "Annuler"
 _announcement:
   forExistingUsers: "Pour les utilisateurs existants seulement"
   readConfirmTitle: "Marquer comme lu ?"
@@ -1302,10 +1306,13 @@ _achievements:
       title: "Régulier III"
       description: "Se connecter pour un total de 400 jours"
     _login500:
+      title: "Expert I"
       description: "Se connecter pour un total de 500 jours"
     _login600:
+      title: "Expert II"
       description: "Se connecter pour un total de 600 jours"
     _login700:
+      title: "Expert III"
       description: "Se connecter pour un total de 700 jours"
     _login800:
       description: "Se connecter pour un total de 800 jours"
@@ -1400,9 +1407,12 @@ _role:
   description: "Description du rôle"
   permission: "Rôle et autorisations"
   assignTarget: "Attribuer"
+  manual: "Manuel"
   manualRoles: "Rôles manuels"
+  conditional: "Conditionnel"
   conditionalRoles: "Rôles conditionnels"
   condition: "Condition"
+  isConditionalRole: "Ceci est un rôle conditionnel."
   isPublic: "Rôle public"
   options: "Options"
   policies: "Stratégies"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 58a248996b95..514a2866ca57 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -1209,6 +1209,7 @@ hemisphere: "Letak kamu tinggal"
 withSensitive: "Lampirkan catatan dengan berkas sensitif"
 userSaysSomethingSensitive: "Postingan oleh {name} mengandung konten sensitif"
 enableHorizontalSwipe: "Geser untuk mengganti tab"
+surrender: "Batalkan"
 _bubbleGame:
   howToPlay: "Cara bermain"
   _howToPlay:
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 378036af6df5..480d11b6bad8 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -991,6 +991,7 @@ neverShow: "Non mostrare più"
 remindMeLater: "Rimanda"
 didYouLikeMisskey: "Ti piace Misskey?"
 pleaseDonate: "Misskey è il software libero utilizzato su {host}. Offrendo una donazione è più facile continuare a svilupparlo!"
+correspondingSourceIsAvailable: ""
 roles: "Ruoli"
 role: "Ruolo"
 noRole: "Ruolo non trovato"
@@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "Questa è una attività irreversibile. Vuoi davvero incl
 confirmHideRepliesAll: "Questa è una attività irreversibile. Vuoi davvero escludere tutte le risposte dei following in TL?"
 externalServices: "Servizi esterni"
 sourceCode: "Codice sorgente"
+sourceCodeIsNotYetProvided: ""
+repositoryUrl: "URL della repository"
+repositoryUrlDescription: "Se esiste un repository il cui il codice sorgente è disponibile pubblicamente, inserisci il suo URL. Se stai utilizzando Misskey così com'è (senza alcuna modifica al codice sorgente), inserisci https://github.com/misskey-dev/misskey."
+repositoryUrlOrTarballRequired: "Se non disponi di un repository pubblico, dovrai fornire un file tarball (tar). Vedere .config/example.yml per i dettagli."
+feedback: "Feedback"
+feedbackUrl: "URL di feedback"
 impressum: "Dichiarazione di proprietà"
 impressumUrl: "URL della dichiarazione di proprietà"
 impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)."
@@ -1199,7 +1206,7 @@ addMfmFunction: "Aggiungi decorazioni"
 enableQuickAddMfmFunction: "Attiva il selettore di funzioni MFM"
 bubbleGame: "Bubble Game"
 sfx: "Effetti sonori"
-soundWillBePlayed: "Verrà riprodotto il suono"
+soundWillBePlayed: "Con musica ed effetti sonori"
 showReplay: "Vedi i replay"
 replay: "Replay"
 replaying: "Replay in corso"
@@ -1210,12 +1217,13 @@ hemisphere: "Geolocalizzazione"
 withSensitive: "Mostra le Note con allegati espliciti"
 userSaysSomethingSensitive: "Note da {name} con allegati espliciti"
 enableHorizontalSwipe: "Trascina per invertire i tab"
+surrender: "Annulla"
 _bubbleGame:
   howToPlay: "Come giocare"
   _howToPlay:
-    section1: "Regola la posizione e rilascia l'oggetto nella casella."
-    section2: "Ottieni un punteggio, quando due oggetti dello stesso tipo si toccano e si trasformano in un oggetto diverso."
-    section3: "Se gli oggetti traboccano dalla scatola, il gioco finisce. Cerca di ottenere un punteggio elevato fondendo gli oggetti, evitando che escano dalla scatola!"
+    section1: "Scegli la posizione e rilascia l'oggetto nel contenitore."
+    section2: "Se due oggetti dello stesso tipo si toccano, si trasformano in un oggetto diverso, aumentando il punteggio."
+    section3: "Se gli oggetti escono dal limite superiore del contenitore, il gioco finisce. Cerca di ottenere un punteggio elevato fondendo gli oggetti, evitando che escano dal contenitore!"
 _announcement:
   forExistingUsers: "Solo ai profili attuali"
   forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."
@@ -1756,6 +1764,8 @@ _aboutMisskey:
   contributors: "Principali sostenitori"
   allContributors: "Tutti i sostenitori"
   source: "Codice sorgente"
+  original: "Originale"
+  thisIsModifiedVersion: "{name} sta usando una versione modificata diversa da Misskey originale."
   translation: "Tradurre Misskey"
   donate: "Sostieni Misskey"
   morePatrons: "Apprezziamo sinceramente il supporto di tante altre persone. Grazie mille! 🥰"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 4b5f98e803dc..7ff26c757dd2 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -991,6 +991,7 @@ neverShow: "今後表示しない"
 remindMeLater: "また後で"
 didYouLikeMisskey: "Misskey気に入ってくれた?"
 pleaseDonate: "Misskeyは{host}が使うとる無料のソフトウェアやで。これからも開発を続けれるように、寄付したってな~。"
+correspondingSourceIsAvailable: "{anchor}"
 roles: "ロール"
 role: "ロール"
 noRole: "ロールはありまへん"
@@ -1208,6 +1209,7 @@ hemisphere: "住んでる地域"
 withSensitive: "センシティブなファイルを含むノートを表示"
 userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿"
 enableHorizontalSwipe: "スワイプしてタブを切り替える"
+surrender: "やめとく"
 _bubbleGame:
   howToPlay: "遊び方"
   _howToPlay:
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index b1702114be09..39492d902ffe 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -640,6 +640,7 @@ icon: "아바타"
 replies: "답하기"
 renotes: "리노트"
 attach: "옇기"
+surrender: "아이예"
 _initialAccountSetting:
   startTutorial: "길라잡이 하기"
 _initialTutorial:
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index c4646b6a863b..877ae6b217be 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -991,6 +991,7 @@ neverShow: "다시 보지 않기"
 remindMeLater: "나중에 알림"
 didYouLikeMisskey: "Misskey가 마음에 드시나요?"
 pleaseDonate: "Misskey는 {host} 서버의 무료 소프트웨어입니다. 앞으로도 개발을 이어 나가려면 후원이 절실히 필요합니다!"
+correspondingSourceIsAvailable: "소스 코드는 {anchor}에서 받아보실 수 있습니다."
 roles: "역할"
 role: "역할"
 noRole: "역할이 없습니다"
@@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타
 confirmHideRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오지 않게 하시겠습니까?"
 externalServices: "외부 서비스"
 sourceCode: "소스 코드"
+sourceCodeIsNotYetProvided: "소스 코드를 아직 제공하지 않습니다. 이 문제를 해결하려면 관리자에게 문의해 주세요."
+repositoryUrl: "저장소 URL"
+repositoryUrlDescription: "소스 코드를 공개한 저장소가 있는 경우, 그 URL을 적습니다. Misskey를 원본 그대로 (소스 코드를 어떤 식으로도 변경하지 않고) 쓰고 있는 경우 https://github.com/misskey-dev/misskey 라고 적습니다."
+repositoryUrlOrTarballRequired: "저장소를 공개하지 않은 경우 대신 tarball을 제공할 필요가 있습니다. 세부사항은 .config/example.yml을 참조해 주세요."
+feedback: "피드백"
+feedbackUrl: "피드백 URL"
 impressum: "운영자 정보"
 impressumUrl: "운영자 정보 URL"
 impressumDescription: "독일 등의 일부 나라와 지역에서는 꼭 표시해야 합니다(Impressum)."
@@ -1210,6 +1217,7 @@ hemisphere: "거주 지역"
 withSensitive: "민감한 파일이 포함된 노트 보기"
 userSaysSomethingSensitive: "{name}의 민감한 파일이 포함된 게시물"
 enableHorizontalSwipe: "스와이프하여 탭 전환"
+surrender: "그만두기"
 _bubbleGame:
   howToPlay: "설명"
   _howToPlay:
@@ -1756,6 +1764,8 @@ _aboutMisskey:
   contributors: "주요 기여자"
   allContributors: "모든 기여자"
   source: "소스 코드"
+  original: "원본"
+  thisIsModifiedVersion: "{name}에서는 원본 미스키를 수정한 버전을 사용하고 있습니다."
   translation: "Misskey를 번역하기"
   donate: "Misskey에 기부하기"
   morePatrons: "이 외에도 다른 많은 분들이 도움을 주시고 계십니다. 감사합니다🥰"
@@ -2371,6 +2381,7 @@ _moderationLogTypes:
   resetPassword: "비밀번호 재설정"
   suspendRemoteInstance: "리모트 서버를 정지"
   unsuspendRemoteInstance: "리모트 서버의 정지를 해제"
+  updateRemoteInstanceNote: "리모트 서버의 조정 기록 갱신"
   markSensitiveDriveFile: "파일에 열람주의를 설정"
   unmarkSensitiveDriveFile: "파일에 열람주의를 해제"
   resolveAbuseReport: "신고 처리"
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 85ccd625665c..098faa8addd7 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -463,6 +463,7 @@ options: "Alternativ"
 icon: "Avatar"
 replies: "Svar"
 renotes: "Renote"
+surrender: "Avbryt"
 _initialAccountSetting:
   theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
 _achievements:
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index bf8a8ca38b29..f62557fb23bb 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -1011,6 +1011,7 @@ renotes: "Repostar"
 keepScreenOn: "Manter a tela do dispositivo sempre ligada"
 flip: "Inversão"
 lastNDays: "Últimos {n} dias"
+surrender: "Cancelar"
 _initialAccountSetting:
   followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
 _serverSettings:
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 6141eba5f0aa..d666b694905d 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -1085,6 +1085,7 @@ loadReplies: "Показать ответы"
 sourceCode: "Исходный код"
 flip: "Переворот"
 lastNDays: "Последние {n} сут"
+surrender: "Этот пост не может быть отменен."
 _initialAccountSetting:
   accountCreated: "Аккаунт успешно создан!"
   letsStartAccountSetup: "Давайте настроим вашу учётную запись."
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index b5a54a39eca3..56021cdbc99d 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -8,12 +8,12 @@ search: "ค้นหา"
 notifications: "การเเจ้งเตือน"
 username: "ชื่อผู้ใช้"
 password: "รหัสผ่าน"
-forgotPassword: "ลืมรหัสผ่านใช่ไหม"
+forgotPassword: "ลืมรหัสผ่าน"
 fetchingAsApObject: "กำลังดึงข้อมูลจากสหพันธ์..."
 ok: "ตกลง"
 gotIt: "เข้าใจแล้ว !"
 cancel: "ยกเลิก"
-noThankYou: "ไม่เป็นไร"
+noThankYou: "ไม่เอาดีกว่า"
 enterUsername: "กรอกชื่อผู้ใช้"
 renotedBy: "รีโน้ตโดย {user}"
 noNotes: "ไม่มีโน้ต"
@@ -31,16 +31,16 @@ login: "เข้าสู่ระบบ"
 loggingIn: "กำลังเข้าสู่ระบบ"
 logout: "ออกจากระบบ"
 signup: "สร้างบัญชีผู้ใช้"
-uploading: "กำลังอัพโหลด..."
+uploading: "กำลังอัปโหลด"
 save: "บันทึก"
 users: "ผู้ใช้งาน"
 addUser: "เพิ่มผู้ใช้"
 favorite: "รายการโปรด"
 favorites: "รายการโปรด"
 unfavorite: "ลบออกจากรายการโปรด"
-favorited: "เพิ่มแล้วในรายการโปรด"
-alreadyFavorited: "เพิ่มในรายการโปรดอยู่แล้ว"
-cantFavorite: "ไม่สามารถเพิ่มในรายการโปรดได้"
+favorited: "เพิ่มลงรายการโปรดแล้ว"
+alreadyFavorited: "เพิ่มลงรายการโปรดอยู่แล้ว"
+cantFavorite: "ไม่สามารถเพิ่มลงรายการโปรดได้"
 pin: "ปักหมุด"
 unpin: "เลิกปักหมุด"
 copyContent: "คัดลอกเนื้อหา"
@@ -65,18 +65,18 @@ loadMore: "แสดงเพิ่มเติม"
 showMore: "แสดงเพิ่มเติม"
 showLess: "ปิด"
 youGotNewFollower: "ได้ติดตามคุณ"
-receiveFollowRequest: "คำขอผู้ติดตามที่ได้รับ"
-followRequestAccepted: "อนุมัติการติดตามแล้ว"
+receiveFollowRequest: "มีคำขอติดตามส่งมาหา"
+followRequestAccepted: "การติดตามได้รับการอนุมัติแล้ว"
 mention: "กล่าวถึง"
 mentions: "พูดถึง"
-directNotes: "ไดเร็คโน้ต"
+directNotes: "โพสต์แบบไดเร็กต์"
 importAndExport: "นำเข้า / ส่งออก"
 import: "นำเข้า"
 export: "ส่งออก"
 files: "ไฟล์"
 download: "ดาวน์โหลด"
-driveFileDeleteConfirm: "ต้องการลบไฟล์ “{name}” ใช่หรือไม่? โน้ตที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย"
-unfollowConfirm: "ต้องการเลิกติดตาม {name}?"
+driveFileDeleteConfirm: "ต้องการลบไฟล์ “{name}” ใช่ไหม? โน้ตที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย"
+unfollowConfirm: "ต้องการเลิกติดตาม {name} ใช่ไหม?"
 exportRequested: "คุณได้ร้องขอการส่งออก อาจใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว"
 importRequested: "คุณได้ร้องขอการนำเข้า การดำเนินการนี้อาจใช้เวลาสักครู่"
 lists: "รายชื่อ"
@@ -128,9 +128,9 @@ emojiPickerDisplay: "แสดงตัวจิ้มเอโมจิ"
 overwriteFromPinnedEmojisForReaction: "เขียนทับการตั้งค่ารีแอคชั่น"
 overwriteFromPinnedEmojis: "เขียนทับการตั้งค่าทั่วไป"
 reactionSettingDescription2: "ลากเพื่อจัดลำดับใหม่ คลิกที่เอโมจินั้นเพื่อลบ กด “+” เพื่อเพิ่ม"
-rememberNoteVisibility: "จดจำการตั้งค่าการมองเห็นตัวโน้ต"
-attachCancel: "ลบไฟล์ออกที่แนบมา"
-deleteFile: "ลบไฟล์ออกแล้ว"
+rememberNoteVisibility: "จำการตั้งค่าการมองเห็นโน้ต"
+attachCancel: "ยกเลิกแนบไฟล์"
+deleteFile: "ลบไฟล์ออก"
 markAsSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
 unmarkAsSensitive: "ยกเลิกทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
 enterFileName: "พิมพ์ชื่อไฟล์"
@@ -138,14 +138,14 @@ mute: "ปิดเสียง"
 unmute: "ยกเลิกการปิดเสียง"
 renoteMute: "ปิดเสียงรีโน้ต"
 renoteUnmute: "เปิดเสียง รีโน้ต"
-block: "บล็อค"
-unblock: "เลิกปิดกั้น"
-suspend: "ถูกระงับ"
-unsuspend: "ยกเลิกระงับ"
-blockConfirm: "ต้องการบล็อกบัญชีนี้?"
-unblockConfirm: "ต้องการปลดบล็อคบัญชีนี้?"
-suspendConfirm: "ต้องการระงับบัญชีนี้?"
-unsuspendConfirm: "ต้องการยกเลิกการระงับบัญชีนี้?"
+block: "บล็อก"
+unblock: "เลิกบล็อก"
+suspend: "ระงับ"
+unsuspend: "เลิกระงับ"
+blockConfirm: "ต้องการบล็อกบัญชีนี้ใช่ไหม?"
+unblockConfirm: "ต้องการเลิกบล็อกบัญชีนี้ใช่ไหม?"
+suspendConfirm: "ต้องการระงับบัญชีนี้ใช่ไหม?"
+unsuspendConfirm: "ต้องการยกเลิกการระงับบัญชีนี้ใช่ไหม?"
 selectList: "เลือกรายชื่อ"
 editList: "แก้ไขรายชื่อ"
 selectChannel: "เลือกช่อง"
@@ -168,7 +168,7 @@ cacheRemoteSensitiveFiles: "แคชไฟล์ระยะไกลที่
 cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานการตั้งค่านี้ ไฟล์ระยะไกลที่มีเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช"
 flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท"
 flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ และยังสามารถปรับเปลี่ยนระบบภายในของ Misskey เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท"
-flagAsCat: "เมี้ยววววววว!!!!!!!!!!! (ทำเครื่องหมายว่าบัญชีนี้เป็นแมว)"
+flagAsCat: "เมี้ยววววววววววววววว!!!!!!!!!!!"
 flagAsCatDescription: "เหมียวเหมียวเมี้ยว??"
 flagShowTimelineReplies: "แสดงตอบกลับ ในไทม์ไลน์"
 flagShowTimelineRepliesDescription: "แสดงการตอบกลับของผู้ใช้งานไปยังโน้ตของผู้ใช้งานรายอื่นๆในไทม์ไลน์หากได้เปิดเอาไว้"
@@ -180,7 +180,7 @@ showOnRemote: "ดูบนอินสแตนซ์ระยะไกล"
 general: "ทั่วไป"
 wallpaper: "ภาพพื้นหลัง"
 setWallpaper: "ตั้งค่าภาพพื้นหลัง"
-removeWallpaper: "น้ำภาพพื้นหลังออก"
+removeWallpaper: "นำภาพพื้นหลังออก"
 searchWith: "ค้นหา: {q}"
 youHaveNoLists: "คุณไม่มีรายชื่อใดๆ "
 followConfirm: "ต้องการติดตาม {name} ใช่ไหม?"
@@ -189,11 +189,11 @@ proxyAccountDescription: "บัญชีพร็อกซี่ คือ บ
 host: "โฮสต์"
 selectUser: "เลือกผู้ใช้งาน"
 recipient: "ผู้รับ"
-annotation: "ความคิดเห็น"
+annotation: "หมายเหตุประกอบ"
 federation: "สหพันธ์"
 instances: "อินสแตนซ์"
-registeredAt: "จดทะเบียนที่"
-latestRequestReceivedAt: "ได้รับคำขอล่าสุดไปแล้ว"
+registeredAt: "วันที่ลงทะเบียน"
+latestRequestReceivedAt: "คำขอล่าสุดที่ได้รับ"
 latestStatus: "สถานะล่าสุด"
 storageUsage: "พื้นที่จัดเก็บข้อมูลที่ใช้ไป"
 charts: "โดดเด่น"
@@ -215,10 +215,10 @@ disk: "ดิสก์"
 instanceInfo: "ข้อมูลอินสแตนซ์"
 statistics: "สถิติการใช้งาน"
 clearQueue: "ล้างคิว"
-clearQueueConfirmTitle: "คุณแน่ใจแล้วหรอว่าต้องการที่จะล้างคิว?"
+clearQueueConfirmTitle: "ต้องการล้างคิวใช่ไหม?"
 clearQueueConfirmText: "โพสต์ที่ยังค้างในคิวจะไม่ถูกจัดส่งอีกต่อไป โดยปกติแล้วการดำเนินการนี้ไม่จำเป็น"
 clearCachedFiles: "ล้างแคช"
-clearCachedFilesConfirm: "ต้องการลบไฟล์ระยะไกลที่แคชไว้ทั้งหมด?"
+clearCachedFilesConfirm: "ต้องการลบไฟล์ระยะไกลที่แคชไว้ทั้งหมดใช่ไหม?"
 blockedInstances: "อินสแตนซ์ที่ถูกบล็อก"
 blockedInstancesDescription: "ระบุชื่อโฮสต์ของอินสแตนซ์ที่คุณต้องการบล็อก อินสแตนซ์ที่อยู่ในรายการนั้นจะไม่สามารถพูดคุยกับอินสแตนซ์นี้ได้อีกต่อไป"
 silencedInstances: "ปิดปากอินสแตนซ์นี้แล้ว"
@@ -228,7 +228,7 @@ mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง"
 blockedUsers: "ผู้ใช้ที่ถูกบล็อก"
 noUsers: "ไม่พบผู้ใช้งาน"
 editProfile: "แก้ไขโปรไฟล์"
-noteDeleteConfirm: "ต้องการลบโน้ตนี้?"
+noteDeleteConfirm: "ต้องการลบโน้ตนี้ใช่ไหม?"
 pinLimitExceeded: "คุณไม่สามารถปักหมุดโน้ตเพิ่มเติมใดๆได้อีก"
 intro: "การติดตั้ง Misskey เสร็จสิ้นแล้วนะ! โปรดสร้างผู้ใช้งานที่เป็นผู้ดูแลระบบ"
 done: "เสร็จสิ้น"
@@ -237,7 +237,7 @@ preview: "แสดงตัวอย่าง"
 default: "ค่าเริ่มต้น"
 defaultValueIs: "ค่าเริ่มต้น: {value}"
 noCustomEmojis: "ไม่มีเอโมจิ"
-noJobs: "ไม่มีชิ้นงาน"
+noJobs: "ไม่มีงาน"
 federating: "สหพันธ์"
 blocked: "ถูกบล็อก"
 suspended: "ถูกระงับ"
@@ -261,11 +261,11 @@ usernameOrUserId: "ชื่อผู้ใช้หรือรหัสผู
 noSuchUser: "ไม่พบผู้ใช้"
 lookup: "การค้นหา"
 announcements: "ประกาศ"
-imageUrl: "url รูปภาพ"
+imageUrl: "URL รูปภาพ"
 remove: "ลบ"
 removed: "ถูกลบไปแล้ว"
-removeAreYouSure: "ต้องการที่จะลบ “{x}” ออก?"
-deleteAreYouSure: "ต้องการลบ {x} หรือไม่คะ?"
+removeAreYouSure: "ต้องการลบ “{x}” ใช่ไหม?"
+deleteAreYouSure: "ต้องการลบ “{x}” ใช่ไหม?"
 resetAreYouSure: "รีเซ็ตเลยไหม?"
 areYouSure: "แน่ใจแล้วใช่ไหมคะ?"
 saved: "บันทึกแล้ว"
@@ -275,7 +275,7 @@ keepOriginalUploading: "เก็บภาพต้นฉบับ"
 keepOriginalUploadingDescription: "เก็บภาพต้นฉบับไว้เมื่ออัปโหลดภาพ หากปิด รูปภาพสำหรับการเผยแพร่ทางเว็บจะถูกสร้างขึ้นในเบราว์เซอร์เมื่อทำการอัปโหลด"
 fromDrive: "จากไดรฟ์"
 fromUrl: "จาก URL"
-uploadFromUrl: "อัพโหลดจาก URL"
+uploadFromUrl: "อัปโหลดจาก URL"
 uploadFromUrlDescription: "URL ของไฟล์ที่คุณต้องการอัปโหลด"
 uploadFromUrlRequested: "ร้องขอการอัปโหลดแล้ว"
 uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์"
@@ -289,7 +289,7 @@ agree: "ยอมรับ"
 agreeBelow: "ฉันยอมรับถึงด้านล่าง"
 basicNotesBeforeCreateAccount: "หมายเหตุสำคัญ"
 termsOfService: "เงื่อนไขการให้บริการ"
-start: "เริ่มต้น​ใช้งาน​"
+start: "เริ่ม"
 home: "หน้าแรก"
 remoteUserCaution: "ข้อมูลอาจไม่สมบูรณ์เนื่องจากผู้ใช้รายนี้มาจากอินสแตนซ์ระยะไกล"
 activity: "กิจกรรม"
@@ -333,11 +333,11 @@ rename: "เปลี่ยนชื่อ"
 avatar: "ไอคอน"
 banner: "แบนเนอร์"
 displayOfSensitiveMedia: "แสดงสื่อที่มีเนื้อหาละเอียดอ่อน"
-whenServerDisconnected: "สูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์"
-disconnectedFromServer: "ถูกตัดการเชื่อมต่อออกจากเซิร์ฟเวอร์"
+whenServerDisconnected: "เมื่อสูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์"
+disconnectedFromServer: "การเชื่อมต่อเซิร์ฟเวอร์ถูกตัด"
 reload: "รีโหลด"
 doNothing: "เมิน"
-reloadConfirm: "นายต้องการรีเฟรชไทม์ไลน์หรือป่าว?"
+reloadConfirm: "รีโหลดเลยไหม?"
 watch: "ดู"
 unwatch: "หยุดดู"
 accept: "ยอมรับ"
@@ -347,7 +347,7 @@ instanceName: "ชื่ออินสแตนซ์"
 instanceDescription: "คำอธิบายอินสแตนซ์"
 maintainerName: "ผู้ดูแล"
 maintainerEmail: "อีเมลผู้ดูแลระบบ"
-tosUrl: "เงื่อนไขการให้บริการ URL"
+tosUrl: "URL เงื่อนไขการให้บริการ"
 thisYear: "ปีนี้"
 thisMonth: "เดือนนี้"
 today: "วันนี้"
@@ -370,7 +370,7 @@ inMb: "เป็นเมกะไบต์"
 bannerUrl: "URL รูปภาพแบนเนอร์"
 backgroundImageUrl: "URL ภาพพื้นหลัง"
 basicInfo: "ข้อมูลเบื้องต้น"
-pinnedUsers: "ผู้ใช้งานที่ได้รับการปักหมุด"
+pinnedUsers: "ผู้ใช้ที่ถูกปักหมุด"
 pinnedUsersDescription: "ป้อนชื่อผู้ใช้ที่คุณต้องการปักหมุดในหน้า “ค้นพบ” ฯลฯ คั่นด้วยการขึ้นบรรทัดใหม่"
 pinnedPages: "หน้าเพจที่ปักหมุด"
 pinnedPagesDescription: "ป้อนเส้นทางของหน้าเพจที่คุณต้องการปักหมุดไว้ที่หน้าแรกของอินสแตนซ์นี้ คั่นด้วยขึ้นบรรทัดใหม่"
@@ -409,16 +409,16 @@ caseSensitive: "อักษรพิมพ์ใหญ่-พิมพ์เล
 withReplies: "รวมตอบกลับ"
 connectedTo: "บัญชีดังต่อไปนี้มีการเชื่อมต่อกัน"
 notesAndReplies: "โพสต์และการตอบกลับ"
-withFiles: "รวบรวมไฟล์"
+withFiles: "มีไฟล์"
 silence: "ถูกปิดปาก"
-silenceConfirm: "ต้องการที่จะ ปิดปาก ผู้ใช้รายนี้?"
+silenceConfirm: "ต้องการปิดปากผู้ใช้รายนี้ใช่ไหม?"
 unsilence: "ยกเลิกการปิดปาก"
-unsilenceConfirm: "ต้องการยกเลิกปิดปากผู้ใช้รายนี้?"
+unsilenceConfirm: "ต้องการเลิกปิดปากผู้ใช้รายนี้ใช่ไหม?"
 popularUsers: "ผู้ใช้ที่เป็นที่นิยม"
 recentlyUpdatedUsers: "ผู้ใช้ที่เพิ่งใช้งานล่าสุด"
 recentlyRegisteredUsers: "ผู้ใช้ที่เข้าร่วมใหม่"
 recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพบใหม่"
-exploreUsersCount: "มีผู้ใช้ {จำนวน} ราย"
+exploreUsersCount: "มีผู้ใช้ {count} ราย"
 exploreFediverse: "สำรวจสหพันธ์"
 popularTags: "แท็กยอดนิยม"
 userList: "ลิสต์"
@@ -435,7 +435,7 @@ moderation: "การกลั่นกรอง"
 moderationNote: "โน้ตการกลั่นกรอง"
 addModerationNote: "เพิ่มโน้ตการกลั่นกรอง"
 moderationLogs: "ปูมการแก้ไข"
-nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} รายนี้"
+nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} ราย"
 securityKeyAndPasskey: "ความปลอดภัยและรหัสผ่าน"
 securityKey: "กุญแจความปลอดภัย"
 lastUsed: "ใช้ล่าสุด"
@@ -449,7 +449,7 @@ reduceUiAnimation: "ลดภาพเคลื่อนไหว UI"
 share: "แบ่งปัน"
 notFound: "ไม่พบหน้าที่ต้องการ"
 notFoundDescription: "ไม่พบหน้าตาม URL ที่ระบุ"
-uploadFolder: "โฟลเดอร์เริ่มต้นสำหรับอัพโหลด"
+uploadFolder: "โฟลเดอร์เริ่มต้นสำหรับอัปโหลด"
 markAsReadAllNotifications: "ทำเครื่องหมายการแจ้งเตือนทั้งหมดว่าอ่านแล้ว"
 markAsReadAllUnreadNotes: "ทำเครื่องหมายโน้ตทั้งหมดว่าอ่านแล้ว"
 markAsReadAllTalkMessages: "ทำเครื่องหมายข้อความทั้งหมดว่าอ่านแล้ว"
@@ -464,7 +464,7 @@ text: "ข้อความ"
 enable: "เปิดใช้งาน"
 next: "ถัด​ไป"
 retype: "พิมพ์รหัสอีกครั้ง"
-noteOf: "โน้ต โดย {user}"
+noteOf: "โน้ตของ {user}"
 quoteAttached: "อ้างอิง"
 quoteQuestion: "ต้องการที่จะแนบมันเพื่ออ้างอิงใช่ไหม?"
 noMessagesYet: "ยังไม่มีข้อความ"
@@ -472,7 +472,7 @@ newMessageExists: "คุณมีข้อความใหม่"
 onlyOneFileCanBeAttached: "สามารถแนบไฟล์ได้เพียงไฟล์เดียวต่อ 1 ข้อความ"
 signinRequired: "กรุณาลงทะเบียนหรือลงชื่อเข้าใช้ก่อนดำเนินการต่อ"
 invitations: "คำเชิญ"
-invitationCode: "รหัสคำเชิญ"
+invitationCode: "รหัสเชิญ"
 checking: "Checking"
 available: "พร้อมใช้งาน"
 unavailable: "ไม่พร้อมใช้"
@@ -557,7 +557,7 @@ popout: "ป๊อปเอาต์"
 volume: "ระดับเสียง"
 masterVolume: "ระดับเสียงหลัก"
 notUseSound: "ไม่ใช้เสียง"
-useSoundOnlyWhenActive: "มีเสียงออกเฉพาะเมื่อ Misskey ทำงานอยู่"
+useSoundOnlyWhenActive: "มีเสียงออกเฉพาะตอนกำลังใช้ Misskey อยู่เท่านั้น"
 details: "รายละเอียด"
 chooseEmoji: "เลือกเอโมจิ"
 unableToProcess: "ไม่สามารถดำเนินการให้เสร็จสิ้นได้"
@@ -570,8 +570,8 @@ installedDate: "วันที่ติดตั้ง"
 lastUsedDate: "ใช้งานครั้งล่าสุด"
 state: "สถานะ"
 sort: "เรียงลำดับ"
-ascendingOrder: "เรียงจากน้อยไปมาก"
-descendingOrder: "เรียงจากมากไปน้อย"
+ascendingOrder: "เรียงลำดับขึ้น"
+descendingOrder: "เรียงลำดับลง"
 scratchpad: "Scratchpad"
 scratchpadDescription: "Scratchpad เป็นการจัดเตรียมสภาพแวดล้อมสำหรับการทดลอง AiScript แต่คุณสามารถเขียน ดำเนินการ และตรวจสอบผลลัพธ์ของการโต้ตอบกับ Misskey มันได้ด้วยนะ"
 output: "เอาท์พุต"
@@ -579,15 +579,15 @@ script: "สคริปต์"
 disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ"
 updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล"
 unsetUserAvatar: "เลิกตั้งอวตาร"
-unsetUserAvatarConfirm: "ต้องการเลิกตั้งอวตาร?"
+unsetUserAvatarConfirm: "ต้องการเลิกตั้งอวตารใข่ไหม?"
 unsetUserBanner: "เลิกตั้งแบนเนอร์"
 unsetUserBannerConfirm: "ต้องการเลิกตั้งแบนเนอร์?"
 deleteAllFiles: "ลบไฟล์ทั้งหมด"
-deleteAllFilesConfirm: "ต้องการลบไฟล์ทั้งหมดหรือไม่?"
+deleteAllFilesConfirm: "ต้องการลบไฟล์ทั้งหมดใช่ไหม?"
 removeAllFollowing: "เลิกติดตามผู้ใช้ที่ติดตามทั้งหมด"
 removeAllFollowingDescription: "เลิกติดตามทั้งหมดจาก {host} โปรดเรียกใช้สิ่งนี้เมื่ออินสแตนซ์ดังกล่าวได้สูญหายตายจากไปแล้ว"
 userSuspended: "ผู้ใช้รายนี้ถูกระงับการใช้งาน"
-userSilenced: "ผู้ใช้รายนี้กำลังถูกปิดกั้น"
+userSilenced: "ผู้ใช้รายนี้ถูกปิดปากอยู่"
 yourAccountSuspendedTitle: "บัญชีนี้นั้นถูกระงับ"
 yourAccountSuspendedDescription: "บัญชีนี้ถูกระงับ เนื่องจากละเมิดข้อกำหนดในการให้บริการของเซิร์ฟเวอร์หรืออาจจะละเมิดหลักเกณฑ์ชุมชน หรือ อาจจะโดนร้องเรียนเรื่องการละเมิดลิขสิทธิ์และอื่นๆอย่างต่อเนื่องซ้ำๆ หากคุณคิดว่าไม่ได้ทำผิดจริงๆหรือตัดสินผิดพลาด ได้โปรดกรุณาติดต่อผู้ดูแลระบบหากคุณต้องการทราบเหตุผลโดยละเอียดเพิ่มเติม และขอความกรุณาอย่าสร้างบัญชีใหม่"
 tokenRevoked: "โทเค็นไม่ถูกต้อง"
@@ -600,7 +600,7 @@ addItem: "เพิ่มรายการ"
 rearrange: "จัดใหม่"
 relays: "รีเลย์"
 addRelay: "เพิ่มรีเลย์"
-inboxUrl: "อินบ็อกซ์ URL"
+inboxUrl: "URL ของอินบ็อกซ์"
 addedRelays: "เพิ่มรีเลย์แล้ว"
 serviceworkerInfo: "ต้องเปิดใช้งานสำหรับการแจ้งเตือนแบบพุช"
 deletedNote: "โน้ตที่ถูกลบ"
@@ -617,7 +617,7 @@ description: "รายละเอียด"
 describeFile: "เพิ่มแคปชั่น"
 enterFileDescription: "ใส่แคปชั่น"
 author: "ผู้เขียน"
-leaveConfirm: "คุณมีการเปลี่ยนแปลงที่ไม่ได้บันทึกนะ นายต้องการทิ้งการเปลี่ยนแปลงเหล่านั้นหรอ?"
+leaveConfirm: "มีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก ต้องการละทิ้งมันใช่ไหม?"
 manage: "การจัดการ"
 plugins: "ปลั๊กอิน"
 preferencesBackups: "ตั้งค่าการสำรองข้อมูล"
@@ -664,7 +664,7 @@ display: "แสดงผล"
 copy: "คัดลอก"
 metrics: "เมตริก"
 overview: "ภาพรวม"
-logs: "บันทึกข้อมูลระบบ"
+logs: "ปูม"
 delayed: "ดีเลย์"
 database: "ฐานข้อมูล"
 channel: "ช่อง"
@@ -672,11 +672,11 @@ create: "สร้าง"
 notificationSetting: "ตั้งค่าการแจ้งเตือน"
 notificationSettingDesc: "เลือกประเภทการแจ้งเตือนที่ต้องการจะแสดง"
 useGlobalSetting: "ใช้การตั้งค่าส่วนกลาง"
-useGlobalSettingDesc: "หากเปิดไว้ ระบบจะใช้การตั้งค่าการแจ้งเตือนของบัญชีของคุณ หากปิดอยู่ สามารถทำการกำหนดค่าแต่ละรายการได้นะ"
+useGlobalSettingDesc: "เมื่อเปิดใช้งาน ใช้การตั้งค่าการแจ้งเตือนจากบัญชีคุณ เมื่อปิดใช้งาน สามารถตั้งค่าได้อย่างอิสระ"
 other: "อื่น ๆ"
 regenerateLoginToken: "สร้างโทเค็นการเข้าสู่ระบบอีกครั้ง"
 regenerateLoginTokenDescription: "สร้างโทเค็นใหม่ที่ใช้ภายในระหว่างการเข้าสู่ระบบ โดยตามหลักปกติแล้วการดำเนินการนี้ไม่จำเป็น หากสร้างใหม่ อุปกรณ์ทั้งหมดจะถูกออกจากระบบนะ"
-theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาอีโมจิที่กำหนดเอง"
+theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาเอโมจิที่กำหนดเอง"
 setMultipleBySeparatingWithSpace: "คั่นหลายรายการด้วยช่องว่าง"
 fileIdOrUrl: "ไฟล์ ID หรือ URL"
 behavior: "พฤติกรรม"
@@ -684,14 +684,14 @@ sample: "ตัวอย่าง"
 abuseReports: "รายงาน"
 reportAbuse: "รายงาน"
 reportAbuseRenote: "รายงานรีโน้ต"
-reportAbuseOf: "รายงาน {ชื่อ}"
+reportAbuseOf: "รายงาน {name}"
 fillAbuseReportDescription: "กรุณากรอกรายละเอียดเกี่ยวกับรายงานนี้ หากเป็นเรื่องเกี่ยวกับโน้ตโดยเฉพาะ ได้โปรดระบุ URL"
 abuseReported: "เราได้ส่งรายงานของคุณไปแล้ว ขอบคุณมากๆนะ"
-reporter: "นักข่าว"
+reporter: "ผู้รายงาน"
 reporteeOrigin: "รายงานต้นทาง"
-reporterOrigin: "นักข่าวต้นทาง"
+reporterOrigin: "แหล่งผู้รายงาน"
 forwardReport: "ส่งต่อรายงานไปยังอินสแตนซ์ระยะไกล"
-forwardReportIsAnonymous: "แทนที่จะเป็นบัญชีของคุณ บัญชีระบบที่ไม่ระบุตัวตนจะแสดงเป็นนักข่าวที่อินสแตนซ์ระยะไกล"
+forwardReportIsAnonymous: "ข้อมูลของคุณจะไม่ปรากฏบนอินสแตนซ์ระยะไกลและปรากฏเป็นบัญชีระบบที่ไม่ระบุชื่อ"
 send: "ส่ง"
 abuseMarkAsResolved: "ทำเครื่องหมายรายงานว่าแก้ไขแล้ว"
 openInNewTab: "เปิดในแท็บใหม่"
@@ -699,7 +699,7 @@ openInSideView: "เปิดในมุมมองด้านข้าง"
 defaultNavigationBehaviour: "พฤติกรรมการนำทางที่เป็นค่าเริ่มต้น"
 editTheseSettingsMayBreakAccount: "การแก้ไขการตั้งค่าเหล่านี้อาจทำให้บัญชีของคุณเสียหายนะ"
 instanceTicker: "ข้อมูลอินสแตนซ์ของโน้ต"
-waitingFor: "กำลังรอคอย {x}"
+waitingFor: "กำลังรอ {x}"
 random: "สุ่มค่า"
 system: "ระบบ"
 switchUi: "สลับ UI"
@@ -709,7 +709,7 @@ createNew: "สร้างใหม่"
 optional: "ไม่บังคับ"
 createNewClip: "สร้างคลิปใหม่"
 unclip: "ลบคลิป"
-confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งของคลิป \"{name}\" แล้ว คุณต้องการลบออกจากคลิปนี้แทนอย่างงั้นหรอ?"
+confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งของคลิป “{name}” อยู่แล้ว ต้องการนำมันออกจากคลิปใช่ไหม?"
 public: "สาธารณะ"
 private: "ส่วนตัว"
 i18nInfo: "Misskey กำลังได้รับการแปลเป็นภาษาต่างๆ โดยอาสาสมัคร คุณสามารถช่วยเหลือได้ที่ {link}"
@@ -732,7 +732,7 @@ driveFilesCount: "จำนวนไฟล์ไดรฟ์"
 driveUsage: "การใช้พื้นที่ไดรฟ์"
 noCrawle: "ปฏิเสธการจัดทำดัชนีของโปรแกรมรวบรวมข้อมูล"
 noCrawleDescription: "ขอให้เครื่องมือค้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ"
-lockedAccountInfo: "เว้นแต่ว่าคุณจะต้องตั้งค่าการเปิดเผยโน้ตเป็น \"ผู้ติดตามเท่านั้น\" โน้ตย่อของคุณจะปรากฏแก่ทุกคน ถึงแม้ว่าคุณจะเป็นกำหนดให้ผู้ติดตามต้องได้รับการอนุมัติด้วยตนเองก็ตาม"
+lockedAccountInfo: "แม้ว่าการอนุมัติการติดตามถูกเปิดใช้งานอยู่ทุกคนก็ยังคงสามารถเห็นโน้ตของคุณได้ เว้นแต่ว่าคุณจะเปลี่ยนการเปิดเผยโน้ตของคุณเป็น  “เฉพาะผู้ติดตาม”"
 alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนเป็นค่าเริ่มต้น"
 loadRawImages: "โหลดภาพต้นฉบับแทนการแสดงภาพขนาดย่อ"
 disableShowingAnimatedImages: "ไม่ต้องเล่นภาพเคลื่อนไหว"
@@ -768,29 +768,29 @@ nNotes: "{n} โน้ต"
 sendErrorReports: "ส่งรายงานว่าข้อผิดพลาด"
 sendErrorReportsDescription: "เมื่อเปิดใช้งาน ข้อมูลข้อผิดพลาดโดยรายละเอียดนั้นจะถูกแชร์ให้กับ Misskey เมื่อเกิดปัญหา ซึ่งช่วยปรับปรุงคุณภาพของ Misskey\nซึ่งจะรวมถึงข้อมูล เช่น เวอร์ชั่นของระบบปฏิบัติการ เบราว์เซอร์ที่คุณใช้ กิจกรรมของคุณใน Misskey เป็นต้น"
 myTheme: "ธีมของฉัน"
-backgroundColor: "ภาพพื้นหลัง"
-accentColor: "รูปแบบสี"
+backgroundColor: "สีพื้นหลัง"
+accentColor: "สีหลัก"
 textColor: "สีข้อความ"
 saveAs: "บันทึกเป็น..."
 advanced: "ขั้นสูง"
 advancedSettings: "การตั้งค่าขั้นสูง"
 value: "ค่า"
 createdAt: "สร้างเมื่อ"
-updatedAt: "อัพเดทล่าสุด"
+updatedAt: "อัปเดตล่าสุด"
 saveConfirm: "บันทึกเปลี่ยนแปลงมั้ย?"
 deleteConfirm: "ลบจริงๆเหรอ?"
 invalidValue: "ค่านี้ไม่ถูกต้อง"
 registry: "ทะเบียน"
 closeAccount: "ปิด บัญชี"
 currentVersion: "เวอร์ชั่นปัจจุบัน"
-latestVersion: "รุ่นปัจจุบัน"
+latestVersion: "เวอร์ชั่นล่าสุด"
 youAreRunningUpToDateClient: "คุณกำลังใช้ไคลเอ็นต์เวอร์ชันใหม่ล่าสุดนะ"
 newVersionOfClientAvailable: "มีไคลเอ็นต์เวอร์ชันใหม่กว่าของคุณพร้อมใช้งานนะ"
 usageAmount: "การใช้งาน"
 capacity: "ความจุ"
 inUse: "ใช้แล้ว"
 editCode: "แก้ไขโค้ด"
-apply: "ตกลง"
+apply: "นำไปใช้"
 receiveAnnouncementFromInstance: "รับการแจ้งเตือนจากอินสแตนซ์นี้"
 emailNotification: "การแจ้งเตือนทางอีเมล"
 publish: "เผยแพร่"
@@ -802,7 +802,7 @@ showingPastTimeline: "กำลังแสดงผลไทม์ไลน์
 clear: "ล้าง"
 markAllAsRead: "ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว"
 goBack: "ย้อนกลับ"
-unlikeConfirm: "เลิกถูกใจจริงๆ หรือ?"
+unlikeConfirm: "ต้องการเลิกถูกใจใช่ไหม?"
 fullView: "มุมมองแบบเต็ม"
 quitFullView: "ออกจากมุมมองแบบเต็ม"
 addDescription: "เพิ่มคำอธิบาย"
@@ -813,12 +813,12 @@ userInfo: "ข้อมูลผู้ใช้"
 unknown: "ไม่ทราบสถานะ"
 onlineStatus: "สถานะออนไลน์"
 hideOnlineStatus: "ซ่อนสถานะออนไลน์"
-hideOnlineStatusDescription: "การซ่อนสถานะออนไลน์ของคุณช่วยลดความสะดวกของคุณสมบัติบางอย่าง เช่น การค้นหา อ่ะนะ"
+hideOnlineStatusDescription: "การซ่อนสถานะออนไลน์อาจทำให้ฟังก์ชันบางอย่าง เช่น การค้นหา สะดวกน้อยลง"
 online: "ออนไลน์"
 active: "ใช้งานอยู่"
 offline: "ออฟไลน์"
 notRecommended: "ไม่แนะนำ"
-botProtection: "การป้องกัน Bot (or AI)"
+botProtection: "การป้องกัน Bot"
 instanceBlocking: "อินสแตนซ์ที่ถูกบล็อก"
 selectAccount: "เลือกบัญชี"
 switchAccount: "สลับบัญชีผู้ใช้"
@@ -880,7 +880,7 @@ itsOff: "ปิดใช้งาน"
 on: "เปิด"
 off: "ปิด"
 emailRequiredForSignup: "จำเป็นต้องการใช้ที่อยู่อีเมลสำหรับการสมัคร"
-unread: "ไม่ได้อ่าน"
+unread: "ยังไม่ได้อ่าน"
 filter: "กรอง"
 controlPanel: "แผงควบคุม"
 manageAccounts: "จัดการบัญชี"
@@ -888,13 +888,13 @@ makeReactionsPublic: "ตั้งค่าประวัติการรี
 makeReactionsPublicDescription: "การทำเช่นนี้จะทำให้รายการรีแอคชั่นของคุณที่ผ่านมาทั้งหมดปรากฏต่อสาธารณะ"
 classic: "คลาสสิค"
 muteThread: "ปิดเสียงเธรด"
-unmuteThread: "เปิดเสียงเธรด"
+unmuteThread: "เลิกปิดเสียงเธรด"
 followingVisibility: "การมองเห็นที่เรากำลังติดตาม"
 followersVisibility: "การมองเห็นผู้ที่กำลังติดตามเรา"
 continueThread: "ดูความต่อเนื่องเธรด"
 deleteAccountConfirm: "การดำเนินการนี้จะลบบัญชีของคุณอย่างถาวรเลยนะ แน่ใจหรอดำเนินการ?"
 incorrectPassword: "รหัสผ่านไม่ถูกต้อง"
-voteConfirm: "ยืนยันการโหวต “{choice}” ไหม?"
+voteConfirm: "ต้องการโหวต “{choice}” ใช่ไหม?"
 hide: "ซ่อน"
 useDrawerReactionPickerForMobile: "แสดง ตัวจิ้มรีแอคชั่น เป็นแบบลิ้นชัก เมื่อใช้บนมือถือ"
 welcomeBackWithName: "ยินดีต้อนรับการกลับมานะคะ, คุณ{name}"
@@ -941,13 +941,13 @@ deleteAccount: "ลบบัญชี"
 document: "เอกสาร"
 numberOfPageCache: "จำนวนหน้าเพจที่แคช"
 numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย"
-logoutConfirm: "ต้องการออกจากระบบ?"
-lastActiveDate: "ใช้งานล่าสุดที่"
+logoutConfirm: "ต้องการออกจากระบบใช่ไหม?"
+lastActiveDate: "ใช้งานล่าสุดเมื่อ"
 statusbar: "แถบสถานะ"
 pleaseSelect: "ตัวเลือก"
-reverse: "ย้อนกลับ"
+reverse: "พลิก"
 colored: "สี"
-refreshInterval: "รอบการอัพเดต"
+refreshInterval: "ความถี่ในการอัปเดต"
 label: "ป้ายชื่อ"
 type: "รูปแบบ"
 speed: "ความเร็ว"
@@ -974,8 +974,8 @@ unsubscribePushNotification: "ปิดการแจ้งเตือนแ
 pushNotificationAlreadySubscribed: "การแจ้งเตือนแบบพุชได้เปิดใช้งานแล้ว"
 pushNotificationNotSupported: "เบราว์เซอร์หรืออินสแตนซ์ของคุณนั้นไม่รองรับการแจ้งเตือนแบบพุช"
 sendPushNotificationReadMessage: "ลบการแจ้งเตือนแบบพุชเมื่ออ่านการแจ้งเตือนหรือข้อความที่เกี่ยวข้องแล้ว"
-sendPushNotificationReadMessageCaption: "การแจ้งเตือนที่มีข้อความ \"{emptyPushNotificationMessage}\" จะแสดงขึ้นมาในช่วงระยะเวลาสั้นๆ การดำเนินการนี้อาจทำให้เพิ่มการใช้งานแบตเตอรี่ของอุปกรณ์ถ้าหากมีนะ"
-windowMaximize: "ขยายใหญ่สุดแล้ว"
+sendPushNotificationReadMessageCaption: "อาจทำให้อุปกรณ์ของคุณใช้พลังงานมากขึ้น"
+windowMaximize: "ขยายใหญ่สุด"
 windowMinimize: "ย่อเล็กที่สุด"
 windowRestore: "เลิกทำ"
 caption: "คำอธิบาย"
@@ -991,6 +991,7 @@ neverShow: "ไม่ต้องแสดงข้อความนี้อ
 remindMeLater: "ไว้ครั้งหน้าแล้วกัน"
 didYouLikeMisskey: "คุณชอบ Misskey ไหม?"
 pleaseDonate: "Misskey เป็นซอฟต์แวร์ฟรีที่ใช้งานโดย {host} เราขอขอบคุณการสนับสนุนของคุณอย่างสูงเพื่อให้การพัฒนา Misskey สามารถดำเนินต่อไปได้!"
+correspondingSourceIsAvailable: "ซอร์สโค้ดที่เกี่ยวข้องมีอยู่ที่ {anchor}"
 roles: "บทบาท"
 role: "บทบาท"
 noRole: "ไม่พบบทบาท"
@@ -1059,7 +1060,7 @@ enableChartsForFederatedInstances: "สร้างแผนภูมิข้
 showClipButtonInNoteFooter: "เพิ่ม “คลิป” ไปยังเมนูสั่งการของโน้ต"
 reactionsDisplaySize: "ขนาดของรีแอคชั่น"
 limitWidthOfReaction: "จำกัดความกว้างสูงสุดของรีแอคชั่นและแสดงให้เล็กลง"
-noteIdOrUrl: "โน้ต ID หรือ URL"
+noteIdOrUrl: "ID ของโน้ต หรือ URL"
 video: "วีดีโอ"
 videos: "วีดีโอ"
 audio: "เสียง"
@@ -1081,7 +1082,7 @@ leftBottom: "ล่างซ้าย"
 rightBottom: "ล่างขวา"
 stackAxis: "ทิศทางการซ้อน"
 vertical: "แนวตั้ง"
-horizontal: "ด้านข้าง"
+horizontal: "แนวนอน"
 position: "ตำแหน่ง"
 serverRules: "กฎของเซิร์ฟเวอร์"
 pleaseConfirmBelowBeforeSignup: "โปรดยืนยันที่ด้านล่างก่อนสมัครใช้งาน"
@@ -1097,17 +1098,17 @@ thisChannelArchived: "ช่องนี้ถูกเก็บถาวรแ
 displayOfNote: "การแสดงโน้ต"
 initialAccountSetting: "ตั้งค่าโปรไฟล์"
 youFollowing: "ติดตามแล้ว"
-preventAiLearning: "ปฏิเสธการใช้งาน ในการเรียนรู้ของเครื่อง (Generative AI)"
-preventAiLearningDescription: "การส่งคำร้องขอโปรแกรมรวบรวมข้อมูลไม่ให้ใช้ข้อความที่โพสต์หรือรูปภาพ ฯลฯ ในชุดข้อมูลแมชชีนเลิร์นนิง (Predictive / Generative AI) สิ่งนี้นั้นทำได้โดยการเพิ่มแฟล็กการตอบสนอง \"noai\" HTML ให้กับเนื้อหาที่เกี่ยวข้อง แต่อย่างไรก็ตามแล้ว การป้องกันโดยสมบูรณ์นั้นไม่สามารถทำได้ผ่านแฟล็กนี้เนื่องจากอาจจะทำให้ถูกเพิกเฉยได้"
+preventAiLearning: "ปฏิเสธการเรียนรู้ด้วย generative AI"
+preventAiLearningDescription: "ส่งคำร้องขอไม่ให้ใช้ ข้อความในโน้ตที่โพสต์, หรือเนื้อหารูปภาพ ฯลฯ ในการเรียนรู้ของเครื่อง(machine learning) / Predictive AI / Generative AI โดยการเพิ่มแฟล็ก “noai” ลง HTML-Response ให้กับเนื้อหาที่เกี่ยวข้อง แต่ทั้งนี้ ไม่ได้ป้องกัน AI จากการเรียนรู้ได้อย่างสมบูรณ์ เนื่องจากมี AI บางตัวเท่านั้นที่จะเคารพคำขอดังกล่าว"
 options: "ตัวเลือกบทบาท"
 specifyUser: "ผู้ใช้เฉพาะ"
 failedToPreviewUrl: "ไม่สามารถดูตัวอย่างได้"
 update: "อัปเดต"
 rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้เอโมจินี้เป็นรีแอคชั่นได้"
-rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหากไม่ได้ระบุบทบาท ทุกคนนั้นก็สามารถใช้เอโมจินี้เพื่อรีแอคชั่นได้นะ"
+rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหากไม่ได้ระบุบทบาท ใคร ๆ ก็สามารถใช้เอโมจินี้เพื่อรีแอคชั่นได้"
 rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "บทบาทเหล่านี้ต้องเป็นสาธารณะ"
-cancelReactionConfirm: "ต้องการลบรีแอคชั่นของคุณจริงๆหรอ?"
-changeReactionConfirm: "ต้องการเปลี่ยนรีแอคชั่นของคุณจริงๆหรอ?"
+cancelReactionConfirm: "ต้องการลบรีแอคชั่นใช่ไหม?"
+changeReactionConfirm: "ต้องการเปลี่ยนรีแอคชั่นใช่ไหม?"
 later: "ไว้ทีหลัง"
 goToMisskey: "ถึง Misskey"
 additionalEmojiDictionary: "พจนานุกรมเอโมจิเพิ่มเติม"
@@ -1116,20 +1117,20 @@ branding: "แบรนดิ้ง"
 enableServerMachineStats: "เผยแพร่สถานะฮาร์ดแวร์ของเซิร์ฟเวอร์"
 enableIdenticonGeneration: "เปิดใช้งานผู้ใช้สร้างตัวระบุ"
 turnOffToImprovePerformance: "การปิดส่วนนี้สามารถเพิ่มประสิทธิภาพได้"
-createInviteCode: "สร้างคำเชิญ"
+createInviteCode: "สร้างรหัสเชิญ"
 createWithOptions: "สร้างด้วยตัวเลือก"
-createCount: "จำนวนการเชิญ"
-inviteCodeCreated: "สร้างคำเชิญแล้ว"
-inviteLimitExceeded: "คุณสร้างคำเชิญเกินถึงขีดจำกัดแล้วนะ"
-createLimitRemaining: "ขีดจำกัดการเชิญ: {limit} ที่เหลืออยู่"
-inviteLimitResetCycle: "ขีดจำกัดนี้จะถูกรีเซ็ตเป็น {limit} ที่ {time}."
+createCount: "จำนวนรหัสเชิญ"
+inviteCodeCreated: "สร้างรหัสเชิญแล้ว"
+inviteLimitExceeded: "จำนวนรหัสเชิญที่สามารถสร้างได้ถึงขีดจำกัดแล้ว"
+createLimitRemaining: "รหัสเชิญที่สามารถสร้างได้: เหลืออยู่ {limit} รหัส"
+inviteLimitResetCycle: "สามารถสร้างรหัสเชิญได้อีกสูงสุด {limit} รหัส ภายใน {time}"
 expirationDate: "วันที่หมดอายุ"
 noExpirationDate: "ไม่มีหมดอายุ"
-inviteCodeUsedAt: "รหัสคำเชิญใช้แล้วที่"
-registeredUserUsingInviteCode: "ใช้คำเชิญแล้วโดย"
+inviteCodeUsedAt: "วันเวลาที่ใช้รหัสเชิญ"
+registeredUserUsingInviteCode: "ผู้ใช้ที่ใช้รหัสเชิญ"
 waitingForMailAuth: "กำลังรอการยืนยันอีเมล"
-inviteCodeCreator: "สร้างการเชิญแล้วโดย"
-usedAt: "ใช้แล้วที่"
+inviteCodeCreator: "ผู้ใช้ที่สร้างรหัสเชิญ"
+usedAt: "วันเวลาที่ถูกใช้"
 unused: "ยังไม่ได้ใช้"
 used: "ถูกใช้แล้ว"
 expired: "หมดอายุแล้ว"
@@ -1148,7 +1149,7 @@ renotes: "รีโน้ต"
 loadReplies: "แสดงการตอบกลับ"
 loadConversation: "แสดงบทสนทนา"
 pinnedList: "รายชื่อที่ปักหมุดไว้"
-keepScreenOn: "เปิดหน้าจอไว้"
+keepScreenOn: "เปิดหน้าจออุปกรณ์ค้างไว้"
 verifiedLink: "ความเป็นเจ้าของลิงก์ได้รับการยืนยันแล้ว"
 notifyNotes: "แจ้งเตือนเกี่ยวกับโพสต์ใหม่"
 unnotifyNotes: "หยุดการแจ้งเตือนเกี่ยวกับโน้ตใหม่"
@@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "การดำเนินการนี้ไม่
 confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
 externalServices: "บริการภายนอก"
 sourceCode: "ซอร์สโค้ด"
+sourceCodeIsNotYetProvided: "ซอร์สโค้ดยังไม่พร้อมใช้งาน โปรดติดต่อผู้ดูแลระบบของคุณเพื่อแก้ไขปัญหานี้"
+repositoryUrl: "URL ของ repository"
+repositoryUrlDescription: "หากมีที่เก็บซอร์สโค้ดที่เปิดเผยต่อสาธารณะ ให้ป้อน URL ที่เก็บซอร์สโค้ดนั้น แต่หากคุณใช้ Misskey ตามต้นฉบับ (ไม่มีการเปลี่ยนแปลงซอร์สโค้ด) ให้ป้อน https://github.com/misskey-dev/misskey"
+repositoryUrlOrTarballRequired: "หากคุณไม่มี repository สาธารณะ คุณจะต้องจัดเตรียม tarball แทน ดู .config/example.yml สำหรับรายละเอียด"
+feedback: "ฟีดแบ็ก"
+feedbackUrl: "URLของฟีดแบ็ก"
 impressum: "อิมเพรสชั่น"
 impressumUrl: "URL อิมเพรสชั่น"
 impressumDescription: "การติดป้ายกำกับ (Impressum) มีผลบังคับใช้ในบางประเทศและภูมิภาค เช่น ประเทศเยอรมนี"
@@ -1179,7 +1186,7 @@ attach: "แนบ"
 detach: "นำออก"
 detachAll: "เอาออกทั้งหมด"
 angle: "แองเกิล"
-flip: "ย้อนกลับ"
+flip: "พลิก"
 showAvatarDecorations: "แสดงตกแต่งอวตาร"
 releaseToRefresh: "ปล่อยเพื่อรีเฟรช"
 refreshing: "กำลังรีเฟรช..."
@@ -1203,6 +1210,8 @@ soundWillBePlayed: "จะมีการเล่นเอฟเฟกต์เ
 showReplay: "ดูรีเพลย์"
 replay: "รีเพลย์"
 replaying: "กำลังรีเพลย์"
+endReplay: "ออกจากรีเพลย์"
+copyReplayData: "คัดลอกข้อมูลรีเพลย์"
 ranking: "อันดับ"
 lastNDays: "ล่าสุด {n} วันที่แล้ว"
 backToTitle: "กลับไปหน้าไตเติ้ล"
@@ -1210,8 +1219,20 @@ hemisphere: "พื้นที่ที่อาศัยอยู่"
 withSensitive: "แสดงโน้ตที่มีไฟล์ที่ระบุว่ามีเนื้อหาละเอียดอ่อน"
 userSaysSomethingSensitive: "โพสต์ที่มีไฟล์เนื้อหาละเอียดอ่อนของ {name}"
 enableHorizontalSwipe: "ปัดเพื่อสลับแท็บ"
+loading: "กำลังโหลด"
+surrender: "ยอมแพ้"
+gameRetry: "เริ่มเกมใหม่"
 _bubbleGame:
   howToPlay: "วิธีเล่น"
+  hold: "หยุดชั่วคราว"
+  _score:
+    score: "คะแนน"
+    scoreYen: "จำนวนเงินที่ได้รับ"
+    highScore: "คะแนนสูงสุด"
+    maxChain: "จำนวน chain สูงสุด"
+    yen: "{yen} เยน"
+    estimatedQty: "{qty} อัน"
+    scoreSweets: "โอนิงิริ {onigiriQtyWithUnit}"
   _howToPlay:
     section1: "ขยับตำแหน่งและวางวัตถุลงในกล่อง"
     section2: "เมื่อวัตถุประเภทเดียวกันมารวมกัน พวกมันจะกลายเป็นวัตถุใหม่และคุณจะได้รับคะแนน"
@@ -1219,16 +1240,16 @@ _bubbleGame:
 _announcement:
   forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น"
   forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน"
-  needConfirmationToRead: "จำเป็นต้องยืนยันเพื่อทำเครื่องหมายบอกว่าอ่านแล้ว"
-  needConfirmationToReadDescription: "ข้อความแจ้งแยก ถ้าหากต้องการเพื่อยืนยันว่ากำลังทำเครื่องหมายประกาศนี้ว่าอ่านแล้วจะแสดงขึ้นถ้าหากเปิดใช้งาน การประกาศนั้นจะไม่รวมอยู่ในฟังก์ชั่นว่า \"ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว\""
+  needConfirmationToRead: "จำเป็นต้องยืนยันว่าอ่านแล้ว"
+  needConfirmationToReadDescription: "กล่องโต้ตอบการยืนยันจะปรากฏขึ้นเมื่อจะทำเครื่องหมายว่าอ่านแล้ว นอกจากนี้ยังทำให้ประกาศนี้ยังไม่ถูกอ่านเมื่อใช้ฟังก์ชั่น “ทำเครื่องหมายฯ ทั้งหมดว่าอ่านแล้ว”"
   end: "เก็บประกาศ"
   tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ"
-  readConfirmTitle: "ทำเครื่องหมายบอกว่าอ่านแล้วเลยมั้ย?"
-  readConfirmText: "การดำเนินการนี้จะทำเครื่องหมายเนื้อหาของ \"{title}\" บอกว่าอ่านแล้วนะ"
+  readConfirmTitle: "ทำเครื่องหมายว่าอ่านแล้วเลยไหม?"
+  readConfirmText: "จะทำเครื่องหมายใส่ “{title}” ว่าอ่านแล้ว"
   shouldNotBeUsedToPresentPermanentInfo: "เราขอแนะนำให้ใช้ประกาศเพื่อโพสต์ข้อมูลแบบ flow มากกว่าข้อมูลแบบ stock เนื่องจากมีแนวโน้มที่จะส่งผลเสียต่อ UX โดยเฉพาะสำหรับผู้ใช้ใหม่"
   dialogAnnouncementUxWarn: "เราขอแนะนำให้ใช้ด้วยความระมัดระวัง เนื่องจากการแจ้งเตือนแบบกล่องโต้ตอบตั้งแต่ 2 รายการขึ้นไปพร้อมกันอาจส่งผลเสียต่อ UX ได้อย่างมาก"
   silence: "ไม่มีการแจ้งเตือน"
-  silenceDescription: "หากเปิดใช้งาน จะไม่ได้แจ้งเตือนประกาศนี้  และผู้ใช้จะไม่จำเป็นต้องอ่าน"
+  silenceDescription: "หากเปิดใช้งาน จะไม่มีการแจ้งเตือนประกาศนี้ และผู้ใช้จะไม่จำเป็นต้องทำเครื่องหมายว่าอ่านแล้ว"
 _initialAccountSetting:
   accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!"
   letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ"
@@ -1315,7 +1336,7 @@ _timelineDescription:
 _serverRules:
   description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ"
 _serverSettings:
-  iconUrl: "ไอคอน URL"
+  iconUrl: "URL ไอคอน"
   appIconDescription: "ระบุไอคอนที่จะใช้เมื่อ {host} แสดงเป็นแอป"
   appIconUsageExample: "E.g. เป็น PWA หรือเมื่อแสดงผลเป็นบุ๊กมาร์กหน้าจอหลักบนโทรศัพท์"
   appIconStyleRecommendation: "เนื่องจากไอคอนอาจถูกครอบตัดเป็นสี่เหลี่ยมจัตุรัสหรือวงกลม จึงแนะนำให้ใช้ไอคอนที่มีขอบสีรอบๆ เนื้อหา"
@@ -1603,7 +1624,7 @@ _role:
   assignTarget: "มอบหมาย"
   descriptionOfAssignTarget: "แบบ<b>ปรับเอง</b> เพิ่มถอนบทบาทนี้แก่ผู้ใช้ด้วยตัวเอง\nแบบ<b>มีเงื่อนไข</b> เพิ่มถอนบทบาทนี้แก่ผู้ใช้โดยอัตโนมัติหากเข้าเงื่อนไขใดต่อไปนี้"
   manual: "ปรับเอง"
-  manualRoles: "บทบาทแบบทำเอง"
+  manualRoles: "บทบาทแบบทำมือ"
   conditional: "มีเงื่อนไข"
   conditionalRoles: "บทบาทแบบมีเงื่อนไข"
   condition: "เงื่อนไข"
@@ -1615,13 +1636,13 @@ _role:
   baseRole: "เทมเพลตบทบาท"
   useBaseValue: "ใช้ตามเทมเพลตบทบาท"
   chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด"
-  iconUrl: "ไอคอน URL"
+  iconUrl: "URL ไอคอน"
   asBadge: "แสดงเป็นตรา"
   descriptionOfAsBadge: "เมื่อเปิดใช้งาน ไอคอนบทบาทจะปรากฏถัดจากชื่อผู้ใช้"
   isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจากบทบาท"
   descriptionOfIsExplorable: "เมื่อเปิดใช้งาน ไทมไลน์บทบาทนี้และสมาชิกที่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ"
-  displayOrder: "ตำแหน่ง"
-  descriptionOfDisplayOrder: "ยิ่งตัวเลขสูง ตำแหน่ง UI ก็ยิ่งสูงขึ้นนะ"
+  displayOrder: "ลำดับการแสดงผล"
+  descriptionOfDisplayOrder: "เลขที่สูงกว่าจะแสดงบน UI ก่อน"
   canEditMembersByModerator: "อนุญาตให้ผู้ควบคุมแก้ไขสมาชิก"
   descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ นอกเหนือจากผู้ควบคุมและผู้ดูแลระบบแล้ว จะสามารถเพิ่มถอนบทบาทนี้แก่ผู้ใช้ได้ แต่เมื่อปิดใช้ จะมีเฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถดำเนินการได้"
   priority: "ลำดับความสำคัญ"
@@ -1656,6 +1677,7 @@ _role:
     canUseTranslator: "การใช้งานแปล"
     avatarDecorationLimit: "จำนวนการตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้"
   _condition:
+    roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ"
     isLocal: "ผู้ใช้ในพื้นที่"
     isRemote: "ผู้ใช้ระยะไกล"
     createdLessThan: "สร้างน้อยกว่า"
@@ -1685,13 +1707,13 @@ _emailUnavailable:
   smtp: "เซิร์ฟเวอร์อีเมลนี้ไม่มีการตอบสนอง"
   banned: "คุณไม่สามารถลงทะเบียนด้วยที่อยู่อีเมลนี้ได้"
 _ffVisibility:
-  public: "เผยแพร่"
+  public: "สาธารณะ"
   followers: "ปรากฏให้แก่ผู้ติดตามเท่านั้น"
   private: "ส่วนตัว"
 _signup:
   almostThere: "เกือบจะเสร็จแล้ว"
   emailAddressInfo: "กรุณากรอกที่อยู่อีเมลที่คุณใช้ ที่อยู่อีเมลของคุณจะไม่ถูกเผยแพร่สู่สาธารณชน"
-  emailSent: "เราได้ส่งอีเมลยืนยันไปยังที่อยู่อีเมลของคุณแล้วนะ ({email}) โปรดคลิกลิงก์ที่รวมไว้เพื่อสร้างบัญชีให้เสร็จสิ้น"
+  emailSent: "อีเมลยืนยันได้ถูกส่งไปยังที่อยู่อีเมลที่คุณป้อน ({email}) แล้ว กรุณาติดตามลิงก์ในอีเมลเพื่อสร้างบัญชีให้เสร็จสมบูรณ์ ลิงก์ที่ให้ไว้จะหมดอายุใน 30 นาที"
 _accountDelete:
   accountDelete: "ลบบัญชีผู้ใช้"
   mayTakeTime: "เนื่องจากการลบบัญชีนี้จะเป็นกระบวนการที่ต้องใช้ทรัพยากรมาก จึงอาจจะต้องใช้เวลาสักครู่ถึงจะเสร็จสมบูรณ์ ทั้งนี้ขึ้นอยู่กับจำนวนเนื้อหาที่คุณสร้างและจำนวนไฟล์ที่คุณอัปโหลดนะ"
@@ -1729,7 +1751,7 @@ _plugin:
   viewSource: "ดูต้นฉบับ"
 _preferencesBackups:
   list: "สร้างการสำรองข้อมูล"
-  saveNew: "บันทึกใหม่"
+  saveNew: "บันทึกข้อมูลสำรองใหม่"
   loadFile: "โหลดจากไฟล์"
   apply: "นำไปใช้กับอุปกรณ์นี้"
   save: "บันทึก"
@@ -1739,8 +1761,8 @@ _preferencesBackups:
   applyConfirm: "คุณต้องการใช้ข้อมูลสำรอง \"{name}\" กับอุปกรณ์นี้อย่างงั้นจริงหรอ การตั้งค่าที่มีอยู่ของอุปกรณ์นี้จะถูกเขียนทับนะ"
   saveConfirm: "บันทึกข้อมูลสำรองเป็น {name} มั้ย?"
   deleteConfirm: "ลบข้อมูลสำรอง {name} มั้ย?"
-  renameConfirm: "เปลี่ยนชื่อข้อมูลสำรองนี้จาก \"{old}\" เป็น \"{new}\" หรือไม่?"
-  noBackups: "ไม่มีข้อมูลสำรองนะ คุณสามารถสำรองข้อมูลการตั้งค่าไคลเอนต์ของคุณบนเซิร์ฟเวอร์นี้โดยใช้ \"สร้างการสำรองข้อมูลใหม่\"ได้นะ"
+  renameConfirm: "ต้องการเปลี่ยนชื่อข้อมูลสำรองจาก “{old}” เป็น “{new}” ใช่ไหม?"
+  noBackups: "ไม่มีข้อมูลสำรอง สามารถบันทึกการตั้งค่าไคลเอนต์ปัจจุบันไปยังเซิร์ฟเวอร์ด้วย “บันทึกข้อมูลสำรองใหม่”"
   createdAt: "สร้างเมื่อ: {date} {time}"
   updatedAt: "อัปเดตเมื่อ: {date} {time}"
   cannotLoad: "การโหลดล้มเหลว"
@@ -1756,10 +1778,12 @@ _aboutMisskey:
   contributors: "ผู้สนับสนุนหลัก"
   allContributors: "ผู้มีส่วนร่วมทั้งหมด"
   source: "ซอร์สโค้ด"
+  original: "ต้นฉบับ"
+  thisIsModifiedVersion: "{name} ใช้ Misskey เวอร์ชันดัดแปลง"
   translation: "แปลภาษา Misskey"
   donate: "บริจาคให้กับ Misskey"
   morePatrons: " ขอบคุณทุกท่านที่ร่วมกันช่วยเหลือตลอดมานะคะ 🥰"
-  patrons: "สมาชิกพันธมิตร"
+  patrons: "ผู้อุปถัมภ์"
   projectMembers: "สมาชิกในโครงการ"
 _displayOfSensitiveMedia:
   respect: "ซ่อนสื่อที่ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
@@ -1831,8 +1855,8 @@ _theme:
   importInfo: "ถ้าหากต้องการป้อนโค้ดที่นี่ คุณยังสามารถนำเข้าไปยังโปรแกรมแก้ไขธีมได้"
   deleteConstantConfirm: "คุณต้องการลบค่าคงที่ {const} หรือป่าว?"
   keys:
-    accent: "เน้น"
-    bg: "ภาพพื้นหลัง"
+    accent: "สีหลัก"
+    bg: "พื้นหลัง"
     fg: "ข้อความ"
     focus: "โฟกัส"
     indicator: "ตัวบ่งชี้"
@@ -1868,11 +1892,11 @@ _theme:
     wallpaperOverlay: "วอลล์เปเปอร์ซ้อนทับ"
     badge: "ตรา"
     messageBg: "พื้นหลังแชท"
-    accentDarken: "เน้น (มืด)"
-    accentLighten: "เน้น (สว่าง)"
+    accentDarken: "สีหลัก (มืด)"
+    accentLighten: "สีหลัก (สว่าง)"
     fgHighlighted: "ข้อความที่ไฮไลต์"
 _sfx:
-  note: "หมายเหตุ"
+  note: "โน้ต"
   noteMy: "โน้ตของตัวเอง"
   notification: "การเเจ้งเตือน"
   antenna: "เสาอากาศ"
@@ -1959,7 +1983,7 @@ _permissions:
   "read:reactions": "ดูรีแอคชั่นของคุณ"
   "write:reactions": "แก้ไขรีแอคชั่นของคุณ"
   "write:votes": "โหวตบนสำรวจความคิดเห็น"
-  "read:pages": "ดหน้าเพจ"
+  "read:pages": "ดูหน้าเพจ"
   "write:pages": "แก้ไขหรือลบเพจของคุณ"
   "read:page-likes": "ดูรายการเพจที่ถูกใจไว้"
   "write:page-likes": "แก้ไขรายการเพจที่ถูกใจ"
@@ -1971,8 +1995,8 @@ _permissions:
   "write:gallery": "แก้ไขแกลเลอรี่ของคุณ"
   "read:gallery-likes": "ดูรายการโพสต์แกลเลอรีที่ถูกใจไว้"
   "write:gallery-likes": "แก้ไขรายการโพสต์แกลเลอรีที่ถูกใจไว้"
-  "read:flash": "วิว เพลย์"
-  "write:flash": "แก้ไขเพลย์"
+  "read:flash": "ดู Play"
+  "write:flash": "แก้ไข Play"
   "read:flash-likes": "ดูรายการ  play ที่ถูกใจไว้"
   "write:flash-likes": "แก้ไขรายการ play ที่ถูกใจไว้"
   "read:admin:abuse-user-reports": "ดูรายงานจากผู้ใช้"
@@ -1999,8 +2023,8 @@ _permissions:
   "read:admin:roles": "ดูบทบาท"
   "write:admin:relays": "จัดการรีเลย์"
   "read:admin:relays": "ดูรีเลย์"
-  "write:admin:invite-codes": "จัดการคำเชิญ"
-  "read:admin:invite-codes": "ดูรหัสคำเชิญ"
+  "write:admin:invite-codes": "จัดการรหัสเชิญ"
+  "read:admin:invite-codes": "ดูรหัสเชิญ"
   "write:admin:announcements": "จัดการประกาศ"
   "read:admin:announcements": "ดูประกาศ"
   "write:admin:avatar-decorations": "จัดการการตกแต่งอวตาร"
@@ -2018,7 +2042,7 @@ _permissions:
   "read:admin:stream": "ใช้ Websocket API สำหรับผู้ดูแลระบบ"
   "write:admin:ad": "จัดการโฆษณา"
   "read:admin:ad": "ดูโฆษณา"
-  "write:invite-codes": "สร้างรหัสคำเชิญ"
+  "write:invite-codes": "สร้างรหัสเชิญ"
   "read:invite-codes": "รับรหัสเชิญ"
   "write:clip-favorite": "ควบคุมการถูกใจของคลิป"
   "read:clip-favorite": "ดูการถูกใจของคลิป"
@@ -2071,8 +2095,8 @@ _widgets:
   onlineUsers: "ผู้ใช้ที่ออนไลน์"
   jobQueue: "คิวงาน"
   serverMetric: "ตัวชี้วัดเซิร์ฟเวอร์"
-  aiscript: "AiScript คอนโซล"
-  aiscriptApp: "AiScript แอพ"
+  aiscript: " คอนโซล AiScript"
+  aiscriptApp: "แอป AiScript"
   aichan: "ไอ"
   userList: "รายชื่อผู้ใช้"
   _userList:
@@ -2086,15 +2110,15 @@ _cw:
   files: "{count} ไฟล์"
 _poll:
   noOnlyOneChoice: "จำเป็นต้องมีอย่างน้อยสองตัวเลือก"
-  choiceN: "ตัวเลือก {n}"
-  noMore: "คุณไม่สามารถเพิ่มตัวเลือกอื่นได้"
+  choiceN: "ตัวเลือกที่ {n}"
+  noMore: "เพิ่มตัวเลือกอีกไม่ได้แล้ว"
   canMultipleVote: "สามารถตอบได้หลายคำตอบ"
-  expiration: "สิ้นสุดการสำรวจความคิดเห็น"
-  infinite: "ไม่ต้องเลย"
-  at: "จบที่..."
-  after: "สิ้นสุดหลัง..."
+  expiration: "สิ้นสุดโพล"
+  infinite: "ไม่กำหนดระยะเวลา"
+  at: "ระบุวันเวลา"
+  after: "ระบุระยะเวลา"
   deadlineDate: "วันสิ้นสุด"
-  deadlineTime: "ชั่วโมง"
+  deadlineTime: "เวลา"
   duration: "ระยะเวลา"
   votesCount: "{n} คะแนนเสียง"
   totalVotes: "{n} คะแนนเสียงทั้งหมด"
@@ -2102,17 +2126,17 @@ _poll:
   showResult: "ดูผลลัพธ์"
   voted: "โหวตแล้ว"
   closed: "สิ้นสุดแล้ว"
-  remainingDays: "จะเสร็จสิ้นในอีก {d} วัน {h} ชั่วโมง"
-  remainingHours: "{h} ชั่วโมง(s) {m} นาที(s) ที่เหลืออยู่"
-  remainingMinutes: "{m} นาที(s) {s} วินาที(s) ที่เหลืออยู่"
-  remainingSeconds: "{s} นาที(s) ที่เหลืออยู่"
+  remainingDays: "เหลืออีก {d} วัน {h} ชั่วโมง"
+  remainingHours: "เหลืออีก {h} ชั่วโมง {m} นาที"
+  remainingMinutes: "เหลืออีก {m} นาที {s} วินาที"
+  remainingSeconds: "เหลืออีก {s} วินาที"
 _visibility:
   public: "สาธารณะ"
   publicDescription: "โน้ตของคุณจะปรากฏแก่ผู้ใช้ทุกคน"
   home: "หน้าแรก"
   homeDescription: "โพสลงไทม์ไลน์ที่บ้านเท่านั้น"
   followers: "ผู้ติดตาม"
-  followersDescription: "ทำให้ผู้ติดตามนั้นมองเห็นแค่คุณเท่านั้น"
+  followersDescription: "เฉพาะผู้ติดตามเท่านั้นที่มองเห็นได้"
   specified: "ไดเร็ค"
   specifiedDescription: "ทำให้มองเห็นได้เฉพาะผู้ใช้ที่ระบุเท่านั้น"
   disableFederation: "ไม่มีสหพันธ์"
@@ -2122,11 +2146,11 @@ _postForm:
   quotePlaceholder: "อ้างโน้ตนี้..."
   channelPlaceholder: "โพสต์ลงช่อง..."
   _placeholders:
-    a: "คุณเป็นอะไรไปหรอ?"
-    b: "เกิดอะไรขึ้นรอบตัวคุณ?"
-    c: "คุณกำลังคิดอะไรอยู่?"
-    d: "คุณต้องการจะพูดอะไร?"
-    e: "เริ่มเขียน..."
+    a: "ตอนนี้เป็นยังไงบ้าง?"
+    b: "มีอะไรเกิดขึ้นหรือเปล่า?"
+    c: "กำลังคิดอะไรอยู่?"
+    d: "ต้องการจะพูดอะไรไหม?"
+    e: "มาเขียนกันเถอะ"
     f: "กำลังรอให้คุณเขียน..."
 _profile:
   name: "ชื่อ"
@@ -2140,11 +2164,11 @@ _profile:
   metadataContent: "เนื้อหา"
   changeAvatar: "เปลี่ยนอวาตาร์"
   changeBanner: "เปลี่ยนแบนเนอร์"
-  verifiedLinkDescription: "โดยการป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณตรงนี้ ส่วนไอคอนการยืนยันความเป็นเจ้าของนั้นก็สามารถแสดงถัดจากฟิลด์ได้นะ"
+  verifiedLinkDescription: "หากป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณ ไอคอนการยืนยันความเป็นเจ้าของจะแสดงถัดจากฟิลด์นั้น ๆ"
   avatarDecorationMax: "คุณสามารถเพิ่มการตกแต่งได้สูงสุด {max}"
 _exportOrImport:
   allNotes: "โน้ตทั้งหมด"
-  favoritedNotes: "บันทึกที่ชื่นชอบ"
+  favoritedNotes: "โน้ตที่ถูกใจไว้"
   clips: "คลิป"
   followingList: "กำลังติดตาม"
   muteList: "ปิดเสียง"
@@ -2253,26 +2277,26 @@ _relayStatus:
   accepted: "ได้รับการอนุมัติ"
   rejected: "ถูกปฏิเสธ"
 _notification:
-  fileUploaded: "ไฟล์ถูกอัพโหลดแล้วน่ะ"
+  fileUploaded: "ไฟล์ถูกอัปโหลดแล้ว"
   youGotMention: "{name} กล่าวถึงคุณ"
   youGotReply: "{name} ตอบกลับถึงคุณ"
-  youGotQuote: "{name} อ้างถึงคุณ"
+  youGotQuote: "{name} อ้างอิงคุณ"
   youRenoted: "รีโน้ตจาก {name}"
   youWereFollowed: "ได้ติดตามคุณ"
-  youReceivedFollowRequest: "คุณมีคำขอติดตามใหม่น่ะ"
-  yourFollowRequestAccepted: "คำขอติดตามของคุณได้รับการยอมรับแล้วน่ะ"
-  pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้อมใช้งาน"
+  youReceivedFollowRequest: "ได้รับคำขอติดตาม"
+  yourFollowRequestAccepted: "คำขอติดตามได้รับการอนุมัติแล้ว"
+  pollEnded: "ผลโพลออกมาแล้ว"
   newNote: "โพสต์ใหม่"
   unreadAntennaNote: "เสาอากาศ {name}"
   roleAssigned: "ได้รับบทบาท"
-  emptyPushNotificationMessage: "การแจ้งเตือนแบบพุชได้รับการอัพเดทแล้ว"
+  emptyPushNotificationMessage: "อัปเดตการแจ้งเตือนแบบพุชแล้ว"
   achievementEarned: "รับความสำเร็จ"
   testNotification: "ทดสอบการแจ้งเตือน"
   checkNotificationBehavior: "กดเพื่อดูลักษณะการแจ้งเตือน"
   sendTestNotification: "ส่งทดสอบการแจ้งเตือน"
   notificationWillBeDisplayedLikeThis: "การแจ้งเตือนมีลักษณะแบบนี้"
   reactedBySomeUsers: "ถูกรีแอคชั่นโดยผู้ใช้ {n} ราย"
-  renotedBySomeUsers: "Renote จากผู้ใช้จำนวน {n} ราย"
+  renotedBySomeUsers: "รีโน้ตจากผู้ใช้ {n} ราย"
   followedBySomeUsers: "มีผู้ติดตาม {n} ราย"
   _types:
     all: "ทั้งหมด"
@@ -2283,9 +2307,9 @@ _notification:
     renote: "รีโน้ต"
     quote: "อ้างคำพูด"
     reaction: "รีแอคชั่น"
-    pollEnded: "โพลนี้สิ้นสุดลงแล้ว"
-    receiveFollowRequest: "ได้รับคำขอติดตาม\n"
-    followRequestAccepted: "ยอมรับคำขอติดตาม"
+    pollEnded: "โพลสิ้นสุดแล้ว"
+    receiveFollowRequest: "ได้รับคำร้องขอติดตาม"
+    followRequestAccepted: "อนุมัติให้ติดตามแล้ว"
     roleAssigned: "ให้บทบาท"
     achievementEarned: "ปลดล็อกความสำเร็จแล้ว"
     app: "การแจ้งเตือนจากแอปที่มีลิงก์"
@@ -2322,7 +2346,7 @@ _deck:
     list: "รายการ"
     channel: "ช่อง"
     mentions: "พูดถึง"
-    direct: "ไดเร็ค"
+    direct: "ไดเร็กต์"
     roleTimeline: "บทบาทไทม์ไลน์"
 _dialog:
   charactersExceeded: "คุณกำลังมีตัวอักขระเกินขีดจำกัดสูงสุดแล้วนะ! ปัจจุบันอยู่ที่ {current} จาก {max}"
@@ -2353,8 +2377,8 @@ _moderationLogTypes:
   updateRole: "อัปเดตบทบาทแล้ว"
   assignRole: "ได้รับมอบหมายบทบาท"
   unassignRole: "ถอดออกจากบทบาทแล้ว"
-  suspend: "ถูกระงับ"
-  unsuspend: "เลิกถูกระงับ"
+  suspend: "ระงับ"
+  unsuspend: "เลิกระงับ"
   addCustomEmoji: "เพิ่มเอโมจิที่กำหนดเองแล้ว"
   updateCustomEmoji: "อัปเดตเอโมจิที่กำหนดเองแล้ว"
   deleteCustomEmoji: "ลบเอโมจิที่กำหนดเองออกแล้ว"
@@ -2369,12 +2393,13 @@ _moderationLogTypes:
   deleteGlobalAnnouncement: "ลบประกาศทั่วโลกออกแล้ว"
   deleteUserAnnouncement: "ลบประกาศผู้ใช้ออกแล้ว"
   resetPassword: "รีเซ็ตรหัสผ่าน"
-  suspendRemoteInstance: "อินสแตนซ์ระยะไกลถูกระงับ"
-  unsuspendRemoteInstance: "อินสแตนซ์ระยะไกลเลิกการระงับ"
+  suspendRemoteInstance: "ระงับอินสแตนซ์ระยะไกล"
+  unsuspendRemoteInstance: "เลิกระงับอินสแตนซ์ระยะไกล"
+  updateRemoteInstanceNote: "อัปเดตโน้ตการกลั่นกรองของอินสแตนซ์ระยะไกลแล้ว"
   markSensitiveDriveFile: "ทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน"
   unmarkSensitiveDriveFile: "ยกเลิกทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน"
   resolveAbuseReport: "รายงานได้รับการแก้ไขแล้ว"
-  createInvitation: "สร้างคำเชิญ"
+  createInvitation: "สร้างรหัสเชิญ"
   createAd: "สร้างโฆษณาแล้ว"
   deleteAd: "ลบโฆษณาออกแล้ว"
   updateAd: "อัปเดตโฆษณาแล้ว"
@@ -2491,6 +2516,8 @@ _reversi:
   opponentHasSettingsChanged: "อีกฝ่ายเปลี่ยนการตั้งค่า"
   allowIrregularRules: "อนุญาตกฎที่ไม่ปรกติ (โหมดฟรีทุกอย่าง)"
   disallowIrregularRules: "ไม่อนุญาตกฎที่ไม่ปรกติ"
+  showBoardLabels: "แสดงหมายเลขแถว/คอลัมน์บนกระดาน"
+  useAvatarAsStone: "ใช้รูปอวตารเป็นหมาก"
 _offlineScreen:
   title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
   header: "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 7cfdde320474..59883f4a6cc0 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1048,6 +1048,7 @@ verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đ
 sourceCode: "Mã nguồn"
 flip: "Lật"
 lastNDays: "{n} ngày trước"
+surrender: "Từ chối"
 _announcement:
   forExistingUsers: "Chỉ những người dùng đã tồn tại"
   forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó."
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index d0891f0678c2..3f54444d9d11 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -336,7 +336,7 @@ displayOfSensitiveMedia: "显示敏感媒体"
 whenServerDisconnected: "与服务器连接中断时"
 disconnectedFromServer: "已和服务器断开连接"
 reload: "重新加载"
-doNothing: "关闭弹窗"
+doNothing: "关闭"
 reloadConfirm: "确定要重新加载吗?"
 watch: "关注"
 unwatch: "取消关注"
@@ -991,6 +991,7 @@ neverShow: "不再显示"
 remindMeLater: "稍后提醒我"
 didYouLikeMisskey: "您喜欢 Misskey 吗?"
 pleaseDonate: "Misskey 是 {host} 所使用的免费软件。为了今后也能够维持 Misskey 的开发,请在有余力的情况下进行捐助!"
+correspondingSourceIsAvailable: "对应的源代码可在{anchor}找到"
 roles: "角色"
 role: "角色"
 noRole: "角色不存在"
@@ -1042,6 +1043,7 @@ sensitiveWords: "敏感词"
 sensitiveWordsDescription: "将包含设置词的帖子的可见范围设置为首页。可以通过用换行符分隔来设置多个。"
 sensitiveWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
 prohibitedWords: "禁用词"
+prohibitedWordsDescription: "发布包含设定词汇的帖子时将出错。可用换行设定多个关键字"
 prohibitedWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
 hiddenTags: "隐藏标签"
 hiddenTagsDescription: "设定的标签将不会在时间线上显示。可使用换行来设置多个标签。"
@@ -1115,7 +1117,7 @@ branding: "品牌"
 enableServerMachineStats: "公开服务器硬件统计信息"
 enableIdenticonGeneration: "启用生成用户 Identicon"
 turnOffToImprovePerformance: "关闭该选项可以提高性能。"
-createInviteCode: "发行邀请码"
+createInviteCode: "生成邀请码"
 createWithOptions: "使用选项来创建"
 createCount: "发行数"
 inviteCodeCreated: "已创建邀请码"
@@ -1127,7 +1129,7 @@ noExpirationDate: "不设置有效日期"
 inviteCodeUsedAt: "邀请码被使用的日期和时间"
 registeredUserUsingInviteCode: "使用了邀请码的用户"
 waitingForMailAuth: "等待验证电子邮件"
-inviteCodeCreator: "发行邀请码的用户"
+inviteCodeCreator: "生成邀请码的用户"
 usedAt: "使用时间"
 unused: "未使用"
 used: "已使用"
@@ -1158,6 +1160,7 @@ showRenotes: "显示转帖"
 edited: "已编辑"
 notificationRecieveConfig: "通知接收设置"
 mutualFollow: "互相关注"
+followingOrFollower: "关注中或关注者"
 fileAttachedOnly: "仅限媒体"
 showRepliesToOthersInTimeline: "在时间线中包含给别人的回复"
 hideRepliesToOthersInTimeline: "在时间线中隐藏给别人的回复"
@@ -1167,6 +1170,12 @@ confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中包含
 confirmHideRepliesAll: "此操作不可撤销。确认要在时间线中隐藏现在关注的所有人的回复吗?"
 externalServices: "外部服务"
 sourceCode: "源代码"
+sourceCodeIsNotYetProvided: "还未提供源代码。要解决此问题请联系管理员。"
+repositoryUrl: "仓库地址"
+repositoryUrlDescription: "若源代码所在的仓库是公开的,请填入对应的 URL。若是按原样使用 Misskey(并未追加或者修改代码)的情况请填入 https://github.com/misskey-dev/misskey。"
+repositoryUrlOrTarballRequired: "若仓库并未公开,则需要提供 tarball 作为替代。详情请看 .config/example.yml。"
+feedback: "反馈"
+feedbackUrl: "反馈地址"
 impressum: "运营商信息"
 impressumUrl: "运营商信息地址"
 impressumDescription: "德国等国家和地区有义务展示此类信息(Impressum)。"
@@ -1196,11 +1205,14 @@ seasonalScreenEffect: "应景的画面效果"
 decorate: "装饰"
 addMfmFunction: "添加装饰"
 enableQuickAddMfmFunction: "显示高级 MFM 选择器"
+bubbleGame: "泡泡游戏"
 sfx: "音效"
 soundWillBePlayed: "声音将会播放"
-showReplay: "查看重播"
+showReplay: "观看回放"
 replay: "重播"
 replaying: "重播中"
+endReplay: "结束回放"
+copyReplayData: "复制回放数据"
 ranking: "排行榜"
 lastNDays: "最近 {n} 天"
 backToTitle: "返回标题"
@@ -1208,8 +1220,19 @@ hemisphere: "居住地区"
 withSensitive: "显示包含敏感媒体的帖子"
 userSaysSomethingSensitive: "含 {name} 敏感文件的帖子"
 enableHorizontalSwipe: "滑动切换标签页"
+loading: "读取中"
+surrender: "取消"
+gameRetry: "重试"
 _bubbleGame:
   howToPlay: "游戏说明"
+  hold: "抓住"
+  _score:
+    score: "得分"
+    scoreYen: "赚到的钱"
+    highScore: "最高分"
+    maxChain: "最高连击数"
+    yen: "{yen} 日元"
+    estimatedQty: "约 {qty} 个"
   _howToPlay:
     section1: "对准位置将Emoji投入盒子。"
     section2: "相同的Emoji相互接触合成后会得到新的Emoji,以此获得分数。"
@@ -1298,8 +1321,8 @@ _initialTutorial:
     description: "对于服务器方针所要求要求的,又或者不适合直接展示的附件,请添加「敏感」标记。\n"
     tryThisFile: "试试看,将附加到此窗口的图像标注为敏感!"
     _exampleNote:
-      note: "不该打开纳豆的盖子的……"
-    method: "要标注附件为敏感内容,请单击该文件以打开菜单,然后单击“设置为敏感”。"
+      note: "拆纳豆包装时出错了…"
+    method: "要标注附件为敏感内容,请单击该文件以打开菜单,然后单击“标记为敏感内容”。"
     sensitiveSucceeded: "附加文件时,请遵循服务器的条款来设置正确敏感设定。\n"
     doItToContinue: "将图像标记为敏感后才能够继续"
   _done:
@@ -1631,7 +1654,7 @@ _role:
     ltlAvailable: "查看本地时间线"
     canPublicNote: "允许公开发帖"
     canInvite: "发放服务器邀请码"
-    inviteLimit: "可发行邀请码的数量"
+    inviteLimit: "可生成邀请码的数量"
     inviteLimitCycle: "邀请码的发行间隔"
     inviteExpirationTime: "邀请码的有效日期"
     canManageCustomEmojis: "管理自定义表情符号"
@@ -1653,6 +1676,7 @@ _role:
     canUseTranslator: "使用翻译功能"
     avatarDecorationLimit: "可添加头像挂件的最大个数"
   _condition:
+    roleAssignedTo: "已分配给手动角色"
     isLocal: "是本地用户"
     isRemote: "是远程用户"
     createdLessThan: "账户创建时间少于"
@@ -1753,6 +1777,8 @@ _aboutMisskey:
   contributors: "主要贡献者"
   allContributors: "全体贡献者"
   source: "源代码"
+  original: "原版"
+  thisIsModifiedVersion: "{name}正在使用修改后的 Misskey。"
   translation: "翻译 Misskey"
   donate: "赞助 Misskey"
   morePatrons: "还有很多其它的人也在支持我们,非常感谢🥰"
@@ -2015,7 +2041,7 @@ _permissions:
   "read:admin:stream": "使用管理员用的 Websocket API"
   "write:admin:ad": "编辑广告"
   "read:admin:ad": "查看广告"
-  "write:invite-codes": "发行邀请码"
+  "write:invite-codes": "生成邀请码"
   "read:invite-codes": "获取已发行的邀请码"
   "write:clip-favorite": "编辑便签的点赞"
   "read:clip-favorite": "查看便签的点赞"
@@ -2368,10 +2394,11 @@ _moderationLogTypes:
   resetPassword: "重置密码"
   suspendRemoteInstance: "停止远程服务器"
   unsuspendRemoteInstance: "恢复远程服务器"
+  updateRemoteInstanceNote: "更新远程服务器的管理笔记"
   markSensitiveDriveFile: "标记网盘文件为敏感媒体"
   unmarkSensitiveDriveFile: "取消标记网盘文件为敏感媒体"
   resolveAbuseReport: "处理举报"
-  createInvitation: "发行邀请码"
+  createInvitation: "生成邀请码"
   createAd: "创建了广告"
   deleteAd: "删除了广告"
   updateAd: "更新了广告"
@@ -2462,6 +2489,8 @@ _reversi:
   myTurn: "你的回合"
   turnOf: "{name}的回合"
   pastTurnOf: "{name}的回合"
+  surrender: "认输"
+  surrendered: "已认输"
   timeout: "超时"
   drawn: "平局"
   won: "{name}获胜"
@@ -2483,6 +2512,8 @@ _reversi:
   opponentHasSettingsChanged: "对手更改了设定"
   allowIrregularRules: "允许非常规规则(完全自由)"
   disallowIrregularRules: "禁止非常规规则"
+  showBoardLabels: "显示行号和列号"
+  useAvatarAsStone: "用头像作为棋子"
 _offlineScreen:
   title: "离线——无法连接到服务器"
   header: "无法连接到服务器"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 2762a612f559..bc872823f1c7 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -991,6 +991,7 @@ neverShow: "不再顯示"
 remindMeLater: "以後再說"
 didYouLikeMisskey: "您喜歡 Misskey 嗎?"
 pleaseDonate: "Misskey 是由 {host} 使用的免費軟體。請贊助我們,讓開發得以持續!"
+correspondingSourceIsAvailable: "對應的原始碼可以在 {anchor} 處找到。"
 roles: "角色"
 role: "角色"
 noRole: "沒有角色"
@@ -1159,6 +1160,7 @@ showRenotes: "顯示其他人的轉發貼文"
 edited: "已編輯"
 notificationRecieveConfig: "接受通知的設定"
 mutualFollow: "互相追隨"
+followingOrFollower: "追隨中或追隨者"
 fileAttachedOnly: "顯示包含附件的貼文"
 showRepliesToOthersInTimeline: "顯示給其他人的回覆"
 hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆"
@@ -1168,6 +1170,12 @@ confirmShowRepliesAll: "進行此操作後無法復原。您真的希望時間
 confirmHideRepliesAll: "進行此操作後無法復原。您真的希望時間軸「不包含」您目前追隨的所有人的回覆嗎?"
 externalServices: "外部服務"
 sourceCode: "原始碼"
+sourceCodeIsNotYetProvided: "尚未提供原始碼,請洽詢管理員解決這個問題。"
+repositoryUrl: "儲存庫 URL"
+repositoryUrlDescription: "如果存在可公開取得原始碼的儲存庫,請輸入其 URL。 如果您按原樣使用 Misskey(不對原始碼進行任何更改),請輸入 https://github.com/misskey-dev/misskey。"
+repositoryUrlOrTarballRequired: "如果儲存庫不是公開的,則必須提供 tarball。 詳細資訊請參閱 .config/example.yml。"
+feedback: "意見回饋"
+feedbackUrl: "意見回饋 URL"
 impressum: "營運者資訊"
 impressumUrl: "營運者資訊網址"
 impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。"
@@ -1203,6 +1211,8 @@ soundWillBePlayed: "將播放音效"
 showReplay: "觀看重播"
 replay: "重播"
 replaying: "重播中"
+endReplay: "退出重播"
+copyReplayData: "複製重播資料"
 ranking: "排行榜"
 lastNDays: "過去 {n} 天"
 backToTitle: "回到遊戲標題頁"
@@ -1210,8 +1220,20 @@ hemisphere: "您居住的地區"
 withSensitive: "顯示包含敏感檔案的貼文"
 userSaysSomethingSensitive: "包含 {name} 敏感檔案的貼文"
 enableHorizontalSwipe: "滑動切換時間軸"
+loading: "載入中"
+surrender: "退出"
+gameRetry: "再試一次"
 _bubbleGame:
   howToPlay: "玩法說明"
+  hold: "保留"
+  _score:
+    score: "分數"
+    scoreYen: "賺取的金額"
+    highScore: "最高分"
+    maxChain: "最大結合數"
+    yen: "{yen} 日圓"
+    estimatedQty: "{qty}個"
+    scoreSweets: "飯糰 {onigiriQtyWithUnit}"
   _howToPlay:
     section1: "調整位置並將物體放入盒子中。"
     section2: "當相同類型的物體黏在一起時,它們會變成不同的物體,您就會得到分數。"
@@ -1615,7 +1637,7 @@ _role:
   baseRole: "基本角色"
   useBaseValue: "使用基本角色的值"
   chooseRoleToAssign: "選擇要指派的角色"
-  iconUrl: "圖示的URL"
+  iconUrl: "圖示的 URL"
   asBadge: "顯示為徽章"
   descriptionOfAsBadge: "開啟的話,角色圖示會顯示在使用者名稱旁邊。"
   isExplorable: "讓使用者更容易找到您"
@@ -1656,6 +1678,7 @@ _role:
     canUseTranslator: "使用翻譯功能"
     avatarDecorationLimit: "頭像裝飾的最大設置量"
   _condition:
+    roleAssignedTo: "手動指派角色完成"
     isLocal: "本地使用者"
     isRemote: "遠端使用者"
     createdLessThan: "帳戶加入時間不超過"
@@ -1756,6 +1779,8 @@ _aboutMisskey:
   contributors: "主要貢獻者"
   allContributors: "全體貢獻人員"
   source: "原始碼"
+  original: "原始"
+  thisIsModifiedVersion: "{name} 使用原始 Misskey 的修改版本。"
   translation: "翻譯 Misskey"
   donate: "贊助 Misskey"
   morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰"
@@ -2359,7 +2384,7 @@ _moderationLogTypes:
   updateCustomEmoji: "更新自訂表情符號"
   deleteCustomEmoji: "刪除自訂表情符號"
   updateServerSettings: "更新伺服器設定"
-  updateUserNote: "更新管理筆記"
+  updateUserNote: "更新了使用者的管理筆記"
   deleteDriveFile: "刪除檔案"
   deleteNote: "刪除貼文"
   createGlobalAnnouncement: "建立全網通知"
@@ -2371,6 +2396,7 @@ _moderationLogTypes:
   resetPassword: "重設密碼"
   suspendRemoteInstance: "封鎖遠端伺服器"
   unsuspendRemoteInstance: "解除封鎖遠端伺服器"
+  updateRemoteInstanceNote: "更新了遠端伺服器的管理筆記"
   markSensitiveDriveFile: "標記為敏感檔案"
   unmarkSensitiveDriveFile: "撤銷標記為敏感檔案"
   resolveAbuseReport: "解決檢舉"
@@ -2491,6 +2517,8 @@ _reversi:
   opponentHasSettingsChanged: "對手更改了設定"
   allowIrregularRules: "允許異常規則(完全自由)"
   disallowIrregularRules: "不允許異常規則"
+  showBoardLabels: "在棋盤上顯示行、列號"
+  useAvatarAsStone: "用大頭貼當作棋子"
 _offlineScreen:
   title: "離線-無法連接伺服器"
   header: "無法連接伺服器"

From 7565f7bec60f37d5229ebd1cf602fc0a42ecba3b Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 29 Feb 2024 11:47:24 +0000
Subject: [PATCH 049/266] fix(client): use colorizeEmoji when
 unicodeEmojisMap.get

---
 packages/frontend/src/components/MkEmojiPicker.vue         | 3 ++-
 .../frontend/src/components/MkReactionsViewer.reaction.vue | 4 ++--
 packages/frontend/src/scripts/emojilist.ts                 | 7 ++++++-
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 061afa66ac03..e8ad351a2380 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -114,6 +114,7 @@ import {
 	unicodeEmojiCategories as categories,
 	getEmojiName,
 	CustomEmojiFolderTree,
+	getUnicodeEmoji,
 } from '@/scripts/emojilist.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import * as os from '@/os.js';
@@ -382,7 +383,7 @@ function getDef(emoji: string) {
 	if (emoji.includes(':')) {
 		return customEmojisMap.get(emoji.replace(/:/g, ''))!;
 	} else {
-		return unicodeEmojisMap.get(emoji)!;
+		return getUnicodeEmoji(emoji)!;
 	}
 }
 
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index bccee5109de9..acfc37ebb7c2 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -34,7 +34,7 @@ import { i18n } from '@/i18n.js';
 import * as sound from '@/scripts/sound.js';
 import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';
 import { customEmojisMap } from '@/custom-emojis.js';
-import { unicodeEmojisMap } from '@/scripts/emojilist.js';
+import { getUnicodeEmoji, unicodeEmojisMap } from '@/scripts/emojilist.js';
 
 const props = defineProps<{
 	reaction: string;
@@ -52,7 +52,7 @@ const emit = defineEmits<{
 const buttonEl = shallowRef<HTMLElement>();
 
 const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
-const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? unicodeEmojisMap.get(props.reaction));
+const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction));
 
 const canToggle = computed(() => {
 	return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value);
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 2a6120f3bad6..d31e49c53dd0 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -21,7 +21,7 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({
 }));
 
 export const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>(
-	emojilist.map(x => [x.char, x])
+	emojilist.map(x => [x.char, x]),
 );
 
 const _indexByChar = new Map<string, number>();
@@ -39,6 +39,11 @@ for (let i = 0; i < emojilist.length; i++) {
 
 export const emojiCharByCategory = _charGroupByCategory;
 
+export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null {
+	// Colorize it because emojilist.json assumes that
+	return unicodeEmojisMap.get(colorizeEmoji(char)) ?? null;
+}
+
 export function getEmojiName(char: string): string | null {
 	// Colorize it because emojilist.json assumes that
 	const idx = _indexByChar.get(colorizeEmoji(char));

From 26d4c5fd94638e332b93feed8dff749ab5564d6a Mon Sep 17 00:00:00 2001
From: Yuriha <121590760+yuriha-chan@users.noreply.github.com>
Date: Thu, 29 Feb 2024 20:48:02 +0900
Subject: [PATCH 050/266] =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=82=B7=E3=83=A7?=
 =?UTF-8?q?=E3=83=B3=E3=81=AE=E6=9C=80=E5=A4=A7=E6=95=B0=E3=82=92=E3=83=AD?=
 =?UTF-8?q?=E3=83=BC=E3=83=AB=E3=81=94=E3=81=A8=E3=81=AB=E8=A8=AD=E5=AE=9A?=
 =?UTF-8?q?=E5=8F=AF=E8=83=BD=E3=81=AB=E3=81=99=E3=82=8B=20(#13343)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add new role policy: maximum mentions per note

* fix

* Reviewを反映

* fix

* Add ChangeLog

* Update type definitions

* Add E2E test

* CHANGELOG に説明を追加

---------

Co-authored-by: taichan <40626578+tai-cha@users.noreply.github.com>
---
 CHANGELOG.md                                  |   3 +
 locales/index.d.ts                            |   4 +
 locales/ja-JP.yml                             |   1 +
 .../backend/src/core/NoteCreateService.ts     |   4 +
 packages/backend/src/core/RoleService.ts      |   3 +
 .../backend/src/models/json-schema/role.ts    |   4 +
 .../src/server/api/endpoints/notes/create.ts  |  13 +-
 packages/backend/test/e2e/note.ts             | 165 ++++++++++++++++++
 packages/frontend/src/const.ts                |   1 +
 .../frontend/src/pages/admin/roles.editor.vue |  19 ++
 packages/frontend/src/pages/admin/roles.vue   |   7 +
 packages/misskey-js/src/autogen/types.ts      |   1 +
 12 files changed, 223 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa976939d52b..dbbd6ae9f023 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,9 @@
 - Fix: 破損した通知をクライアントに送信しないように
 	* 通知欄が無限にリロードされる問題が改善する可能性があります
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
+- Feat: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
+  * デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
+  * 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
 - Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
 - Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正
 - Fix: エンドポイント`admin/emoji/update`の各種修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 0883749a3358..c1aa163f9822 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -6442,6 +6442,10 @@ export interface Locale extends ILocale {
              * パブリック投稿の許可
              */
             "canPublicNote": string;
+            /**
+             * ノート内の最大メンション数
+             */
+            "mentionMax": string;
             /**
              * サーバー招待コードの発行
              */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index dc91b9f21022..51380e49c523 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1665,6 +1665,7 @@ _role:
     gtlAvailable: "グローバルタイムラインの閲覧"
     ltlAvailable: "ローカルタイムラインの閲覧"
     canPublicNote: "パブリック投稿の許可"
+    mentionMax: "ノート内の最大メンション数"
     canInvite: "サーバー招待コードの発行"
     inviteLimit: "招待コードの作成可能数"
     inviteLimitCycle: "招待コードの発行間隔"
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index b412d5db113e..727787f868a8 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -379,6 +379,10 @@ export class NoteCreateService implements OnApplicationShutdown {
 			}
 		}
 
+		if (mentionedUsers.length > 0 && mentionedUsers.length > (await this.roleService.getUserPolicies(user.id)).mentionLimit) {
+			throw new IdentifiableError('9f466dab-c856-48cd-9e65-ff90ff750580', 'Note contains too many mentions');
+		}
+
 		const note = await this.insertNote(user, data, tags, emojis, mentionedUsers);
 
 		setImmediate('post created', { signal: this.#shutdownController.signal }).then(
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 8312489a78f1..09f309711445 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -35,6 +35,7 @@ export type RolePolicies = {
 	gtlAvailable: boolean;
 	ltlAvailable: boolean;
 	canPublicNote: boolean;
+	mentionLimit: number;
 	canInvite: boolean;
 	inviteLimit: number;
 	inviteLimitCycle: number;
@@ -62,6 +63,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
 	gtlAvailable: true,
 	ltlAvailable: true,
 	canPublicNote: true,
+	mentionLimit: 20,
 	canInvite: false,
 	inviteLimit: 0,
 	inviteLimitCycle: 60 * 24 * 7,
@@ -328,6 +330,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 			gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
 			ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
 			canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
+			mentionLimit: calc('mentionLimit', vs => Math.max(...vs)),
 			canInvite: calc('canInvite', vs => vs.some(v => v === true)),
 			inviteLimit: calc('inviteLimit', vs => Math.max(...vs)),
 			inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)),
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index 9f2b5b17eddf..7c8982a9edd3 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -160,6 +160,10 @@ export const packedRolePoliciesSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		mentionLimit: {
+			type: 'integer',
+			optional: false, nullable: false,
+		},
 		canInvite: {
 			type: 'boolean',
 			optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 27463577fe27..bfb92144393d 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -126,6 +126,12 @@ export const meta = {
 			code: 'CONTAINS_PROHIBITED_WORDS',
 			id: 'aa6e01d3-a85c-669d-758a-76aab43af334',
 		},
+
+		containsTooManyMentions: {
+			message: 'Cannot post because it exceeds the allowed number of mentions.',
+			code: 'CONTAINS_TOO_MANY_MENTIONS',
+			id: '4de0363a-3046-481b-9b0f-feff3e211025',
+		},
 	},
 } as const;
 
@@ -386,9 +392,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			} catch (e) {
 				// TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい
 				if (e instanceof IdentifiableError) {
-					if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') throw new ApiError(meta.errors.containsProhibitedWords);
+					if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
+						throw new ApiError(meta.errors.containsProhibitedWords);
+					} else if (e.id === '9f466dab-c856-48cd-9e65-ff90ff750580') {
+						throw new ApiError(meta.errors.containsTooManyMentions);
+					}
 				}
-
 				throw e;
 			}
 		});
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 23de94889d36..2406204f4101 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -761,6 +761,171 @@ describe('Note', () => {
 
 			assert.strictEqual(note1.status, 400);
 		});
+
+		test('メンションの数が上限を超えるとエラーになる', async () => {
+			const res = await api('admin/roles/create', {
+				name: 'test',
+				description: '',
+				color: null,
+				iconUrl: null,
+				displayOrder: 0,
+				target: 'manual',
+				condFormula: {},
+				isAdministrator: false,
+				isModerator: false,
+				isPublic: false,
+				isExplorable: false,
+				asBadge: false,
+				canEditMembersByModerator: false,
+				policies: {
+					mentionLimit: {
+						useDefault: false,
+						priority: 1,
+						value: 0,
+					},
+				},
+			}, alice);
+
+			assert.strictEqual(res.status, 200);
+
+			await new Promise(x => setTimeout(x, 2));
+
+			const assign = await api('admin/roles/assign', {
+				userId: alice.id,
+				roleId: res.body.id,
+			}, alice);
+
+			assert.strictEqual(assign.status, 204);
+
+			await new Promise(x => setTimeout(x, 2));
+
+			const note = await api('/notes/create', {
+				text: '@bob potentially annoying text',
+			}, alice);
+
+			assert.strictEqual(note.status, 400);
+			assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
+
+			await api('admin/roles/unassign', {
+				userId: alice.id,
+				roleId: res.body.id,
+			});
+
+			await api('admin/roles/delete', {
+				roleId: res.body.id,
+			}, alice);
+		});
+
+		test('ダイレクト投稿もエラーになる', async () => {
+			const res = await api('admin/roles/create', {
+				name: 'test',
+				description: '',
+				color: null,
+				iconUrl: null,
+				displayOrder: 0,
+				target: 'manual',
+				condFormula: {},
+				isAdministrator: false,
+				isModerator: false,
+				isPublic: false,
+				isExplorable: false,
+				asBadge: false,
+				canEditMembersByModerator: false,
+				policies: {
+					mentionLimit: {
+						useDefault: false,
+						priority: 1,
+						value: 0,
+					},
+				},
+			}, alice);
+
+			assert.strictEqual(res.status, 200);
+
+			await new Promise(x => setTimeout(x, 2));
+
+			const assign = await api('admin/roles/assign', {
+				userId: alice.id,
+				roleId: res.body.id,
+			}, alice);
+
+			assert.strictEqual(assign.status, 204);
+
+			await new Promise(x => setTimeout(x, 2));
+
+			const note = await api('/notes/create', {
+				text: 'potentially annoying text',
+				visibility: 'specified',
+				visibleUserIds: [ bob.id ],
+			}, alice);
+
+			assert.strictEqual(note.status, 400);
+			assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
+
+			await api('admin/roles/unassign', {
+				userId: alice.id,
+				roleId: res.body.id,
+			});
+
+			await api('admin/roles/delete', {
+				roleId: res.body.id,
+			}, alice);
+		});
+
+		test('ダイレクトの宛先とメンションが同じ場合は重複してカウントしない', async () => {
+			const res = await api('admin/roles/create', {
+				name: 'test',
+				description: '',
+				color: null,
+				iconUrl: null,
+				displayOrder: 0,
+				target: 'manual',
+				condFormula: {},
+				isAdministrator: false,
+				isModerator: false,
+				isPublic: false,
+				isExplorable: false,
+				asBadge: false,
+				canEditMembersByModerator: false,
+				policies: {
+					mentionLimit: {
+						useDefault: false,
+						priority: 1,
+						value: 1,
+					},
+				},
+			}, alice);
+
+			assert.strictEqual(res.status, 200);
+
+			await new Promise(x => setTimeout(x, 2));
+
+			const assign = await api('admin/roles/assign', {
+				userId: alice.id,
+				roleId: res.body.id,
+			}, alice);
+
+			assert.strictEqual(assign.status, 204);
+
+			await new Promise(x => setTimeout(x, 2));
+
+			const note = await api('/notes/create', {
+				text: '@bob potentially annoying text',
+				visibility: 'specified',
+				visibleUserIds: [ bob.id ],
+			}, alice);
+
+			assert.strictEqual(note.status, 200);
+
+			await api('admin/roles/unassign', {
+				userId: alice.id,
+				roleId: res.body.id,
+			});
+
+			await api('admin/roles/delete', {
+				roleId: res.body.id,
+			}, alice);
+		});
 	});
 
 	describe('notes/delete', () => {
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 0bac4d0b7cad..9e41926a97a1 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -75,6 +75,7 @@ export const ROLE_POLICIES = [
 	'gtlAvailable',
 	'ltlAvailable',
 	'canPublicNote',
+	'mentionLimit',
 	'canInvite',
 	'inviteLimit',
 	'inviteLimitCycle',
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index ad9df35dbf26..eb8a59b34f48 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -160,6 +160,25 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</MkFolder>
 
+			<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
+				<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
+				<template #suffix>
+					<span v-if="role.policies.mentionLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+					<span v-else>{{ role.policies.mentionLimit.value }}</span>
+					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionLimit)"></i></span>
+				</template>
+				<div class="_gaps">
+					<MkSwitch v-model="role.policies.mentionLimit.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
+					</MkSwitch>
+					<MkInput v-model="role.policies.mentionLimit.value" :disabled="role.policies.mentionLimit.useDefault" type="number" :readonly="readonly">
+					</MkInput>
+					<MkRange v-model="role.policies.mentionLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+						<template #label>{{ i18n.ts._role.priority }}</template>
+					</MkRange>
+				</div>
+			</MkFolder>
+
 			<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
 				<template #label>{{ i18n.ts._role._options.canInvite }}</template>
 				<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 496cb096645f..9753d9f6cb7a 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -48,6 +48,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</MkSwitch>
 						</MkFolder>
 
+						<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
+							<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
+							<template #suffix>{{ policies.mentionLimit }}</template>
+							<MkInput v-model="policies.mentionLimit" type="number">
+							</MkInput>
+						</MkFolder>
+
 						<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
 							<template #label>{{ i18n.ts._role._options.canInvite }}</template>
 							<template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template>
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index a89e18ea76ac..227295fbb80d 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4652,6 +4652,7 @@ export type components = {
       gtlAvailable: boolean;
       ltlAvailable: boolean;
       canPublicNote: boolean;
+      mentionLimit: number;
       canInvite: boolean;
       inviteLimit: number;
       inviteLimitCycle: number;

From 01f55a9d59c1fe54721c79f4d975bc2fff8afb41 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 29 Feb 2024 20:48:48 +0900
Subject: [PATCH 051/266] Update CHANGELOG.md

---
 CHANGELOG.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbbd6ae9f023..800c646c676e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,9 @@
 ## 202x.x.x (unreleased)
 
 ### General
+- Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
+  * デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
+  * 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
 - Enhance: 通知がミュート、凍結を考慮するようになりました
 - Enhance: サーバーごとにモデレーションノートを残せるように
 - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
@@ -37,9 +40,6 @@
 - Fix: 破損した通知をクライアントに送信しないように
 	* 通知欄が無限にリロードされる問題が改善する可能性があります
 - Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
-- Feat: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
-  * デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
-  * 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
 - Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
 - Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正
 - Fix: エンドポイント`admin/emoji/update`の各種修正

From bc30dc6bffa92946342c18b51378901c90e5f23f Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 29 Feb 2024 11:49:40 +0000
Subject: [PATCH 052/266] refactor: remove export of unicodeEmojisMap

---
 packages/frontend/src/components/MkEmojiPicker.vue              | 1 -
 packages/frontend/src/components/MkReactionsViewer.reaction.vue | 2 +-
 packages/frontend/src/scripts/emojilist.ts                      | 2 +-
 3 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index e8ad351a2380..06243e5b04e9 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -108,7 +108,6 @@ import * as Misskey from 'misskey-js';
 import XSection from '@/components/MkEmojiPicker.section.vue';
 import {
 	emojilist,
-	unicodeEmojisMap,
 	emojiCharByCategory,
 	UnicodeEmojiDef,
 	unicodeEmojiCategories as categories,
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index acfc37ebb7c2..c41811febeb8 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -34,7 +34,7 @@ import { i18n } from '@/i18n.js';
 import * as sound from '@/scripts/sound.js';
 import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';
 import { customEmojisMap } from '@/custom-emojis.js';
-import { getUnicodeEmoji, unicodeEmojisMap } from '@/scripts/emojilist.js';
+import { getUnicodeEmoji } from '@/scripts/emojilist.js';
 
 const props = defineProps<{
 	reaction: string;
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index d31e49c53dd0..f2fbeae0ed08 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -20,7 +20,7 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({
 	category: unicodeEmojiCategories[x[2]],
 }));
 
-export const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>(
+const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>(
 	emojilist.map(x => [x.char, x]),
 );
 

From a74406677c0cae6df1a9325680a236f5f4245cbc Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 12:03:33 +0900
Subject: [PATCH 053/266] fix packedRoleCondFormulaValueAssignedRoleSchema

---
 packages/backend/src/models/json-schema/role.ts | 3 +++
 packages/misskey-js/src/autogen/types.ts        | 1 +
 2 files changed, 4 insertions(+)

diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index 7c8982a9edd3..c7702505039d 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -60,6 +60,9 @@ export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = {
 export const packedRoleCondFormulaValueAssignedRoleSchema = {
 	type: 'object',
 	properties: {
+		id: {
+			type: 'string', optional: false,
+		},
 		type: {
 			type: 'string',
 			nullable: false, optional: false,
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 227295fbb80d..b1e6a194f9df 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4583,6 +4583,7 @@ export type components = {
       type: 'isLocal' | 'isRemote';
     };
     RoleCondFormulaValueAssignedRole: {
+      id: string;
       /** @enum {string} */
       type: 'roleAssignedTo';
       /**

From 59f80c08ea5e257387969f4477c5dcbfc03f96c2 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 12:07:25 +0900
Subject: [PATCH 054/266] New Crowdin updates (#13478)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)
---
 locales/en-US.yml |  4 ++++
 locales/th-TH.yml | 21 ++++++++++++---------
 locales/zh-CN.yml |  2 ++
 3 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 0ed30bc3de0f..d00f23632cc1 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1160,6 +1160,7 @@ showRenotes: "Show renotes"
 edited: "Edited"
 notificationRecieveConfig: "Notification Settings"
 mutualFollow: "Mutual follow"
+followingOrFollower: "Following or follower"
 fileAttachedOnly: "Only notes with files"
 showRepliesToOthersInTimeline: "Show replies to others in timeline"
 hideRepliesToOthersInTimeline: "Hide replies to others from timeline"
@@ -1654,6 +1655,7 @@ _role:
     gtlAvailable: "Can view the global timeline"
     ltlAvailable: "Can view the local timeline"
     canPublicNote: "Can send public notes"
+    mentionMax: "Maximum number of mentions in a note"
     canInvite: "Can create instance invite codes"
     inviteLimit: "Invite limit"
     inviteLimitCycle: "Invite limit cooldown"
@@ -1677,6 +1679,7 @@ _role:
     canUseTranslator: "Translator usage"
     avatarDecorationLimit: "Maximum number of avatar decorations that can be applied"
   _condition:
+    roleAssignedTo: "Assigned to manual roles"
     isLocal: "Local user"
     isRemote: "Remote user"
     createdLessThan: "Less than X has passed since account creation"
@@ -2297,6 +2300,7 @@ _notification:
   reactedBySomeUsers: "{n} users reacted"
   renotedBySomeUsers: "Renote from {n} users"
   followedBySomeUsers: "Followed by {n} users"
+  flushNotification: "Clear notifications"
   _types:
     all: "All"
     note: "New notes"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index 56021cdbc99d..c0e79d5e1649 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -162,11 +162,11 @@ emojiUrl: "URL ของเอโมจิ"
 addEmoji: "แทรกเอโมจิ"
 settingGuide: "การตั้งค่าที่แนะนำ"
 cacheRemoteFiles: "แคชไฟล์ระยะไกล"
-cacheRemoteFilesDescription: "เมื่อปิดใช้งานการตั้งค่านี้ ไฟล์ระยะไกลนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกล แต่กรณีการปิดใช้งานนี้จะช่วยลดปริมาณการใช้พื้นที่จัดเก็บข้อมูล แต่เพิ่มปริมาณการใช้งาน เพราะเนื่องจากจะไม่มีการสร้างภาพขนาดย่อ"
+cacheRemoteFilesDescription: "หากเปิดใช้งาน ไฟล์ระยะไกลจะถูกแคชไว้ ทำให้แสดงภาพเร็วขึ้น แต่ก็ใช้พื้นที่เก็บข้อมูลของเซิร์ฟเวอร์มากขึ้นเช่นกัน สำหรับขีดจำกัดที่ผู้ใช้ระยะไกลถูกแคชไว้จะขึ้นอยู่กับความจุไดรฟ์ตามบทบาทของเขา เมื่อเกินแล้วไฟล์เก่าจะถูกลบออกและเก็บเป็นลิงก์แทน หากปิดใช้งาน ไฟล์ระยะไกลจะถูกเก็บเป็นลิงก์ตั้งแต่ต้น เราแนะนำให้ตั้งค่า proxyRemoteFiles ใน default.yml เป็น true เพื่อสร้างธัมบ์เนลและปกป้องความเป็นส่วนตัวของผู้ใช้"
 youCanCleanRemoteFilesCache: "คุณสามารถล้างแคชได้โดยคลิกที่ปุ่ม 🗑️ ในมุมมองการจัดการไฟล์"
-cacheRemoteSensitiveFiles: "แคชไฟล์ระยะไกลที่มีเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
+cacheRemoteSensitiveFiles: "แคชไฟล์ระยะไกลที่มีเนื้อหาละเอียดอ่อน"
 cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานการตั้งค่านี้ ไฟล์ระยะไกลที่มีเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช"
-flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท"
+flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอต"
 flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ และยังสามารถปรับเปลี่ยนระบบภายในของ Misskey เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท"
 flagAsCat: "เมี้ยววววววววววววววว!!!!!!!!!!!"
 flagAsCatDescription: "เหมียวเหมียวเมี้ยว??"
@@ -678,7 +678,7 @@ regenerateLoginToken: "สร้างโทเค็นการเข้าส
 regenerateLoginTokenDescription: "สร้างโทเค็นใหม่ที่ใช้ภายในระหว่างการเข้าสู่ระบบ โดยตามหลักปกติแล้วการดำเนินการนี้ไม่จำเป็น หากสร้างใหม่ อุปกรณ์ทั้งหมดจะถูกออกจากระบบนะ"
 theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาเอโมจิที่กำหนดเอง"
 setMultipleBySeparatingWithSpace: "คั่นหลายรายการด้วยช่องว่าง"
-fileIdOrUrl: "ไฟล์ ID หรือ URL"
+fileIdOrUrl: "ID ของไฟล์ หรือ URL"
 behavior: "พฤติกรรม"
 sample: "ตัวอย่าง"
 abuseReports: "รายงาน"
@@ -1160,6 +1160,7 @@ showRenotes: "แสดงรีโน้ต"
 edited: "แก้ไขแล้ว"
 notificationRecieveConfig: "การตั้งค่าการแจ้งเตือน"
 mutualFollow: "ติดตามซึ่งกันและกัน"
+followingOrFollower: "กำลังติดตามหรือผู้ติดตาม"
 fileAttachedOnly: "เฉพาะโน้ตที่มีไฟล์เท่านั้น"
 showRepliesToOthersInTimeline: "แสดงการตอบกลับผู้อื่นลงในไทม์ไลน์"
 hideRepliesToOthersInTimeline: "ไม่แสดงการตอบกลับผู้อื่นลงในไทม์ไลน์"
@@ -1216,7 +1217,7 @@ ranking: "อันดับ"
 lastNDays: "ล่าสุด {n} วันที่แล้ว"
 backToTitle: "กลับไปหน้าไตเติ้ล"
 hemisphere: "พื้นที่ที่อาศัยอยู่"
-withSensitive: "แสดงโน้ตที่มีไฟล์ที่ระบุว่ามีเนื้อหาละเอียดอ่อน"
+withSensitive: "แสดงโน้ตที่มีไฟล์เนื้อหาละเอียดอ่อน"
 userSaysSomethingSensitive: "โพสต์ที่มีไฟล์เนื้อหาละเอียดอ่อนของ {name}"
 enableHorizontalSwipe: "ปัดเพื่อสลับแท็บ"
 loading: "กำลังโหลด"
@@ -1654,6 +1655,7 @@ _role:
     gtlAvailable: "การดูไทม์ไลน์ทั่วโลก"
     ltlAvailable: "การดูไทม์ไลน์ในท้องถิ่น"
     canPublicNote: "สามารถโพสต์แบบสาธารณะ"
+    mentionMax: "จำนวนการกล่าวถึงสูงสุดต่อโน้ต"
     canInvite: "สร้างรหัสเชิญอินสแตนซ์"
     inviteLimit: "จำกัดการเชิญ"
     inviteLimitCycle: "คูลดาวน์ในการเชิญ"
@@ -1782,12 +1784,12 @@ _aboutMisskey:
   thisIsModifiedVersion: "{name} ใช้ Misskey เวอร์ชันดัดแปลง"
   translation: "แปลภาษา Misskey"
   donate: "บริจาคให้กับ Misskey"
-  morePatrons: " ขอบคุณทุกท่านที่ร่วมกันช่วยเหลือตลอดมานะคะ 🥰"
+  morePatrons: "และอีกหลายท่านที่ไม่ได้เอ่ยนาม ขอบคุณที่ร่วมช่วยเหลือตลอดมานะคะ 🥰"
   patrons: "ผู้อุปถัมภ์"
   projectMembers: "สมาชิกในโครงการ"
 _displayOfSensitiveMedia:
-  respect: "ซ่อนสื่อที่ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
-  ignore: "แสดงสื่อที่ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
+  respect: "ซ่อนสื่อที่มีเนื้อหาละเอียดอ่อน"
+  ignore: "แสดงสื่อที่มีเนื้อหาละเอียดอ่อน"
   force: "ซ่อนสื่อทั้งหมด"
 _instanceTicker:
   none: "ไม่ต้องแสดง"
@@ -2251,7 +2253,7 @@ _pages:
   summary: "สรุปเพจ"
   alignCenter: "เซ็นเตอร์"
   hideTitleWhenPinned: "ซ่อนชื่อหน้าเพจเมื่อปักหมุดไว้ที่โปรไฟล์"
-  font: "ตัวอักษร"
+  font: "แบบอักษร"
   fontSerif: "Serif"
   fontSansSerif: "Sans Serif"
   eyeCatchingImageSet: "ตั้งค่าภาพขนาดย่อ"
@@ -2298,6 +2300,7 @@ _notification:
   reactedBySomeUsers: "ถูกรีแอคชั่นโดยผู้ใช้ {n} ราย"
   renotedBySomeUsers: "รีโน้ตจากผู้ใช้ {n} ราย"
   followedBySomeUsers: "มีผู้ติดตาม {n} ราย"
+  flushNotification: "ล้างประวัติการแจ้งเตือน"
   _types:
     all: "ทั้งหมด"
     note: "โน้ตใหม่"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 3f54444d9d11..17ad6e715061 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -1653,6 +1653,7 @@ _role:
     gtlAvailable: "查看全局时间线"
     ltlAvailable: "查看本地时间线"
     canPublicNote: "允许公开发帖"
+    mentionMax: "帖子内最多提及数"
     canInvite: "发放服务器邀请码"
     inviteLimit: "可生成邀请码的数量"
     inviteLimitCycle: "邀请码的发行间隔"
@@ -2297,6 +2298,7 @@ _notification:
   reactedBySomeUsers: "{n} 人回应了"
   renotedBySomeUsers: "{n} 人转发了"
   followedBySomeUsers: "被 {n} 人关注"
+  flushNotification: "重置通知历史"
   _types:
     all: "全部"
     note: "用户的新帖子"

From b55b77c8ae1f09320a50a10c3ee4769369a5dcd3 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 13:52:23 +0900
Subject: [PATCH 055/266] update pnpm

---
 package.json | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/package.json b/package.json
index ad77a08d18db..68814f74b863 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
 		"type": "git",
 		"url": "https://github.com/misskey-dev/misskey.git"
 	},
-	"packageManager": "pnpm@8.15.1",
+	"packageManager": "pnpm@8.15.4",
 	"workspaces": [
 		"packages/frontend",
 		"packages/backend",
@@ -59,11 +59,11 @@
 		"typescript": "5.3.3"
 	},
 	"devDependencies": {
-		"@typescript-eslint/eslint-plugin": "6.18.1",
-		"@typescript-eslint/parser": "6.18.1",
+		"@typescript-eslint/eslint-plugin": "7.1.0",
+		"@typescript-eslint/parser": "7.1.0",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.6",
-		"eslint": "8.56.0",
+		"eslint": "8.57.0",
 		"ncp": "2.0.0",
 		"start-server-and-test": "2.0.3"
 	},

From 033d71ee28edbb069e3692ceaedd34d99757f1aa Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 13:52:39 +0900
Subject: [PATCH 056/266] update deps

---
 packages/backend/package.json             |   6 +-
 packages/frontend/package.json            |   6 +-
 packages/misskey-bubble-game/package.json |   6 +-
 packages/misskey-js/package.json          |   6 +-
 packages/misskey-reversi/package.json     |   6 +-
 packages/sw/package.json                  |   4 +-
 pnpm-lock.yaml                            | 267 ++++++++++++----------
 7 files changed, 158 insertions(+), 143 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 9b38fd6228b6..d2aaf369400b 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -226,11 +226,11 @@
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.18.1",
-		"@typescript-eslint/parser": "6.18.1",
+		"@typescript-eslint/eslint-plugin": "7.1.0",
+		"@typescript-eslint/parser": "7.1.0",
 		"aws-sdk-client-mock": "3.0.1",
 		"cross-env": "7.0.3",
-		"eslint": "8.56.0",
+		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
 		"execa": "8.0.1",
 		"fkill": "^9.0.0",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index b4b486c97994..09e0d922556c 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -108,14 +108,14 @@
 		"@types/tinycolor2": "1.4.6",
 		"@types/uuid": "9.0.8",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.18.1",
-		"@typescript-eslint/parser": "6.18.1",
+		"@typescript-eslint/eslint-plugin": "7.1.0",
+		"@typescript-eslint/parser": "7.1.0",
 		"@vitest/coverage-v8": "0.34.6",
 		"@vue/runtime-core": "3.4.21",
 		"acorn": "8.11.3",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.6",
-		"eslint": "8.56.0",
+		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
 		"eslint-plugin-vue": "9.22.0",
 		"fast-glob": "3.3.2",
diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json
index 9de7ba005f66..ddc4c2134bcf 100644
--- a/packages/misskey-bubble-game/package.json
+++ b/packages/misskey-bubble-game/package.json
@@ -29,9 +29,9 @@
 		"@types/matter-js": "0.19.6",
 		"@types/node": "20.11.5",
 		"@types/seedrandom": "3.0.8",
-		"@typescript-eslint/eslint-plugin": "6.18.1",
-		"@typescript-eslint/parser": "6.18.1",
-		"eslint": "8.56.0",
+		"@typescript-eslint/eslint-plugin": "7.1.0",
+		"@typescript-eslint/parser": "7.1.0",
+		"eslint": "8.57.0",
 		"nodemon": "3.0.2",
 		"typescript": "5.3.3"
 	},
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index d45c24a01773..1069e85b23b7 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -40,9 +40,9 @@
 		"@swc/jest": "0.2.31",
 		"@types/jest": "29.5.12",
 		"@types/node": "20.11.22",
-		"@typescript-eslint/eslint-plugin": "6.18.1",
-		"@typescript-eslint/parser": "6.18.1",
-		"eslint": "8.56.0",
+		"@typescript-eslint/eslint-plugin": "7.1.0",
+		"@typescript-eslint/parser": "7.1.0",
+		"eslint": "8.57.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json
index 52d497d3f8dd..7bfc890fef85 100644
--- a/packages/misskey-reversi/package.json
+++ b/packages/misskey-reversi/package.json
@@ -27,9 +27,9 @@
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@types/node": "20.11.5",
-		"@typescript-eslint/eslint-plugin": "6.18.1",
-		"@typescript-eslint/parser": "6.18.1",
-		"eslint": "8.56.0",
+		"@typescript-eslint/eslint-plugin": "7.1.0",
+		"@typescript-eslint/parser": "7.1.0",
+		"eslint": "8.57.0",
 		"nodemon": "3.0.2",
 		"typescript": "5.3.3"
 	},
diff --git a/packages/sw/package.json b/packages/sw/package.json
index de38a3d5fd9a..bac0cc1ff501 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -15,9 +15,9 @@
 	},
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@typescript-eslint/parser": "6.18.1",
+		"@typescript-eslint/parser": "7.1.0",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
-		"eslint": "8.56.0",
+		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
 		"nodemon": "3.1.0",
 		"typescript": "5.3.3"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 26add9a11e24..dce81e95f708 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -45,11 +45,11 @@ importers:
         version: 4.4.0
     devDependencies:
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.18.1
-        version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.18.1
-        version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
@@ -57,8 +57,8 @@ importers:
         specifier: 13.6.6
         version: 13.6.6
       eslint:
-        specifier: 8.56.0
-        version: 8.56.0
+        specifier: 8.57.0
+        version: 8.57.0
       ncp:
         specifier: 2.0.0
         version: 2.0.0
@@ -510,7 +510,7 @@ importers:
         version: 29.7.0
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@nestjs/platform-express':
         specifier: 10.3.3
         version: 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
@@ -635,11 +635,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.18.1
-        version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.18.1
-        version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       aws-sdk-client-mock:
         specifier: 3.0.1
         version: 3.0.1
@@ -647,11 +647,11 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       eslint:
-        specifier: 8.56.0
-        version: 8.56.0
+        specifier: 8.57.0
+        version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)
+        version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -850,7 +850,7 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@misskey-dev/summaly':
         specifier: 5.0.3
         version: 5.0.3
@@ -945,11 +945,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.18.1
-        version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.18.1
-        version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
@@ -966,14 +966,14 @@ importers:
         specifier: 13.6.6
         version: 13.6.6
       eslint:
-        specifier: 8.56.0
-        version: 8.56.0
+        specifier: 8.57.0
+        version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)
+        version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
       eslint-plugin-vue:
         specifier: 9.22.0
-        version: 9.22.0(eslint@8.56.0)
+        version: 9.22.0(eslint@8.57.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
@@ -1027,7 +1027,7 @@ importers:
         version: 1.8.27
       vue-eslint-parser:
         specifier: 9.4.2
-        version: 9.4.2(eslint@8.56.0)
+        version: 9.4.2(eslint@8.57.0)
       vue-tsc:
         specifier: 1.8.27
         version: 1.8.27(typescript@5.3.3)
@@ -1052,7 +1052,7 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@types/matter-js':
         specifier: 0.19.6
         version: 0.19.6
@@ -1063,14 +1063,14 @@ importers:
         specifier: 3.0.8
         version: 3.0.8
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.18.1
-        version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.18.1
-        version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       eslint:
-        specifier: 8.56.0
-        version: 8.56.0
+        specifier: 8.57.0
+        version: 8.57.0
       nodemon:
         specifier: 3.0.2
         version: 3.0.2
@@ -1098,7 +1098,7 @@ importers:
         version: 7.39.1(@types/node@20.11.22)
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@swc/jest':
         specifier: 0.2.31
         version: 0.2.31(@swc/core@1.3.105)
@@ -1109,14 +1109,14 @@ importers:
         specifier: 20.11.22
         version: 20.11.22
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.18.1
-        version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.18.1
-        version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       eslint:
-        specifier: 8.56.0
-        version: 8.56.0
+        specifier: 8.57.0
+        version: 8.57.0
       jest:
         specifier: 29.7.0
         version: 29.7.0(@types/node@20.11.22)
@@ -1192,19 +1192,19 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@types/node':
         specifier: 20.11.5
         version: 20.11.5
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.18.1
-        version: 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.18.1
-        version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       eslint:
-        specifier: 8.56.0
-        version: 8.56.0
+        specifier: 8.57.0
+        version: 8.57.0
       nodemon:
         specifier: 3.0.2
         version: 3.0.2
@@ -1226,19 +1226,19 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@typescript-eslint/parser':
-        specifier: 6.18.1
-        version: 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+        specifier: 7.1.0
+        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
       eslint:
-        specifier: 8.56.0
-        version: 8.56.0
+        specifier: 8.57.0
+        version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)
+        version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
       nodemon:
         specifier: 3.1.0
         version: 3.1.0
@@ -3849,13 +3849,13 @@ packages:
       eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0):
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0):
     resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
     dependencies:
-      eslint: 8.56.0
+      eslint: 8.57.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -3886,8 +3886,8 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /@eslint/js@8.56.0:
-    resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
+  /@eslint/js@8.57.0:
+    resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
@@ -4082,6 +4082,17 @@ packages:
       - supports-color
     dev: true
 
+  /@humanwhocodes/config-array@0.11.14:
+    resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
+    engines: {node: '>=10.10.0'}
+    dependencies:
+      '@humanwhocodes/object-schema': 2.0.2
+      debug: 4.3.4(supports-color@8.1.1)
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@humanwhocodes/module-importer@1.0.1:
     resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
     engines: {node: '>=12.22'}
@@ -4096,6 +4107,10 @@ packages:
     resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
     dev: true
 
+  /@humanwhocodes/object-schema@2.0.2:
+    resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
+    dev: true
+
   /@img/sharp-darwin-arm64@0.33.2:
     resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==}
     engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
@@ -4721,7 +4736,7 @@ packages:
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)
     dev: true
 
-  /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.18.1)(@typescript-eslint/parser@6.18.1)(eslint-plugin-import@2.29.1)(eslint@8.56.0):
+  /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
     resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==}
     peerDependencies:
       '@typescript-eslint/eslint-plugin': '>= 6'
@@ -4729,10 +4744,10 @@ packages:
       eslint: '>= 3'
       eslint-plugin-import: '>= 2'
     dependencies:
-      '@typescript-eslint/eslint-plugin': 6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
-      eslint: 8.56.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)
+      '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      eslint: 8.57.0
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
     dev: true
 
   /@misskey-dev/sharp-read-bmp@1.2.0:
@@ -7924,29 +7939,29 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.18.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==}
+  /@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
-      '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
-      eslint: ^7.0.0 || ^8.0.0
+      '@typescript-eslint/parser': ^7.0.0
+      eslint: ^8.56.0
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
-      '@typescript-eslint/scope-manager': 6.18.1
-      '@typescript-eslint/type-utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 6.18.1
+      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/scope-manager': 7.1.0
+      '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.56.0
+      eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
-      semver: 7.5.4
+      semver: 7.6.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
       typescript: 5.3.3
     transitivePeerDependencies:
@@ -7974,22 +7989,22 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.18.1(eslint@8.56.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==}
+  /@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
+      eslint: ^8.56.0
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.18.1
-      '@typescript-eslint/types': 6.18.1
-      '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 6.18.1
+      '@typescript-eslint/scope-manager': 7.1.0
+      '@typescript-eslint/types': 7.1.0
+      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.56.0
+      eslint: 8.57.0
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
@@ -8003,12 +8018,12 @@ packages:
       '@typescript-eslint/visitor-keys': 6.11.0
     dev: true
 
-  /@typescript-eslint/scope-manager@6.18.1:
-    resolution: {integrity: sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==}
+  /@typescript-eslint/scope-manager@7.1.0:
+    resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.18.1
-      '@typescript-eslint/visitor-keys': 6.18.1
+      '@typescript-eslint/types': 7.1.0
+      '@typescript-eslint/visitor-keys': 7.1.0
     dev: true
 
   /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3):
@@ -8031,20 +8046,20 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/type-utils@6.18.1(eslint@8.56.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==}
+  /@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
+      eslint: ^8.56.0
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.56.0
+      eslint: 8.57.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
       typescript: 5.3.3
     transitivePeerDependencies:
@@ -8056,8 +8071,8 @@ packages:
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
-  /@typescript-eslint/types@6.18.1:
-    resolution: {integrity: sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==}
+  /@typescript-eslint/types@7.1.0:
+    resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
@@ -8082,8 +8097,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.18.1(typescript@5.3.3):
-    resolution: {integrity: sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==}
+  /@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3):
+    resolution: {integrity: sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       typescript: '*'
@@ -8091,13 +8106,13 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/types': 6.18.1
-      '@typescript-eslint/visitor-keys': 6.18.1
+      '@typescript-eslint/types': 7.1.0
+      '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.3
-      semver: 7.5.4
+      semver: 7.6.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
       typescript: 5.3.3
     transitivePeerDependencies:
@@ -8123,20 +8138,20 @@ packages:
       - typescript
     dev: true
 
-  /@typescript-eslint/utils@6.18.1(eslint@8.56.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==}
+  /@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
+      eslint: ^8.56.0
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.8
-      '@typescript-eslint/scope-manager': 6.18.1
-      '@typescript-eslint/types': 6.18.1
-      '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3)
-      eslint: 8.56.0
-      semver: 7.5.4
+      '@typescript-eslint/scope-manager': 7.1.0
+      '@typescript-eslint/types': 7.1.0
+      '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
+      eslint: 8.57.0
+      semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -8150,11 +8165,11 @@ packages:
       eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@typescript-eslint/visitor-keys@6.18.1:
-    resolution: {integrity: sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==}
+  /@typescript-eslint/visitor-keys@7.1.0:
+    resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.18.1
+      '@typescript-eslint/types': 7.1.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -11035,7 +11050,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.18.1)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11056,9 +11071,9 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       debug: 3.2.7(supports-color@8.1.1)
-      eslint: 8.56.0
+      eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
@@ -11099,7 +11114,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.18.1)(eslint@8.56.0):
+  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0):
     resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11109,16 +11124,16 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.18.1(eslint@8.56.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
       array.prototype.flatmap: 1.3.2
       debug: 3.2.7(supports-color@8.1.1)
       doctrine: 2.1.0
-      eslint: 8.56.0
+      eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.18.1)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -11134,19 +11149,19 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-vue@9.22.0(eslint@8.56.0):
+  /eslint-plugin-vue@9.22.0(eslint@8.57.0):
     resolution: {integrity: sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
-      eslint: 8.56.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+      eslint: 8.57.0
       natural-compare: 1.4.0
       nth-check: 2.1.1
       postcss-selector-parser: 6.0.15
       semver: 7.6.0
-      vue-eslint-parser: 9.4.2(eslint@8.56.0)
+      vue-eslint-parser: 9.4.2(eslint@8.57.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -11216,16 +11231,16 @@ packages:
       - supports-color
     dev: true
 
-  /eslint@8.56.0:
-    resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==}
+  /eslint@8.57.0:
+    resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       '@eslint-community/regexpp': 4.6.2
       '@eslint/eslintrc': 2.1.4
-      '@eslint/js': 8.56.0
-      '@humanwhocodes/config-array': 0.11.13
+      '@eslint/js': 8.57.0
+      '@humanwhocodes/config-array': 0.11.14
       '@humanwhocodes/module-importer': 1.0.1
       '@nodelib/fs.walk': 1.2.8
       '@ungap/structured-clone': 1.2.0
@@ -19468,14 +19483,14 @@ packages:
       vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.21)
     dev: true
 
-  /vue-eslint-parser@9.4.2(eslint@8.56.0):
+  /vue-eslint-parser@9.4.2(eslint@8.57.0):
     resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: '>=6.0.0'
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.56.0
+      eslint: 8.57.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1

From 14a3af679df09aa7f19150ffd46ecfb49e5ca082 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 14:06:34 +0900
Subject: [PATCH 057/266] update deps

---
 packages/frontend/package.json |  8 +++---
 pnpm-lock.yaml                 | 50 +++++++++++++++++-----------------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 09e0d922556c..682def8e8d7e 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -33,7 +33,7 @@
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
 		"buraha": "0.0.1",
-		"canvas-confetti": "1.6.1",
+		"canvas-confetti": "1.9.2",
 		"chart.js": "4.4.2",
 		"chartjs-adapter-date-fns": "3.0.0",
 		"chartjs-chart-matrix": "2.0.1",
@@ -60,17 +60,17 @@
 		"rollup": "4.12.0",
 		"sanitize-html": "2.12.1",
 		"sass": "1.71.1",
-		"shiki": "1.0.0-beta.3",
+		"shiki": "1.1.7",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
-		"three": "0.161.0",
+		"three": "0.162.0",
 		"throttle-debounce": "5.0.0",
 		"tinycolor2": "1.6.0",
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
 		"typescript": "5.3.3",
 		"uuid": "9.0.1",
-		"v-code-diff": "1.7.2",
+		"v-code-diff": "1.9.0",
 		"vite": "5.1.4",
 		"vue": "3.4.21",
 		"vuedraggable": "next"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dce81e95f708..3ff570c10869 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -725,8 +725,8 @@ importers:
         specifier: 0.0.1
         version: 0.0.1
       canvas-confetti:
-        specifier: 1.6.1
-        version: 1.6.1
+        specifier: 1.9.2
+        version: 1.9.2
       chart.js:
         specifier: 4.4.2
         version: 4.4.2
@@ -806,8 +806,8 @@ importers:
         specifier: 1.71.1
         version: 1.71.1
       shiki:
-        specifier: 1.0.0-beta.3
-        version: 1.0.0-beta.3
+        specifier: 1.1.7
+        version: 1.1.7
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -815,8 +815,8 @@ importers:
         specifier: 3.1.0
         version: 3.1.0
       three:
-        specifier: 0.161.0
-        version: 0.161.0
+        specifier: 0.162.0
+        version: 0.162.0
       throttle-debounce:
         specifier: 5.0.0
         version: 5.0.0
@@ -836,8 +836,8 @@ importers:
         specifier: 9.0.1
         version: 9.0.1
       v-code-diff:
-        specifier: 1.7.2
-        version: 1.7.2(vue@3.4.21)
+        specifier: 1.9.0
+        version: 1.9.0(vue@3.4.21)
       vite:
         specifier: 5.1.4
         version: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
@@ -5324,8 +5324,8 @@ packages:
       string-argv: 0.3.1
     dev: true
 
-  /@shikijs/core@1.0.0-beta.3:
-    resolution: {integrity: sha512-SCwPom2Wn8XxNlEeqdzycU93SKgzYeVsedjqDsgZaz4XiiPpZUzlHt2NAEQTwTnPcHNZapZ6vbkwJ8P11ggL3Q==}
+  /@shikijs/core@1.1.7:
+    resolution: {integrity: sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==}
     dev: false
 
   /@sideway/address@4.1.4:
@@ -9466,8 +9466,8 @@ packages:
     resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
     dev: false
 
-  /canvas-confetti@1.6.1:
-    resolution: {integrity: sha512-CgGR5DL9+dkne4AwcpvWQc0LIQq43yDIxlwdZcyrq3yklricNfuPHoOSoM6Ya7yCQ+sXmZ2iNV2feiKjVG8C1g==}
+  /canvas-confetti@1.9.2:
+    resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==}
     dev: false
 
   /caseless@0.12.0:
@@ -12493,8 +12493,8 @@ packages:
     resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
     dev: false
 
-  /highlight.js@11.8.0:
-    resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
+  /highlight.js@11.9.0:
+    resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==}
     engines: {node: '>=12.0.0'}
     dev: false
 
@@ -17662,10 +17662,10 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
-  /shiki@1.0.0-beta.3:
-    resolution: {integrity: sha512-z7cHTNSSvwGx2DfeLwjSNLo+HcVxifgNIzLm6Ye52eXcIwNHXT0wHbhy7FDOKSKveuEHBwt9opfj3Hoc8LE1Yg==}
+  /shiki@1.1.7:
+    resolution: {integrity: sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==}
     dependencies:
-      '@shikijs/core': 1.0.0-beta.3
+      '@shikijs/core': 1.1.7
     dev: false
 
   /side-channel@1.0.4:
@@ -18500,8 +18500,8 @@ packages:
       real-require: 0.2.0
     dev: false
 
-  /three@0.161.0:
-    resolution: {integrity: sha512-LC28VFtjbOyEu5b93K0bNRLw1rQlMJ85lilKsYj6dgTu+7i17W+JCCEbvrpmNHF1F3NAUqDSWq50UD7w9H2xQw==}
+  /three@0.162.0:
+    resolution: {integrity: sha512-xfCYj4RnlozReCmUd+XQzj6/5OjDNHBy5nT6rVwrOKGENAvpXe2z1jL+DZYaMu4/9pNsjH/4Os/VvS9IrH7IOQ==}
     dev: false
 
   /throttle-debounce@5.0.0:
@@ -19180,8 +19180,8 @@ packages:
     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
     hasBin: true
 
-  /v-code-diff@1.7.2(vue@3.4.21):
-    resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==}
+  /v-code-diff@1.9.0(vue@3.4.21):
+    resolution: {integrity: sha512-alg6krCxFvwTob/rJq+3LzjdIbLb/ni8tS8YmBbI0wckOkbJuN1cShFJ6XEkm82tMgpv5NYEeWLEWhggeV7BDg==}
     requiresBuild: true
     peerDependencies:
       '@vue/composition-api': ^1.4.9
@@ -19192,9 +19192,9 @@ packages:
     dependencies:
       diff: 5.1.0
       diff-match-patch: 1.0.5
-      highlight.js: 11.8.0
+      highlight.js: 11.9.0
       vue: 3.4.21(typescript@5.3.3)
-      vue-demi: 0.13.11(vue@3.4.21)
+      vue-demi: 0.14.7(vue@3.4.21)
     dev: false
 
   /v8-to-istanbul@9.2.0:
@@ -19449,8 +19449,8 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-demi@0.13.11(vue@3.4.21):
-    resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
+  /vue-demi@0.14.7(vue@3.4.21):
+    resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
     engines: {node: '>=12'}
     hasBin: true
     requiresBuild: true

From 2f31606effaa4836e8b1bcc0ab1cceab82fa262f Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 14:16:44 +0900
Subject: [PATCH 058/266] update deps

---
 packages/backend/package.json |  8 ++---
 pnpm-lock.yaml                | 58 ++++++++++++++++-------------------
 2 files changed, 31 insertions(+), 35 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index d2aaf369400b..8680610441b3 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -81,9 +81,9 @@
 		"@fastify/view": "8.2.0",
 		"@misskey-dev/sharp-read-bmp": "1.2.0",
 		"@misskey-dev/summaly": "5.0.3",
-		"@nestjs/common": "10.2.10",
-		"@nestjs/core": "10.2.10",
-		"@nestjs/testing": "10.2.10",
+		"@nestjs/common": "10.3.3",
+		"@nestjs/core": "10.3.3",
+		"@nestjs/testing": "10.3.3",
 		"@peertube/http-signature": "1.7.0",
 		"@simplewebauthn/server": "9.0.3",
 		"@sinonjs/fake-timers": "11.2.2",
@@ -159,7 +159,7 @@
 		"ratelimiter": "3.4.1",
 		"re2": "1.20.9",
 		"redis-lock": "0.1.4",
-		"reflect-metadata": "0.1.14",
+		"reflect-metadata": "0.2.1",
 		"rename": "1.0.4",
 		"rss-parser": "3.13.0",
 		"rxjs": "7.8.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3ff570c10869..5e29c1162be4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -117,14 +117,14 @@ importers:
         specifier: 5.0.3
         version: 5.0.3
       '@nestjs/common':
-        specifier: 10.2.10
-        version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
+        specifier: 10.3.3
+        version: 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
       '@nestjs/core':
-        specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+        specifier: 10.3.3
+        version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
       '@nestjs/testing':
-        specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.3)
+        specifier: 10.3.3
+        version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3)
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
@@ -351,8 +351,8 @@ importers:
         specifier: 0.1.4
         version: 0.1.4
       reflect-metadata:
-        specifier: 0.1.14
-        version: 0.1.14
+        specifier: 0.2.1
+        version: 0.2.1
       rename:
         specifier: 1.0.4
         version: 1.0.4
@@ -513,7 +513,7 @@ importers:
         version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@nestjs/platform-express':
         specifier: 10.3.3
-        version: 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
+        version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
       '@simplewebauthn/types':
         specifier: 9.0.1
         version: 9.0.1
@@ -4857,12 +4857,12 @@ packages:
       tar-fs: 2.1.1
     dev: true
 
-  /@nestjs/common@10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1):
-    resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==}
+  /@nestjs/common@10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1):
+    resolution: {integrity: sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==}
     peerDependencies:
       class-transformer: '*'
       class-validator: '*'
-      reflect-metadata: ^0.1.12
+      reflect-metadata: ^0.1.12 || ^0.2.0
       rxjs: ^7.1.0
     peerDependenciesMeta:
       class-transformer:
@@ -4871,20 +4871,20 @@ packages:
         optional: true
     dependencies:
       iterare: 1.2.1
-      reflect-metadata: 0.1.14
+      reflect-metadata: 0.2.1
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
 
-  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1):
-    resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
+  /@nestjs/core@10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1):
+    resolution: {integrity: sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==}
     requiresBuild: true
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/microservices': ^10.0.0
       '@nestjs/platform-express': ^10.0.0
       '@nestjs/websockets': ^10.0.0
-      reflect-metadata: ^0.1.12
+      reflect-metadata: ^0.1.12 || ^0.2.0
       rxjs: ^7.1.0
     peerDependenciesMeta:
       '@nestjs/microservices':
@@ -4894,27 +4894,27 @@ packages:
       '@nestjs/websockets':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
+      '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
+      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
       path-to-regexp: 3.2.0
-      reflect-metadata: 0.1.14
+      reflect-metadata: 0.2.1
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
     transitivePeerDependencies:
       - encoding
 
-  /@nestjs/platform-express@10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10):
+  /@nestjs/platform-express@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3):
     resolution: {integrity: sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/core': ^10.0.0
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
+      '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
       body-parser: 1.20.2
       cors: 2.8.5
       express: 4.18.2
@@ -4923,8 +4923,8 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)(@nestjs/platform-express@10.3.3):
-    resolution: {integrity: sha512-IVLUnPz/+fkBtPATYfqTIP+phN9yjkXejmj+JyhmcfPJZpxBmD1i9VSMqa4u54l37j0xkGPscQ0IXpbhqMYUKw==}
+  /@nestjs/testing@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3):
+    resolution: {integrity: sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/core': ^10.0.0
@@ -4936,9 +4936,9 @@ packages:
       '@nestjs/platform-express':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
+      '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
+      '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
+      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
       tslib: 2.6.2
     dev: false
 
@@ -17103,12 +17103,8 @@ packages:
       redis-errors: 1.2.0
     dev: false
 
-  /reflect-metadata@0.1.14:
-    resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
-
   /reflect-metadata@0.2.1:
     resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==}
-    dev: false
 
   /regenerate-unicode-properties@10.1.0:
     resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}

From 16440d6be2b5e0461c2f96add006155539af6243 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:24:59 +0900
Subject: [PATCH 059/266] Update CHANGELOG.md

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 800c646c676e..4bbc18850dcc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,7 +15,7 @@
 
 ### General
 - Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
-  * デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
+  * デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
   * 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
 - Enhance: 通知がミュート、凍結を考慮するようになりました
 - Enhance: サーバーごとにモデレーションノートを残せるように

From 5befd66e2120ba6e44b4c324264ca862374a1869 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:25:54 +0900
Subject: [PATCH 060/266] Update CHANGELOG.md

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bbc18850dcc..359b8acf563e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@
 - Fix: 設定のバックアップ作成時に名前を入力しなかった場合、ローカライゼーションがおかしくなる問題を修正
 - Fix: ページ`/admin/emojis`の絵文字編集ダイアログで「リアクションとして使えるロール」を追加する際に何も選択せずOKを押下すると画面が固まる問題を修正
 - Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
+- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正
 
 ### Server
 - Enhance: エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
@@ -119,7 +120,6 @@
 - Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
 - Fix: MkCodeEditorで行がずれていってしまう問題の修正
 - Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
-- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正
 
 ### Server
 - Enhance: 連合先のレートリミットを超過した際にリトライするようになりました

From ca6399437cfb4836e19e707c5a985050bfcc9834 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:26:13 +0900
Subject: [PATCH 061/266] format

---
 packages/backend/src/types.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index d894ef730e25..929070d0d290 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -18,7 +18,6 @@
  * achievementEarned - 実績を獲得
  * app - アプリ通知
  * test - テスト通知(サーバー側)
- *
  */
 export const notificationTypes = [
 	'note',

From 5904d98208769b96cbbb1b7a8471408f0a5dfe1c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:26:27 +0900
Subject: [PATCH 062/266] Update packages/backend/test/e2e/mute.ts

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
---
 packages/backend/test/e2e/mute.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts
index 1e4225184af4..1d28e07b7d72 100644
--- a/packages/backend/test/e2e/mute.ts
+++ b/packages/backend/test/e2e/mute.ts
@@ -117,6 +117,7 @@ describe('Mute', () => {
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
 		});
+
 		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
 			const aliceNote = await post(alice, { text: 'hi' });
 			await post(bob, { text: '@alice hi', replyId: aliceNote.id });

From 6158ef138ec8088c0e3c615b2605c0fa1a6f402c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:27:03 +0900
Subject: [PATCH 063/266] format

---
 packages/frontend/test/autocomplete.test.ts | 40 ++++++++++-----------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/packages/frontend/test/autocomplete.test.ts b/packages/frontend/test/autocomplete.test.ts
index f6a7ce945009..394ac3a82168 100644
--- a/packages/frontend/test/autocomplete.test.ts
+++ b/packages/frontend/test/autocomplete.test.ts
@@ -7,28 +7,28 @@ import { assert, describe, test } from 'vitest';
 import { searchEmoji } from '@/scripts/search-emoji.js';
 
 describe('emoji autocomplete', () => {
-  test('名前の完全一致は名前の前方一致より優先される', async () => {
-    const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
-    assert.equal(result[0].emoji, ':foooo:');
-  });
+	test('名前の完全一致は名前の前方一致より優先される', async () => {
+		const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
+		assert.equal(result[0].emoji, ':foooo:');
+	});
 
-  test('名前の前方一致は名前の部分一致より優先される', async () => {
-    const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
-    assert.equal(result[0].emoji, ':baaar:');
-  });
+	test('名前の前方一致は名前の部分一致より優先される', async () => {
+		const result = searchEmoji('baaa', [{ emoji: ':baaar:', name: 'baaar' }, { emoji: ':foooobaaar:', name: 'foooobaaar' }]);
+		assert.equal(result[0].emoji, ':baaar:');
+	});
 
-  test('名前の完全一致はタグの完全一致より優先される', async () => {
-    const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
-    assert.equal(result[0].emoji, ':foooo:');
-  });
+	test('名前の完全一致はタグの完全一致より優先される', async () => {
+		const result = searchEmoji('foooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
+		assert.equal(result[0].emoji, ':foooo:');
+	});
 
-  test('名前の前方一致はタグの前方一致より優先される', async () => {
-    const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
-    assert.equal(result[0].emoji, ':foooo:');
-  });
+	test('名前の前方一致はタグの前方一致より優先される', async () => {
+		const result = searchEmoji('foo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
+		assert.equal(result[0].emoji, ':foooo:');
+	});
 
-  test('名前の部分一致はタグの部分一致より優先される', async () => {
-    const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
-    assert.equal(result[0].emoji, ':foooo:');
-  });
+	test('名前の部分一致はタグの部分一致より優先される', async () => {
+		const result = searchEmoji('oooo', [{ emoji: ':foooo:', name: 'foooo' }, { emoji: ':baaar:', name: 'foooo', aliasOf: 'baaar' }]);
+		assert.equal(result[0].emoji, ':foooo:');
+	});
 });

From d1bf432e14faa7bb420b6338038195963f6b73d2 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:28:46 +0900
Subject: [PATCH 064/266] add missing license headers

---
 packages/backend/src/misc/FileWriterStream.ts | 5 +++++
 packages/backend/src/misc/JsonArrayStream.ts  | 5 +++++
 packages/frontend/src/scripts/search-emoji.ts | 5 +++++
 3 files changed, 15 insertions(+)

diff --git a/packages/backend/src/misc/FileWriterStream.ts b/packages/backend/src/misc/FileWriterStream.ts
index 828851df0e9e..367a8eb56054 100644
--- a/packages/backend/src/misc/FileWriterStream.ts
+++ b/packages/backend/src/misc/FileWriterStream.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import * as fs from 'node:fs/promises';
 import type { PathLike } from 'node:fs';
 
diff --git a/packages/backend/src/misc/JsonArrayStream.ts b/packages/backend/src/misc/JsonArrayStream.ts
index ad35bb3a7905..754938989d8b 100644
--- a/packages/backend/src/misc/JsonArrayStream.ts
+++ b/packages/backend/src/misc/JsonArrayStream.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { TransformStream } from 'node:stream/web';
 
 /**
diff --git a/packages/frontend/src/scripts/search-emoji.ts b/packages/frontend/src/scripts/search-emoji.ts
index 07f55e5531d4..371f69b9a77d 100644
--- a/packages/frontend/src/scripts/search-emoji.ts
+++ b/packages/frontend/src/scripts/search-emoji.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export type EmojiDef = {
 	emoji: string;
 	name: string;

From eb60460d28be24513b567d378cec6ecba5c158c7 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Fri, 1 Mar 2024 11:57:26 +0900
Subject: [PATCH 065/266] =?UTF-8?q?enhance:=20=E7=A6=81=E6=AD=A2=E3=83=AF?=
 =?UTF-8?q?=E3=83=BC=E3=83=89=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E5=BC=B7?=
 =?UTF-8?q?=E5=8C=96=20(#27)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: 禁止ワードチェック強化
* リモートの禁止ワードチェックを添付ファイルとユーザーを登録する前に行うなど
  Resolve https://github.com/misskey-dev/misskey/issues/13374
* 禁止ワートの対象の見直し

* performActivityで特定のエラーが出た際にDelayedに追加しないように

* use IdentifiableError

* NoteCreateService.checkProhibitedWords

* https://github.com/misskey-dev/misskey-private/pull/27/files#r1507416135

* remove comment
---
 .../backend/src/core/NoteCreateService.ts     | 25 +++++++-
 packages/backend/src/core/UtilityService.ts   | 14 +++++
 .../src/core/activitypub/ApInboxService.ts    |  1 -
 .../core/activitypub/models/ApNoteService.ts  | 62 ++++++++++++-------
 .../queue/processors/InboxProcessorService.ts |  5 +-
 5 files changed, 83 insertions(+), 24 deletions(-)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 727787f868a8..81ae2908d3dc 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -263,7 +263,13 @@ export class NoteCreateService implements OnApplicationShutdown {
 			}
 		}
 
-		if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
+		const hasProhibitedWords = await this.checkProhibitedWordsContain({
+			cw: data.cw,
+			text: data.text,
+			pollChoices: data.poll?.choices,
+		}, meta.prohibitedWords);
+
+		if (hasProhibitedWords) {
 			throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
 		}
 
@@ -995,6 +1001,23 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 	}
 
+	public async checkProhibitedWordsContain(content: Parameters<UtilityService['concatNoteContentsForKeyWordCheck']>[0], prohibitedWords?: string[]) {
+		if (prohibitedWords == null) {
+			prohibitedWords = (await this.metaService.fetch()).prohibitedWords;
+		}
+
+		if (
+			this.utilityService.isKeyWordIncluded(
+				this.utilityService.concatNoteContentsForKeyWordCheck(content),
+				prohibitedWords,
+			)
+		) {
+			return true;
+		}
+
+		return false;
+	}
+
 	@bindThis
 	public dispose(): void {
 		this.#shutdownController.abort();
diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts
index 638a0c019e06..652e8f744990 100644
--- a/packages/backend/src/core/UtilityService.ts
+++ b/packages/backend/src/core/UtilityService.ts
@@ -42,6 +42,20 @@ export class UtilityService {
 		return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
 	}
 
+	@bindThis
+	public concatNoteContentsForKeyWordCheck(content: {
+		cw?: string | null;
+		text?: string | null;
+		pollChoices?: string[] | null;
+		others?: string[] | null;
+	}): string {
+		/**
+		 * ノートの内容を結合してキーワードチェック用の文字列を生成する
+		 * cwとtextは内容が繋がっているかもしれないので間に何も入れずにチェックする
+		 */
+		return `${content.cw ?? ''}${content.text ?? ''}\n${(content.pollChoices ?? []).join('\n')}\n${(content.others ?? []).join('\n')}`;
+	}
+
 	@bindThis
 	public isKeyWordIncluded(text: string, keyWords: string[]): boolean {
 		if (keyWords.length === 0) return false;
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index b0f56a5d8284..1621c41bcc54 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -36,7 +36,6 @@ import { ApResolverService } from './ApResolverService.js';
 import { ApAudienceService } from './ApAudienceService.js';
 import { ApPersonService } from './models/ApPersonService.js';
 import { ApQuestionService } from './models/ApQuestionService.js';
-import { CacheService } from '@/core/CacheService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import type { Resolver } from './ApResolverService.js';
 import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js';
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index e201b881733e..b2fd435f93a0 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -24,6 +24,8 @@ import { StatusError } from '@/misc/status-error.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { bindThis } from '@/decorators.js';
 import { checkHttps } from '@/misc/check-https.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
 import { ApLoggerService } from '../ApLoggerService.js';
 import { ApMfmService } from '../ApMfmService.js';
@@ -37,7 +39,6 @@ import { ApQuestionService } from './ApQuestionService.js';
 import { ApImageService } from './ApImageService.js';
 import type { Resolver } from '../ApResolverService.js';
 import type { IObject, IPost } from '../type.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 
 @Injectable()
 export class ApNoteService {
@@ -152,11 +153,47 @@ export class ApNoteService {
 			throw new Error('invalid note.attributedTo: ' + note.attributedTo);
 		}
 
-		const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser;
+		const uri = getOneApId(note.attributedTo);
+
+		// ローカルで投稿者を検索し、もし凍結されていたらスキップ
+		const cachedActor = await this.apPersonService.fetchPerson(uri) as MiRemoteUser;
+		if (cachedActor && cachedActor.isSuspended) {
+			throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended');
+		}
+
+		const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
+		const apHashtags = extractApHashtags(note.tag);
+
+		const cw = note.summary === '' ? null : note.summary;
+
+		// テキストのパース
+		let text: string | null = null;
+		if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
+			text = note.source.content;
+		} else if (typeof note._misskey_content !== 'undefined') {
+			text = note._misskey_content;
+		} else if (typeof note.content === 'string') {
+			text = this.apMfmService.htmlToMfm(note.content, note.tag);
+		}
+
+		const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
 
-		// 投稿者が凍結されていたらスキップ
+		//#region Contents Check
+		// 添付ファイルとユーザーをこのサーバーで登録する前に内容をチェックする
+		/**
+		 * 禁止ワードチェック
+		 */
+		const hasProhibitedWords = await this.noteCreateService.checkProhibitedWordsContain({ cw, text, pollChoices: poll?.choices });
+		if (hasProhibitedWords) {
+			throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words');
+		}
+		//#endregion
+
+		const actor = cachedActor ?? await this.apPersonService.resolvePerson(uri, resolver) as MiRemoteUser;
+
+		// 解決した投稿者が凍結されていたらスキップ
 		if (actor.isSuspended) {
-			throw new Error('actor has been suspended');
+			throw new IdentifiableError('85ab9bd7-3a41-4530-959d-f07073900109', 'actor has been suspended');
 		}
 
 		const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver);
@@ -171,9 +208,6 @@ export class ApNoteService {
 			}
 		}
 
-		const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
-		const apHashtags = extractApHashtags(note.tag);
-
 		// 添付ファイル
 		// TODO: attachmentは必ずしもImageではない
 		// TODO: attachmentは必ずしも配列ではない
@@ -233,18 +267,6 @@ export class ApNoteService {
 			}
 		}
 
-		const cw = note.summary === '' ? null : note.summary;
-
-		// テキストのパース
-		let text: string | null = null;
-		if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
-			text = note.source.content;
-		} else if (typeof note._misskey_content !== 'undefined') {
-			text = note._misskey_content;
-		} else if (typeof note.content === 'string') {
-			text = this.apMfmService.htmlToMfm(note.content, note.tag);
-		}
-
 		// vote
 		if (reply && reply.hasPoll) {
 			const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
@@ -274,8 +296,6 @@ export class ApNoteService {
 
 		const apEmojis = emojis.map(emoji => emoji.name);
 
-		const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined);
-
 		try {
 			return await this.noteCreateService.create(actor, {
 				createdAt: note.published ? new Date(note.published) : null,
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 0a713149e5f1..3addead0587b 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -185,7 +185,10 @@ export class InboxProcessorService {
 			await this.apInboxService.performActivity(authUser.user, activity);
 		} catch (e) {
 			if (e instanceof IdentifiableError) {
-				if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') return 'blocked notes with prohibited words';
+				if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
+					return 'blocked notes with prohibited words';
+				}
+				if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') return 'actor has been suspended';
 			}
 			throw e;
 		}

From ba9d47fb69f6254a4a07c5a1ad18585a99a3fc8e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 20:22:06 +0900
Subject: [PATCH 066/266] 2024.3.0

---
 CHANGELOG.md                     | 3 ++-
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 359b8acf563e..15028e700868 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,7 +11,8 @@
 -
 
 -->
-## 202x.x.x (unreleased)
+
+## 2024.3.0
 
 ### General
 - Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
diff --git a/package.json b/package.json
index 68814f74b863..dee4645ee3e0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.2.0",
+	"version": "2024.3.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1069e85b23b7..a7c629119cee 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.2.0",
+	"version": "2024.3.0",
 	"description": "Misskey SDK for JavaScript",
 	"types": "./built/dts/index.d.ts",
 	"exports": {

From fe5efd926efca8a4b14f54a5af930eb51eb4d5ec Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 1 Mar 2024 21:00:43 +0900
Subject: [PATCH 067/266] New translations ja-jp.yml (Chinese Traditional)
 (#13480)

---
 locales/zh-TW.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index bc872823f1c7..5cdecc10ac97 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -1655,6 +1655,7 @@ _role:
     gtlAvailable: "瀏覽全域時間軸"
     ltlAvailable: "瀏覽本地時間軸"
     canPublicNote: "允許公開貼文"
+    mentionMax: "貼文內的最大提及數"
     canInvite: "發行伺服器邀請碼"
     inviteLimit: "可建立邀請碼的數量"
     inviteLimitCycle: "邀請碼的發放間隔"
@@ -2299,6 +2300,7 @@ _notification:
   reactedBySomeUsers: "{n}人做出了反應"
   renotedBySomeUsers: "{n}人做了轉發"
   followedBySomeUsers: "被{n}人追隨了"
+  flushNotification: "重置通知歷史紀錄"
   _types:
     all: "全部 "
     note: "使用者的最新貼文"

From f70489193270a689e37b8f31f6563471a26eaee5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Sat, 2 Mar 2024 05:53:43 +0900
Subject: [PATCH 068/266] fix: emoji colorization

---
 packages/frontend/src/scripts/emojilist.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index f2fbeae0ed08..35a08ef33112 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -41,7 +41,7 @@ export const emojiCharByCategory = _charGroupByCategory;
 
 export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null {
 	// Colorize it because emojilist.json assumes that
-	return unicodeEmojisMap.get(colorizeEmoji(char)) ?? null;
+	return unicodeEmojisMap.get(colorizeEmoji(char)) ?? unicodeEmojisMap.get(char) ?? null;
 }
 
 export function getEmojiName(char: string): string | null {

From 114d3319e819aa920e21c91034c46ece9afd5d3d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sat, 2 Mar 2024 13:26:21 +0900
Subject: [PATCH 069/266] =?UTF-8?q?chore(client):=20=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=81=AE=E7=94=BB=E5=83=8F=E8=AA=AD=E3=81=BF=E8=BE=BC?=
 =?UTF-8?q?=E3=81=BF=E3=81=AB=E5=A4=B1=E6=95=97=E3=81=97=E3=81=9F=E9=9A=9B?=
 =?UTF-8?q?=E3=81=AF=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=81=A7=E3=81=AF?=
 =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=83=80=E3=83=9F=E3=83=BC=E7=94=BB=E5=83=8F?=
 =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA=20(#13487)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/components/global/MkCustomEmoji.stories.impl.ts    | 7 +++++++
 packages/frontend/src/components/global/MkCustomEmoji.vue  | 7 ++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts
index e0da6a4a1374..2e791e991e59 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts
@@ -48,3 +48,10 @@ export const Missing = {
 		name: Default.args.name,
 	},
 } satisfies StoryObj<typeof MkCustomEmoji>;
+export const Error = {
+	...Default,
+	args: {
+		url: 'https://example.com/404',
+		name: Default.args.name,
+	},
+} satisfies StoryObj<typeof MkCustomEmoji>;
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index dbcb00460cdc..67927ddd2241 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -4,7 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<span v-if="errored">:{{ customEmojiName }}:</span>
+<img
+	v-if="errored"
+	:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
+	src="/client-assets/dummy.png"
+	:title="alt"
+/>
 <img
 	v-else
 	:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"

From 32690f576f75f7bd8fe141c5e3ae524a64e61614 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 2 Mar 2024 13:28:10 +0900
Subject: [PATCH 070/266] =?UTF-8?q?fix(frontend):=20=E3=83=94=E3=83=B3?=
 =?UTF-8?q?=E7=95=99=E3=82=81=20or=20=E5=B1=A5=E6=AD=B4=E3=81=AB=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=82=8B=E3=82=AB=E3=82=B9=E3=82=BF?=
 =?UTF-8?q?=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=8C=E3=82=B5=E3=83=BC?=
 =?UTF-8?q?=E3=83=90=E3=81=8B=E3=82=89=E5=89=8A=E9=99=A4=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=82=8B=E3=81=A8=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7?=
 =?UTF-8?q?=E3=83=B3=E3=81=8C=E5=87=BA=E6=9D=A5=E3=81=AA=E3=81=8F=E3=81=AA?=
 =?UTF-8?q?=E3=82=8B=20(#13486)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): ピン留めに登録されているカスタム絵文字がサーバから削除されるとリアクションが出来なくなる

* fix CHANGELOG.md

* fix Unicode Emojis

* fix Unicode Emojis

* fix
---
 CHANGELOG.md                                  | 13 ++++++++++++
 .../src/components/MkAutocomplete.vue         |  2 +-
 .../src/components/MkEmojiPicker.section.vue  |  2 +-
 .../frontend/src/components/MkEmojiPicker.vue | 13 +++++++-----
 .../components/MkReactionsViewer.details.vue  |  2 +-
 .../src/components/global/MkEmoji.vue         |  5 ++---
 .../src/scripts/check-reaction-permissions.ts | 10 +++++-----
 packages/frontend/src/scripts/emojilist.ts    | 20 +++++++++++++------
 8 files changed, 45 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15028e700868..6ce43b12d1f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,19 @@
 
 -->
 
+## 202x.x.x (unreleased)
+
+### General
+-
+
+### Client
+- Fix: 絵文字関係の不具合を修正 (#13485)
+  - 履歴に残っている or ピン留めされた絵文字がコントロールパネルより削除されていた際にリアクションデッキが表示できなくなる
+  - Unicode絵文字が履歴に残っている or ピン留めされているとリアクションデッキが表示できなくなる
+
+### Server
+-
+
 ## 2024.3.0
 
 ### General
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index cae6bc711132..2f3855266cdc 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -77,7 +77,7 @@ const emojiDb = computed(() => {
 				unicodeEmojiDB.push({
 					emoji: emoji,
 					name: k,
-					aliasOf: getEmojiName(emoji)!,
+					aliasOf: getEmojiName(emoji),
 					url: char2path(emoji),
 				});
 			}
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index c295ab6bb7cd..012972d8ebdf 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -87,7 +87,7 @@ const shown = ref(!!props.initialShown);
 function computeButtonTitle(ev: MouseEvent): void {
 	const elm = ev.target as HTMLElement;
 	const emoji = elm.dataset.emoji as string;
-	elm.title = getEmojiName(emoji) ?? emoji;
+	elm.title = getEmojiName(emoji);
 }
 
 function nestedChosen(emoji: any, ev: MouseEvent) {
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 06243e5b04e9..e30d5969d961 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -353,7 +353,7 @@ watch(q, () => {
 	searchResultUnicode.value = Array.from(searchUnicode());
 });
 
-function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
+function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
 	return !props.targetNote || checkReactionPermissions($i!, props.targetNote, emoji);
 }
 
@@ -378,11 +378,14 @@ function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef):
 	return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
 }
 
-function getDef(emoji: string) {
+function getDef(emoji: string): string | Misskey.entities.EmojiSimple | UnicodeEmojiDef {
 	if (emoji.includes(':')) {
-		return customEmojisMap.get(emoji.replace(/:/g, ''))!;
+		// カスタム絵文字が存在する場合はその情報を持つオブジェクトを返し、
+		// サーバの管理画面から削除された等で情報が見つからない場合は名前の文字列をそのまま返しておく(undefinedを返すとエラーになるため)
+		const name = emoji.replaceAll(':', '');
+		return customEmojisMap.get(name) ?? emoji;
 	} else {
-		return getUnicodeEmoji(emoji)!;
+		return getUnicodeEmoji(emoji);
 	}
 }
 
@@ -390,7 +393,7 @@ function getDef(emoji: string) {
 function computeButtonTitle(ev: MouseEvent): void {
 	const elm = ev.target as HTMLElement;
 	const emoji = elm.dataset.emoji as string;
-	elm.title = getEmojiName(emoji) ?? emoji;
+	elm.title = getEmojiName(emoji);
 }
 
 function chosen(emoji: any, ev?: MouseEvent) {
diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index 3158ba436e1e..8b5e6efdf3c6 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -44,7 +44,7 @@ function getReactionName(reaction: string): string {
 	if (trimLocal.startsWith(':')) {
 		return trimLocal;
 	}
-	return getEmojiName(reaction) ?? reaction;
+	return getEmojiName(reaction);
 }
 </script>
 
diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue
index ba4026f0f649..65f75642b27a 100644
--- a/packages/frontend/src/components/global/MkEmoji.vue
+++ b/packages/frontend/src/components/global/MkEmoji.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject } from 'vue';
-import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
+import { char2fluentEmojiFilePath, char2twemojiFilePath } from '@/scripts/emoji-base.js';
 import { defaultStore } from '@/store.js';
 import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
 import * as os from '@/os.js';
@@ -34,8 +34,7 @@ const colorizedNativeEmoji = computed(() => colorizeEmoji(props.emoji));
 
 // Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter
 function computeTitle(event: PointerEvent): void {
-	const title = getEmojiName(props.emoji as string) ?? props.emoji as string;
-	(event.target as HTMLElement).title = title;
+	(event.target as HTMLElement).title = getEmojiName(props.emoji);
 }
 
 function onClick(ev: MouseEvent) {
diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts
index da704717c127..e7b473dd7588 100644
--- a/packages/frontend/src/scripts/check-reaction-permissions.ts
+++ b/packages/frontend/src/scripts/check-reaction-permissions.ts
@@ -1,12 +1,12 @@
 import * as Misskey from 'misskey-js';
 import { UnicodeEmojiDef } from './emojilist.js';
 
-export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
-  if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
+export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string): boolean {
+	if (typeof emoji === 'string') return true; // UnicodeEmojiDefにも無い絵文字であれば文字列で来る。Unicode絵文字であることには変わりないので常にリアクション可能とする;
+	if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
 
-  emoji = emoji as Misskey.entities.EmojiSimple;
-  const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
-  return !(emoji.localOnly && note.user.host !== me.host)
+	const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
+	return !(emoji.localOnly && note.user.host !== me.host)
       && !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))
       && (roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || me.roles.some(role => roleIdsThatCanBeUsedThisEmojiAsReaction.includes(role.id)));
 }
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 35a08ef33112..6565feba97d2 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -39,21 +39,29 @@ for (let i = 0; i < emojilist.length; i++) {
 
 export const emojiCharByCategory = _charGroupByCategory;
 
-export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null {
+export function getUnicodeEmoji(char: string): UnicodeEmojiDef | string {
 	// Colorize it because emojilist.json assumes that
-	return unicodeEmojisMap.get(colorizeEmoji(char)) ?? unicodeEmojisMap.get(char) ?? null;
+	return unicodeEmojisMap.get(colorizeEmoji(char))
+		// カラースタイル絵文字がjsonに無い場合はテキストスタイル絵文字にフォールバックする
+		?? unicodeEmojisMap.get(char)
+		// それでも見つからない場合はそのまま返す(絵文字情報がjsonに無い場合、このフォールバックが無いとレンダリングに失敗する)
+		?? char;
 }
 
-export function getEmojiName(char: string): string | null {
+export function getEmojiName(char: string): string {
 	// Colorize it because emojilist.json assumes that
-	const idx = _indexByChar.get(colorizeEmoji(char));
-	if (idx == null) {
-		return null;
+	const idx = _indexByChar.get(colorizeEmoji(char)) ?? _indexByChar.get(char);
+	if (idx === undefined) {
+		// 絵文字情報がjsonに無い場合は名前の取得が出来ないのでそのまま返すしか無い
+		return char;
 	} else {
 		return emojilist[idx].name;
 	}
 }
 
+/**
+ * テキストスタイル絵文字(U+260Eなどの1文字で表現される絵文字)をカラースタイル絵文字に変換します(VS16:U+FE0Fを付与)。
+ */
 export function colorizeEmoji(char: string) {
 	return char.length === 1 ? `${char}\uFE0F` : char;
 }

From ecc5decaa51a698934041788a2191945e8514c81 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 2 Mar 2024 13:28:22 +0900
Subject: [PATCH 071/266] New Crowdin updates (#13489)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)
---
 locales/fr-FR.yml | 46 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index b7ab4d37c450..9629726f54bd 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -380,8 +380,11 @@ hcaptcha: "hCaptcha"
 enableHcaptcha: "Activer hCaptcha"
 hcaptchaSiteKey: "Clé du site"
 hcaptchaSecretKey: "Clé secrète"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "Activer mCaptcha"
 mcaptchaSiteKey: "Clé du site"
 mcaptchaSecretKey: "Clé secrète"
+mcaptchaInstanceUrl: "URL de l'instance de mCaptcha"
 recaptcha: "reCAPTCHA"
 enableRecaptcha: "Activer reCAPTCHA"
 recaptchaSiteKey: "Clé du site"
@@ -523,7 +526,7 @@ hideThisNote: "Masquer cette note"
 showFeaturedNotesInTimeline: "Afficher les notes des Tendances dans le fil d'actualité"
 objectStorage: "Stockage d'objets"
 useObjectStorage: "Utiliser le stockage d'objets"
-objectStorageBaseUrl: "Base URL"
+objectStorageBaseUrl: "URL de base"
 objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez l’adresse accessible au public selon le guide de service que vous allez utiliser. P.ex. 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>' pour GCS."
 objectStorageBucket: "Bucket"
 objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le service configuré."
@@ -628,6 +631,7 @@ medium: "Moyen"
 small: "Petit"
 generateAccessToken: "Générer un jeton d'accès"
 permission: "Autorisations "
+adminPermission: "Droits de l'administrateur"
 enableAll: "Tout activer"
 disableAll: "Tout désactiver"
 tokenRequested: "Autoriser l'accès au compte"
@@ -1031,12 +1035,18 @@ nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j'
 rolesAssignedToMe: "Rôles attribués à moi"
 resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?"
 sensitiveWords: "Mots sensibles"
+sensitiveWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
+prohibitedWords: "Mots interdits"
+prohibitedWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
 hiddenTags: "Hashtags cachés"
 hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
 notesSearchNotAvailable: "La recherche de notes n'est pas disponible."
 license: "Licence"
+unfavoriteConfirm: "Vraiment supprimer des favoris ?"
 myClips: "Mes clips"
 drivecleaner: "Nettoyeur du Disque"
+retryAllQueuesNow: "Réessayer tous les fils d'attente immédiatement"
+retryAllQueuesConfirmTitle: "Vraiment réessayer ?"
 retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur."
 enableChartsForRemoteUser: "Générer les graphiques pour les utilisateurs distants"
 enableChartsForFederatedInstances: "Générer les graphiques pour les instances distantes"
@@ -1046,6 +1056,8 @@ limitWidthOfReaction: "Limiter la largeur maximale des réactions et les affiche
 noteIdOrUrl: "Identifiant de la note ou URL"
 video: "Vidéo"
 videos: "Vidéos"
+audio: "Audio"
+audioFiles: "Fichiers audio"
 dataSaver: "Économiseur de données"
 accountMigration: "Migration de compte"
 accountMoved: "Cet·te utilisateur·rice a migré son compte vers :"
@@ -1084,7 +1096,10 @@ specifyUser: "Spécifier l'utilisateur·rice"
 failedToPreviewUrl: "Aperçu d'URL échoué"
 update: "Mettre à jour"
 rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction"
+rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Si aucun rôle n'est spécifié, tout le monde peut utiliser cet émoji comme réaction."
+rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Il faut un rôle public."
 cancelReactionConfirm: "Supprimez la réaction ?"
+changeReactionConfirm: "Changer la réaction ?"
 later: "Plus tard"
 goToMisskey: "Retour vers Misskey"
 additionalEmojiDictionary: "Dictionnaires d'émojis additionnels"
@@ -1110,11 +1125,13 @@ used: "Utilisé"
 expired: "Expiré"
 doYouAgree: "Êtes-vous d’accord ?"
 beSureToReadThisAsItIsImportant: "Assurez-vous de le lire ; c'est important."
+iHaveReadXCarefullyAndAgree: "J'ai lu le contenu de « {x} » et donne mon accord."
 dialog: "Dialogue"
 icon: "Avatar"
 forYou: "Pour vous"
 currentAnnouncements: "Annonces actuelles"
 pastAnnouncements: "Annonces passées"
+youHaveUnreadAnnouncements: "Il y a des annonces non lues."
 replies: "Réponses"
 renotes: "Renotes"
 loadReplies: "Inclure les réponses"
@@ -1129,6 +1146,7 @@ showRenotes: "Afficher les renotes"
 edited: "Modifié"
 notificationRecieveConfig: "Paramètres des notifications"
 mutualFollow: "Abonnement mutuel"
+fileAttachedOnly: "Avec fichiers joints seulement"
 showRepliesToOthersInTimeline: "Afficher les réponses aux autres dans le fil"
 hideRepliesToOthersInTimeline: "Masquer les réponses aux autres dans le fil"
 showRepliesToOthersInTimelineAll: "Afficher les réponses de toutes les personnes que vous suivez dans le fil"
@@ -1137,6 +1155,11 @@ confirmShowRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment
 confirmHideRepliesAll: "Cette opération est irréversible. Voulez-vous vraiment masquer les réponses de toutes les personnes que vous suivez dans le fil ?"
 externalServices: "Services externes"
 sourceCode: "Code source"
+sourceCodeIsNotYetProvided: "Le code source n'est pas encore disponible. Veuillez signaler ce problème aux administrateurs."
+repositoryUrl: "URL du dépôt"
+repositoryUrlDescription: "Entrez l'URL du dépôt où se trouve le code source ici. Si vous utilisez Misskey tel quel (sans changer le code source), entrez https://github.com/misskey-dev/misskey"
+feedback: "Commentaires"
+feedbackUrl: "URL pour les commentaires"
 impressum: "Impressum"
 impressumUrl: "URL de l'impressum"
 impressumDescription: "Dans certains pays comme l'Allemagne, il est obligatoire d'afficher les informations sur l'opérateur d'un site (un impressum)."
@@ -1164,11 +1187,32 @@ remainingN: "Restants : {n}"
 overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?"
 seasonalScreenEffect: "Effet d'écran saisonnier"
 decorate: "Décorer"
+addMfmFunction: "Insérer MFM"
+enableQuickAddMfmFunction: "Afficher le sélecteur de MFM avancé"
+bubbleGame: "Jeu de bulles"
 sfx: "Effets sonores"
+soundWillBePlayed: "Le son sera joué"
 showReplay: "Voir le replay"
+replay: "Rediffusion"
+replaying: "En cours de rediffusion"
+endReplay: "Arrêter la rediffusion"
+copyReplayData: "Copier les données de la rediffusion"
 ranking: "Classement"
 lastNDays: "Derniers {n} jours"
+backToTitle: "Retourner au titre"
+hemisphere: "Votre région"
+enableHorizontalSwipe: "Glisser pour changer d'onglet"
+loading: "Chargement en cours"
 surrender: "Annuler"
+gameRetry: "Réessayer"
+_bubbleGame:
+  howToPlay: "Comment jouer"
+  hold: "Réserver"
+  _score:
+    score: "Score"
+    scoreYen: "Montant gagné"
+    highScore: "Meilleur score"
+    yen: "{yen} yens"
 _announcement:
   forExistingUsers: "Pour les utilisateurs existants seulement"
   readConfirmTitle: "Marquer comme lu ?"

From 21e3a91393054207c7a619420f48a7a348bef32e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 2 Mar 2024 13:35:33 +0900
Subject: [PATCH 072/266] Update CHANGELOG.md

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ce43b12d1f1..c5662a28f4d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@
 
 -->
 
-## 202x.x.x (unreleased)
+## 2024.3.1 (unreleased)
 
 ### General
 -

From b83cbc6d4d7f6ef8234855d4e2c4a80a712ac708 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 2 Mar 2024 13:39:49 +0900
Subject: [PATCH 073/266] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c5662a28f4d3..d223db819a15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
 - Fix: 絵文字関係の不具合を修正 (#13485)
   - 履歴に残っている or ピン留めされた絵文字がコントロールパネルより削除されていた際にリアクションデッキが表示できなくなる
   - Unicode絵文字が履歴に残っている or ピン留めされているとリアクションデッキが表示できなくなる
+- Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487
 
 ### Server
 -

From 2744cbd310e7e1b7792fe455602f6e2cf376120d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sat, 2 Mar 2024 07:05:17 +0000
Subject: [PATCH 074/266] =?UTF-8?q?fix(frontend):=20MkCustomEmoji=E3=81=A7?=
 =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=AB=E3=83=90=E3=83=83=E3=82=AF?=
 =?UTF-8?q?=E3=82=92=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=81=8B=E7=94=BB?=
 =?UTF-8?q?=E5=83=8F=E3=81=8B=E9=81=B8=E3=81=B9=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20fix=20of=20#13487?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkAutocomplete.vue    |  2 +-
 .../frontend/src/components/MkEmojiPicker.section.vue  |  2 +-
 packages/frontend/src/components/MkEmojiPicker.vue     |  2 +-
 packages/frontend/src/components/MkReactionIcon.vue    |  2 +-
 .../components/global/MkCustomEmoji.stories.impl.ts    | 10 +++++++++-
 .../frontend/src/components/global/MkCustomEmoji.vue   |  4 +++-
 .../src/components/global/MkMisskeyFlavoredMarkdown.ts |  1 +
 packages/frontend/src/pages/about-misskey.vue          |  2 +-
 packages/frontend/src/pages/settings/emoji-picker.vue  |  4 ++--
 9 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index 2f3855266cdc..932c4ecb2e9c 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</ol>
 	<ol v-else-if="emojis.length > 0" ref="suggests" :class="$style.list">
 		<li v-for="emoji in emojis" :key="emoji.emoji" :class="$style.item" tabindex="-1" @click="complete(type, emoji.emoji)" @keydown="onKeydown">
-			<MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji"/>
+			<MkCustomEmoji v-if="'isCustomEmoji' in emoji && emoji.isCustomEmoji" :name="emoji.emoji" :class="$style.emoji" :fallbackToImage="true"/>
 			<MkEmoji v-else :emoji="emoji.emoji" :class="$style.emoji"/>
 			<!-- eslint-disable-next-line vue/no-v-html -->
 			<span v-if="q" :class="$style.emojiName" v-html="sanitizeHtml(emoji.name.replace(q, `<b>${q}</b>`))"></span>
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 012972d8ebdf..c13164c2968d 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			@pointerenter="computeButtonTitle"
 			@click="emit('chosen', emoji, $event)"
 		>
-			<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
+			<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true" :fallbackToImage="true"/>
 			<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
 		</button>
 	</div>
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index e30d5969d961..8a6bef54d83f 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					tabindex="0"
 					@click="chosen(emoji, $event)"
 				>
-					<MkCustomEmoji class="emoji" :name="emoji.name"/>
+					<MkCustomEmoji class="emoji" :name="emoji.name" :fallbackToImage="true"/>
 				</button>
 			</div>
 			<div v-if="searchResultUnicode.length > 0" class="body">
diff --git a/packages/frontend/src/components/MkReactionIcon.vue b/packages/frontend/src/components/MkReactionIcon.vue
index 59ceab27dc20..068a2968dbe7 100644
--- a/packages/frontend/src/components/MkReactionIcon.vue
+++ b/packages/frontend/src/components/MkReactionIcon.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkCustomEmoji v-if="reaction[0] === ':'" ref="elRef" :name="reaction" :normal="true" :noStyle="noStyle" :url="emojiUrl"/>
+<MkCustomEmoji v-if="reaction[0] === ':'" ref="elRef" :name="reaction" :normal="true" :noStyle="noStyle" :url="emojiUrl" :fallbackToImage="true"/>
 <MkEmoji v-else ref="elRef" :emoji="reaction" :normal="true" :noStyle="noStyle"/>
 </template>
 
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts
index 2e791e991e59..9e6177045d3e 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkCustomEmoji.stories.impl.ts
@@ -48,10 +48,18 @@ export const Missing = {
 		name: Default.args.name,
 	},
 } satisfies StoryObj<typeof MkCustomEmoji>;
-export const Error = {
+export const ErrorToText = {
 	...Default,
 	args: {
 		url: 'https://example.com/404',
 		name: Default.args.name,
 	},
 } satisfies StoryObj<typeof MkCustomEmoji>;
+export const ErrorToImage = {
+	...Default,
+	args: {
+		url: 'https://example.com/404',
+		name: Default.args.name,
+		fallbackToImage: true,
+	},
+} satisfies StoryObj<typeof MkCustomEmoji>;
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index 67927ddd2241..612383534045 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -5,11 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <img
-	v-if="errored"
+	v-if="errored && fallbackToImage"
 	:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
 	src="/client-assets/dummy.png"
 	:title="alt"
 />
+<span v-else-if="errored">:{{ customEmojiName }}:</span>
 <img
 	v-else
 	:class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]"
@@ -44,6 +45,7 @@ const props = defineProps<{
 	useOriginalSize?: boolean;
 	menu?: boolean;
 	menuReaction?: boolean;
+	fallbackToImage?: boolean;
 }>();
 
 const react = inject<((name: string) => void) | null>('react', null);
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 6ce3b6752f01..4ed76f6bc402 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -407,6 +407,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 						useOriginalSize: scale >= 2.5,
 						menu: props.enableEmojiMenu,
 						menuReaction: props.enableEmojiMenuReaction,
+						fallbackToImage: false,
 					})];
 				} else {
 					// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index fd97ab97b919..1a49dbf1d512 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<div class="misskey">Misskey</div>
 						<div class="version">v{{ version }}</div>
 						<span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }">
-							<MkCustomEmoji v-if="emoji.emoji[0] === ':'" class="emoji" :name="emoji.emoji" :normal="true" :noStyle="true"/>
+							<MkCustomEmoji v-if="emoji.emoji[0] === ':'" class="emoji" :name="emoji.emoji" :normal="true" :noStyle="true" :fallbackToImage="true"/>
 							<MkEmoji v-else class="emoji" :emoji="emoji.emoji" :normal="true" :noStyle="true"/>
 						</span>
 					</div>
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index ce296ec183da..dc3e3ee503fe 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					>
 						<template #item="{element}">
 							<button class="_button" :class="$style.emojisItem" @click="removeReaction(element, $event)">
-								<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
+								<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true" :fallbackToImage="true"/>
 								<MkEmoji v-else :emoji="element" :normal="true"/>
 							</button>
 						</template>
@@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					>
 						<template #item="{element}">
 							<button class="_button" :class="$style.emojisItem" @click="removeEmoji(element, $event)">
-								<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
+								<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true" :fallbackToImage="true"/>
 								<MkEmoji v-else :emoji="element" :normal="true"/>
 							</button>
 						</template>

From 3afdafed610f2fe3e3b8c98ccf41aa5a1999559d Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 2 Mar 2024 17:06:01 +0900
Subject: [PATCH 075/266] 2024.3.1

---
 CHANGELOG.md                     | 2 +-
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d223db819a15..bafee277d259 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@
 
 -->
 
-## 2024.3.1 (unreleased)
+## 2024.3.1
 
 ### General
 -
diff --git a/package.json b/package.json
index dee4645ee3e0..41b865456dbb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.3.0",
+	"version": "2024.3.1",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index a7c629119cee..772f001c0734 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.3.0",
+	"version": "2024.3.1",
 	"description": "Misskey SDK for JavaScript",
 	"types": "./built/dts/index.d.ts",
 	"exports": {

From efda2e9baa54945391a7c7c155e67e9ffc67a82e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 2 Mar 2024 18:34:49 +0900
Subject: [PATCH 076/266] Update README.md

---
 README.md | 41 ++++-------------------------------------
 1 file changed, 4 insertions(+), 37 deletions(-)

diff --git a/README.md b/README.md
index 6fa804f1fa83..24013a7bd817 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
 <div align="center">
 <a href="https://misskey-hub.net">
-	<img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="400"/>
+	<img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="300"/>
 </a>
 
-**🌎 **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! 🚀**
+**🌎 **Misskey** is an open source, federated social media platform that's free forever! 🚀**
+
+[Learn more](https://misskey-hub.net/)
 
 ---
 
@@ -22,41 +24,6 @@
 <a href="https://www.patreon.com/syuilo">
 		<img src="https://custom-icon-badges.herokuapp.com/badge/become_a-patron-F96854?logoColor=F96854&style=for-the-badge&logo=patreon&labelColor=363B40" alt="become a patron"/></a>
 
----
-
-[![codecov](https://codecov.io/gh/misskey-dev/misskey/branch/develop/graph/badge.svg?token=R6IQZ3QJOL)](https://codecov.io/gh/misskey-dev/misskey)
-
-</div>
-
-<div>
-
-<a href="https://xn--931a.moe/"><img src="https://github.com/misskey-dev/misskey/blob/develop/assets/ai.png?raw=true" align="right" height="320px"/></a>
-
-## ✨ Features
-- **ActivityPub support**\
-Not on Misskey? No problem! Not only can Misskey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed!
-- **Reactions**\
-You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button.
-- **Drive**\
-With Misskey's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made!
-- **Rich Web UI**\
-	Misskey has a rich and easy to use Web UI!
-	It is highly customizable, from changing the layout and adding widgets to making custom themes.
-	Furthermore, plugins can be created using AiScript, an original programming language.
-- And much more...
-
-</div>
-
-<div style="clear: both;"></div>
-
-## Documentation
-
-Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/docs/), some of the links and graphics above also lead to specific portions of it.
-
-## Sponsors
-
-<div align="center">
-	<a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank"><img src="https://rss3.mypinata.cloud/ipfs/QmUG6H3Z7D5P511shn7sB4CPmpjH5uZWu4m5mWX7U3Gqbu" alt="RSS3" height="60"></a>
 </div>
 
 ## Thanks

From 38837bd388621f5ba49bd7bf0f657a08ec2f19c4 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 3 Mar 2024 20:15:35 +0900
Subject: [PATCH 077/266] test(backend): refactor tests (#13499)

* test(backend): refactor tests

* fix: failed test
---
 packages/backend/test/e2e/2fa.ts              |  96 ++--
 packages/backend/test/e2e/antennas.ts         | 116 +++--
 packages/backend/test/e2e/api-visibility.ts   |  74 +--
 packages/backend/test/e2e/api.ts              |  43 +-
 packages/backend/test/e2e/block.ts            |  19 +-
 packages/backend/test/e2e/clips.ts            | 298 ++++++-----
 packages/backend/test/e2e/drive.ts            |  36 +-
 packages/backend/test/e2e/endpoints.ts        | 261 +++++-----
 packages/backend/test/e2e/exports.ts          |  38 +-
 packages/backend/test/e2e/fetch-resource.ts   |  18 +-
 packages/backend/test/e2e/ff-visibility.ts    | 206 ++++----
 packages/backend/test/e2e/move.ts             | 186 +++----
 packages/backend/test/e2e/mute.ts             |  87 ++--
 packages/backend/test/e2e/note.ts             | 169 +++----
 packages/backend/test/e2e/renote-mute.ts      |   6 +-
 packages/backend/test/e2e/streaming.ts        |   6 +-
 packages/backend/test/e2e/thread-mute.ts      |  18 +-
 packages/backend/test/e2e/timelines.ts        | 336 ++++++-------
 packages/backend/test/e2e/user-notes.ts       |   8 +-
 packages/backend/test/e2e/users.ts            | 469 +++++++++---------
 .../backend/test/unit/AnnouncementService.ts  |   2 +-
 .../test/unit/FetchInstanceMetadataService.ts |  19 +-
 packages/backend/test/unit/RoleService.ts     |   3 +
 packages/backend/test/utils.ts                |  61 ++-
 24 files changed, 1274 insertions(+), 1301 deletions(-)

diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts
index 87a3c227d64d..13c56b88a693 100644
--- a/packages/backend/test/e2e/2fa.ts
+++ b/packages/backend/test/e2e/2fa.ts
@@ -187,7 +187,7 @@ describe('2要素認証', () => {
 	}, 1000 * 60 * 2);
 
 	test('が設定でき、OTPでログインできる。', async () => {
-		const registerResponse = await api('/i/2fa/register', {
+		const registerResponse = await api('i/2fa/register', {
 			password,
 		}, alice);
 		assert.strictEqual(registerResponse.status, 200);
@@ -197,18 +197,18 @@ describe('2要素認証', () => {
 		assert.strictEqual(registerResponse.body.label, username);
 		assert.strictEqual(registerResponse.body.issuer, config.host);
 
-		const doneResponse = await api('/i/2fa/done', {
+		const doneResponse = await api('i/2fa/done', {
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 		assert.strictEqual(doneResponse.status, 200);
 
-		const usersShowResponse = await api('/users/show', {
+		const usersShowResponse = await api('users/show', {
 			username,
 		}, alice);
 		assert.strictEqual(usersShowResponse.status, 200);
 		assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
 
-		const signinResponse = await api('/signin', {
+		const signinResponse = await api('signin', {
 			...signinParam(),
 			token: otpToken(registerResponse.body.secret),
 		});
@@ -216,24 +216,24 @@ describe('2要素認証', () => {
 		assert.notEqual(signinResponse.body.i, undefined);
 
 		// 後片付け
-		await api('/i/2fa/unregister', {
+		await api('i/2fa/unregister', {
 			password,
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 	});
 
 	test('が設定でき、セキュリティキーでログインできる。', async () => {
-		const registerResponse = await api('/i/2fa/register', {
+		const registerResponse = await api('i/2fa/register', {
 			password,
 		}, alice);
 		assert.strictEqual(registerResponse.status, 200);
 
-		const doneResponse = await api('/i/2fa/done', {
+		const doneResponse = await api('i/2fa/done', {
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 		assert.strictEqual(doneResponse.status, 200);
 
-		const registerKeyResponse = await api('/i/2fa/register-key', {
+		const registerKeyResponse = await api('i/2fa/register-key', {
 			password,
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
@@ -243,23 +243,23 @@ describe('2要素認証', () => {
 
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
-		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+		const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
 			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}), alice);
+		}) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 		assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
 		assert.strictEqual(keyDoneResponse.body.name, keyName);
 
-		const usersShowResponse = await api('/users/show', {
+		const usersShowResponse = await api('users/show', {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
 		assert.strictEqual(usersShowResponse.body.securityKeys, true);
 
-		const signinResponse = await api('/signin', {
+		const signinResponse = await api('signin', {
 			...signinParam(),
 		});
 		assert.strictEqual(signinResponse.status, 200);
@@ -268,7 +268,7 @@ describe('2要素認証', () => {
 		assert.notEqual(signinResponse.body.allowCredentials, undefined);
 		assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url'));
 
-		const signinResponse2 = await api('/signin', signinWithSecurityKeyParam({
+		const signinResponse2 = await api('signin', signinWithSecurityKeyParam({
 			keyName,
 			credentialId,
 			requestOptions: signinResponse.body,
@@ -277,24 +277,24 @@ describe('2要素認証', () => {
 		assert.notEqual(signinResponse2.body.i, undefined);
 
 		// 後片付け
-		await api('/i/2fa/unregister', {
+		await api('i/2fa/unregister', {
 			password,
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 	});
 
 	test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => {
-		const registerResponse = await api('/i/2fa/register', {
+		const registerResponse = await api('i/2fa/register', {
 			password,
 		}, alice);
 		assert.strictEqual(registerResponse.status, 200);
 
-		const doneResponse = await api('/i/2fa/done', {
+		const doneResponse = await api('i/2fa/done', {
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 		assert.strictEqual(doneResponse.status, 200);
 
-		const registerKeyResponse = await api('/i/2fa/register-key', {
+		const registerKeyResponse = await api('i/2fa/register-key', {
 			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
@@ -302,33 +302,33 @@ describe('2要素認証', () => {
 
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
-		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+		const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
 			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}), alice);
+		}) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 
-		const passwordLessResponse = await api('/i/2fa/password-less', {
+		const passwordLessResponse = await api('i/2fa/password-less', {
 			value: true,
 		}, alice);
 		assert.strictEqual(passwordLessResponse.status, 204);
 
-		const usersShowResponse = await api('/users/show', {
+		const usersShowResponse = await api('users/show', {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
 		assert.strictEqual(usersShowResponse.body.usePasswordLessLogin, true);
 
-		const signinResponse = await api('/signin', {
+		const signinResponse = await api('signin', {
 			...signinParam(),
 			password: '',
 		});
 		assert.strictEqual(signinResponse.status, 200);
 		assert.strictEqual(signinResponse.body.i, undefined);
 
-		const signinResponse2 = await api('/signin', {
+		const signinResponse2 = await api('signin', {
 			...signinWithSecurityKeyParam({
 				keyName,
 				credentialId,
@@ -340,24 +340,24 @@ describe('2要素認証', () => {
 		assert.notEqual(signinResponse2.body.i, undefined);
 
 		// 後片付け
-		await api('/i/2fa/unregister', {
+		await api('i/2fa/unregister', {
 			password,
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 	});
 
 	test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => {
-		const registerResponse = await api('/i/2fa/register', {
+		const registerResponse = await api('i/2fa/register', {
 			password,
 		}, alice);
 		assert.strictEqual(registerResponse.status, 200);
 
-		const doneResponse = await api('/i/2fa/done', {
+		const doneResponse = await api('i/2fa/done', {
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 		assert.strictEqual(doneResponse.status, 200);
 
-		const registerKeyResponse = await api('/i/2fa/register-key', {
+		const registerKeyResponse = await api('i/2fa/register-key', {
 			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
@@ -365,22 +365,22 @@ describe('2要素認証', () => {
 
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
-		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+		const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
 			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}), alice);
+		}) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 
 		const renamedKey = 'other-key';
-		const updateKeyResponse = await api('/i/2fa/update-key', {
+		const updateKeyResponse = await api('i/2fa/update-key', {
 			name: renamedKey,
 			credentialId: credentialId.toString('base64url'),
 		}, alice);
 		assert.strictEqual(updateKeyResponse.status, 200);
 
-		const iResponse = await api('/i', {
+		const iResponse = await api('i', {
 		}, alice);
 		assert.strictEqual(iResponse.status, 200);
 		const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url'));
@@ -389,24 +389,24 @@ describe('2要素認証', () => {
 		assert.notEqual(securityKeys[0].lastUsed, undefined);
 
 		// 後片付け
-		await api('/i/2fa/unregister', {
+		await api('i/2fa/unregister', {
 			password,
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 	});
 
 	test('が設定でき、設定したセキュリティキーを削除できる。', async () => {
-		const registerResponse = await api('/i/2fa/register', {
+		const registerResponse = await api('i/2fa/register', {
 			password,
 		}, alice);
 		assert.strictEqual(registerResponse.status, 200);
 
-		const doneResponse = await api('/i/2fa/done', {
+		const doneResponse = await api('i/2fa/done', {
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 		assert.strictEqual(doneResponse.status, 200);
 
-		const registerKeyResponse = await api('/i/2fa/register-key', {
+		const registerKeyResponse = await api('i/2fa/register-key', {
 			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
@@ -414,20 +414,20 @@ describe('2要素認証', () => {
 
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
-		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+		const keyDoneResponse = await api('i/2fa/key-done', keyDoneParam({
 			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}), alice);
+		}) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 
 		// テストの実行順によっては複数残ってるので全部消す
-		const iResponse = await api('/i', {
+		const iResponse = await api('i', {
 		}, alice);
 		assert.strictEqual(iResponse.status, 200);
 		for (const key of iResponse.body.securityKeysList) {
-			const removeKeyResponse = await api('/i/2fa/remove-key', {
+			const removeKeyResponse = await api('i/2fa/remove-key', {
 				token: otpToken(registerResponse.body.secret),
 				password,
 				credentialId: key.id,
@@ -435,13 +435,13 @@ describe('2要素認証', () => {
 			assert.strictEqual(removeKeyResponse.status, 200);
 		}
 
-		const usersShowResponse = await api('/users/show', {
+		const usersShowResponse = await api('users/show', {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
 		assert.strictEqual(usersShowResponse.body.securityKeys, false);
 
-		const signinResponse = await api('/signin', {
+		const signinResponse = await api('signin', {
 			...signinParam(),
 			token: otpToken(registerResponse.body.secret),
 		});
@@ -449,43 +449,43 @@ describe('2要素認証', () => {
 		assert.notEqual(signinResponse.body.i, undefined);
 
 		// 後片付け
-		await api('/i/2fa/unregister', {
+		await api('i/2fa/unregister', {
 			password,
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 	});
 
 	test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => {
-		const registerResponse = await api('/i/2fa/register', {
+		const registerResponse = await api('i/2fa/register', {
 			password,
 		}, alice);
 		assert.strictEqual(registerResponse.status, 200);
 
-		const doneResponse = await api('/i/2fa/done', {
+		const doneResponse = await api('i/2fa/done', {
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
 		assert.strictEqual(doneResponse.status, 200);
 
-		const usersShowResponse = await api('/users/show', {
+		const usersShowResponse = await api('users/show', {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
 		assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
 
-		const unregisterResponse = await api('/i/2fa/unregister', {
+		const unregisterResponse = await api('i/2fa/unregister', {
 			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
 		assert.strictEqual(unregisterResponse.status, 204);
 
-		const signinResponse = await api('/signin', {
+		const signinResponse = await api('signin', {
 			...signinParam(),
 		});
 		assert.strictEqual(signinResponse.status, 200);
 		assert.notEqual(signinResponse.body.i, undefined);
 
 		// 後片付け
-		await api('/i/2fa/unregister', {
+		await api('i/2fa/unregister', {
 			password,
 			token: otpToken(registerResponse.body.secret),
 		}, alice);
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index 1a9d5bf1f01f..7370b1963ca3 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -7,7 +7,6 @@ process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import { DEFAULT_POLICIES } from '@/core/RoleService.js';
-import type { Packed } from '@/misc/json-schema.js';
 import {
 	api,
 	failedApiCall,
@@ -29,10 +28,7 @@ describe('アンテナ', () => {
 	// エンティティとしてのアンテナを主眼においたテストを記述する
 	// (Antennaを返すエンドポイント、Antennaエンティティを書き換えるエンドポイント、Antennaからノートを取得するエンドポイントをテストする)
 
-	// BUG misskey-jsとjson-schemaが一致していない。
-	// - srcのenumにgroupが残っている
-	// - userGroupIdが残っている, isActiveがない
-	type Antenna = misskey.entities.Antenna | Packed<'Antenna'>;
+	type Antenna = misskey.entities.Antenna;
 	type User = misskey.entities.SignupResponse;
 	type Note = misskey.entities.Note;
 
@@ -80,7 +76,7 @@ describe('アンテナ', () => {
 		aliceList = await userList(alice, {});
 		bob = await signup({ username: 'bob' });
 		aliceList = await userList(alice, {});
-		bobFile = (await uploadFile(bob)).body;
+		bobFile = (await uploadFile(bob)).body!;
 		bobList = await userList(bob);
 		carol = await signup({ username: 'carol' });
 		await api('users/lists/push', { listId: aliceList.id, userId: bob.id }, alice);
@@ -129,9 +125,9 @@ describe('アンテナ', () => {
 	beforeEach(async () => {
 		// テスト間で影響し合わないように毎回全部消す。
 		for (const user of [alice, bob]) {
-			const list = await api('/antennas/list', {}, user);
+			const list = await api('antennas/list', {}, user);
 			for (const antenna of list.body) {
-				await api('/antennas/delete', { antennaId: antenna.id }, user);
+				await api('antennas/delete', { antennaId: antenna.id }, user);
 			}
 		}
 	});
@@ -141,11 +137,11 @@ describe('アンテナ', () => {
 	test('が作成できること、キーが過不足なく入っていること。', async () => {
 		const response = await successfulApiCall({
 			endpoint: 'antennas/create',
-			parameters: { ...defaultParam },
+			parameters: defaultParam,
 			user: alice,
 		});
 		assert.match(response.id, /[0-9a-z]{10}/);
-		const expected = {
+		const expected: Antenna = {
 			id: response.id,
 			caseSensitive: false,
 			createdAt: new Date(response.createdAt).toISOString(),
@@ -161,7 +157,7 @@ describe('アンテナ', () => {
 			withFile: false,
 			withReplies: false,
 			localOnly: false,
-		} as Antenna;
+		};
 		assert.deepStrictEqual(response, expected);
 	});
 
@@ -202,27 +198,27 @@ describe('アンテナ', () => {
 	});
 
 	const antennaParamPattern = [
-		{ parameters: (): object => ({ name: 'x'.repeat(100) }) },
-		{ parameters: (): object => ({ name: 'x' }) },
-		{ parameters: (): object => ({ src: 'home' }) },
-		{ parameters: (): object => ({ src: 'all' }) },
-		{ parameters: (): object => ({ src: 'users' }) },
-		{ parameters: (): object => ({ src: 'list' }) },
-		{ parameters: (): object => ({ userListId: null }) },
-		{ parameters: (): object => ({ src: 'list', userListId: aliceList.id }) },
-		{ parameters: (): object => ({ keywords: [['x']] }) },
-		{ parameters: (): object => ({ keywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
-		{ parameters: (): object => ({ excludeKeywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
-		{ parameters: (): object => ({ users: [alice.username] }) },
-		{ parameters: (): object => ({ users: [alice.username, bob.username, carol.username] }) },
-		{ parameters: (): object => ({ caseSensitive: false }) },
-		{ parameters: (): object => ({ caseSensitive: true }) },
-		{ parameters: (): object => ({ withReplies: false }) },
-		{ parameters: (): object => ({ withReplies: true }) },
-		{ parameters: (): object => ({ withFile: false }) },
-		{ parameters: (): object => ({ withFile: true }) },
-		{ parameters: (): object => ({ notify: false }) },
-		{ parameters: (): object => ({ notify: true }) },
+		{ parameters: () => ({ name: 'x'.repeat(100) }) },
+		{ parameters: () => ({ name: 'x' }) },
+		{ parameters: () => ({ src: 'home' as const }) },
+		{ parameters: () => ({ src: 'all' as const }) },
+		{ parameters: () => ({ src: 'users' as const }) },
+		{ parameters: () => ({ src: 'list' as const }) },
+		{ parameters: () => ({ userListId: null }) },
+		{ parameters: () => ({ src: 'list' as const, userListId: aliceList.id }) },
+		{ parameters: () => ({ keywords: [['x']] }) },
+		{ parameters: () => ({ keywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
+		{ parameters: () => ({ excludeKeywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
+		{ parameters: () => ({ users: [alice.username] }) },
+		{ parameters: () => ({ users: [alice.username, bob.username, carol.username] }) },
+		{ parameters: () => ({ caseSensitive: false }) },
+		{ parameters: () => ({ caseSensitive: true }) },
+		{ parameters: () => ({ withReplies: false }) },
+		{ parameters: () => ({ withReplies: true }) },
+		{ parameters: () => ({ withFile: false }) },
+		{ parameters: () => ({ withFile: true }) },
+		{ parameters: () => ({ notify: false }) },
+		{ parameters: () => ({ notify: true }) },
 	];
 	test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => {
 		const response = await successfulApiCall({
@@ -335,7 +331,7 @@ describe('アンテナ', () => {
 		test.each([
 			{
 				label: '全体から',
-				parameters: (): object => ({ src: 'all' }),
+				parameters: () => ({ src: 'all' }),
 				posts: [
 					{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
 					{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
@@ -346,7 +342,7 @@ describe('アンテナ', () => {
 			{
 				// BUG e4144a1 以降home指定は壊れている(allと同じ)
 				label: 'ホーム指定はallと同じ',
-				parameters: (): object => ({ src: 'home' }),
+				parameters: () => ({ src: 'home' }),
 				posts: [
 					{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
 					{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
@@ -357,7 +353,7 @@ describe('アンテナ', () => {
 			{
 				// https://github.com/misskey-dev/misskey/issues/9025
 				label: 'ただし、フォロワー限定投稿とDM投稿を含まない。フォロワーであっても。',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'public' }), included: true },
 					{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'home' }), included: true },
@@ -367,56 +363,56 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'ブロックしているユーザーのノートは含む',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userBlockedByAlice, { text: `${keyword}` }), included: true },
 				],
 			},
 			{
 				label: 'ブロックされているユーザーのノートは含まない',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userBlockingAlice, { text: `${keyword}` }) },
 				],
 			},
 			{
 				label: 'ミュートしているユーザーのノートは含まない',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userMutedByAlice, { text: `${keyword}` }) },
 				],
 			},
 			{
 				label: 'ミュートされているユーザーのノートは含む',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userMutingAlice, { text: `${keyword}` }), included: true },
 				],
 			},
 			{
 				label: '「見つけやすくする」がOFFのユーザーのノートも含まれる',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userNotExplorable, { text: `${keyword}` }), included: true },
 				],
 			},
 			{
 				label: '鍵付きユーザーのノートも含まれる',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userLocking, { text: `${keyword}` }), included: true },
 				],
 			},
 			{
 				label: 'サイレンスのノートも含まれる',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userSilenced, { text: `${keyword}` }), included: true },
 				],
 			},
 			{
 				label: '削除ユーザーのノートも含まれる',
-				parameters: (): object => ({}),
+				parameters: () => ({}),
 				posts: [
 					{ note: (): Promise<Note> => post(userDeletedBySelf, { text: `${keyword}` }), included: true },
 					{ note: (): Promise<Note> => post(userDeletedByAdmin, { text: `${keyword}` }), included: true },
@@ -424,7 +420,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'ユーザー指定で',
-				parameters: (): object => ({ src: 'users', users: [`@${bob.username}`, `@${carol.username}`] }),
+				parameters: () => ({ src: 'users', users: [`@${bob.username}`, `@${carol.username}`] }),
 				posts: [
 					{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
 					{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
@@ -433,7 +429,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'リスト指定で',
-				parameters: (): object => ({ src: 'list', userListId: aliceList.id }),
+				parameters: () => ({ src: 'list', userListId: aliceList.id }),
 				posts: [
 					{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
 					{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
@@ -442,14 +438,14 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'CWにもマッチする',
-				parameters: (): object => ({ keywords: [[keyword]] }),
+				parameters: () => ({ keywords: [[keyword]] }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: 'test', cw: `cw ${keyword}` }), included: true },
 				],
 			},
 			{
 				label: 'キーワード1つ',
-				parameters: (): object => ({ keywords: [[keyword]] }),
+				parameters: () => ({ keywords: [[keyword]] }),
 				posts: [
 					{ note: (): Promise<Note> => post(alice, { text: 'test' }) },
 					{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
@@ -458,7 +454,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'キーワード3つ(AND)',
-				parameters: (): object => ({ keywords: [['A', 'B', 'C']] }),
+				parameters: () => ({ keywords: [['A', 'B', 'C']] }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: 'test A' }) },
 					{ note: (): Promise<Note> => post(bob, { text: 'test A B' }) },
@@ -469,7 +465,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'キーワード3つ(OR)',
-				parameters: (): object => ({ keywords: [['A'], ['B'], ['C']] }),
+				parameters: () => ({ keywords: [['A'], ['B'], ['C']] }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: 'test' }) },
 					{ note: (): Promise<Note> => post(bob, { text: 'test A' }), included: true },
@@ -482,7 +478,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: '除外ワード3つ(AND)',
-				parameters: (): object => ({ excludeKeywords: [['A', 'B', 'C']] }),
+				parameters: () => ({ excludeKeywords: [['A', 'B', 'C']] }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }), included: true },
@@ -495,7 +491,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: '除外ワード3つ(OR)',
-				parameters: (): object => ({ excludeKeywords: [['A'], ['B'], ['C']] }),
+				parameters: () => ({ excludeKeywords: [['A'], ['B'], ['C']] }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }) },
@@ -508,7 +504,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'キーワード1つ(大文字小文字区別する)',
-				parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: true }),
+				parameters: () => ({ keywords: [['KEYWORD']], caseSensitive: true }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: 'keyword' }) },
 					{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }) },
@@ -517,7 +513,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'キーワード1つ(大文字小文字区別しない)',
-				parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: false }),
+				parameters: () => ({ keywords: [['KEYWORD']], caseSensitive: false }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: 'keyword' }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }), included: true },
@@ -526,7 +522,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: '除外ワード1つ(大文字小文字区別する)',
-				parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: true }),
+				parameters: () => ({ excludeKeywords: [['KEYWORD']], caseSensitive: true }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }), included: true },
@@ -536,7 +532,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: '除外ワード1つ(大文字小文字区別しない)',
-				parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: false }),
+				parameters: () => ({ excludeKeywords: [['KEYWORD']], caseSensitive: false }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }) },
@@ -546,7 +542,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: '添付ファイルを問わない',
-				parameters: (): object => ({ withFile: false }),
+				parameters: () => ({ withFile: false }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
@@ -554,7 +550,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: '添付ファイル付きのみ',
-				parameters: (): object => ({ withFile: true }),
+				parameters: () => ({ withFile: true }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }) },
@@ -562,7 +558,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'リプライ以外',
-				parameters: (): object => ({ withReplies: false }),
+				parameters: () => ({ withReplies: false }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }) },
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
@@ -570,7 +566,7 @@ describe('アンテナ', () => {
 			},
 			{
 				label: 'リプライも含む',
-				parameters: (): object => ({ withReplies: true }),
+				parameters: () => ({ withReplies: true }),
 				posts: [
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }), included: true },
 					{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
@@ -633,7 +629,7 @@ describe('アンテナ', () => {
 					endpoint: 'antennas/notes',
 					parameters: { antennaId: antenna.id, ...paginationParam },
 					user: alice,
-				}) as any as Note[];
+				});
 			}, offsetBy, 'desc');
 		});
 
diff --git a/packages/backend/test/e2e/api-visibility.ts b/packages/backend/test/e2e/api-visibility.ts
index f92384525cf9..c61b0c2a8674 100644
--- a/packages/backend/test/e2e/api-visibility.ts
+++ b/packages/backend/test/e2e/api-visibility.ts
@@ -6,7 +6,7 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { api, post, signup } from '../utils.js';
+import { UserToken, api, post, signup } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('API visibility', () => {
@@ -24,38 +24,38 @@ describe('API visibility', () => {
 		let target2: misskey.entities.SignupResponse;
 
 		/** public-post */
-		let pub: any;
+		let pub: misskey.entities.Note;
 		/** home-post */
-		let home: any;
+		let home: misskey.entities.Note;
 		/** followers-post */
-		let fol: any;
+		let fol: misskey.entities.Note;
 		/** specified-post */
-		let spe: any;
+		let spe: misskey.entities.Note;
 
 		/** public-reply to target's post */
-		let pubR: any;
+		let pubR: misskey.entities.Note;
 		/** home-reply to target's post */
-		let homeR: any;
+		let homeR: misskey.entities.Note;
 		/** followers-reply to target's post */
-		let folR: any;
+		let folR: misskey.entities.Note;
 		/** specified-reply to target's post */
-		let speR: any;
+		let speR: misskey.entities.Note;
 
 		/** public-mention to target */
-		let pubM: any;
+		let pubM: misskey.entities.Note;
 		/** home-mention to target */
-		let homeM: any;
+		let homeM: misskey.entities.Note;
 		/** followers-mention to target */
-		let folM: any;
+		let folM: misskey.entities.Note;
 		/** specified-mention to target */
-		let speM: any;
+		let speM: misskey.entities.Note;
 
 		/** reply target post */
-		let tgt: any;
+		let tgt: misskey.entities.Note;
 		//#endregion
 
-		const show = async (noteId: any, by: any) => {
-			return await api('/notes/show', {
+		const show = async (noteId: misskey.entities.Note['id'], by?: UserToken) => {
+			return await api('notes/show', {
 				noteId,
 			}, by);
 		};
@@ -70,7 +70,7 @@ describe('API visibility', () => {
 			target2 = await signup({ username: 'target2' });
 
 			// follow alice <= follower
-			await api('/following/create', { userId: alice.id }, follower);
+			await api('following/create', { userId: alice.id }, follower);
 
 			// normal posts
 			pub = await post(alice, { text: 'x', visibility: 'public' });
@@ -111,7 +111,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] public-postを未認証が見れる', async () => {
-			const res = await show(pub.id, null);
+			const res = await show(pub.id);
 			assert.strictEqual(res.body.text, 'x');
 		});
 
@@ -132,7 +132,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] home-postを未認証が見れる', async () => {
-			const res = await show(home.id, null);
+			const res = await show(home.id);
 			assert.strictEqual(res.body.text, 'x');
 		});
 
@@ -153,7 +153,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] followers-postを未認証が見れない', async () => {
-			const res = await show(fol.id, null);
+			const res = await show(fol.id);
 			assert.strictEqual(res.body.isHidden, true);
 		});
 
@@ -179,7 +179,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] specified-postを未認証が見れない', async () => {
-			const res = await show(spe.id, null);
+			const res = await show(spe.id);
 			assert.strictEqual(res.body.isHidden, true);
 		});
 		//#endregion
@@ -207,7 +207,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] public-replyを未認証が見れる', async () => {
-			const res = await show(pubR.id, null);
+			const res = await show(pubR.id);
 			assert.strictEqual(res.body.text, 'x');
 		});
 
@@ -233,7 +233,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] home-replyを未認証が見れる', async () => {
-			const res = await show(homeR.id, null);
+			const res = await show(homeR.id);
 			assert.strictEqual(res.body.text, 'x');
 		});
 
@@ -259,7 +259,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] followers-replyを未認証が見れない', async () => {
-			const res = await show(folR.id, null);
+			const res = await show(folR.id);
 			assert.strictEqual(res.body.isHidden, true);
 		});
 
@@ -290,7 +290,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] specified-replyを未認証が見れない', async () => {
-			const res = await show(speR.id, null);
+			const res = await show(speR.id);
 			assert.strictEqual(res.body.isHidden, true);
 		});
 		//#endregion
@@ -318,7 +318,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] public-mentionを未認証が見れる', async () => {
-			const res = await show(pubM.id, null);
+			const res = await show(pubM.id);
 			assert.strictEqual(res.body.text, '@target x');
 		});
 
@@ -344,7 +344,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] home-mentionを未認証が見れる', async () => {
-			const res = await show(homeM.id, null);
+			const res = await show(homeM.id);
 			assert.strictEqual(res.body.text, '@target x');
 		});
 
@@ -370,7 +370,7 @@ describe('API visibility', () => {
 		});
 
 		test('[show] followers-mentionを未認証が見れない', async () => {
-			const res = await show(folM.id, null);
+			const res = await show(folM.id);
 			assert.strictEqual(res.body.isHidden, true);
 		});
 
@@ -401,28 +401,28 @@ describe('API visibility', () => {
 		});
 
 		test('[show] specified-mentionを未認証が見れない', async () => {
-			const res = await show(speM.id, null);
+			const res = await show(speM.id);
 			assert.strictEqual(res.body.isHidden, true);
 		});
 		//#endregion
 
 		//#region HTL
 		test('[HTL] public-post が 自分が見れる', async () => {
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === pub.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 
 		test('[HTL] public-post が 非フォロワーから見れない', async () => {
-			const res = await api('/notes/timeline', { limit: 100 }, other);
+			const res = await api('notes/timeline', { limit: 100 }, other);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === pub.id);
 			assert.strictEqual(notes.length, 0);
 		});
 
 		test('[HTL] followers-post が フォロワーから見れる', async () => {
-			const res = await api('/notes/timeline', { limit: 100 }, follower);
+			const res = await api('notes/timeline', { limit: 100 }, follower);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === fol.id);
 			assert.strictEqual(notes[0].text, 'x');
@@ -431,21 +431,21 @@ describe('API visibility', () => {
 
 		//#region RTL
 		test('[replies] followers-reply が フォロワーから見れる', async () => {
-			const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, follower);
+			const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, follower);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === folR.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 
 		test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
-			const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, other);
+			const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, other);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === folR.id);
 			assert.strictEqual(notes.length, 0);
 		});
 
 		test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
-			const res = await api('/notes/replies', { noteId: tgt.id, limit: 100 }, target);
+			const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, target);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === folR.id);
 			assert.strictEqual(notes[0].text, 'x');
@@ -454,14 +454,14 @@ describe('API visibility', () => {
 
 		//#region MTL
 		test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
-			const res = await api('/notes/mentions', { limit: 100 }, target);
+			const res = await api('notes/mentions', { limit: 100 }, target);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === folR.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 
 		test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
-			const res = await api('/notes/mentions', { limit: 100 }, target);
+			const res = await api('notes/mentions', { limit: 100 }, target);
 			assert.strictEqual(res.status, 200);
 			const notes = res.body.filter((n: any) => n.id === folM.id);
 			assert.strictEqual(notes[0].text, '@target x');
diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts
index b6eeec99d762..49c6a0636bed 100644
--- a/packages/backend/test/e2e/api.ts
+++ b/packages/backend/test/e2e/api.ts
@@ -23,32 +23,32 @@ import type * as misskey from 'misskey-js';
 describe('API', () => {
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
-	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
-		carol = await signup({ username: 'carol' });
 	}, 1000 * 60 * 2);
 
 	describe('General validation', () => {
 		test('wrong type', async () => {
-			const res = await api('/test', {
+			const res = await api('test', {
 				required: true,
+				// @ts-expect-error string must be string
 				string: 42,
 			});
 			assert.strictEqual(res.status, 400);
 		});
 
 		test('missing require param', async () => {
-			const res = await api('/test', {
+			// @ts-expect-error required is required
+			const res = await api('test', {
 				string: 'a',
 			});
 			assert.strictEqual(res.status, 400);
 		});
 
 		test('invalid misskey:id (empty string)', async () => {
-			const res = await api('/test', {
+			const res = await api('test', {
 				required: true,
 				id: '',
 			});
@@ -56,7 +56,7 @@ describe('API', () => {
 		});
 
 		test('valid misskey:id', async () => {
-			const res = await api('/test', {
+			const res = await api('test', {
 				required: true,
 				id: '8wvhjghbxu',
 			});
@@ -64,7 +64,7 @@ describe('API', () => {
 		});
 
 		test('default value', async () => {
-			const res = await api('/test', {
+			const res = await api('test', {
 				required: true,
 				string: 'a',
 			});
@@ -73,7 +73,7 @@ describe('API', () => {
 		});
 
 		test('can set null even if it has default value', async () => {
-			const res = await api('/test', {
+			const res = await api('test', {
 				required: true,
 				nullableDefault: null,
 			});
@@ -82,7 +82,7 @@ describe('API', () => {
 		});
 
 		test('cannot set undefined if it has default value', async () => {
-			const res = await api('/test', {
+			const res = await api('test', {
 				required: true,
 				nullableDefault: undefined,
 			});
@@ -99,14 +99,14 @@ describe('API', () => {
 
 		// aliceは管理者、APIを使える
 		await successfulApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: alice,
 		});
 
 		// bobは一般ユーザーだからダメ
 		await failedApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: bob,
 		}, {
@@ -117,7 +117,7 @@ describe('API', () => {
 
 		// publicアクセスももちろんダメ
 		await failedApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: undefined,
 		}, {
@@ -128,7 +128,7 @@ describe('API', () => {
 
 		// ごまがしもダメ
 		await failedApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: { token: 'tsukawasete' },
 		}, {
@@ -138,13 +138,13 @@ describe('API', () => {
 		});
 
 		await successfulApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: { token: application2 },
 		});
 
 		await failedApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: { token: application },
 		}, {
@@ -154,7 +154,7 @@ describe('API', () => {
 		});
 
 		await failedApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: { token: application3 },
 		}, {
@@ -164,7 +164,7 @@ describe('API', () => {
 		});
 
 		await failedApiCall({
-			endpoint: '/admin/get-index-stats',
+			endpoint: 'admin/get-index-stats',
 			parameters: {},
 			user: { token: application4 },
 		}, {
@@ -177,7 +177,7 @@ describe('API', () => {
 	describe('Authentication header', () => {
 		test('一般リクエスト', async () => {
 			await successfulApiCall({
-				endpoint: '/admin/get-index-stats',
+				endpoint: 'admin/get-index-stats',
 				parameters: {},
 				user: {
 					token: alice.token,
@@ -211,7 +211,7 @@ describe('API', () => {
 	describe('tokenエラー応答でWWW-Authenticate headerを送る', () => {
 		describe('invalid_token', () => {
 			test('一般リクエスト', async () => {
-				const result = await api('/admin/get-index-stats', {}, {
+				const result = await api('admin/get-index-stats', {}, {
 					token: 'syuilo',
 					bearer: true,
 				});
@@ -246,7 +246,7 @@ describe('API', () => {
 
 		describe('tokenがないとrealmだけおくる', () => {
 			test('一般リクエスト', async () => {
-				const result = await api('/admin/get-index-stats', {});
+				const result = await api('admin/get-index-stats', {});
 				assert.strictEqual(result.status, 401);
 				assert.strictEqual(result.headers.get('WWW-Authenticate'), 'Bearer realm="Misskey"');
 			});
@@ -259,7 +259,8 @@ describe('API', () => {
 		});
 
 		test('invalid_request', async () => {
-			const result = await api('/notes/create', { text: true }, {
+			// @ts-expect-error text must be string
+			const result = await api('notes/create', { text: true }, {
 				token: alice.token,
 				bearer: true,
 			});
diff --git a/packages/backend/test/e2e/block.ts b/packages/backend/test/e2e/block.ts
index cbd91e6e42ac..e4f798498f56 100644
--- a/packages/backend/test/e2e/block.ts
+++ b/packages/backend/test/e2e/block.ts
@@ -22,7 +22,7 @@ describe('Block', () => {
 	}, 1000 * 60 * 2);
 
 	test('Block作成', async () => {
-		const res = await api('/blocking/create', {
+		const res = await api('blocking/create', {
 			userId: bob.id,
 		}, alice);
 
@@ -30,7 +30,7 @@ describe('Block', () => {
 	});
 
 	test('ブロックされているユーザーをフォローできない', async () => {
-		const res = await api('/following/create', { userId: alice.id }, bob);
+		const res = await api('following/create', { userId: alice.id }, bob);
 
 		assert.strictEqual(res.status, 400);
 		assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
@@ -39,7 +39,7 @@ describe('Block', () => {
 	test('ブロックされているユーザーにリアクションできない', async () => {
 		const note = await post(alice, { text: 'hello' });
 
-		const res = await api('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
+		const res = await api('notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
 
 		assert.strictEqual(res.status, 400);
 		assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
@@ -48,7 +48,7 @@ describe('Block', () => {
 	test('ブロックされているユーザーに返信できない', async () => {
 		const note = await post(alice, { text: 'hello' });
 
-		const res = await api('/notes/create', { replyId: note.id, text: 'yo' }, bob);
+		const res = await api('notes/create', { replyId: note.id, text: 'yo' }, bob);
 
 		assert.strictEqual(res.status, 400);
 		assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
@@ -57,7 +57,7 @@ describe('Block', () => {
 	test('ブロックされているユーザーのノートをRenoteできない', async () => {
 		const note = await post(alice, { text: 'hello' });
 
-		const res = await api('/notes/create', { renoteId: note.id, text: 'yo' }, bob);
+		const res = await api('notes/create', { renoteId: note.id, text: 'yo' }, bob);
 
 		assert.strictEqual(res.status, 400);
 		assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
@@ -72,12 +72,13 @@ describe('Block', () => {
 		const bobNote = await post(bob, { text: 'hi' });
 		const carolNote = await post(carol, { text: 'hi' });
 
-		const res = await api('/notes/local-timeline', {}, bob);
+		const res = await api('notes/local-timeline', {}, bob);
+		const body = res.body as misskey.entities.Note[];
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), false);
-		assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+		assert.strictEqual(body.some(note => note.id === aliceNote.id), false);
+		assert.strictEqual(body.some(note => note.id === bobNote.id), true);
+		assert.strictEqual(body.some(note => note.id === carolNote.id), true);
 	});
 });
diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts
index 2cf397e22da9..ba6f9d6a651c 100644
--- a/packages/backend/test/e2e/clips.ts
+++ b/packages/backend/test/e2e/clips.ts
@@ -6,47 +6,34 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { JTDDataType } from 'ajv/dist/jtd';
 import { DEFAULT_POLICIES } from '@/core/RoleService.js';
-import type { Packed } from '@/misc/json-schema.js';
-import { paramDef as CreateParamDef } from '@/server/api/endpoints/clips/create.js';
-import { paramDef as UpdateParamDef } from '@/server/api/endpoints/clips/update.js';
-import { paramDef as DeleteParamDef } from '@/server/api/endpoints/clips/delete.js';
-import { paramDef as ShowParamDef } from '@/server/api/endpoints/clips/show.js';
-import { paramDef as FavoriteParamDef } from '@/server/api/endpoints/clips/favorite.js';
-import { paramDef as UnfavoriteParamDef } from '@/server/api/endpoints/clips/unfavorite.js';
-import { paramDef as AddNoteParamDef } from '@/server/api/endpoints/clips/add-note.js';
-import { paramDef as RemoveNoteParamDef } from '@/server/api/endpoints/clips/remove-note.js';
-import { paramDef as NotesParamDef } from '@/server/api/endpoints/clips/notes.js';
 import { api, ApiRequest, failedApiCall, hiddenNote, post, signup, successfulApiCall } from '../utils.js';
+import type * as Misskey from 'misskey-js';
+
+type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
 
 describe('クリップ', () => {
-	type User = Packed<'User'>;
-	type Note = Packed<'Note'>;
-	type Clip = Packed<'Clip'>;
-
-	let alice: User;
-	let bob: User;
-	let aliceNote: Note;
-	let aliceHomeNote: Note;
-	let aliceFollowersNote: Note;
-	let aliceSpecifiedNote: Note;
-	let bobNote: Note;
-	let bobHomeNote: Note;
-	let bobFollowersNote: Note;
-	let bobSpecifiedNote: Note;
+	let alice: Misskey.entities.SignupResponse;
+	let bob: Misskey.entities.SignupResponse;
+	let aliceNote: Misskey.entities.Note;
+	let aliceHomeNote: Misskey.entities.Note;
+	let aliceFollowersNote: Misskey.entities.Note;
+	let aliceSpecifiedNote: Misskey.entities.Note;
+	let bobNote: Misskey.entities.Note;
+	let bobHomeNote: Misskey.entities.Note;
+	let bobFollowersNote: Misskey.entities.Note;
+	let bobSpecifiedNote: Misskey.entities.Note;
 
 	const compareBy = <T extends { id: string }, >(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => {
 		return selector(a).localeCompare(selector(b));
 	};
 
-	type CreateParam = JTDDataType<typeof CreateParamDef>;
-	const defaultCreate = (): Partial<CreateParam> => ({
+	const defaultCreate = (): Pick<Misskey.entities.ClipsCreateRequest, 'name'> => ({
 		name: 'test',
 	});
-	const create = async (parameters: Partial<CreateParam> = {}, request: Partial<ApiRequest> = {}): Promise<Clip> => {
-		const clip = await successfulApiCall<Clip>({
-			endpoint: '/clips/create',
+	const create = async (parameters: Partial<Misskey.entities.ClipsCreateRequest> = {}, request: Partial<ApiRequest<'clips/create'>> = {}): Promise<Misskey.entities.Clip> => {
+		const clip = await successfulApiCall({
+			endpoint: 'clips/create',
 			parameters: {
 				...defaultCreate(),
 				...parameters,
@@ -64,17 +51,16 @@ describe('クリップ', () => {
 		return clip;
 	};
 
-	const createMany = async (parameters: Partial<CreateParam>, count = 10, user = alice): Promise<Clip[]> => {
+	const createMany = async (parameters: Partial<Misskey.entities.ClipsCreateRequest>, count = 10, user = alice): Promise<Misskey.entities.Clip[]> => {
 		return await Promise.all([...Array(count)].map((_, i) => create({
 			name: `test${i}`,
 			...parameters,
 		}, { user })));
 	};
 
-	type UpdateParam = JTDDataType<typeof UpdateParamDef>;
-	const update = async (parameters: Partial<UpdateParam>, request: Partial<ApiRequest> = {}): Promise<Clip> => {
-		const clip = await successfulApiCall<Clip>({
-			endpoint: '/clips/update',
+	const update = async (parameters: Optional<Misskey.entities.ClipsUpdateRequest, 'name'>, request: Partial<ApiRequest<'clips/update'>> = {}): Promise<Misskey.entities.Clip> => {
+		const clip = await successfulApiCall({
+			endpoint: 'clips/update',
 			parameters: {
 				name: 'updated',
 				...parameters,
@@ -92,41 +78,39 @@ describe('クリップ', () => {
 		return clip;
 	};
 
-	type DeleteParam = JTDDataType<typeof DeleteParamDef>;
-	const deleteClip = async (parameters: DeleteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
-		return await successfulApiCall<void>({
-			endpoint: '/clips/delete',
+	const deleteClip = async (parameters: Misskey.entities.ClipsDeleteRequest, request: Partial<ApiRequest<'clips/delete'>> = {}): Promise<void> => {
+		return await successfulApiCall({
+			endpoint: 'clips/delete',
 			parameters,
 			user: alice,
 			...request,
 		}, {
 			status: 204,
-		});
+		}) as any as void;
 	};
 
-	type ShowParam = JTDDataType<typeof ShowParamDef>;
-	const show = async (parameters: ShowParam, request: Partial<ApiRequest> = {}): Promise<Clip> => {
-		return await successfulApiCall<Clip>({
-			endpoint: '/clips/show',
+	const show = async (parameters: Misskey.entities.ClipsShowRequest, request: Partial<ApiRequest<'clips/show'>> = {}): Promise<Misskey.entities.Clip> => {
+		return await successfulApiCall({
+			endpoint: 'clips/show',
 			parameters,
 			user: alice,
 			...request,
 		});
 	};
 
-	const list = async (request: Partial<ApiRequest>): Promise<Clip[]> => {
-		return successfulApiCall<Clip[]>({
-			endpoint: '/clips/list',
+	const list = async (request: Partial<ApiRequest<'clips/list'>>): Promise<Misskey.entities.Clip[]> => {
+		return successfulApiCall({
+			endpoint: 'clips/list',
 			parameters: {},
 			user: alice,
 			...request,
 		});
 	};
 
-	const usersClips = async (request: Partial<ApiRequest>): Promise<Clip[]> => {
-		return await successfulApiCall<Clip[]>({
-			endpoint: '/users/clips',
-			parameters: {},
+	const usersClips = async (parameters: Misskey.entities.UsersClipsRequest, request: Partial<ApiRequest<'users/clips'>> = {}): Promise<Misskey.entities.Clip[]> => {
+		return await successfulApiCall({
+			endpoint: 'users/clips',
+			parameters,
 			user: alice,
 			...request,
 		});
@@ -136,23 +120,22 @@ describe('クリップ', () => {
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 
-		// FIXME: misskey-jsのNoteはoutdatedなので直接変換できない
-		aliceNote = await post(alice, { text: 'test' }) as any;
-		aliceHomeNote = await post(alice, { text: 'home only', visibility: 'home' }) as any;
-		aliceFollowersNote = await post(alice, { text: 'followers only', visibility: 'followers' }) as any;
-		aliceSpecifiedNote = await post(alice, { text: 'specified only', visibility: 'specified' }) as any;
-		bobNote = await post(bob, { text: 'test' }) as any;
-		bobHomeNote = await post(bob, { text: 'home only', visibility: 'home' }) as any;
-		bobFollowersNote = await post(bob, { text: 'followers only', visibility: 'followers' }) as any;
-		bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' }) as any;
+		aliceNote = await post(alice, { text: 'test' });
+		aliceHomeNote = await post(alice, { text: 'home only', visibility: 'home' });
+		aliceFollowersNote = await post(alice, { text: 'followers only', visibility: 'followers' });
+		aliceSpecifiedNote = await post(alice, { text: 'specified only', visibility: 'specified' });
+		bobNote = await post(bob, { text: 'test' });
+		bobHomeNote = await post(bob, { text: 'home only', visibility: 'home' });
+		bobFollowersNote = await post(bob, { text: 'followers only', visibility: 'followers' });
+		bobSpecifiedNote = await post(bob, { text: 'specified only', visibility: 'specified' });
 	}, 1000 * 60 * 2);
 
 	afterEach(async () => {
 		// テスト間で影響し合わないように毎回全部消す。
 		for (const user of [alice, bob]) {
-			const list = await api('/clips/list', { limit: 11 }, user);
+			const list = await api('clips/list', { limit: 11 }, user);
 			for (const clip of list.body) {
-				await api('/clips/delete', { clipId: clip.id }, user);
+				await api('clips/delete', { clipId: clip.id }, user);
 			}
 		}
 	});
@@ -177,7 +160,7 @@ describe('クリップ', () => {
 		}
 
 		await failedApiCall({
-			endpoint: '/clips/create',
+			endpoint: 'clips/create',
 			parameters: defaultCreate(),
 			user: alice,
 		}, {
@@ -204,7 +187,8 @@ describe('クリップ', () => {
 		{ label: 'descriptionが最大長+1', parameters: { description: 'a'.repeat(2049) } },
 	];
 	test.each(createClipDenyPattern)('の作成は$labelならできない', async ({ parameters }) => failedApiCall({
-		endpoint: '/clips/create',
+		endpoint: 'clips/create',
+		// @ts-expect-error invalid params
 		parameters: {
 			...defaultCreate(),
 			...parameters,
@@ -246,15 +230,15 @@ describe('クリップ', () => {
 			code: 'NO_SUCH_CLIP',
 			id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
 		} },
-		{ label: '他人のクリップ', user: (): User => bob, assertion: {
+		{ label: '他人のクリップ', user: () => bob, assertion: {
 			code: 'NO_SUCH_CLIP',
 			id: 'b4d92d70-b216-46fa-9a3f-a8c811699257',
 		} },
 		...createClipDenyPattern as any,
 	])('の更新は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
-		endpoint: '/clips/update',
+		endpoint: 'clips/update',
 		parameters: {
-			clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
+			clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
 			name: 'updated',
 			...parameters,
 		},
@@ -279,14 +263,15 @@ describe('クリップ', () => {
 			code: 'NO_SUCH_CLIP',
 			id: '70ca08ba-6865-4630-b6fb-8494759aa754',
 		} },
-		{ label: '他人のクリップ', user: (): User => bob, assertion: {
+		{ label: '他人のクリップ', user: () => bob, assertion: {
 			code: 'NO_SUCH_CLIP',
 			id: '70ca08ba-6865-4630-b6fb-8494759aa754',
 		} },
 	])('の削除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
-		endpoint: '/clips/delete',
+		endpoint: 'clips/delete',
 		parameters: {
-			clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
+			// @ts-expect-error clipId must not be null
+			clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
 			...parameters,
 		},
 		user: alice,
@@ -306,7 +291,7 @@ describe('クリップ', () => {
 	test('のID指定取得は他人のPrivateなクリップは取得できない', async () => {
 		const clip = await create({ isPublic: false }, { user: bob } );
 		failedApiCall({
-			endpoint: '/clips/show',
+			endpoint: 'clips/show',
 			parameters: { clipId: clip.id },
 			user: alice,
 		}, {
@@ -323,7 +308,8 @@ describe('クリップ', () => {
 			id: 'c3c5fe33-d62c-44d2-9ea5-d997703f5c20',
 		} },
 	])('のID指定取得は$labelならできない', async ({ parameters, assetion }) => failedApiCall({
-		endpoint: '/clips/show',
+		endpoint: 'clips/show',
+		// @ts-expect-error clipId must not be undefined
 		parameters: {
 			...parameters,
 		},
@@ -356,27 +342,23 @@ describe('クリップ', () => {
 
 	test('の一覧が取得できる(空)', async () => {
 		const res = await usersClips({
-			parameters: {
-				userId: alice.id,
-			},
+			userId: alice.id,
 		});
 		assert.deepStrictEqual(res, []);
 	});
 
 	test.each([
 		{ label: '' },
-		{ label: '他人アカウントから', user: (): User => bob },
+		{ label: '他人アカウントから', user: () => bob },
 	])('の一覧が$label取得できる', async () => {
 		const clips = await createMany({ isPublic: true });
 		const res = await usersClips({
-			parameters: {
-				userId: alice.id,
-			},
+			userId: alice.id,
 		});
 
 		// 返ってくる配列には順序保障がないのでidでソートして厳密比較
 		assert.deepStrictEqual(
-			res.sort(compareBy<Clip>(s => s.id)),
+			res.sort(compareBy<Misskey.entities.Clip>(s => s.id)),
 			clips.sort(compareBy(s => s.id)));
 
 		// 認証状態で見たときだけisFavoritedが入っている
@@ -386,17 +368,16 @@ describe('クリップ', () => {
 	});
 
 	test.each([
-		{ label: '未認証', user: (): undefined => undefined },
+		{ label: '未認証', user: () => undefined },
 		{ label: '存在しないユーザーのもの', parameters: { userId: 'xxxxxxx' } },
 	])('の一覧は$labelでも取得できる', async ({ parameters, user }) => {
 		const clips = await createMany({ isPublic: true });
 		const res = await usersClips({
-			parameters: {
-				userId: alice.id,
-				limit: clips.length,
-				...parameters,
-			},
-			user: (user ?? ((): User => alice))(),
+			userId: alice.id,
+			limit: clips.length,
+			...parameters,
+		}, {
+			user: (user ?? (() => alice))(),
 		});
 
 		// 未認証で見たときはisFavoritedは入らない
@@ -409,10 +390,8 @@ describe('クリップ', () => {
 		await create({ isPublic: false });
 		const aliceClip = await create({ isPublic: true });
 		const res = await usersClips({
-			parameters: {
-				userId: alice.id,
-				limit: 2,
-			},
+			userId: alice.id,
+			limit: 2,
 		});
 		assert.deepStrictEqual(res, [aliceClip]);
 	});
@@ -421,17 +400,15 @@ describe('クリップ', () => {
 		const clips = await createMany({ isPublic: true }, 7);
 		clips.sort(compareBy(s => s.id));
 		const res = await usersClips({
-			parameters: {
-				userId: alice.id,
-				sinceId: clips[1].id,
-				untilId: clips[5].id,
-				limit: 4,
-			},
+			userId: alice.id,
+			sinceId: clips[1].id,
+			untilId: clips[5].id,
+			limit: 4,
 		});
 
 		// Promise.allで返ってくる配列には順序保障がないのでidでソートして厳密比較
 		assert.deepStrictEqual(
-			res.sort(compareBy<Clip>(s => s.id)),
+			res.sort(compareBy<Misskey.entities.Clip>(s => s.id)),
 			[clips[2], clips[3], clips[4]], // sinceIdとuntilId自体は結果に含まれない
 			clips[1].id + ' ... ' + clips[3].id + ' with ' + clips.map(s => s.id) + ' vs. ' + res.map(s => s.id));
 	});
@@ -441,8 +418,9 @@ describe('クリップ', () => {
 		{ label: 'limitゼロ', parameters: { limit: 0 } },
 		{ label: 'limit最大+1', parameters: { limit: 101 } },
 	])('の一覧は$labelだと取得できない', async ({ parameters }) => failedApiCall({
-		endpoint: '/users/clips',
+		endpoint: 'users/clips',
 		parameters: {
+			// @ts-expect-error userId must not be undefined
 			userId: alice.id,
 			...parameters,
 		},
@@ -454,15 +432,15 @@ describe('クリップ', () => {
 	}));
 
 	test.each([
-		{ label: '作成', endpoint: '/clips/create' },
-		{ label: '更新', endpoint: '/clips/update' },
-		{ label: '削除', endpoint: '/clips/delete' },
-		{ label: '取得', endpoint: '/clips/list' },
-		{ label: 'お気に入り設定', endpoint: '/clips/favorite' },
-		{ label: 'お気に入り解除', endpoint: '/clips/unfavorite' },
-		{ label: 'お気に入り取得', endpoint: '/clips/my-favorites' },
-		{ label: 'ノート追加', endpoint: '/clips/add-note' },
-		{ label: 'ノート削除', endpoint: '/clips/remove-note' },
+		{ label: '作成', endpoint: 'clips/create' as const },
+		{ label: '更新', endpoint: 'clips/update' as const },
+		{ label: '削除', endpoint: 'clips/delete' as const },
+		{ label: '取得', endpoint: 'clips/list' as const },
+		{ label: 'お気に入り設定', endpoint: 'clips/favorite' as const },
+		{ label: 'お気に入り解除', endpoint: 'clips/unfavorite' as const },
+		{ label: 'お気に入り取得', endpoint: 'clips/my-favorites' as const },
+		{ label: 'ノート追加', endpoint: 'clips/add-note' as const },
+		{ label: 'ノート削除', endpoint: 'clips/remove-note' as const },
 	])('の$labelは未認証ではできない', async ({ endpoint }) => await failedApiCall({
 		endpoint: endpoint,
 		parameters: {},
@@ -474,35 +452,33 @@ describe('クリップ', () => {
 	}));
 
 	describe('のお気に入り', () => {
-		let aliceClip: Clip;
+		let aliceClip: Misskey.entities.Clip;
 
-		type FavoriteParam = JTDDataType<typeof FavoriteParamDef>;
-		const favorite = async (parameters: FavoriteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
-			return successfulApiCall<void>({
-				endpoint: '/clips/favorite',
+		const favorite = async (parameters: Misskey.entities.ClipsFavoriteRequest, request: Partial<ApiRequest<'clips/favorite'>> = {}): Promise<void> => {
+			return successfulApiCall({
+				endpoint: 'clips/favorite',
 				parameters,
 				user: alice,
 				...request,
 			}, {
 				status: 204,
-			});
+			}) as any as void;
 		};
 
-		type UnfavoriteParam = JTDDataType<typeof UnfavoriteParamDef>;
-		const unfavorite = async (parameters: UnfavoriteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
-			return successfulApiCall<void>({
-				endpoint: '/clips/unfavorite',
+		const unfavorite = async (parameters: Misskey.entities.ClipsUnfavoriteRequest, request: Partial<ApiRequest<'clips/unfavorite'>> = {}): Promise<void> => {
+			return successfulApiCall({
+				endpoint: 'clips/unfavorite',
 				parameters,
 				user: alice,
 				...request,
 			}, {
 				status: 204,
-			});
+			}) as any as void;
 		};
 
-		const myFavorites = async (request: Partial<ApiRequest> = {}): Promise<Clip[]> => {
-			return successfulApiCall<Clip[]>({
-				endpoint: '/clips/my-favorites',
+		const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => {
+			return successfulApiCall({
+				endpoint: 'clips/my-favorites',
 				parameters: {},
 				user: alice,
 				...request,
@@ -568,7 +544,7 @@ describe('クリップ', () => {
 		test('は同じクリップに対して二回設定できない。', async () => {
 			await favorite({ clipId: aliceClip.id });
 			await failedApiCall({
-				endpoint: '/clips/favorite',
+				endpoint: 'clips/favorite',
 				parameters: {
 					clipId: aliceClip.id,
 				},
@@ -586,14 +562,15 @@ describe('クリップ', () => {
 				code: 'NO_SUCH_CLIP',
 				id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
 			} },
-			{ label: '他人のクリップ', user: (): User => bob, assertion: {
+			{ label: '他人のクリップ', user: () => bob, assertion: {
 				code: 'NO_SUCH_CLIP',
 				id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
 			} },
 		])('の設定は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
-			endpoint: '/clips/favorite',
+			endpoint: 'clips/favorite',
 			parameters: {
-				clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
+				// @ts-expect-error clipId must not be null
+				clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
 				...parameters,
 			},
 			user: alice,
@@ -619,7 +596,7 @@ describe('クリップ', () => {
 				code: 'NO_SUCH_CLIP',
 				id: '2603966e-b865-426c-94a7-af4a01241dc1',
 			} },
-			{ label: '他人のクリップ', user: (): User => bob, assertion: {
+			{ label: '他人のクリップ', user: () => bob, assertion: {
 				code: 'NOT_FAVORITED',
 				id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
 			} },
@@ -628,9 +605,10 @@ describe('クリップ', () => {
 				id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
 			} },
 		])('の設定解除は$labelならできない', async ({ parameters, user, assertion }) => failedApiCall({
-			endpoint: '/clips/unfavorite',
+			endpoint: 'clips/unfavorite',
 			parameters: {
-				clipId: (await create({}, { user: (user ?? ((): User => alice))() })).id,
+				// @ts-expect-error clipId must not be null
+				clipId: (await create({}, { user: (user ?? (() => alice))() })).id,
 				...parameters,
 			},
 			user: alice,
@@ -655,41 +633,38 @@ describe('クリップ', () => {
 	});
 
 	describe('に紐づくノート', () => {
-		let aliceClip: Clip;
+		let aliceClip: Misskey.entities.Clip;
 
-		const sampleNotes = (): Note[] => [
+		const sampleNotes = (): Misskey.entities.Note[] => [
 			aliceNote, aliceHomeNote, aliceFollowersNote, aliceSpecifiedNote,
 			bobNote, bobHomeNote, bobFollowersNote, bobSpecifiedNote,
 		];
 
-		type AddNoteParam = JTDDataType<typeof AddNoteParamDef>;
-		const addNote = async (parameters: AddNoteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
-			return successfulApiCall<void>({
-				endpoint: '/clips/add-note',
+		const addNote = async (parameters: Misskey.entities.ClipsAddNoteRequest, request: Partial<ApiRequest<'clips/add-note'>> = {}): Promise<void> => {
+			return successfulApiCall({
+				endpoint: 'clips/add-note',
 				parameters,
 				user: alice,
 				...request,
 			}, {
 				status: 204,
-			});
+			}) as any as void;
 		};
 
-		type RemoveNoteParam = JTDDataType<typeof RemoveNoteParamDef>;
-		const removeNote = async (parameters: RemoveNoteParam, request: Partial<ApiRequest> = {}): Promise<void> => {
-			return successfulApiCall<void>({
-				endpoint: '/clips/remove-note',
+		const removeNote = async (parameters: Misskey.entities.ClipsRemoveNoteRequest, request: Partial<ApiRequest<'clips/remove-note'>> = {}): Promise<void> => {
+			return successfulApiCall({
+				endpoint: 'clips/remove-note',
 				parameters,
 				user: alice,
 				...request,
 			}, {
 				status: 204,
-			});
+			}) as any as void;
 		};
 
-		type NotesParam = JTDDataType<typeof NotesParamDef>;
-		const notes = async (parameters: Partial<NotesParam>, request: Partial<ApiRequest> = {}): Promise<Note[]> => {
-			return successfulApiCall<Note[]>({
-				endpoint: '/clips/notes',
+		const notes = async (parameters: Misskey.entities.ClipsNotesRequest, request: Partial<ApiRequest<'clips/notes'>> = {}): Promise<Misskey.entities.Note[]> => {
+			return successfulApiCall({
+				endpoint: 'clips/notes',
 				parameters,
 				user: alice,
 				...request,
@@ -715,7 +690,7 @@ describe('クリップ', () => {
 		test('として同じノートを二回紐づけることはできない', async () => {
 			await addNote({ clipId: aliceClip.id, noteId: aliceNote.id });
 			await failedApiCall({
-				endpoint: '/clips/add-note',
+				endpoint: 'clips/add-note',
 				parameters: {
 					clipId: aliceClip.id,
 					noteId: aliceNote.id,
@@ -733,11 +708,11 @@ describe('クリップ', () => {
 			const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1;
 			const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
 				text: `test ${i}`,
-			}) as unknown)) as Note[];
+			}) as unknown)) as Misskey.entities.Note[];
 			await Promise.all(noteList.map(s => addNote({ clipId: aliceClip.id, noteId: s.id })));
 
 			await failedApiCall({
-				endpoint: '/clips/add-note',
+				endpoint: 'clips/add-note',
 				parameters: {
 					clipId: aliceClip.id,
 					noteId: aliceNote.id,
@@ -751,7 +726,7 @@ describe('クリップ', () => {
 		});
 
 		test('は他人のクリップへ追加できない。', async () => await failedApiCall({
-			endpoint: '/clips/add-note',
+			endpoint: 'clips/add-note',
 			parameters: {
 				clipId: aliceClip.id,
 				noteId: aliceNote.id,
@@ -774,18 +749,20 @@ describe('クリップ', () => {
 				code: 'NO_SUCH_NOTE',
 				id: 'fc8c0b49-c7a3-4664-a0a6-b418d386bb8b',
 			} },
-			{ label: '他人のクリップ', user: (): object => bob, assetion: {
+			{ label: '他人のクリップ', user: () => bob, assetion: {
 				code: 'NO_SUCH_CLIP',
 				id: 'd6e76cc0-a1b5-4c7c-a287-73fa9c716dcf',
 			} },
 		])('の追加は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({
-			endpoint: '/clips/add-note',
+			endpoint: 'clips/add-note',
 			parameters: {
+				// @ts-expect-error clipId must not be undefined
 				clipId: aliceClip.id,
+				// @ts-expect-error noteId must not be undefined
 				noteId: aliceNote.id,
 				...parameters,
 			},
-			user: (user ?? ((): User => alice))(),
+			user: (user ?? (() => alice))(),
 		}, {
 			status: 400,
 			code: 'INVALID_PARAM',
@@ -810,18 +787,20 @@ describe('クリップ', () => {
 				code: 'NO_SUCH_NOTE',
 				id: 'aff017de-190e-434b-893e-33a9ff5049d8', // add-noteと異なる
 			} },
-			{ label: '他人のクリップ', user: (): object => bob, assetion: {
+			{ label: '他人のクリップ', user: () => bob, assetion: {
 				code: 'NO_SUCH_CLIP',
 				id: 'b80525c6-97f7-49d7-a42d-ebccd49cfd52', // add-noteと異なる
 			} },
 		])('の削除は$labelだとできない', async ({ parameters, user, assetion }) => failedApiCall({
-			endpoint: '/clips/remove-note',
+			endpoint: 'clips/remove-note',
 			parameters: {
+				// @ts-expect-error clipId must not be undefined
 				clipId: aliceClip.id,
+				// @ts-expect-error noteId must not be undefined
 				noteId: aliceNote.id,
 				...parameters,
 			},
-			user: (user ?? ((): User => alice))(),
+			user: (user ?? (() => alice))(),
 		}, {
 			status: 400,
 			code: 'INVALID_PARAM',
@@ -925,21 +904,22 @@ describe('クリップ', () => {
 				code: 'NO_SUCH_CLIP',
 				id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
 			} },
-			{ label: '他人のPrivateなクリップから', user: (): object => bob, assertion: {
+			{ label: '他人のPrivateなクリップから', user: () => bob, assertion: {
 				code: 'NO_SUCH_CLIP',
 				id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
 			} },
-			{ label: '未認証でPrivateなクリップから', user: (): undefined => undefined, assertion: {
+			{ label: '未認証でPrivateなクリップから', user: () => undefined, assertion: {
 				code: 'NO_SUCH_CLIP',
 				id: '1d7645e6-2b6d-4635-b0fe-fe22b0e72e00',
 			} },
 		])('は$labelだと取得できない', async ({ parameters, user, assertion }) => failedApiCall({
-			endpoint: '/clips/notes',
+			endpoint: 'clips/notes',
 			parameters: {
+				// @ts-expect-error clipId must not be undefined
 				clipId: aliceClip.id,
 				...parameters,
 			},
-			user: (user ?? ((): User => alice))(),
+			user: (user ?? (() => alice))(),
 		}, {
 			status: 400,
 			code: 'INVALID_PARAM',
diff --git a/packages/backend/test/e2e/drive.ts b/packages/backend/test/e2e/drive.ts
index 22ec66e2afcb..828c5200ef0d 100644
--- a/packages/backend/test/e2e/drive.ts
+++ b/packages/backend/test/e2e/drive.ts
@@ -6,22 +6,14 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { MiNote } from '@/models/Note.js';
-import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
+import { api, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
-import type{ Repository } from 'typeorm'
-import type { Packed } from '@/misc/json-schema.js';
-
 
 describe('Drive', () => {
-	let Notes: Repository<MiNote>;
-
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
-		const connection = await initTestDb(true);
-		Notes = connection.getRepository(MiNote);
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 	}, 1000 * 60 * 2);
@@ -31,13 +23,13 @@ describe('Drive', () => {
 
 		const marker = Math.random().toString();
 
-		const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'
+		const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg';
 
 		const catcher = makeStreamCatcher(
 			alice,
 			'main',
 			(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
-			(msg) => msg.body.file as Packed<'DriveFile'>,
+			(msg) => msg.body.file,
 			10 * 1000);
 
 		const res = await api('drive/files/upload-from-url', {
@@ -51,7 +43,7 @@ describe('Drive', () => {
 		assert.strictEqual(res.status, 204);
 		assert.strictEqual(file.name, 'Lenna.jpg');
 		assert.strictEqual(file.type, 'image/jpeg');
-	})
+	});
 
 	test('ローカルからアップロードできる', async () => {
 		// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
@@ -59,27 +51,27 @@ describe('Drive', () => {
 		const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
 
 		assert.strictEqual(res.body?.name, 'テスト画像.jpg');
-		assert.strictEqual(res.body?.type, 'image/jpeg');
-	})
+		assert.strictEqual(res.body.type, 'image/jpeg');
+	});
 
 	test('添付ノート一覧を取得できる', async () => {
-		const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id)
+		const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id);
 
 		const note0 = await post(alice, { fileIds: [ids[0]] });
 		const note1 = await post(alice, { fileIds: [ids[0], ids[1]] });
 
 		const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice);
 		assert.strictEqual(attached0.body.length, 2);
-		assert.strictEqual(attached0.body[0].id, note1.id)
-		assert.strictEqual(attached0.body[1].id, note0.id)
+		assert.strictEqual(attached0.body[0].id, note1.id);
+		assert.strictEqual(attached0.body[1].id, note0.id);
 
 		const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice);
 		assert.strictEqual(attached1.body.length, 1);
-		assert.strictEqual(attached1.body[0].id, note1.id)
+		assert.strictEqual(attached1.body[0].id, note1.id);
 
 		const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice);
-		assert.strictEqual(attached2.body.length, 0)
-	})
+		assert.strictEqual(attached2.body.length, 0);
+	});
 
 	test('添付ノート一覧は他の人から見えない', async () => {
 		const file = await uploadFile(alice);
@@ -89,7 +81,5 @@ describe('Drive', () => {
 		const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob);
 		assert.strictEqual(res.status, 400);
 		assert.strictEqual('error' in res.body, true);
-
-	})
+	});
 });
-
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index d4695978053e..bc89dc37f4d8 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -79,6 +79,7 @@ describe('Endpoints', () => {
 		test('クエリをインジェクションできない', async () => {
 			const res = await api('signin', {
 				username: 'test1',
+				// @ts-expect-error password must be string
 				password: {
 					$gt: '',
 				},
@@ -103,7 +104,7 @@ describe('Endpoints', () => {
 			const myLocation = '七森中';
 			const myBirthday = '2000-09-07';
 
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				name: myName,
 				location: myLocation,
 				birthday: myBirthday,
@@ -117,7 +118,7 @@ describe('Endpoints', () => {
 		});
 
 		test('名前を空白にできる', async () => {
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				name: ' ',
 			}, alice);
 			assert.strictEqual(res.status, 200);
@@ -125,11 +126,11 @@ describe('Endpoints', () => {
 		});
 
 		test('誕生日の設定を削除できる', async () => {
-			await api('/i/update', {
+			await api('i/update', {
 				birthday: '2000-09-07',
 			}, alice);
 
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				birthday: null,
 			}, alice);
 
@@ -139,7 +140,7 @@ describe('Endpoints', () => {
 		});
 
 		test('不正な誕生日の形式で怒られる', async () => {
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				birthday: '2000/09/07',
 			}, alice);
 			assert.strictEqual(res.status, 400);
@@ -148,7 +149,7 @@ describe('Endpoints', () => {
 
 	describe('users/show', () => {
 		test('ユーザーが取得できる', async () => {
-			const res = await api('/users/show', {
+			const res = await api('users/show', {
 				userId: alice.id,
 			}, alice);
 
@@ -158,14 +159,14 @@ describe('Endpoints', () => {
 		});
 
 		test('ユーザーが存在しなかったら怒る', async () => {
-			const res = await api('/users/show', {
+			const res = await api('users/show', {
 				userId: '000000000000000000000000',
 			});
 			assert.strictEqual(res.status, 404);
 		});
 
 		test('間違ったIDで怒られる', async () => {
-			const res = await api('/users/show', {
+			const res = await api('users/show', {
 				userId: 'kyoppie',
 			});
 			assert.strictEqual(res.status, 404);
@@ -178,7 +179,7 @@ describe('Endpoints', () => {
 				text: 'test',
 			});
 
-			const res = await api('/notes/show', {
+			const res = await api('notes/show', {
 				noteId: myPost.id,
 			}, alice);
 
@@ -189,14 +190,14 @@ describe('Endpoints', () => {
 		});
 
 		test('投稿が存在しなかったら怒る', async () => {
-			const res = await api('/notes/show', {
+			const res = await api('notes/show', {
 				noteId: '000000000000000000000000',
 			});
 			assert.strictEqual(res.status, 400);
 		});
 
 		test('間違ったIDで怒られる', async () => {
-			const res = await api('/notes/show', {
+			const res = await api('notes/show', {
 				noteId: 'kyoppie',
 			});
 			assert.strictEqual(res.status, 400);
@@ -207,14 +208,14 @@ describe('Endpoints', () => {
 		test('リアクションできる', async () => {
 			const bobPost = await post(bob, { text: 'hi' });
 
-			const res = await api('/notes/reactions/create', {
+			const res = await api('notes/reactions/create', {
 				noteId: bobPost.id,
 				reaction: '🚀',
 			}, alice);
 
 			assert.strictEqual(res.status, 204);
 
-			const resNote = await api('/notes/show', {
+			const resNote = await api('notes/show', {
 				noteId: bobPost.id,
 			}, alice);
 
@@ -225,7 +226,7 @@ describe('Endpoints', () => {
 		test('自分の投稿にもリアクションできる', async () => {
 			const myPost = await post(alice, { text: 'hi' });
 
-			const res = await api('/notes/reactions/create', {
+			const res = await api('notes/reactions/create', {
 				noteId: myPost.id,
 				reaction: '🚀',
 			}, alice);
@@ -236,19 +237,19 @@ describe('Endpoints', () => {
 		test('二重にリアクションすると上書きされる', async () => {
 			const bobPost = await post(bob, { text: 'hi' });
 
-			await api('/notes/reactions/create', {
+			await api('notes/reactions/create', {
 				noteId: bobPost.id,
 				reaction: '🥰',
 			}, alice);
 
-			const res = await api('/notes/reactions/create', {
+			const res = await api('notes/reactions/create', {
 				noteId: bobPost.id,
 				reaction: '🚀',
 			}, alice);
 
 			assert.strictEqual(res.status, 204);
 
-			const resNote = await api('/notes/show', {
+			const resNote = await api('notes/show', {
 				noteId: bobPost.id,
 			}, alice);
 
@@ -257,7 +258,7 @@ describe('Endpoints', () => {
 		});
 
 		test('存在しない投稿にはリアクションできない', async () => {
-			const res = await api('/notes/reactions/create', {
+			const res = await api('notes/reactions/create', {
 				noteId: '000000000000000000000000',
 				reaction: '🚀',
 			}, alice);
@@ -266,13 +267,14 @@ describe('Endpoints', () => {
 		});
 
 		test('空のパラメータで怒られる', async () => {
-			const res = await api('/notes/reactions/create', {}, alice);
+			// @ts-expect-error param must not be empty
+			const res = await api('notes/reactions/create', {}, alice);
 
 			assert.strictEqual(res.status, 400);
 		});
 
 		test('間違ったIDで怒られる', async () => {
-			const res = await api('/notes/reactions/create', {
+			const res = await api('notes/reactions/create', {
 				noteId: 'kyoppie',
 				reaction: '🚀',
 			}, alice);
@@ -283,7 +285,7 @@ describe('Endpoints', () => {
 
 	describe('following/create', () => {
 		test('フォローできる', async () => {
-			const res = await api('/following/create', {
+			const res = await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
@@ -301,7 +303,7 @@ describe('Endpoints', () => {
 		});
 
 		test('既にフォローしている場合は怒る', async () => {
-			const res = await api('/following/create', {
+			const res = await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
@@ -309,7 +311,7 @@ describe('Endpoints', () => {
 		});
 
 		test('存在しないユーザーはフォローできない', async () => {
-			const res = await api('/following/create', {
+			const res = await api('following/create', {
 				userId: '000000000000000000000000',
 			}, alice);
 
@@ -317,7 +319,7 @@ describe('Endpoints', () => {
 		});
 
 		test('自分自身はフォローできない', async () => {
-			const res = await api('/following/create', {
+			const res = await api('following/create', {
 				userId: alice.id,
 			}, alice);
 
@@ -325,13 +327,14 @@ describe('Endpoints', () => {
 		});
 
 		test('空のパラメータで怒られる', async () => {
-			const res = await api('/following/create', {}, alice);
+			// @ts-expect-error params must not be empty
+			const res = await api('following/create', {}, alice);
 
 			assert.strictEqual(res.status, 400);
 		});
 
 		test('間違ったIDで怒られる', async () => {
-			const res = await api('/following/create', {
+			const res = await api('following/create', {
 				userId: 'foo',
 			}, alice);
 
@@ -341,11 +344,11 @@ describe('Endpoints', () => {
 
 	describe('following/delete', () => {
 		test('フォロー解除できる', async () => {
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
-			const res = await api('/following/delete', {
+			const res = await api('following/delete', {
 				userId: alice.id,
 			}, bob);
 
@@ -363,7 +366,7 @@ describe('Endpoints', () => {
 		});
 
 		test('フォローしていない場合は怒る', async () => {
-			const res = await api('/following/delete', {
+			const res = await api('following/delete', {
 				userId: alice.id,
 			}, bob);
 
@@ -371,7 +374,7 @@ describe('Endpoints', () => {
 		});
 
 		test('存在しないユーザーはフォロー解除できない', async () => {
-			const res = await api('/following/delete', {
+			const res = await api('following/delete', {
 				userId: '000000000000000000000000',
 			}, alice);
 
@@ -379,7 +382,7 @@ describe('Endpoints', () => {
 		});
 
 		test('自分自身はフォロー解除できない', async () => {
-			const res = await api('/following/delete', {
+			const res = await api('following/delete', {
 				userId: alice.id,
 			}, alice);
 
@@ -387,13 +390,14 @@ describe('Endpoints', () => {
 		});
 
 		test('空のパラメータで怒られる', async () => {
-			const res = await api('/following/delete', {}, alice);
+			// @ts-expect-error params must not be empty
+			const res = await api('following/delete', {}, alice);
 
 			assert.strictEqual(res.status, 400);
 		});
 
 		test('間違ったIDで怒られる', async () => {
-			const res = await api('/following/delete', {
+			const res = await api('following/delete', {
 				userId: 'kyoppie',
 			}, alice);
 
@@ -403,20 +407,20 @@ describe('Endpoints', () => {
 
 	describe('channels/search', () => {
 		test('空白検索で一覧を取得できる', async () => {
-			await api('/channels/create', {
+			await api('channels/create', {
 				name: 'aaa',
 				description: 'bbb',
 			}, bob);
-			await api('/channels/create', {
+			await api('channels/create', {
 				name: 'ccc1',
 				description: 'ddd1',
 			}, bob);
-			await api('/channels/create', {
+			await api('channels/create', {
 				name: 'ccc2',
 				description: 'ddd2',
 			}, bob);
 
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: '',
 			}, bob);
 
@@ -425,7 +429,7 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body.length, 3);
 		});
 		test('名前のみの検索で名前を検索できる', async () => {
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: 'aaa',
 				type: 'nameOnly',
 			}, bob);
@@ -436,7 +440,7 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body[0].name, 'aaa');
 		});
 		test('名前のみの検索で名前を複数検索できる', async () => {
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: 'ccc',
 				type: 'nameOnly',
 			}, bob);
@@ -446,7 +450,7 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body.length, 2);
 		});
 		test('名前のみの検索で説明は検索できない', async () => {
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: 'bbb',
 				type: 'nameOnly',
 			}, bob);
@@ -456,7 +460,7 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body.length, 0);
 		});
 		test('名前と説明の検索で名前を検索できる', async () => {
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: 'ccc1',
 			}, bob);
 
@@ -466,7 +470,7 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body[0].name, 'ccc1');
 		});
 		test('名前と説明での検索で説明を検索できる', async () => {
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: 'ddd1',
 			}, bob);
 
@@ -476,7 +480,7 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body[0].name, 'ccc1');
 		});
 		test('名前と説明の検索で名前を複数検索できる', async () => {
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: 'ccc',
 			}, bob);
 
@@ -485,7 +489,7 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body.length, 2);
 		});
 		test('名前と説明での検索で説明を複数検索できる', async () => {
-			const res = await api('/channels/search', {
+			const res = await api('channels/search', {
 				query: 'ddd',
 			}, bob);
 
@@ -506,7 +510,7 @@ describe('Endpoints', () => {
 			await uploadFile(alice, {
 				blob: new Blob([new Uint8Array(1024)]),
 			});
-			const res = await api('/drive', {}, alice);
+			const res = await api('drive', {}, alice);
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
 			expect(res.body).toHaveProperty('usage', 1792);
@@ -519,7 +523,7 @@ describe('Endpoints', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
-			assert.strictEqual(res.body.name, 'Lenna.jpg');
+			assert.strictEqual(res.body!.name, 'Lenna.jpg');
 		});
 
 		test('ファイルに名前を付けられる', async () => {
@@ -527,7 +531,7 @@ describe('Endpoints', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
-			assert.strictEqual(res.body.name, 'Belmond.jpg');
+			assert.strictEqual(res.body!.name, 'Belmond.jpg');
 		});
 
 		test('ファイルに名前を付けられるが、拡張子は正しいものになる', async () => {
@@ -535,11 +539,12 @@ describe('Endpoints', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
-			assert.strictEqual(res.body.name, 'Belmond.png.jpg');
+			assert.strictEqual(res.body!.name, 'Belmond.png.jpg');
 		});
 
 		test('ファイル無しで怒られる', async () => {
-			const res = await api('/drive/files/create', {}, alice);
+			// @ts-expect-error params must not be empty
+			const res = await api('drive/files/create', {}, alice);
 
 			assert.strictEqual(res.status, 400);
 		});
@@ -549,14 +554,14 @@ describe('Endpoints', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
-			assert.strictEqual(res.body.name, 'image.svg');
-			assert.strictEqual(res.body.type, 'image/svg+xml');
+			assert.strictEqual(res.body!.name, 'image.svg');
+			assert.strictEqual(res.body!.type, 'image/svg+xml');
 		});
 
 		for (const type of ['webp', 'avif']) {
 			const mediaType = `image/${type}`;
 
-			const getWebpublicType = async (user: any, fileId: string): Promise<string> => {
+			const getWebpublicType = async (user: misskey.entities.SignupResponse, fileId: string): Promise<string> => {
 				// drive/files/create does not expose webpublicType directly, so get it by posting it
 				const res = await post(user, {
 					text: mediaType,
@@ -573,10 +578,10 @@ describe('Endpoints', () => {
 				const res = await uploadFile(alice, { path });
 
 				assert.strictEqual(res.status, 200);
-				assert.strictEqual(res.body.name, path);
-				assert.strictEqual(res.body.type, mediaType);
+				assert.strictEqual(res.body!.name, path);
+				assert.strictEqual(res.body!.type, mediaType);
 
-				const webpublicType = await getWebpublicType(alice, res.body.id);
+				const webpublicType = await getWebpublicType(alice, res.body!.id);
 				assert.strictEqual(webpublicType, 'image/webp');
 			});
 
@@ -584,10 +589,10 @@ describe('Endpoints', () => {
 				const path = `without-alpha.${type}`;
 				const res = await uploadFile(alice, { path });
 				assert.strictEqual(res.status, 200);
-				assert.strictEqual(res.body.name, path);
-				assert.strictEqual(res.body.type, mediaType);
+				assert.strictEqual(res.body!.name, path);
+				assert.strictEqual(res.body!.type, mediaType);
 
-				const webpublicType = await getWebpublicType(alice, res.body.id);
+				const webpublicType = await getWebpublicType(alice, res.body!.id);
 				assert.strictEqual(webpublicType, 'image/webp');
 			});
 		}
@@ -598,8 +603,8 @@ describe('Endpoints', () => {
 			const file = (await uploadFile(alice)).body;
 			const newName = 'いちごパスタ.png';
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				name: newName,
 			}, alice);
 
@@ -611,8 +616,8 @@ describe('Endpoints', () => {
 		test('他人のファイルは更新できない', async () => {
 			const file = (await uploadFile(alice)).body;
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				name: 'いちごパスタ.png',
 			}, bob);
 
@@ -621,12 +626,12 @@ describe('Endpoints', () => {
 
 		test('親フォルダを更新できる', async () => {
 			const file = (await uploadFile(alice)).body;
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				folderId: folder.id,
 			}, alice);
 
@@ -638,17 +643,17 @@ describe('Endpoints', () => {
 		test('親フォルダを無しにできる', async () => {
 			const file = (await uploadFile(alice)).body;
 
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
 
-			await api('/drive/files/update', {
-				fileId: file.id,
+			await api('drive/files/update', {
+				fileId: file!.id,
 				folderId: folder.id,
 			}, alice);
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				folderId: null,
 			}, alice);
 
@@ -659,12 +664,12 @@ describe('Endpoints', () => {
 
 		test('他人のフォルダには入れられない', async () => {
 			const file = (await uploadFile(alice)).body;
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, bob)).body;
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				folderId: folder.id,
 			}, alice);
 
@@ -674,8 +679,8 @@ describe('Endpoints', () => {
 		test('存在しないフォルダで怒られる', async () => {
 			const file = (await uploadFile(alice)).body;
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				folderId: '000000000000000000000000',
 			}, alice);
 
@@ -685,8 +690,8 @@ describe('Endpoints', () => {
 		test('不正なフォルダIDで怒られる', async () => {
 			const file = (await uploadFile(alice)).body;
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				folderId: 'foo',
 			}, alice);
 
@@ -694,7 +699,7 @@ describe('Endpoints', () => {
 		});
 
 		test('ファイルが存在しなかったら怒る', async () => {
-			const res = await api('/drive/files/update', {
+			const res = await api('drive/files/update', {
 				fileId: '000000000000000000000000',
 				name: 'いちごパスタ.png',
 			}, alice);
@@ -706,8 +711,8 @@ describe('Endpoints', () => {
 			const file = (await uploadFile(alice)).body;
 			const newName = '';
 
-			const res = await api('/drive/files/update', {
-				fileId: file.id,
+			const res = await api('drive/files/update', {
+				fileId: file!.id,
 				name: newName,
 			}, alice);
 
@@ -715,7 +720,7 @@ describe('Endpoints', () => {
 		});
 
 		test('間違ったIDで怒られる', async () => {
-			const res = await api('/drive/files/update', {
+			const res = await api('drive/files/update', {
 				fileId: 'kyoppie',
 				name: 'いちごパスタ.png',
 			}, alice);
@@ -726,7 +731,7 @@ describe('Endpoints', () => {
 
 	describe('drive/folders/create', () => {
 		test('フォルダを作成できる', async () => {
-			const res = await api('/drive/folders/create', {
+			const res = await api('drive/folders/create', {
 				name: 'test',
 			}, alice);
 
@@ -738,11 +743,11 @@ describe('Endpoints', () => {
 
 	describe('drive/folders/update', () => {
 		test('名前を更新できる', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				name: 'new name',
 			}, alice);
@@ -753,11 +758,11 @@ describe('Endpoints', () => {
 		});
 
 		test('他人のフォルダを更新できない', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, bob)).body;
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				name: 'new name',
 			}, alice);
@@ -766,14 +771,14 @@ describe('Endpoints', () => {
 		});
 
 		test('親フォルダを更新できる', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
-			const parentFolder = (await api('/drive/folders/create', {
+			const parentFolder = (await api('drive/folders/create', {
 				name: 'parent',
 			}, alice)).body;
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				parentId: parentFolder.id,
 			}, alice);
@@ -784,18 +789,18 @@ describe('Endpoints', () => {
 		});
 
 		test('親フォルダを無しに更新できる', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
-			const parentFolder = (await api('/drive/folders/create', {
+			const parentFolder = (await api('drive/folders/create', {
 				name: 'parent',
 			}, alice)).body;
-			await api('/drive/folders/update', {
+			await api('drive/folders/update', {
 				folderId: folder.id,
 				parentId: parentFolder.id,
 			}, alice);
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				parentId: null,
 			}, alice);
@@ -806,14 +811,14 @@ describe('Endpoints', () => {
 		});
 
 		test('他人のフォルダを親フォルダに設定できない', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
-			const parentFolder = (await api('/drive/folders/create', {
+			const parentFolder = (await api('drive/folders/create', {
 				name: 'parent',
 			}, bob)).body;
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				parentId: parentFolder.id,
 			}, alice);
@@ -822,18 +827,18 @@ describe('Endpoints', () => {
 		});
 
 		test('フォルダが循環するような構造にできない', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
-			const parentFolder = (await api('/drive/folders/create', {
+			const parentFolder = (await api('drive/folders/create', {
 				name: 'parent',
 			}, alice)).body;
-			await api('/drive/folders/update', {
+			await api('drive/folders/update', {
 				folderId: parentFolder.id,
 				parentId: folder.id,
 			}, alice);
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				parentId: parentFolder.id,
 			}, alice);
@@ -842,25 +847,25 @@ describe('Endpoints', () => {
 		});
 
 		test('フォルダが循環するような構造にできない(再帰的)', async () => {
-			const folderA = (await api('/drive/folders/create', {
+			const folderA = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
-			const folderB = (await api('/drive/folders/create', {
+			const folderB = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
-			const folderC = (await api('/drive/folders/create', {
+			const folderC = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
-			await api('/drive/folders/update', {
+			await api('drive/folders/update', {
 				folderId: folderB.id,
 				parentId: folderA.id,
 			}, alice);
-			await api('/drive/folders/update', {
+			await api('drive/folders/update', {
 				folderId: folderC.id,
 				parentId: folderB.id,
 			}, alice);
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folderA.id,
 				parentId: folderC.id,
 			}, alice);
@@ -869,11 +874,11 @@ describe('Endpoints', () => {
 		});
 
 		test('フォルダが循環するような構造にできない(自身)', async () => {
-			const folderA = (await api('/drive/folders/create', {
+			const folderA = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folderA.id,
 				parentId: folderA.id,
 			}, alice);
@@ -882,11 +887,11 @@ describe('Endpoints', () => {
 		});
 
 		test('存在しない親フォルダを設定できない', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				parentId: '000000000000000000000000',
 			}, alice);
@@ -895,11 +900,11 @@ describe('Endpoints', () => {
 		});
 
 		test('不正な親フォルダIDで怒られる', async () => {
-			const folder = (await api('/drive/folders/create', {
+			const folder = (await api('drive/folders/create', {
 				name: 'test',
 			}, alice)).body;
 
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: folder.id,
 				parentId: 'foo',
 			}, alice);
@@ -908,7 +913,7 @@ describe('Endpoints', () => {
 		});
 
 		test('存在しないフォルダを更新できない', async () => {
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: '000000000000000000000000',
 			}, alice);
 
@@ -916,7 +921,7 @@ describe('Endpoints', () => {
 		});
 
 		test('不正なフォルダIDで怒られる', async () => {
-			const res = await api('/drive/folders/update', {
+			const res = await api('drive/folders/update', {
 				folderId: 'foo',
 			}, alice);
 
@@ -937,7 +942,7 @@ describe('Endpoints', () => {
 				visibleUserIds: [alice.id],
 			});
 
-			const res = await api('/notes/replies', {
+			const res = await api('notes/replies', {
 				noteId: alicePost.id,
 			}, carol);
 
@@ -949,7 +954,7 @@ describe('Endpoints', () => {
 
 	describe('notes/timeline', () => {
 		test('フォロワー限定投稿が含まれる', async () => {
-			await api('/following/create', {
+			await api('following/create', {
 				userId: carol.id,
 			}, dave);
 
@@ -958,7 +963,7 @@ describe('Endpoints', () => {
 				visibility: 'followers',
 			});
 
-			const res = await api('/notes/timeline', {}, dave);
+			const res = await api('notes/timeline', {}, dave);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -979,12 +984,12 @@ describe('Endpoints', () => {
 		test('他者に関するメモを更新できる', async () => {
 			const memo = '10月まで低浮上とのこと。';
 
-			const res1 = await api('/users/update-memo', {
+			const res1 = await api('users/update-memo', {
 				memo,
 				userId: bob.id,
 			}, alice);
 
-			const res2 = await api('/users/show', {
+			const res2 = await api('users/show', {
 				userId: bob.id,
 			}, alice);
 			assert.strictEqual(res1.status, 204);
@@ -994,12 +999,12 @@ describe('Endpoints', () => {
 		test('自分に関するメモを更新できる', async () => {
 			const memo = 'チケットを月末までに買う。';
 
-			const res1 = await api('/users/update-memo', {
+			const res1 = await api('users/update-memo', {
 				memo,
 				userId: alice.id,
 			}, alice);
 
-			const res2 = await api('/users/show', {
+			const res2 = await api('users/show', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(res1.status, 204);
@@ -1009,17 +1014,17 @@ describe('Endpoints', () => {
 		test('メモを削除できる', async () => {
 			const memo = '10月まで低浮上とのこと。';
 
-			await api('/users/update-memo', {
+			await api('users/update-memo', {
 				memo,
 				userId: bob.id,
 			}, alice);
 
-			await api('/users/update-memo', {
+			await api('users/update-memo', {
 				memo: '',
 				userId: bob.id,
 			}, alice);
 
-			const res = await api('/users/show', {
+			const res = await api('users/show', {
 				userId: bob.id,
 			}, alice);
 
@@ -1032,21 +1037,21 @@ describe('Endpoints', () => {
 			const memoCarolToBob = '例の件について今度問いただす。';
 
 			await Promise.all([
-				api('/users/update-memo', {
+				api('users/update-memo', {
 					memo: memoAliceToBob,
 					userId: bob.id,
 				}, alice),
-				api('/users/update-memo', {
+				api('users/update-memo', {
 					memo: memoCarolToBob,
 					userId: bob.id,
 				}, carol),
 			]);
 
 			const [resAlice, resCarol] = await Promise.all([
-				api('/users/show', {
+				api('users/show', {
 					userId: bob.id,
 				}, alice),
-				api('/users/show', {
+				api('users/show', {
 					userId: bob.id,
 				}, carol),
 			]);
diff --git a/packages/backend/test/e2e/exports.ts b/packages/backend/test/e2e/exports.ts
index eb03935a2ad6..80a5331a6dce 100644
--- a/packages/backend/test/e2e/exports.ts
+++ b/packages/backend/test/e2e/exports.ts
@@ -18,7 +18,7 @@ describe('export-clips', () => {
 	// XXX: Any better way to get the result?
 	async function pollFirstDriveFile() {
 		while (true) {
-			const files = (await api('/drive/files', {}, alice)).body;
+			const files = (await api('drive/files', {}, alice)).body;
 			if (!files.length) {
 				await new Promise(r => setTimeout(r, 100));
 				continue;
@@ -26,7 +26,7 @@ describe('export-clips', () => {
 			if (files.length > 1) {
 				throw new Error('Too many files?');
 			}
-			const file = (await api('/drive/files/show', { fileId: files[0].id }, alice)).body;
+			const file = (await api('drive/files/show', { fileId: files[0].id }, alice)).body;
 			const res = await fetch(new URL(new URL(file.url).pathname, `http://127.0.0.1:${port}`));
 			return await res.json();
 		}
@@ -44,16 +44,16 @@ describe('export-clips', () => {
 
 	beforeEach(async () => {
 		// Clean all clips and files of alice
-		const clips = (await api('/clips/list', {}, alice)).body;
+		const clips = (await api('clips/list', {}, alice)).body;
 		for (const clip of clips) {
-			const res = await api('/clips/delete', { clipId: clip.id }, alice);
+			const res = await api('clips/delete', { clipId: clip.id }, alice);
 			if (res.status !== 204) {
 				throw new Error('Failed to delete clip');
 			}
 		}
-		const files = (await api('/drive/files', {}, alice)).body;
+		const files = (await api('drive/files', {}, alice)).body;
 		for (const file of files) {
-			const res = await api('/drive/files/delete', { fileId: file.id }, alice);
+			const res = await api('drive/files/delete', { fileId: file.id }, alice);
 			if (res.status !== 204) {
 				throw new Error('Failed to delete file');
 			}
@@ -61,13 +61,13 @@ describe('export-clips', () => {
 	});
 
 	test('basic export', async () => {
-		let res = await api('/clips/create', {
+		let res = await api('clips/create', {
 			name: 'foo',
 			description: 'bar',
 		}, alice);
 		assert.strictEqual(res.status, 200);
 
-		res = await api('/i/export-clips', {}, alice);
+		res = await api('i/export-clips', {}, alice);
 		assert.strictEqual(res.status, 204);
 
 		const exported = await pollFirstDriveFile();
@@ -77,7 +77,7 @@ describe('export-clips', () => {
 	});
 
 	test('export with notes', async () => {
-		let res = await api('/clips/create', {
+		let res = await api('clips/create', {
 			name: 'foo',
 			description: 'bar',
 		}, alice);
@@ -96,14 +96,14 @@ describe('export-clips', () => {
 		});
 
 		for (const note of [note1, note2]) {
-			res = await api('/clips/add-note', {
+			res = await api('clips/add-note', {
 				clipId: clip.id,
 				noteId: note.id,
 			}, alice);
 			assert.strictEqual(res.status, 204);
 		}
 
-		res = await api('/i/export-clips', {}, alice);
+		res = await api('i/export-clips', {}, alice);
 		assert.strictEqual(res.status, 204);
 
 		const exported = await pollFirstDriveFile();
@@ -116,14 +116,14 @@ describe('export-clips', () => {
 	});
 
 	test('multiple clips', async () => {
-		let res = await api('/clips/create', {
+		let res = await api('clips/create', {
 			name: 'kawaii',
 			description: 'kawaii',
 		}, alice);
 		assert.strictEqual(res.status, 200);
 		const clip1 = res.body;
 
-		res = await api('/clips/create', {
+		res = await api('clips/create', {
 			name: 'yuri',
 			description: 'yuri',
 		}, alice);
@@ -138,19 +138,19 @@ describe('export-clips', () => {
 			text: 'baz2',
 		});
 
-		res = await api('/clips/add-note', {
+		res = await api('clips/add-note', {
 			clipId: clip1.id,
 			noteId: note1.id,
 		}, alice);
 		assert.strictEqual(res.status, 204);
 
-		res = await api('/clips/add-note', {
+		res = await api('clips/add-note', {
 			clipId: clip2.id,
 			noteId: note2.id,
 		}, alice);
 		assert.strictEqual(res.status, 204);
 
-		res = await api('/i/export-clips', {}, alice);
+		res = await api('i/export-clips', {}, alice);
 		assert.strictEqual(res.status, 204);
 
 		const exported = await pollFirstDriveFile();
@@ -163,7 +163,7 @@ describe('export-clips', () => {
 	});
 
 	test('Clipping other user\'s note', async () => {
-		let res = await api('/clips/create', {
+		let res = await api('clips/create', {
 			name: 'kawaii',
 			description: 'kawaii',
 		}, alice);
@@ -175,13 +175,13 @@ describe('export-clips', () => {
 			visibility: 'followers',
 		});
 
-		res = await api('/clips/add-note', {
+		res = await api('clips/add-note', {
 			clipId: clip.id,
 			noteId: note.id,
 		}, alice);
 		assert.strictEqual(res.status, 204);
 
-		res = await api('/i/export-clips', {}, alice);
+		res = await api('i/export-clips', {}, alice);
 		assert.strictEqual(res.status, 204);
 
 		const exported = await pollFirstDriveFile();
diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts
index 74033b7dfff2..4851ed14be6a 100644
--- a/packages/backend/test/e2e/fetch-resource.ts
+++ b/packages/backend/test/e2e/fetch-resource.ts
@@ -23,13 +23,13 @@ const JSON_UTF8 = 'application/json; charset=utf-8';
 
 describe('Webリソース', () => {
 	let alice: misskey.entities.SignupResponse;
-	let aliceUploadedFile: any;
-	let alicesPost: any;
-	let alicePage: any;
-	let alicePlay: any;
-	let aliceClip: any;
-	let aliceGalleryPost: any;
-	let aliceChannel: any;
+	let aliceUploadedFile: misskey.entities.DriveFile | null;
+	let alicesPost: misskey.entities.Note;
+	let alicePage: misskey.entities.Page;
+	let alicePlay: misskey.entities.Flash;
+	let aliceClip: misskey.entities.Clip;
+	let aliceGalleryPost: misskey.entities.GalleryPost;
+	let aliceChannel: misskey.entities.Channel;
 
 	let bob: misskey.entities.SignupResponse;
 
@@ -77,7 +77,7 @@ describe('Webリソース', () => {
 
 	beforeAll(async () => {
 		alice = await signup({ username: 'alice' });
-		aliceUploadedFile = await uploadFile(alice);
+		aliceUploadedFile = (await uploadFile(alice)).body;
 		alicesPost = await post(alice, {
 			text: 'test',
 		});
@@ -85,7 +85,7 @@ describe('Webリソース', () => {
 		alicePlay = await play(alice, {});
 		aliceClip = await clip(alice, {});
 		aliceGalleryPost = await galleryPost(alice, {
-			fileIds: [aliceUploadedFile.body.id],
+			fileIds: [aliceUploadedFile!.id],
 		});
 		aliceChannel = await channel(alice, {});
 
diff --git a/packages/backend/test/e2e/ff-visibility.ts b/packages/backend/test/e2e/ff-visibility.ts
index b59dd8824ab1..5d0c70a3c24e 100644
--- a/packages/backend/test/e2e/ff-visibility.ts
+++ b/packages/backend/test/e2e/ff-visibility.ts
@@ -19,15 +19,15 @@ describe('FF visibility', () => {
 	}, 1000 * 60 * 2);
 
 	test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
-		await api('/i/update', {
+		await api('i/update', {
 			followingVisibility: 'public',
 			followersVisibility: 'public',
 		}, alice);
 
-		const followingRes = await api('/users/following', {
+		const followingRes = await api('users/following', {
 			userId: alice.id,
 		}, bob);
-		const followersRes = await api('/users/followers', {
+		const followersRes = await api('users/followers', {
 			userId: alice.id,
 		}, bob);
 
@@ -39,36 +39,36 @@ describe('FF visibility', () => {
 
 	test('followingVisibility が public であれば followersVisibility の設定に関わらずユーザーのフォローを誰でも見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 200);
@@ -78,36 +78,36 @@ describe('FF visibility', () => {
 
 	test('followersVisibility が public であれば followingVisibility の設定に関わらずユーザーのフォロワーを誰でも見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 200);
@@ -116,15 +116,15 @@ describe('FF visibility', () => {
 	});
 
 	test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを自分で見れる', async () => {
-		await api('/i/update', {
+		await api('i/update', {
 			followingVisibility: 'followers',
 			followersVisibility: 'followers',
 		}, alice);
 
-		const followingRes = await api('/users/following', {
+		const followingRes = await api('users/following', {
 			userId: alice.id,
 		}, alice);
-		const followersRes = await api('/users/followers', {
+		const followersRes = await api('users/followers', {
 			userId: alice.id,
 		}, alice);
 
@@ -136,36 +136,36 @@ describe('FF visibility', () => {
 
 	test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followingRes.status, 200);
@@ -175,36 +175,36 @@ describe('FF visibility', () => {
 
 	test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followersRes.status, 200);
@@ -213,15 +213,15 @@ describe('FF visibility', () => {
 	});
 
 	test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => {
-		await api('/i/update', {
+		await api('i/update', {
 			followingVisibility: 'followers',
 			followersVisibility: 'followers',
 		}, alice);
 
-		const followingRes = await api('/users/following', {
+		const followingRes = await api('users/following', {
 			userId: alice.id,
 		}, bob);
-		const followersRes = await api('/users/followers', {
+		const followersRes = await api('users/followers', {
 			userId: alice.id,
 		}, bob);
 
@@ -231,34 +231,34 @@ describe('FF visibility', () => {
 
 	test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず非フォロワーが見れない', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 400);
@@ -267,34 +267,34 @@ describe('FF visibility', () => {
 
 	test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず非フォロワーが見れない', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 400);
@@ -302,19 +302,19 @@ describe('FF visibility', () => {
 	});
 
 	test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => {
-		await api('/i/update', {
+		await api('i/update', {
 			followingVisibility: 'followers',
 			followersVisibility: 'followers',
 		}, alice);
 
-		await api('/following/create', {
+		await api('following/create', {
 			userId: alice.id,
 		}, bob);
 
-		const followingRes = await api('/users/following', {
+		const followingRes = await api('users/following', {
 			userId: alice.id,
 		}, bob);
-		const followersRes = await api('/users/followers', {
+		const followersRes = await api('users/followers', {
 			userId: alice.id,
 		}, bob);
 
@@ -326,45 +326,45 @@ describe('FF visibility', () => {
 
 	test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらずフォロワーが見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'public',
 			}, alice);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'followers',
 			}, alice);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'private',
 			}, alice);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 200);
@@ -374,45 +374,45 @@ describe('FF visibility', () => {
 
 	test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらずフォロワーが見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'followers',
 			}, alice);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'followers',
 			}, alice);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'followers',
 			}, alice);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, bob);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 200);
@@ -421,15 +421,15 @@ describe('FF visibility', () => {
 	});
 
 	test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを自分で見れる', async () => {
-		await api('/i/update', {
+		await api('i/update', {
 			followingVisibility: 'private',
 			followersVisibility: 'private',
 		}, alice);
 
-		const followingRes = await api('/users/following', {
+		const followingRes = await api('users/following', {
 			userId: alice.id,
 		}, alice);
-		const followersRes = await api('/users/followers', {
+		const followersRes = await api('users/followers', {
 			userId: alice.id,
 		}, alice);
 
@@ -441,36 +441,36 @@ describe('FF visibility', () => {
 
 	test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followingRes.status, 200);
 			assert.strictEqual(Array.isArray(followingRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followingRes.status, 200);
@@ -480,36 +480,36 @@ describe('FF visibility', () => {
 
 	test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followersRes.status, 200);
 			assert.strictEqual(Array.isArray(followersRes.body), true);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(followersRes.status, 200);
@@ -518,15 +518,15 @@ describe('FF visibility', () => {
 	});
 
 	test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを他人が見れない', async () => {
-		await api('/i/update', {
+		await api('i/update', {
 			followingVisibility: 'private',
 			followersVisibility: 'private',
 		}, alice);
 
-		const followingRes = await api('/users/following', {
+		const followingRes = await api('users/following', {
 			userId: alice.id,
 		}, bob);
-		const followersRes = await api('/users/followers', {
+		const followersRes = await api('users/followers', {
 			userId: alice.id,
 		}, bob);
 
@@ -536,34 +536,34 @@ describe('FF visibility', () => {
 
 	test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず他人が見れない', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'public',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'followers',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followingRes = await api('/users/following', {
+			const followingRes = await api('users/following', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followingRes.status, 400);
@@ -572,34 +572,34 @@ describe('FF visibility', () => {
 
 	test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず他人が見れない', async () => {
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'public',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'followers',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 400);
 		}
 		{
-			await api('/i/update', {
+			await api('i/update', {
 				followingVisibility: 'private',
 				followersVisibility: 'private',
 			}, alice);
 
-			const followersRes = await api('/users/followers', {
+			const followersRes = await api('users/followers', {
 				userId: alice.id,
 			}, bob);
 			assert.strictEqual(followersRes.status, 400);
@@ -609,7 +609,7 @@ describe('FF visibility', () => {
 	describe('AP', () => {
 		test('followingVisibility が public 以外ならばAPからはフォローを取得できない', async () => {
 			{
-				await api('/i/update', {
+				await api('i/update', {
 					followingVisibility: 'public',
 				}, alice);
 
@@ -617,7 +617,7 @@ describe('FF visibility', () => {
 				assert.strictEqual(followingRes.status, 200);
 			}
 			{
-				await api('/i/update', {
+				await api('i/update', {
 					followingVisibility: 'followers',
 				}, alice);
 
@@ -625,7 +625,7 @@ describe('FF visibility', () => {
 				assert.strictEqual(followingRes.status, 403);
 			}
 			{
-				await api('/i/update', {
+				await api('i/update', {
 					followingVisibility: 'private',
 				}, alice);
 
@@ -636,7 +636,7 @@ describe('FF visibility', () => {
 
 		test('followersVisibility が public 以外ならばAPからはフォロワーを取得できない', async () => {
 			{
-				await api('/i/update', {
+				await api('i/update', {
 					followersVisibility: 'public',
 				}, alice);
 
@@ -644,7 +644,7 @@ describe('FF visibility', () => {
 				assert.strictEqual(followersRes.status, 200);
 			}
 			{
-				await api('/i/update', {
+				await api('i/update', {
 					followersVisibility: 'followers',
 				}, alice);
 
@@ -652,7 +652,7 @@ describe('FF visibility', () => {
 				assert.strictEqual(followersRes.status, 403);
 			}
 			{
-				await api('/i/update', {
+				await api('i/update', {
 					followersVisibility: 'private',
 				}, alice);
 
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index f6417e39b5ec..4e5306da97da 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -55,7 +55,7 @@ describe('Account Move', () => {
 		}, 1000 * 10);
 
 		test('Able to create an alias', async () => {
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				alsoKnownAs: [`@alice@${url.hostname}`],
 			}, bob);
 
@@ -67,7 +67,7 @@ describe('Account Move', () => {
 		});
 
 		test('Able to create a local alias without hostname', async () => {
-			await api('/i/update', {
+			await api('i/update', {
 				alsoKnownAs: ['@alice'],
 			}, bob);
 
@@ -77,7 +77,7 @@ describe('Account Move', () => {
 		});
 
 		test('Able to create a local alias without @', async () => {
-			await api('/i/update', {
+			await api('i/update', {
 				alsoKnownAs: ['alice'],
 			}, bob);
 
@@ -87,7 +87,7 @@ describe('Account Move', () => {
 		});
 
 		test('Able to set remote user (but may fail)', async () => {
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				alsoKnownAs: ['@syuilo@example.com'],
 			}, bob);
 
@@ -97,7 +97,7 @@ describe('Account Move', () => {
 		});
 
 		test('Unable to add duplicated aliases to alsoKnownAs', async () => {
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				alsoKnownAs: [`@alice@${url.hostname}`, `@alice@${url.hostname}`],
 			}, bob);
 
@@ -107,7 +107,7 @@ describe('Account Move', () => {
 		});
 
 		test('Unable to add itself', async () => {
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				alsoKnownAs: [`@bob@${url.hostname}`],
 			}, bob);
 
@@ -117,7 +117,7 @@ describe('Account Move', () => {
 		});
 
 		test('Unable to add a nonexisting local account to alsoKnownAs', async () => {
-			const res1 = await api('/i/update', {
+			const res1 = await api('i/update', {
 				alsoKnownAs: [`@nonexist@${url.hostname}`],
 			}, bob);
 
@@ -125,7 +125,7 @@ describe('Account Move', () => {
 			assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER');
 			assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
 
-			const res2 = await api('/i/update', {
+			const res2 = await api('i/update', {
 				alsoKnownAs: ['@alice', 'nonexist'],
 			}, bob);
 
@@ -135,7 +135,7 @@ describe('Account Move', () => {
 		});
 
 		test('Able to add two existing local account to alsoKnownAs', async () => {
-			await api('/i/update', {
+			await api('i/update', {
 				alsoKnownAs: [`@alice@${url.hostname}`, `@carol@${url.hostname}`],
 			}, bob);
 
@@ -146,10 +146,10 @@ describe('Account Move', () => {
 		});
 
 		test('Able to properly overwrite alsoKnownAs', async () => {
-			await api('/i/update', {
+			await api('i/update', {
 				alsoKnownAs: [`@alice@${url.hostname}`],
 			}, bob);
-			await api('/i/update', {
+			await api('i/update', {
 				alsoKnownAs: [`@carol@${url.hostname}`, `@dave@${url.hostname}`],
 			}, bob);
 
@@ -164,27 +164,27 @@ describe('Account Move', () => {
 		let antennaId = '';
 
 		beforeAll(async () => {
-			await api('/i/update', {
+			await api('i/update', {
 				alsoKnownAs: [`@alice@${url.hostname}`],
 			}, root);
-			const listRoot = await api('/users/lists/create', {
+			const listRoot = await api('users/lists/create', {
 				name: secureRndstr(8),
 			}, root);
-			await api('/users/lists/push', {
+			await api('users/lists/push', {
 				listId: listRoot.body.id,
 				userId: alice.id,
 			}, root);
 
-			await api('/following/create', {
+			await api('following/create', {
 				userId: root.id,
 			}, alice);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: eve.id,
 			}, alice);
-			const antenna = await api('/antennas/create', {
+			const antenna = await api('antennas/create', {
 				name: secureRndstr(8),
 				src: 'home',
-				keywords: [secureRndstr(8)],
+				keywords: [[secureRndstr(8)]],
 				excludeKeywords: [],
 				users: [],
 				caseSensitive: false,
@@ -195,48 +195,48 @@ describe('Account Move', () => {
 			}, alice);
 			antennaId = antenna.body.id;
 
-			await api('/i/update', {
+			await api('i/update', {
 				alsoKnownAs: [`@alice@${url.hostname}`],
 			}, bob);
 
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, carol);
 
-			await api('/mute/create', {
+			await api('mute/create', {
 				userId: alice.id,
 			}, dave);
-			await api('/blocking/create', {
+			await api('blocking/create', {
 				userId: alice.id,
 			}, dave);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: eve.id,
 			}, dave);
 
-			await api('/following/create', {
+			await api('following/create', {
 				userId: dave.id,
 			}, eve);
-			const listEve = await api('/users/lists/create', {
+			const listEve = await api('users/lists/create', {
 				name: secureRndstr(8),
 			}, eve);
-			await api('/users/lists/push', {
+			await api('users/lists/push', {
 				listId: listEve.body.id,
 				userId: bob.id,
 			}, eve);
 
-			await api('/i/update', {
+			await api('i/update', {
 				isLocked: true,
 			}, frank);
-			await api('/following/create', {
+			await api('following/create', {
 				userId: frank.id,
 			}, alice);
-			await api('/following/requests/accept', {
+			await api('following/requests/accept', {
 				userId: alice.id,
 			}, frank);
 		}, 1000 * 10);
 
 		test('Prohibit the root account from moving', async () => {
-			const res = await api('/i/move', {
+			const res = await api('i/move', {
 				moveToAccount: `@bob@${url.hostname}`,
 			}, root);
 
@@ -246,7 +246,7 @@ describe('Account Move', () => {
 		});
 
 		test('Unable to move to a nonexisting local account', async () => {
-			const res = await api('/i/move', {
+			const res = await api('i/move', {
 				moveToAccount: `@nonexist@${url.hostname}`,
 			}, alice);
 
@@ -256,7 +256,7 @@ describe('Account Move', () => {
 		});
 
 		test('Unable to move if alsoKnownAs is invalid', async () => {
-			const res = await api('/i/move', {
+			const res = await api('i/move', {
 				moveToAccount: `@carol@${url.hostname}`,
 			}, alice);
 
@@ -266,7 +266,7 @@ describe('Account Move', () => {
 		});
 
 		test('Relationships have been properly migrated', async () => {
-			const move = await api('/i/move', {
+			const move = await api('i/move', {
 				moveToAccount: `@bob@${url.hostname}`,
 			}, alice);
 
@@ -275,13 +275,13 @@ describe('Account Move', () => {
 			await sleep(1000 * 3); // wait for jobs to finish
 
 			// Unfollow delayed?
-			const aliceFollowings = await api('/users/following', {
+			const aliceFollowings = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(aliceFollowings.status, 200);
 			assert.strictEqual(aliceFollowings.body.length, 3);
 
-			const carolFollowings = await api('/users/following', {
+			const carolFollowings = await api('users/following', {
 				userId: carol.id,
 			}, carol);
 			assert.strictEqual(carolFollowings.status, 200);
@@ -289,25 +289,25 @@ describe('Account Move', () => {
 			assert.strictEqual(carolFollowings.body[0].followeeId, bob.id);
 			assert.strictEqual(carolFollowings.body[1].followeeId, alice.id);
 
-			const blockings = await api('/blocking/list', {}, dave);
+			const blockings = await api('blocking/list', {}, dave);
 			assert.strictEqual(blockings.status, 200);
 			assert.strictEqual(blockings.body.length, 2);
 			assert.strictEqual(blockings.body[0].blockeeId, bob.id);
 			assert.strictEqual(blockings.body[1].blockeeId, alice.id);
 
-			const mutings = await api('/mute/list', {}, dave);
+			const mutings = await api('mute/list', {}, dave);
 			assert.strictEqual(mutings.status, 200);
 			assert.strictEqual(mutings.body.length, 2);
 			assert.strictEqual(mutings.body[0].muteeId, bob.id);
 			assert.strictEqual(mutings.body[1].muteeId, alice.id);
 
-			const rootLists = await api('/users/lists/list', {}, root);
+			const rootLists = await api('users/lists/list', {}, root);
 			assert.strictEqual(rootLists.status, 200);
 			assert.strictEqual(rootLists.body[0].userIds.length, 2);
 			assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id));
 			assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id));
 
-			const eveLists = await api('/users/lists/list', {}, eve);
+			const eveLists = await api('users/lists/list', {}, eve);
 			assert.strictEqual(eveLists.status, 200);
 			assert.strictEqual(eveLists.body[0].userIds.length, 1);
 			assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
@@ -315,13 +315,13 @@ describe('Account Move', () => {
 
 		test('A locked account automatically accept the follow request if it had already accepted the old account.', async () => {
 			await successfulApiCall({
-				endpoint: '/following/create',
+				endpoint: 'following/create',
 				parameters: {
 					userId: frank.id,
 				},
 				user: bob,
 			});
-			const followers = await api('/users/followers', {
+			const followers = await api('users/followers', {
 				userId: frank.id,
 			}, frank);
 
@@ -333,7 +333,7 @@ describe('Account Move', () => {
 		test('Unfollowed after 10 sec (24 hours in production).', async () => {
 			await sleep(1000 * 8);
 
-			const following = await api('/users/following', {
+			const following = await api('users/following', {
 				userId: alice.id,
 			}, alice);
 
@@ -342,7 +342,7 @@ describe('Account Move', () => {
 		});
 
 		test('Unable to move if the destination account has already moved.', async () => {
-			const res = await api('/i/move', {
+			const res = await api('i/move', {
 				moveToAccount: `@alice@${url.hostname}`,
 			}, bob);
 
@@ -352,7 +352,7 @@ describe('Account Move', () => {
 		});
 
 		test('Follow and follower counts are properly adjusted', async () => {
-			await api('/following/create', {
+			await api('following/create', {
 				userId: alice.id,
 			}, eve);
 			const newAlice = await Users.findOneByOrFail({ id: alice.id });
@@ -365,7 +365,7 @@ describe('Account Move', () => {
 			assert.strictEqual(newEve.followingCount, 1);
 			assert.strictEqual(newEve.followersCount, 1);
 
-			await api('/following/delete', {
+			await api('following/delete', {
 				userId: alice.id,
 			}, eve);
 			newEve = await Users.findOneByOrFail({ id: eve.id });
@@ -374,49 +374,49 @@ describe('Account Move', () => {
 		});
 
 		test.each([
-			'/antennas/create',
-			'/channels/create',
-			'/channels/favorite',
-			'/channels/follow',
-			'/channels/unfavorite',
-			'/channels/unfollow',
-			'/clips/add-note',
-			'/clips/create',
-			'/clips/favorite',
-			'/clips/remove-note',
-			'/clips/unfavorite',
-			'/clips/update',
-			'/drive/files/upload-from-url',
-			'/flash/create',
-			'/flash/like',
-			'/flash/unlike',
-			'/flash/update',
-			'/following/create',
-			'/gallery/posts/create',
-			'/gallery/posts/like',
-			'/gallery/posts/unlike',
-			'/gallery/posts/update',
-			'/i/claim-achievement',
-			'/i/move',
-			'/i/import-blocking',
-			'/i/import-following',
-			'/i/import-muting',
-			'/i/import-user-lists',
-			'/i/pin',
-			'/mute/create',
-			'/notes/create',
-			'/notes/favorites/create',
-			'/notes/polls/vote',
-			'/notes/reactions/create',
-			'/pages/create',
-			'/pages/like',
-			'/pages/unlike',
-			'/pages/update',
-			'/renote-mute/create',
-			'/users/lists/create',
-			'/users/lists/pull',
-			'/users/lists/push',
-		])('Prohibit access after moving: %s', async (endpoint) => {
+			'antennas/create',
+			'channels/create',
+			'channels/favorite',
+			'channels/follow',
+			'channels/unfavorite',
+			'channels/unfollow',
+			'clips/add-note',
+			'clips/create',
+			'clips/favorite',
+			'clips/remove-note',
+			'clips/unfavorite',
+			'clips/update',
+			'drive/files/upload-from-url',
+			'flash/create',
+			'flash/like',
+			'flash/unlike',
+			'flash/update',
+			'following/create',
+			'gallery/posts/create',
+			'gallery/posts/like',
+			'gallery/posts/unlike',
+			'gallery/posts/update',
+			'i/claim-achievement',
+			'i/move',
+			'i/import-blocking',
+			'i/import-following',
+			'i/import-muting',
+			'i/import-user-lists',
+			'i/pin',
+			'mute/create',
+			'notes/create',
+			'notes/favorites/create',
+			'notes/polls/vote',
+			'notes/reactions/create',
+			'pages/create',
+			'pages/like',
+			'pages/unlike',
+			'pages/update',
+			'renote-mute/create',
+			'users/lists/create',
+			'users/lists/pull',
+			'users/lists/push',
+		] as const)('Prohibit access after moving: %s', async (endpoint) => {
 			const res = await api(endpoint, {}, alice);
 			assert.strictEqual(res.status, 403);
 			assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
@@ -424,11 +424,11 @@ describe('Account Move', () => {
 		});
 
 		test('Prohibit access after moving: /antennas/update', async () => {
-			const res = await api('/antennas/update', {
+			const res = await api('antennas/update', {
 				antennaId,
 				name: secureRndstr(8),
 				src: 'users',
-				keywords: [secureRndstr(8)],
+				keywords: [[secureRndstr(8)]],
 				excludeKeywords: [],
 				users: [eve.id],
 				caseSensitive: false,
@@ -447,12 +447,12 @@ describe('Account Move', () => {
 			const res = await uploadFile(alice);
 
 			assert.strictEqual(res.status, 403);
-			assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
-			assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+			assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.code, 'YOUR_ACCOUNT_MOVED');
+			assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
 		});
 
 		test('Prohibit updating alsoKnownAs after moving', async () => {
-			const res = await api('/i/update', {
+			const res = await api('i/update', {
 				alsoKnownAs: [`@eve@${url.hostname}`],
 			}, alice);
 
diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts
index 1d28e07b7d72..0e52c5decc37 100644
--- a/packages/backend/test/e2e/mute.ts
+++ b/packages/backend/test/e2e/mute.ts
@@ -19,21 +19,31 @@ describe('Mute', () => {
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		carol = await signup({ username: 'carol' });
+
+		// Mute: alice ==> carol
+		await api('mute/create', {
+			userId: carol.id,
+		}, alice);
 	}, 1000 * 60 * 2);
 
 	test('ミュート作成', async () => {
-		const res = await api('/mute/create', {
-			userId: carol.id,
+		const res = await api('mute/create', {
+			userId: bob.id,
 		}, alice);
 
 		assert.strictEqual(res.status, 204);
+
+		// 単体でも走らせられるように副作用消す
+		await api('mute/delete', {
+			userId: bob.id,
+		}, alice);
 	});
 
 	test('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => {
 		const bobNote = await post(bob, { text: '@alice hi' });
 		const carolNote = await post(carol, { text: '@alice hi' });
 
-		const res = await api('/notes/mentions', {}, alice);
+		const res = await api('notes/mentions', {}, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
@@ -43,11 +53,11 @@ describe('Mute', () => {
 
 	test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
 		// 状態リセット
-		await api('/i/read-all-unread-notes', {}, alice);
+		await api('i/read-all-unread-notes', {}, alice);
 
 		await post(carol, { text: '@alice hi' });
 
-		const res = await api('/i', {}, alice);
+		const res = await api('i', {}, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(res.body.hasUnreadMentions, false);
@@ -55,7 +65,7 @@ describe('Mute', () => {
 
 	test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => {
 		// 状態リセット
-		await api('/i/read-all-unread-notes', {}, alice);
+		await api('i/read-all-unread-notes', {}, alice);
 
 		const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention');
 
@@ -64,8 +74,8 @@ describe('Mute', () => {
 
 	test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => {
 		// 状態リセット
-		await api('/i/read-all-unread-notes', {}, alice);
-		await api('/notifications/mark-all-as-read', {}, alice);
+		await api('i/read-all-unread-notes', {}, alice);
+		await api('notifications/mark-all-as-read', {}, alice);
 
 		const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification');
 
@@ -78,7 +88,7 @@ describe('Mute', () => {
 			const bobNote = await post(bob, { text: 'hi' });
 			const carolNote = await post(carol, { text: 'hi' });
 
-			const res = await api('/notes/local-timeline', {}, alice);
+			const res = await api('notes/local-timeline', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -94,7 +104,7 @@ describe('Mute', () => {
 				renoteId: carolNote.id,
 			});
 
-			const res = await api('/notes/local-timeline', {}, alice);
+			const res = await api('notes/local-timeline', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -110,7 +120,7 @@ describe('Mute', () => {
 			await react(bob, aliceNote, 'like');
 			await react(carol, aliceNote, 'like');
 
-			const res = await api('/i/notifications', {}, alice);
+			const res = await api('i/notifications', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -123,7 +133,7 @@ describe('Mute', () => {
 			await post(bob, { text: '@alice hi', replyId: aliceNote.id });
 			await post(carol, { text: '@alice hi', replyId: aliceNote.id });
 
-			const res = await api('/i/notifications', {}, alice);
+			const res = await api('i/notifications', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -137,7 +147,7 @@ describe('Mute', () => {
 			await post(bob, { text: '@alice hi' });
 			await post(carol, { text: '@alice hi' });
 
-			const res = await api('/i/notifications', {}, alice);
+			const res = await api('i/notifications', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -151,7 +161,7 @@ describe('Mute', () => {
 			await post(bob, { text: 'hi', renoteId: aliceNote.id });
 			await post(carol, { text: 'hi', renoteId: aliceNote.id });
 
-			const res = await api('/i/notifications', {}, alice);
+			const res = await api('i/notifications', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -165,7 +175,7 @@ describe('Mute', () => {
 			await post(bob, { renoteId: aliceNote.id });
 			await post(carol, { renoteId: aliceNote.id });
 
-			const res = await api('/i/notifications', {}, alice);
+			const res = await api('i/notifications', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -175,30 +185,36 @@ describe('Mute', () => {
 		});
 
 		test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
-			await api('/i/follow', { userId: alice.id }, bob);
-			await api('/i/follow', { userId: alice.id }, carol);
+			await api('following/create', { userId: alice.id }, bob);
+			await api('following/create', { userId: alice.id }, carol);
 
-			const res = await api('/i/notifications', {}, alice);
+			const res = await api('i/notifications', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+
+			await api('following/delete', { userId: alice.id }, bob);
+			await api('following/delete', { userId: alice.id }, carol);
 		});
 
 		test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
-			await api('/i/update/', { isLocked: true }, alice);
-			await api('/following/create', { userId: alice.id }, bob);
-			await api('/following/create', { userId: alice.id }, carol);
+			await api('i/update', { isLocked: true }, alice);
+			await api('following/create', { userId: alice.id }, bob);
+			await api('following/create', { userId: alice.id }, carol);
 
-			const res = await api('/i/notifications', {}, alice);
+			const res = await api('i/notifications', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+
+			await api('following/delete', { userId: alice.id }, bob);
+			await api('following/delete', { userId: alice.id }, carol);
 		});
 	});
 
@@ -208,7 +224,7 @@ describe('Mute', () => {
 			await react(bob, aliceNote, 'like');
 			await react(carol, aliceNote, 'like');
 
-			const res = await api('/i/notifications-grouped', {}, alice);
+			const res = await api('i/notifications-grouped', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -220,7 +236,7 @@ describe('Mute', () => {
 			await post(bob, { text: '@alice hi', replyId: aliceNote.id });
 			await post(carol, { text: '@alice hi', replyId: aliceNote.id });
 
-			const res = await api('/i/notifications-grouped', {}, alice);
+			const res = await api('i/notifications-grouped', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -234,7 +250,7 @@ describe('Mute', () => {
 			await post(bob, { text: '@alice hi' });
 			await post(carol, { text: '@alice hi' });
 
-			const res = await api('/i/notifications-grouped', {}, alice);
+			const res = await api('i/notifications-grouped', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -248,7 +264,7 @@ describe('Mute', () => {
 			await post(bob, { text: 'hi', renoteId: aliceNote.id });
 			await post(carol, { text: 'hi', renoteId: aliceNote.id });
 
-			const res = await api('/i/notifications-grouped', {}, alice);
+			const res = await api('i/notifications-grouped', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -262,7 +278,7 @@ describe('Mute', () => {
 			await post(bob, { renoteId: aliceNote.id });
 			await post(carol, { renoteId: aliceNote.id });
 
-			const res = await api('/i/notifications-grouped', {}, alice);
+			const res = await api('i/notifications-grouped', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
@@ -272,24 +288,27 @@ describe('Mute', () => {
 		});
 
 		test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
-			await api('/i/follow', { userId: alice.id }, bob);
-			await api('/i/follow', { userId: alice.id }, carol);
+			await api('following/create', { userId: alice.id }, bob);
+			await api('following/create', { userId: alice.id }, carol);
 
-			const res = await api('/i/notifications-grouped', {}, alice);
+			const res = await api('i/notifications-grouped', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
 			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+
+			await api('following/delete', { userId: alice.id }, bob);
+			await api('following/delete', { userId: alice.id }, carol);
 		});
 
 		test('通知にミュートしているユーザーからのフォローリクエストが含まれない', async () => {
-			await api('/i/update/', { isLocked: true }, alice);
-			await api('/following/create', { userId: alice.id }, bob);
-			await api('/following/create', { userId: alice.id }, carol);
+			await api('i/update', { isLocked: true }, alice);
+			await api('following/create', { userId: alice.id }, bob);
+			await api('following/create', { userId: alice.id }, carol);
 
-			const res = await api('/i/notifications-grouped', {}, alice);
+			const res = await api('i/notifications-grouped', {}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 2406204f4101..973bcbd750cf 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -31,7 +31,7 @@ describe('Note', () => {
 			text: 'test',
 		};
 
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -41,7 +41,7 @@ describe('Note', () => {
 	test('ファイルを添付できる', async () => {
 		const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
 
-		const res = await api('/notes/create', {
+		const res = await api('notes/create', {
 			fileIds: [file.id],
 		}, alice);
 
@@ -53,7 +53,7 @@ describe('Note', () => {
 	test('他人のファイルで怒られる', async () => {
 		const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
 
-		const res = await api('/notes/create', {
+		const res = await api('notes/create', {
 			text: 'test',
 			fileIds: [file.id],
 		}, alice);
@@ -64,7 +64,7 @@ describe('Note', () => {
 	}, 1000 * 10);
 
 	test('存在しないファイルで怒られる', async () => {
-		const res = await api('/notes/create', {
+		const res = await api('notes/create', {
 			text: 'test',
 			fileIds: ['000000000000000000000000'],
 		}, alice);
@@ -75,7 +75,7 @@ describe('Note', () => {
 	});
 
 	test('不正なファイルIDで怒られる', async () => {
-		const res = await api('/notes/create', {
+		const res = await api('notes/create', {
 			fileIds: ['kyoppie'],
 		}, alice);
 		assert.strictEqual(res.status, 400);
@@ -93,7 +93,7 @@ describe('Note', () => {
 			replyId: bobPost.id,
 		};
 
-		const res = await api('/notes/create', alicePost, alice);
+		const res = await api('notes/create', alicePost, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -111,7 +111,7 @@ describe('Note', () => {
 			renoteId: bobPost.id,
 		};
 
-		const res = await api('/notes/create', alicePost, alice);
+		const res = await api('notes/create', alicePost, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -129,7 +129,7 @@ describe('Note', () => {
 			renoteId: bobPost.id,
 		};
 
-		const res = await api('/notes/create', alicePost, alice);
+		const res = await api('notes/create', alicePost, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -142,7 +142,7 @@ describe('Note', () => {
 		const bobPost = await post(bob, {
 			text: 'test',
 		});
-		const res = await api('/notes/create', {
+		const res = await api('notes/create', {
 			text: ' ',
 			renoteId: bobPost.id,
 		}, alice);
@@ -152,7 +152,7 @@ describe('Note', () => {
 	});
 
 	test('visibility: followersでrenoteできる', async () => {
-		const createRes = await api('/notes/create', {
+		const createRes = await api('notes/create', {
 			text: 'test',
 			visibility: 'followers',
 		}, alice);
@@ -160,7 +160,7 @@ describe('Note', () => {
 		assert.strictEqual(createRes.status, 200);
 
 		const renoteId = createRes.body.createdNote.id;
-		const renoteRes = await api('/notes/create', {
+		const renoteRes = await api('notes/create', {
 			visibility: 'followers',
 			renoteId,
 		}, alice);
@@ -169,7 +169,7 @@ describe('Note', () => {
 		assert.strictEqual(renoteRes.body.createdNote.renoteId, renoteId);
 		assert.strictEqual(renoteRes.body.createdNote.visibility, 'followers');
 
-		const deleteRes = await api('/notes/delete', {
+		const deleteRes = await api('notes/delete', {
 			noteId: renoteRes.body.createdNote.id,
 		}, alice);
 
@@ -177,11 +177,11 @@ describe('Note', () => {
 	});
 
 	test('visibility: followersなノートに対してフォロワーはリプライできる', async () => {
-		await api('/following/create', {
+		await api('following/create', {
 			userId: alice.id,
 		}, bob);
 
-		const aliceNote = await api('/notes/create', {
+		const aliceNote = await api('notes/create', {
 			text: 'direct note to bob',
 			visibility: 'followers',
 		}, alice);
@@ -189,7 +189,7 @@ describe('Note', () => {
 		assert.strictEqual(aliceNote.status, 200);
 
 		const replyId = aliceNote.body.createdNote.id;
-		const bobReply = await api('/notes/create', {
+		const bobReply = await api('notes/create', {
 			text: 'reply to alice note',
 			replyId,
 		}, bob);
@@ -197,20 +197,20 @@ describe('Note', () => {
 		assert.strictEqual(bobReply.status, 200);
 		assert.strictEqual(bobReply.body.createdNote.replyId, replyId);
 
-		await api('/following/delete', {
+		await api('following/delete', {
 			userId: alice.id,
 		}, bob);
 	});
 
 	test('visibility: followersなノートに対してフォロワーでないユーザーがリプライしようとすると怒られる', async () => {
-		const aliceNote = await api('/notes/create', {
+		const aliceNote = await api('notes/create', {
 			text: 'direct note to bob',
 			visibility: 'followers',
 		}, alice);
 
 		assert.strictEqual(aliceNote.status, 200);
 
-		const bobReply = await api('/notes/create', {
+		const bobReply = await api('notes/create', {
 			text: 'reply to alice note',
 			replyId: aliceNote.body.createdNote.id,
 		}, bob);
@@ -220,7 +220,7 @@ describe('Note', () => {
 	});
 
 	test('visibility: specifiedなノートに対してvisibility: specifiedで返信できる', async () => {
-		const aliceNote = await api('/notes/create', {
+		const aliceNote = await api('notes/create', {
 			text: 'direct note to bob',
 			visibility: 'specified',
 			visibleUserIds: [bob.id],
@@ -228,7 +228,7 @@ describe('Note', () => {
 
 		assert.strictEqual(aliceNote.status, 200);
 
-		const bobReply = await api('/notes/create', {
+		const bobReply = await api('notes/create', {
 			text: 'reply to alice note',
 			replyId: aliceNote.body.createdNote.id,
 			visibility: 'specified',
@@ -239,7 +239,7 @@ describe('Note', () => {
 	});
 
 	test('visibility: specifiedなノートに対してvisibility: follwersで返信しようとすると怒られる', async () => {
-		const aliceNote = await api('/notes/create', {
+		const aliceNote = await api('notes/create', {
 			text: 'direct note to bob',
 			visibility: 'specified',
 			visibleUserIds: [bob.id],
@@ -247,7 +247,7 @@ describe('Note', () => {
 
 		assert.strictEqual(aliceNote.status, 200);
 
-		const bobReply = await api('/notes/create', {
+		const bobReply = await api('notes/create', {
 			text: 'reply to alice note with visibility: followers',
 			replyId: aliceNote.body.createdNote.id,
 			visibility: 'followers',
@@ -261,7 +261,7 @@ describe('Note', () => {
 		const post = {
 			text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000文字
 		};
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 		assert.strictEqual(res.status, 200);
 	});
 
@@ -269,7 +269,7 @@ describe('Note', () => {
 		const post = {
 			text: '!'.repeat(MAX_NOTE_TEXT_LENGTH + 1), // 3001文字
 		};
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 		assert.strictEqual(res.status, 400);
 	});
 
@@ -278,7 +278,7 @@ describe('Note', () => {
 			text: 'test',
 			replyId: '000000000000000000000000',
 		};
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 		assert.strictEqual(res.status, 400);
 	});
 
@@ -286,7 +286,7 @@ describe('Note', () => {
 		const post = {
 			renoteId: '000000000000000000000000',
 		};
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 		assert.strictEqual(res.status, 400);
 	});
 
@@ -295,7 +295,7 @@ describe('Note', () => {
 			text: 'test',
 			replyId: 'foo',
 		};
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 		assert.strictEqual(res.status, 400);
 	});
 
@@ -303,7 +303,7 @@ describe('Note', () => {
 		const post = {
 			renoteId: 'foo',
 		};
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 		assert.strictEqual(res.status, 400);
 	});
 
@@ -312,7 +312,7 @@ describe('Note', () => {
 			text: '@ghost yo',
 		};
 
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -324,7 +324,7 @@ describe('Note', () => {
 			text: '@bob @bob @bob yo',
 		};
 
-		const res = await api('/notes/create', post, alice);
+		const res = await api('notes/create', post, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
@@ -337,25 +337,25 @@ describe('Note', () => {
 	describe('添付ファイル情報', () => {
 		test('ファイルを添付した場合、投稿成功時にファイル情報入りのレスポンスが帰ってくる', async () => {
 			const file = await uploadFile(alice);
-			const res = await api('/notes/create', {
-				fileIds: [file.body.id],
+			const res = await api('notes/create', {
+				fileIds: [file.body!.id],
 			}, alice);
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
 			assert.strictEqual(res.body.createdNote.files.length, 1);
-			assert.strictEqual(res.body.createdNote.files[0].id, file.body.id);
+			assert.strictEqual(res.body.createdNote.files[0].id, file.body!.id);
 		});
 
 		test('ファイルを添付した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
 			const file = await uploadFile(alice);
-			const createdNote = await api('/notes/create', {
-				fileIds: [file.body.id],
+			const createdNote = await api('notes/create', {
+				fileIds: [file.body!.id],
 			}, alice);
 
 			assert.strictEqual(createdNote.status, 200);
 
-			const res = await api('/notes', {
+			const res = await api('notes', {
 				withFiles: true,
 			}, alice);
 
@@ -364,23 +364,23 @@ describe('Note', () => {
 			const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id);
 			assert.notEqual(myNote, null);
 			assert.strictEqual(myNote.files.length, 1);
-			assert.strictEqual(myNote.files[0].id, file.body.id);
+			assert.strictEqual(myNote.files[0].id, file.body!.id);
 		});
 
 		test('ファイルが添付されたノートをリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
 			const file = await uploadFile(alice);
-			const createdNote = await api('/notes/create', {
-				fileIds: [file.body.id],
+			const createdNote = await api('notes/create', {
+				fileIds: [file.body!.id],
 			}, alice);
 
 			assert.strictEqual(createdNote.status, 200);
 
-			const renoted = await api('/notes/create', {
+			const renoted = await api('notes/create', {
 				renoteId: createdNote.body.createdNote.id,
 			}, alice);
 			assert.strictEqual(renoted.status, 200);
 
-			const res = await api('/notes', {
+			const res = await api('notes', {
 				renote: true,
 			}, alice);
 
@@ -389,24 +389,24 @@ describe('Note', () => {
 			const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
 			assert.notEqual(myNote, null);
 			assert.strictEqual(myNote.renote.files.length, 1);
-			assert.strictEqual(myNote.renote.files[0].id, file.body.id);
+			assert.strictEqual(myNote.renote.files[0].id, file.body!.id);
 		});
 
 		test('ファイルが添付されたノートに返信した場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
 			const file = await uploadFile(alice);
-			const createdNote = await api('/notes/create', {
-				fileIds: [file.body.id],
+			const createdNote = await api('notes/create', {
+				fileIds: [file.body!.id],
 			}, alice);
 
 			assert.strictEqual(createdNote.status, 200);
 
-			const reply = await api('/notes/create', {
+			const reply = await api('notes/create', {
 				replyId: createdNote.body.createdNote.id,
 				text: 'this is reply',
 			}, alice);
 			assert.strictEqual(reply.status, 200);
 
-			const res = await api('/notes', {
+			const res = await api('notes', {
 				reply: true,
 			}, alice);
 
@@ -415,29 +415,29 @@ describe('Note', () => {
 			const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id);
 			assert.notEqual(myNote, null);
 			assert.strictEqual(myNote.reply.files.length, 1);
-			assert.strictEqual(myNote.reply.files[0].id, file.body.id);
+			assert.strictEqual(myNote.reply.files[0].id, file.body!.id);
 		});
 
 		test('ファイルが添付されたノートへの返信をリノートした場合、タイムラインでファイル情報入りのレスポンスが帰ってくる', async () => {
 			const file = await uploadFile(alice);
-			const createdNote = await api('/notes/create', {
-				fileIds: [file.body.id],
+			const createdNote = await api('notes/create', {
+				fileIds: [file.body!.id],
 			}, alice);
 
 			assert.strictEqual(createdNote.status, 200);
 
-			const reply = await api('/notes/create', {
+			const reply = await api('notes/create', {
 				replyId: createdNote.body.createdNote.id,
 				text: 'this is reply',
 			}, alice);
 			assert.strictEqual(reply.status, 200);
 
-			const renoted = await api('/notes/create', {
+			const renoted = await api('notes/create', {
 				renoteId: reply.body.createdNote.id,
 			}, alice);
 			assert.strictEqual(renoted.status, 200);
 
-			const res = await api('/notes', {
+			const res = await api('notes', {
 				renote: true,
 			}, alice);
 
@@ -446,7 +446,7 @@ describe('Note', () => {
 			const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
 			assert.notEqual(myNote, null);
 			assert.strictEqual(myNote.renote.reply.files.length, 1);
-			assert.strictEqual(myNote.renote.reply.files[0].id, file.body.id);
+			assert.strictEqual(myNote.renote.reply.files[0].id, file.body!.id);
 		});
 
 		test('NSFWが強制されている場合変更できない', async () => {
@@ -483,15 +483,15 @@ describe('Note', () => {
 			}, alice);
 
 			assert.strictEqual(assign.status, 204);
-			assert.strictEqual(file.body.isSensitive, false);
+			assert.strictEqual(file.body!.isSensitive, false);
 
 			const nsfwfile = await uploadFile(alice);
 
 			assert.strictEqual(nsfwfile.status, 200);
-			assert.strictEqual(nsfwfile.body.isSensitive, true);
+			assert.strictEqual(nsfwfile.body!.isSensitive, true);
 
 			const liftnsfw = await api('drive/files/update', {
-				fileId: nsfwfile.body.id,
+				fileId: nsfwfile.body!.id,
 				isSensitive: false,
 			}, alice);
 
@@ -499,7 +499,7 @@ describe('Note', () => {
 			assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE');
 
 			const oldaddnsfw = await api('drive/files/update', {
-				fileId: file.body.id,
+				fileId: file.body!.id,
 				isSensitive: true,
 			}, alice);
 
@@ -518,7 +518,7 @@ describe('Note', () => {
 
 	describe('notes/create', () => {
 		test('投票を添付できる', async () => {
-			const res = await api('/notes/create', {
+			const res = await api('notes/create', {
 				text: 'test',
 				poll: {
 					choices: ['foo', 'bar'],
@@ -531,14 +531,15 @@ describe('Note', () => {
 		});
 
 		test('投票の選択肢が無くて怒られる', async () => {
-			const res = await api('/notes/create', {
+			const res = await api('notes/create', {
+				// @ts-expect-error poll must not be empty
 				poll: {},
 			}, alice);
 			assert.strictEqual(res.status, 400);
 		});
 
 		test('投票の選択肢が無くて怒られる (空の配列)', async () => {
-			const res = await api('/notes/create', {
+			const res = await api('notes/create', {
 				poll: {
 					choices: [],
 				},
@@ -547,7 +548,7 @@ describe('Note', () => {
 		});
 
 		test('投票の選択肢が1つで怒られる', async () => {
-			const res = await api('/notes/create', {
+			const res = await api('notes/create', {
 				poll: {
 					choices: ['Strawberry Pasta'],
 				},
@@ -556,14 +557,14 @@ describe('Note', () => {
 		});
 
 		test('投票できる', async () => {
-			const { body } = await api('/notes/create', {
+			const { body } = await api('notes/create', {
 				text: 'test',
 				poll: {
 					choices: ['sakura', 'izumi', 'ako'],
 				},
 			}, alice);
 
-			const res = await api('/notes/polls/vote', {
+			const res = await api('notes/polls/vote', {
 				noteId: body.createdNote.id,
 				choice: 1,
 			}, alice);
@@ -572,19 +573,19 @@ describe('Note', () => {
 		});
 
 		test('複数投票できない', async () => {
-			const { body } = await api('/notes/create', {
+			const { body } = await api('notes/create', {
 				text: 'test',
 				poll: {
 					choices: ['sakura', 'izumi', 'ako'],
 				},
 			}, alice);
 
-			await api('/notes/polls/vote', {
+			await api('notes/polls/vote', {
 				noteId: body.createdNote.id,
 				choice: 0,
 			}, alice);
 
-			const res = await api('/notes/polls/vote', {
+			const res = await api('notes/polls/vote', {
 				noteId: body.createdNote.id,
 				choice: 2,
 			}, alice);
@@ -593,7 +594,7 @@ describe('Note', () => {
 		});
 
 		test('許可されている場合は複数投票できる', async () => {
-			const { body } = await api('/notes/create', {
+			const { body } = await api('notes/create', {
 				text: 'test',
 				poll: {
 					choices: ['sakura', 'izumi', 'ako'],
@@ -601,17 +602,17 @@ describe('Note', () => {
 				},
 			}, alice);
 
-			await api('/notes/polls/vote', {
+			await api('notes/polls/vote', {
 				noteId: body.createdNote.id,
 				choice: 0,
 			}, alice);
 
-			await api('/notes/polls/vote', {
+			await api('notes/polls/vote', {
 				noteId: body.createdNote.id,
 				choice: 1,
 			}, alice);
 
-			const res = await api('/notes/polls/vote', {
+			const res = await api('notes/polls/vote', {
 				noteId: body.createdNote.id,
 				choice: 2,
 			}, alice);
@@ -620,7 +621,7 @@ describe('Note', () => {
 		});
 
 		test('締め切られている場合は投票できない', async () => {
-			const { body } = await api('/notes/create', {
+			const { body } = await api('notes/create', {
 				text: 'test',
 				poll: {
 					choices: ['sakura', 'izumi', 'ako'],
@@ -630,7 +631,7 @@ describe('Note', () => {
 
 			await new Promise(x => setTimeout(x, 2));
 
-			const res = await api('/notes/polls/vote', {
+			const res = await api('notes/polls/vote', {
 				noteId: body.createdNote.id,
 				choice: 1,
 			}, alice);
@@ -649,7 +650,7 @@ describe('Note', () => {
 
 			await new Promise(x => setTimeout(x, 2));
 
-			const note1 = await api('/notes/create', {
+			const note1 = await api('notes/create', {
 				text: 'hogetesthuge',
 			}, alice);
 
@@ -666,7 +667,7 @@ describe('Note', () => {
 
 			assert.strictEqual(sensitive.status, 204);
 
-			const note2 = await api('/notes/create', {
+			const note2 = await api('notes/create', {
 				text: 'hogetesthuge',
 			}, alice);
 
@@ -683,7 +684,7 @@ describe('Note', () => {
 
 			assert.strictEqual(sensitive.status, 204);
 
-			const note2 = await api('/notes/create', {
+			const note2 = await api('notes/create', {
 				text: 'hogeTesthuge',
 			}, alice);
 
@@ -702,7 +703,7 @@ describe('Note', () => {
 
 			await new Promise(x => setTimeout(x, 2));
 
-			const note1 = await api('/notes/create', {
+			const note1 = await api('notes/create', {
 				text: 'hogetesthuge',
 			}, alice);
 
@@ -719,7 +720,7 @@ describe('Note', () => {
 
 			assert.strictEqual(prohibited.status, 204);
 
-			const note2 = await api('/notes/create', {
+			const note2 = await api('notes/create', {
 				text: 'hogetesthuge',
 			}, alice);
 
@@ -736,7 +737,7 @@ describe('Note', () => {
 
 			assert.strictEqual(prohibited.status, 204);
 
-			const note2 = await api('/notes/create', {
+			const note2 = await api('notes/create', {
 				text: 'hogeTesthuge',
 			}, alice);
 
@@ -755,7 +756,7 @@ describe('Note', () => {
 
 			await new Promise(x => setTimeout(x, 2));
 
-			const note1 = await api('/notes/create', {
+			const note1 = await api('notes/create', {
 				text: 'hogetesthuge',
 			}, tom);
 
@@ -799,7 +800,7 @@ describe('Note', () => {
 
 			await new Promise(x => setTimeout(x, 2));
 
-			const note = await api('/notes/create', {
+			const note = await api('notes/create', {
 				text: '@bob potentially annoying text',
 			}, alice);
 
@@ -853,10 +854,10 @@ describe('Note', () => {
 
 			await new Promise(x => setTimeout(x, 2));
 
-			const note = await api('/notes/create', {
+			const note = await api('notes/create', {
 				text: 'potentially annoying text',
 				visibility: 'specified',
-				visibleUserIds: [ bob.id ],
+				visibleUserIds: [bob.id],
 			}, alice);
 
 			assert.strictEqual(note.status, 400);
@@ -909,10 +910,10 @@ describe('Note', () => {
 
 			await new Promise(x => setTimeout(x, 2));
 
-			const note = await api('/notes/create', {
+			const note = await api('notes/create', {
 				text: '@bob potentially annoying text',
 				visibility: 'specified',
-				visibleUserIds: [ bob.id ],
+				visibleUserIds: [bob.id],
 			}, alice);
 
 			assert.strictEqual(note.status, 200);
diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts
index 403de0cb8d81..9826068e4826 100644
--- a/packages/backend/test/e2e/renote-mute.ts
+++ b/packages/backend/test/e2e/renote-mute.ts
@@ -22,7 +22,7 @@ describe('Renote Mute', () => {
 	}, 1000 * 60 * 2);
 
 	test('ミュート作成', async () => {
-		const res = await api('/renote-mute/create', {
+		const res = await api('renote-mute/create', {
 			userId: carol.id,
 		}, alice);
 
@@ -37,7 +37,7 @@ describe('Renote Mute', () => {
 		// redisに追加されるのを待つ
 		await sleep(100);
 
-		const res = await api('/notes/local-timeline', {}, alice);
+		const res = await api('notes/local-timeline', {}, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
@@ -54,7 +54,7 @@ describe('Renote Mute', () => {
 		// redisに追加されるのを待つ
 		await sleep(100);
 
-		const res = await api('/notes/local-timeline', {}, alice);
+		const res = await api('notes/local-timeline', {}, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index 57ce73ba603b..edb930617fff 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -601,7 +601,7 @@ describe('Streaming', () => {
 
 			// #10443
 			test('ミュートしているサーバのノートがリストTLに流れない', async () => {
-				await api('/i/update', {
+				await api('i/update', {
 					mutedInstances: ['example.com'],
 				}, chitose);
 
@@ -618,7 +618,7 @@ describe('Streaming', () => {
 
 			// #10443
 			test('ミュートしているサーバのノートに対するリプライがリストTLに流れない', async () => {
-				await api('/i/update', {
+				await api('i/update', {
 					mutedInstances: ['example.com'],
 				}, chitose);
 
@@ -635,7 +635,7 @@ describe('Streaming', () => {
 
 			// #10443
 			test('ミュートしているサーバのノートに対するリノートがリストTLに流れない', async () => {
-				await api('/i/update', {
+				await api('i/update', {
 					mutedInstances: ['example.com'],
 				}, chitose);
 
diff --git a/packages/backend/test/e2e/thread-mute.ts b/packages/backend/test/e2e/thread-mute.ts
index b4570cdef15c..53bb6eb765a6 100644
--- a/packages/backend/test/e2e/thread-mute.ts
+++ b/packages/backend/test/e2e/thread-mute.ts
@@ -24,12 +24,12 @@ describe('Note thread mute', () => {
 		const bobNote = await post(bob, { text: '@alice @carol root note' });
 		const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
 
-		await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
+		await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
 
 		const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
 		const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
 
-		const res = await api('/notes/mentions', {}, alice);
+		const res = await api('notes/mentions', {}, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
@@ -40,15 +40,15 @@ describe('Note thread mute', () => {
 
 	test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
 		// 状態リセット
-		await api('/i/read-all-unread-notes', {}, alice);
+		await api('i/read-all-unread-notes', {}, alice);
 
 		const bobNote = await post(bob, { text: '@alice @carol root note' });
 
-		await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
+		await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
 
 		const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
 
-		const res = await api('/i', {}, alice);
+		const res = await api('i', {}, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(res.body.hasUnreadMentions, false);
@@ -56,11 +56,11 @@ describe('Note thread mute', () => {
 
 	test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise<void>(async done => {
 		// 状態リセット
-		await api('/i/read-all-unread-notes', {}, alice);
+		await api('i/read-all-unread-notes', {}, alice);
 
 		const bobNote = await post(bob, { text: '@alice @carol root note' });
 
-		await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
+		await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
 
 		let fired = false;
 
@@ -84,12 +84,12 @@ describe('Note thread mute', () => {
 		const bobNote = await post(bob, { text: '@alice @carol root note' });
 		const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' });
 
-		await api('/notes/thread-muting/create', { noteId: bobNote.id }, alice);
+		await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
 
 		const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
 		const carolReplyWithoutMention = await post(carol, { replyId: aliceReply.id, text: 'child note' });
 
-		const res = await api('/i/notifications', {}, alice);
+		const res = await api('i/notifications', {}, alice);
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index 0e71d707dd13..d413703edeee 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -26,7 +26,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
@@ -35,14 +35,14 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーのノートが含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi' });
 			const carolNote = await post(carol, { text: 'hi' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -51,14 +51,14 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 			const carolNote = await post(carol, { text: 'hi' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
@@ -68,14 +68,14 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: false でフォローしているユーザーの他人への返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -84,15 +84,15 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/following/update', { userId: bob.id, withReplies: true }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -101,15 +101,15 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーの他人へのDM返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/following/update', { userId: bob.id, withReplies: true }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -118,15 +118,15 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/following/update', { userId: bob.id, withReplies: true }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -135,17 +135,17 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/following/create', { userId: carol.id }, alice);
-			await api('/following/create', { userId: carol.id }, bob);
-			await api('/following/update', { userId: bob.id, withReplies: true }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: carol.id }, alice);
+			await api('following/create', { userId: carol.id }, bob);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
@@ -155,16 +155,16 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/following/create', { userId: carol.id }, alice);
-			await api('/following/update', { userId: bob.id, withReplies: true }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: carol.id }, alice);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
@@ -173,14 +173,14 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: false でフォローしているユーザーのそのユーザー自身への返信が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -189,14 +189,14 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
@@ -210,7 +210,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
@@ -219,14 +219,14 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーの他人の投稿のリノートが含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { renoteId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -235,14 +235,14 @@ describe('Timelines', () => {
 		test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { renoteId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', {
+			const res = await api('notes/timeline', {
 				withRenotes: false,
 			}, alice);
 
@@ -253,14 +253,14 @@ describe('Timelines', () => {
 		test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿の引用が含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', {
+			const res = await api('notes/timeline', {
 				withRenotes: false,
 			}, alice);
 
@@ -271,13 +271,13 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーの他人への visibility: specified なノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -285,15 +285,15 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/mute/create', { userId: carol.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('mute/create', { userId: carol.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -302,16 +302,16 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/following/update', { userId: bob.id, withReplies: true }, alice);
-			await api('/mute/create', { userId: carol.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
+			await api('mute/create', { userId: carol.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -321,13 +321,13 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
 			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -336,13 +336,13 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
 			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -350,7 +350,7 @@ describe('Timelines', () => {
 		test.concurrent('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const [bobFile, carolFile] = await Promise.all([
 				uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
@@ -363,7 +363,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100, withFiles: true }, alice);
+			const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -374,14 +374,14 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body);
-			await api('/following/create', { userId: bob.id }, alice);
+			const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -393,7 +393,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
@@ -402,13 +402,13 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
@@ -421,7 +421,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -429,13 +429,13 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -448,7 +448,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok');
@@ -463,7 +463,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok');
@@ -479,7 +479,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/timeline', { limit: 100 }, alice);
+			const res = await api('notes/timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -494,7 +494,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -508,7 +508,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
@@ -522,7 +522,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -531,12 +531,12 @@ describe('Timelines', () => {
 		test.concurrent('チャンネル投稿が含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body);
+			const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -548,7 +548,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -557,14 +557,14 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーの visibility: home なノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: carol.id }, alice);
+			await api('following/create', { userId: carol.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi', visibility: 'home' });
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -573,14 +573,14 @@ describe('Timelines', () => {
 		test.concurrent('ミュートしているユーザーのノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/mute/create', { userId: carol.id }, alice);
+			await api('mute/create', { userId: carol.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -589,15 +589,15 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/mute/create', { userId: carol.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('mute/create', { userId: carol.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -606,16 +606,16 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			await api('/following/update', { userId: bob.id, withReplies: true }, alice);
-			await api('/mute/create', { userId: carol.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
+			await api('mute/create', { userId: carol.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
@@ -624,14 +624,14 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
@@ -645,7 +645,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100, withReplies: true }, alice);
+			const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -659,7 +659,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100, withFiles: true }, alice);
+			const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -674,7 +674,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -686,7 +686,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -694,13 +694,13 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -708,14 +708,14 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
@@ -729,7 +729,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
@@ -742,7 +742,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/local-timeline', { limit: 100 }, alice);
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -751,13 +751,13 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
 			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -766,13 +766,13 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
 
 			await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' });
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100 }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -785,7 +785,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -799,7 +799,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
+			const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -810,14 +810,14 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているフォローしていないユーザーのノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -825,14 +825,14 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -840,14 +840,14 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -855,15 +855,15 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -871,15 +871,15 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -888,15 +888,15 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id, withReplies: false }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id, withReplies: false }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -904,16 +904,16 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await api('/users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -921,15 +921,15 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -937,15 +937,15 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
@@ -954,14 +954,14 @@ describe('Timelines', () => {
 		test.concurrent('リスインしている自分の visibility: followers なノートが含まれる', async () => {
 			const [alice] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: alice.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: alice.id }, alice);
 			await sleep(1000);
 			const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
@@ -970,15 +970,15 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body);
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -986,15 +986,15 @@ describe('Timelines', () => {
 		test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { fileIds: [file.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id, withFiles: true }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -1003,14 +1003,14 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
@@ -1019,15 +1019,15 @@ describe('Timelines', () => {
 		test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
-			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await api('/users/lists/push', { listId: list.id, userId: carol.id }, alice);
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			await api('users/lists/push', { listId: list.id, userId: carol.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
 
 			await waitForPushToTl();
 
-			const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -1041,7 +1041,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id }, alice);
+			const res = await api('users/notes', { userId: bob.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -1053,7 +1053,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id }, alice);
+			const res = await api('users/notes', { userId: bob.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -1061,13 +1061,13 @@ describe('Timelines', () => {
 		test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id }, alice);
+			const res = await api('users/notes', { userId: bob.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
@@ -1080,7 +1080,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: alice.id }, alice);
+			const res = await api('users/notes', { userId: alice.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
@@ -1089,12 +1089,12 @@ describe('Timelines', () => {
 		test.concurrent('チャンネル投稿が含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body);
+			const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id }, alice);
+			const res = await api('users/notes', { userId: bob.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -1108,7 +1108,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id }, alice);
+			const res = await api('users/notes', { userId: bob.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false);
@@ -1123,7 +1123,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice);
+			const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -1138,7 +1138,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice);
+			const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false);
@@ -1153,7 +1153,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id, withFiles: true }, alice);
+			const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -1162,12 +1162,12 @@ describe('Timelines', () => {
 		test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body);
+			const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, alice);
+			const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -1175,12 +1175,12 @@ describe('Timelines', () => {
 		test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
+			const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, alice);
+			const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -1188,12 +1188,12 @@ describe('Timelines', () => {
 		test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => {
 			const [bob] = await Promise.all([signup()]);
 
-			const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
+			const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, bob);
+			const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
@@ -1201,14 +1201,14 @@ describe('Timelines', () => {
 		test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
-			await api('/mute/create', { userId: carol.id }, alice);
+			await api('mute/create', { userId: carol.id }, alice);
 			await sleep(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id }, alice);
+			const res = await api('users/notes', { userId: bob.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
@@ -1216,7 +1216,7 @@ describe('Timelines', () => {
 		test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			await api('/mute/create', { userId: bob.id }, alice);
+			await api('mute/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
@@ -1224,7 +1224,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id }, alice);
+			const res = await api('users/notes', { userId: bob.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
@@ -1238,7 +1238,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: alice.id, withReplies: true }, alice);
+			const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
 		});
@@ -1250,7 +1250,7 @@ describe('Timelines', () => {
 
 			await waitForPushToTl();
 
-			const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice);
+			const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
diff --git a/packages/backend/test/e2e/user-notes.ts b/packages/backend/test/e2e/user-notes.ts
index 6897cf08c626..331e053935f8 100644
--- a/packages/backend/test/e2e/user-notes.ts
+++ b/packages/backend/test/e2e/user-notes.ts
@@ -11,9 +11,9 @@ import type * as misskey from 'misskey-js';
 
 describe('users/notes', () => {
 	let alice: misskey.entities.SignupResponse;
-	let jpgNote: any;
-	let pngNote: any;
-	let jpgPngNote: any;
+	let jpgNote: misskey.entities.Note;
+	let pngNote: misskey.entities.Note;
+	let jpgPngNote: misskey.entities.Note;
 
 	beforeAll(async () => {
 		alice = await signup({ username: 'alice' });
@@ -31,7 +31,7 @@ describe('users/notes', () => {
 	}, 1000 * 60 * 2);
 
 	test('withFiles', async () => {
-		const res = await api('/users/notes', {
+		const res = await api('users/notes', {
 			userId: alice.id,
 			withFiles: true,
 		}, alice);
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 3cf2a5dee186..3458e06384f2 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -8,7 +8,7 @@ process.env.NODE_ENV = 'test';
 import * as assert from 'assert';
 import { inspect } from 'node:util';
 import { DEFAULT_POLICIES } from '@/core/RoleService.js';
-import { api, page, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
+import { api, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('ユーザー', () => {
@@ -24,31 +24,12 @@ describe('ユーザー', () => {
 			}, {});
 	};
 
-	// BUG misskey-jsとjson-schemaと実際に返ってくるデータが全部違う
-	type UserLite = misskey.entities.UserLite & {
-		badgeRoles: any[],
-	};
-
-	type UserDetailedNotMe = UserLite &
-	misskey.entities.UserDetailed & {
-		roles: any[],
-	};
-
-	type MeDetailed = UserDetailedNotMe &
-		misskey.entities.MeDetailed & {
-		achievements: object[],
-		loggedInDays: number,
-		policies: object,
-	};
-
-	type User = MeDetailed & { token: string };
-
-	const show = async (id: string, me = root): Promise<MeDetailed | UserDetailedNotMe> => {
-		return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me }) as any;
+	const show = async (id: string, me = root): Promise<misskey.entities.UserDetailed> => {
+		return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me });
 	};
 
 	// UserLiteのキーが過不足なく入っている?
-	const userLite = (user: User): Partial<UserLite> => {
+	const userLite = (user: misskey.entities.UserLite): Partial<misskey.entities.UserLite> => {
 		return stripUndefined({
 			id: user.id,
 			name: user.name,
@@ -71,7 +52,7 @@ describe('ユーザー', () => {
 	};
 
 	// UserDetailedNotMeのキーが過不足なく入っている?
-	const userDetailedNotMe = (user: User): Partial<UserDetailedNotMe> => {
+	const userDetailedNotMe = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => {
 		return stripUndefined({
 			...userLite(user),
 			url: user.url,
@@ -111,7 +92,7 @@ describe('ユーザー', () => {
 	};
 
 	// Relations関連のキーが過不足なく入っている?
-	const userDetailedNotMeWithRelations = (user: User): Partial<UserDetailedNotMe> => {
+	const userDetailedNotMeWithRelations = (user: misskey.entities.SignupResponse): Partial<misskey.entities.UserDetailedNotMe> => {
 		return stripUndefined({
 			...userDetailedNotMe(user),
 			isFollowing: user.isFollowing ?? false,
@@ -128,7 +109,7 @@ describe('ユーザー', () => {
 	};
 
 	// MeDetailedのキーが過不足なく入っている?
-	const meDetailed = (user: User, security = false): Partial<MeDetailed> => {
+	const meDetailed = (user: misskey.entities.SignupResponse, security = false): Partial<misskey.entities.MeDetailed> => {
 		return stripUndefined({
 			...userDetailedNotMe(user),
 			avatarId: user.avatarId,
@@ -159,6 +140,7 @@ describe('ユーザー', () => {
 			mutedWords: user.mutedWords,
 			hardMutedWords: user.hardMutedWords,
 			mutedInstances: user.mutedInstances,
+			// @ts-expect-error 後方互換性
 			mutingNotificationTypes: user.mutingNotificationTypes,
 			notificationRecieveConfig: user.notificationRecieveConfig,
 			emailNotificationTypes: user.emailNotificationTypes,
@@ -173,61 +155,53 @@ describe('ユーザー', () => {
 		});
 	};
 
-	let root: User;
-	let alice: User;
+	let root: misskey.entities.SignupResponse;
+	let alice: misskey.entities.SignupResponse;
 	let aliceNote: misskey.entities.Note;
-	let alicePage: misskey.entities.Page;
-	let aliceList: misskey.entities.UserList;
-
-	let bob: User;
-	let bobNote: misskey.entities.Note;
-
-	let carol: User;
-	let dave: User;
-	let ellen: User;
-	let frank: User;
-
-	let usersReplying: User[];
-
-	let userNoNote: User;
-	let userNotExplorable: User;
-	let userLocking: User;
-	let userAdmin: User;
-	let roleAdmin: any;
-	let userModerator: User;
-	let roleModerator: any;
-	let userRolePublic: User;
-	let rolePublic: any;
-	let userRoleBadge: User;
-	let roleBadge: any;
-	let userSilenced: User;
-	let roleSilenced: any;
-	let userSuspended: User;
-	let userDeletedBySelf: User;
-	let userDeletedByAdmin: User;
-	let userFollowingAlice: User;
-	let userFollowedByAlice: User;
-	let userBlockingAlice: User;
-	let userBlockedByAlice: User;
-	let userMutingAlice: User;
-	let userMutedByAlice: User;
-	let userRnMutingAlice: User;
-	let userRnMutedByAlice: User;
-	let userFollowRequesting: User;
-	let userFollowRequested: User;
+
+	let bob: misskey.entities.SignupResponse;
+
+	// NOTE: これがないと落ちる(bob の updatedAt が null になってしまうため?)
+	let bobNote: misskey.entities.Note; // eslint-disable-line @typescript-eslint/no-unused-vars
+
+	let carol: misskey.entities.SignupResponse;
+
+	let usersReplying: misskey.entities.SignupResponse[];
+
+	let userNoNote: misskey.entities.SignupResponse;
+	let userNotExplorable: misskey.entities.SignupResponse;
+	let userLocking: misskey.entities.SignupResponse;
+	let userAdmin: misskey.entities.SignupResponse;
+	let roleAdmin: misskey.entities.Role;
+	let userModerator: misskey.entities.SignupResponse;
+	let roleModerator: misskey.entities.Role;
+	let userRolePublic: misskey.entities.SignupResponse;
+	let rolePublic: misskey.entities.Role;
+	let userRoleBadge: misskey.entities.SignupResponse;
+	let roleBadge: misskey.entities.Role;
+	let userSilenced: misskey.entities.SignupResponse;
+	let roleSilenced: misskey.entities.Role;
+	let userSuspended: misskey.entities.SignupResponse;
+	let userDeletedBySelf: misskey.entities.SignupResponse;
+	let userDeletedByAdmin: misskey.entities.SignupResponse;
+	let userFollowingAlice: misskey.entities.SignupResponse;
+	let userFollowedByAlice: misskey.entities.SignupResponse;
+	let userBlockingAlice: misskey.entities.SignupResponse;
+	let userBlockedByAlice: misskey.entities.SignupResponse;
+	let userMutingAlice: misskey.entities.SignupResponse;
+	let userMutedByAlice: misskey.entities.SignupResponse;
+	let userRnMutingAlice: misskey.entities.SignupResponse;
+	let userRnMutedByAlice: misskey.entities.SignupResponse;
+	let userFollowRequesting: misskey.entities.SignupResponse;
+	let userFollowRequested: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		root = await signup({ username: 'root' });
 		alice = await signup({ username: 'alice' });
-		aliceNote = await post(alice, { text: 'test' }) as any;
-		alicePage = await page(alice);
-		aliceList = (await api('users/list/create', { name: 'aliceList' }, alice)).body;
+		aliceNote = await post(alice, { text: 'test' });
 		bob = await signup({ username: 'bob' });
-		bobNote = await post(bob, { text: 'test' }) as any;
+		bobNote = await post(bob, { text: 'test' });
 		carol = await signup({ username: 'carol' });
-		dave = await signup({ username: 'dave' });
-		ellen = await signup({ username: 'ellen' });
-		frank = await signup({ username: 'frank' });
 
 		// @alice -> @replyingへのリプライ。Promise.allで一気に作るとtimeoutしてしまうのでreduceで一つ一つawaitする
 		usersReplying = await [...Array(10)].map((_, i) => i).reduce(async (acc, i) => {
@@ -238,7 +212,7 @@ describe('ユーザー', () => {
 			}
 
 			return (await acc).concat(u);
-		}, Promise.resolve([] as User[]));
+		}, Promise.resolve([] as misskey.entities.SignupResponse[]));
 
 		userNoNote = await signup({ username: 'userNoNote' });
 		userNotExplorable = await signup({ username: 'userNotExplorable' });
@@ -306,7 +280,7 @@ describe('ユーザー', () => {
 	beforeEach(async () => {
 		alice = {
 			...alice,
-			...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }) as any,
+			...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }),
 		};
 		aliceNote = await successfulApiCall({ endpoint: 'notes/show', parameters: { noteId: aliceNote.id }, user: alice });
 	});
@@ -319,7 +293,7 @@ describe('ユーザー', () => {
 			endpoint: 'signup',
 			parameters: { username: 'zoe', password: 'password' },
 			user: undefined,
-		}) as unknown as User; // BUG MeDetailedに足りないキーがある
+		}) as unknown as misskey.entities.SignupResponse; // BUG MeDetailedに足りないキーがある
 
 		// signupの時はtokenが含まれる特別なMeDetailedが返ってくる
 		assert.match(response.token, /[a-zA-Z0-9]{16}/);
@@ -329,7 +303,7 @@ describe('ユーザー', () => {
 		assert.strictEqual(response.name, null);
 		assert.strictEqual(response.username, 'zoe');
 		assert.strictEqual(response.host, null);
-		assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
+		response.avatarUrl && assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
 		assert.strictEqual(response.avatarBlurhash, null);
 		assert.deepStrictEqual(response.avatarDecorations, []);
 		assert.strictEqual(response.isBot, false);
@@ -401,6 +375,7 @@ describe('ユーザー', () => {
 		assert.deepStrictEqual(response.unreadAnnouncements, []);
 		assert.deepStrictEqual(response.mutedWords, []);
 		assert.deepStrictEqual(response.mutedInstances, []);
+		// @ts-expect-error 後方互換のため
 		assert.deepStrictEqual(response.mutingNotificationTypes, []);
 		assert.deepStrictEqual(response.notificationRecieveConfig, {});
 		assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']);
@@ -430,66 +405,66 @@ describe('ユーザー', () => {
 	//#region 自分の情報の更新(i/update)
 
 	test.each([
-		{ parameters: (): object => ({ name: null }) },
-		{ parameters: (): object => ({ name: 'x'.repeat(50) }) },
-		{ parameters: (): object => ({ name: 'x' }) },
-		{ parameters: (): object => ({ name: 'My name' }) },
-		{ parameters: (): object => ({ description: null }) },
-		{ parameters: (): object => ({ description: 'x'.repeat(1500) }) },
-		{ parameters: (): object => ({ description: 'x' }) },
-		{ parameters: (): object => ({ description: 'My description' }) },
-		{ parameters: (): object => ({ location: null }) },
-		{ parameters: (): object => ({ location: 'x'.repeat(50) }) },
-		{ parameters: (): object => ({ location: 'x' }) },
-		{ parameters: (): object => ({ location: 'My location' }) },
-		{ parameters: (): object => ({ birthday: '0000-00-00' }) },
-		{ parameters: (): object => ({ birthday: '9999-99-99' }) },
-		{ parameters: (): object => ({ lang: 'en-US' }) },
-		{ parameters: (): object => ({ fields: [] }) },
-		{ parameters: (): object => ({ fields: [{ name: 'x', value: 'x' }] }) },
-		{ parameters: (): object => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない
-		{ parameters: (): object => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) },
-		{ parameters: (): object => ({ isLocked: true }) },
-		{ parameters: (): object => ({ isLocked: false }) },
-		{ parameters: (): object => ({ isExplorable: false }) },
-		{ parameters: (): object => ({ isExplorable: true }) },
-		{ parameters: (): object => ({ hideOnlineStatus: true }) },
-		{ parameters: (): object => ({ hideOnlineStatus: false }) },
-		{ parameters: (): object => ({ publicReactions: false }) },
-		{ parameters: (): object => ({ publicReactions: true }) },
-		{ parameters: (): object => ({ autoAcceptFollowed: true }) },
-		{ parameters: (): object => ({ autoAcceptFollowed: false }) },
-		{ parameters: (): object => ({ noCrawle: true }) },
-		{ parameters: (): object => ({ noCrawle: false }) },
-		{ parameters: (): object => ({ preventAiLearning: false }) },
-		{ parameters: (): object => ({ preventAiLearning: true }) },
-		{ parameters: (): object => ({ isBot: true }) },
-		{ parameters: (): object => ({ isBot: false }) },
-		{ parameters: (): object => ({ isCat: true }) },
-		{ parameters: (): object => ({ isCat: false }) },
-		{ parameters: (): object => ({ injectFeaturedNote: true }) },
-		{ parameters: (): object => ({ injectFeaturedNote: false }) },
-		{ parameters: (): object => ({ receiveAnnouncementEmail: true }) },
-		{ parameters: (): object => ({ receiveAnnouncementEmail: false }) },
-		{ parameters: (): object => ({ alwaysMarkNsfw: true }) },
-		{ parameters: (): object => ({ alwaysMarkNsfw: false }) },
-		{ parameters: (): object => ({ autoSensitive: true }) },
-		{ parameters: (): object => ({ autoSensitive: false }) },
-		{ parameters: (): object => ({ followingVisibility: 'private' }) },
-		{ parameters: (): object => ({ followingVisibility: 'followers' }) },
-		{ parameters: (): object => ({ followingVisibility: 'public' }) },
-		{ parameters: (): object => ({ followersVisibility: 'private' }) },
-		{ parameters: (): object => ({ followersVisibility: 'followers' }) },
-		{ parameters: (): object => ({ followersVisibility: 'public' }) },
-		{ parameters: (): object => ({ mutedWords: Array(19).fill(['xxxxx']) }) },
-		{ parameters: (): object => ({ mutedWords: [['x'.repeat(194)]] }) },
-		{ parameters: (): object => ({ mutedWords: [] }) },
-		{ parameters: (): object => ({ mutedInstances: ['xxxx.xxxxx'] }) },
-		{ parameters: (): object => ({ mutedInstances: [] }) },
-		{ parameters: (): object => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) },
-		{ parameters: (): object => ({ notificationRecieveConfig: {} }) },
-		{ parameters: (): object => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) },
-		{ parameters: (): object => ({ emailNotificationTypes: [] }) },
+		{ parameters: () => ({ name: null }) },
+		{ parameters: () => ({ name: 'x'.repeat(50) }) },
+		{ parameters: () => ({ name: 'x' }) },
+		{ parameters: () => ({ name: 'My name' }) },
+		{ parameters: () => ({ description: null }) },
+		{ parameters: () => ({ description: 'x'.repeat(1500) }) },
+		{ parameters: () => ({ description: 'x' }) },
+		{ parameters: () => ({ description: 'My description' }) },
+		{ parameters: () => ({ location: null }) },
+		{ parameters: () => ({ location: 'x'.repeat(50) }) },
+		{ parameters: () => ({ location: 'x' }) },
+		{ parameters: () => ({ location: 'My location' }) },
+		{ parameters: () => ({ birthday: '0000-00-00' }) },
+		{ parameters: () => ({ birthday: '9999-99-99' }) },
+		{ parameters: () => ({ lang: 'en-US' as const }) },
+		{ parameters: () => ({ fields: [] }) },
+		{ parameters: () => ({ fields: [{ name: 'x', value: 'x' }] }) },
+		{ parameters: () => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない
+		{ parameters: () => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) },
+		{ parameters: () => ({ isLocked: true }) },
+		{ parameters: () => ({ isLocked: false }) },
+		{ parameters: () => ({ isExplorable: false }) },
+		{ parameters: () => ({ isExplorable: true }) },
+		{ parameters: () => ({ hideOnlineStatus: true }) },
+		{ parameters: () => ({ hideOnlineStatus: false }) },
+		{ parameters: () => ({ publicReactions: false }) },
+		{ parameters: () => ({ publicReactions: true }) },
+		{ parameters: () => ({ autoAcceptFollowed: true }) },
+		{ parameters: () => ({ autoAcceptFollowed: false }) },
+		{ parameters: () => ({ noCrawle: true }) },
+		{ parameters: () => ({ noCrawle: false }) },
+		{ parameters: () => ({ preventAiLearning: false }) },
+		{ parameters: () => ({ preventAiLearning: true }) },
+		{ parameters: () => ({ isBot: true }) },
+		{ parameters: () => ({ isBot: false }) },
+		{ parameters: () => ({ isCat: true }) },
+		{ parameters: () => ({ isCat: false }) },
+		{ parameters: () => ({ injectFeaturedNote: true }) },
+		{ parameters: () => ({ injectFeaturedNote: false }) },
+		{ parameters: () => ({ receiveAnnouncementEmail: true }) },
+		{ parameters: () => ({ receiveAnnouncementEmail: false }) },
+		{ parameters: () => ({ alwaysMarkNsfw: true }) },
+		{ parameters: () => ({ alwaysMarkNsfw: false }) },
+		{ parameters: () => ({ autoSensitive: true }) },
+		{ parameters: () => ({ autoSensitive: false }) },
+		{ parameters: () => ({ followingVisibility: 'private' as const }) },
+		{ parameters: () => ({ followingVisibility: 'followers' as const }) },
+		{ parameters: () => ({ followingVisibility: 'public' as const }) },
+		{ parameters: () => ({ followersVisibility: 'private' as const }) },
+		{ parameters: () => ({ followersVisibility: 'followers' as const }) },
+		{ parameters: () => ({ followersVisibility: 'public' as const }) },
+		{ parameters: () => ({ mutedWords: Array(19).fill(['xxxxx']) }) },
+		{ parameters: () => ({ mutedWords: [['x'.repeat(194)]] }) },
+		{ parameters: () => ({ mutedWords: [] }) },
+		{ parameters: () => ({ mutedInstances: ['xxxx.xxxxx'] }) },
+		{ parameters: () => ({ mutedInstances: [] }) },
+		{ parameters: () => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) },
+		{ parameters: () => ({ notificationRecieveConfig: {} }) },
+		{ parameters: () => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) },
+		{ parameters: () => ({ emailNotificationTypes: [] }) },
 	] as const)('を書き換えることができる($#)', async ({ parameters }) => {
 		const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters(), user: alice });
 		const expected = { ...meDetailed(alice, true), ...parameters() };
@@ -498,13 +473,13 @@ describe('ユーザー', () => {
 
 	test('を書き換えることができる(Avatar)', async () => {
 		const aliceFile = (await uploadFile(alice)).body;
-		const parameters = { avatarId: aliceFile.id };
+		const parameters = { avatarId: aliceFile!.id };
 		const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
 		assert.match(response.avatarUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
 		assert.match(response.avatarBlurhash ?? '.', /[ -~]{54}/);
 		const expected = {
 			...meDetailed(alice, true),
-			avatarId: aliceFile.id,
+			avatarId: aliceFile!.id,
 			avatarBlurhash: response.avatarBlurhash,
 			avatarUrl: response.avatarUrl,
 		};
@@ -523,13 +498,13 @@ describe('ユーザー', () => {
 
 	test('を書き換えることができる(Banner)', async () => {
 		const aliceFile = (await uploadFile(alice)).body;
-		const parameters = { bannerId: aliceFile.id };
+		const parameters = { bannerId: aliceFile!.id };
 		const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice });
 		assert.match(response.bannerUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/);
 		assert.match(response.bannerBlurhash ?? '.', /[ -~]{54}/);
 		const expected = {
 			...meDetailed(alice, true),
-			bannerId: aliceFile.id,
+			bannerId: aliceFile!.id,
 			bannerBlurhash: response.bannerBlurhash,
 			bannerUrl: response.bannerUrl,
 		};
@@ -579,13 +554,13 @@ describe('ユーザー', () => {
 	//#region ユーザー(users)
 
 	test.each([
-		{ label: 'ID昇順', parameters: { limit: 5 }, selector: (u: UserLite): string => u.id },
-		{ label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
-		{ label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
-		{ label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
-		{ label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
-		{ label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
-		{ label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
+		{ label: 'ID昇順', parameters: { limit: 5 }, selector: (u: misskey.entities.UserLite): string => u.id },
+		{ label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
+		{ label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
+		{ label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
+		{ label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
+		{ label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
+		{ label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
 	] as const)('をリスト形式で取得することができる($label)', async ({ parameters, selector }) => {
 		const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
 
@@ -598,15 +573,15 @@ describe('ユーザー', () => {
 		assert.deepStrictEqual(response, expected);
 	});
 	test.each([
-		{ label: '「見つけやすくする」がOFFのユーザーが含まれない', user: (): User => userNotExplorable, excluded: true },
-		{ label: 'ミュートユーザーが含まれない', user: (): User => userMutedByAlice, excluded: true },
-		{ label: 'ブロックされているユーザーが含まれない', user: (): User => userBlockedByAlice, excluded: true },
-		{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice, excluded: true },
-		{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
-		{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
-		{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
-		{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
-		{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
+		{ label: '「見つけやすくする」がOFFのユーザーが含まれない', user: () => userNotExplorable, excluded: true },
+		{ label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true },
+		{ label: 'ブロックされているユーザーが含まれない', user: () => userBlockedByAlice, excluded: true },
+		{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice, excluded: true },
+		{ label: '承認制ユーザーが含まれる', user: () => userLocking },
+		{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
+		{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
+		{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
+		{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
 	] as const)('をリスト形式で取得することができ、結果に$label', async ({ user, excluded }) => {
 		const parameters = { limit: 100 };
 		const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice });
@@ -620,39 +595,44 @@ describe('ユーザー', () => {
 	//#region ユーザー情報(users/show)
 
 	test.each([
-		{ label: 'ID指定で自分自身を', parameters: (): object => ({ userId: alice.id }), user: (): User => alice, type: meDetailed },
-		{ label: 'ID指定で他人を', parameters: (): object => ({ userId: alice.id }), user: (): User => bob, type: userDetailedNotMeWithRelations },
-		{ label: 'ID指定かつ未認証', parameters: (): object => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe },
-		{ label: '@指定で自分自身を', parameters: (): object => ({ username: alice.username }), user: (): User => alice, type: meDetailed },
-		{ label: '@指定で他人を', parameters: (): object => ({ username: alice.username }), user: (): User => bob, type: userDetailedNotMeWithRelations },
-		{ label: '@指定かつ未認証', parameters: (): object => ({ username: alice.username }), user: undefined, type: userDetailedNotMe },
+		{ label: 'ID指定で自分自身を', parameters: () => ({ userId: alice.id }), user: () => alice, type: meDetailed },
+		{ label: 'ID指定で他人を', parameters: () => ({ userId: alice.id }), user: () => bob, type: userDetailedNotMeWithRelations },
+		{ label: 'ID指定かつ未認証', parameters: () => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe },
+		{ label: '@指定で自分自身を', parameters: () => ({ username: alice.username }), user: () => alice, type: meDetailed },
+		{ label: '@指定で他人を', parameters: () => ({ username: alice.username }), user: () => bob, type: userDetailedNotMeWithRelations },
+		{ label: '@指定かつ未認証', parameters: () => ({ username: alice.username }), user: undefined, type: userDetailedNotMe },
 	] as const)('を取得することができる($label)', async ({ parameters, user, type }) => {
 		const response = await successfulApiCall({ endpoint: 'users/show', parameters: parameters(), user: user?.() });
 		const expected = type(alice);
 		assert.deepStrictEqual(response, expected);
 	});
 	test.each([
-		{ label: 'Administratorになっている', user: (): User => userAdmin, me: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin },
-		{ label: '自分以外から見たときはAdministratorか判定できない', user: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin, expected: (): undefined => undefined },
-		{ label: 'Moderatorになっている', user: (): User => userModerator, me: (): User => userModerator, selector: (user: User): unknown => user.isModerator },
-		{ label: '自分以外から見たときはModeratorか判定できない', user: (): User => userModerator, selector: (user: User): unknown => user.isModerator, expected: (): undefined => undefined },
-		{ label: 'サイレンスになっている', user: (): User => userSilenced, selector: (user: User): unknown => user.isSilenced },
-		//{ label: 'サスペンドになっている', user: (): User => userSuspended, selector: (user: User): unknown => user.isSuspended },
-		{ label: '削除済みになっている', user: (): User => userDeletedBySelf, me: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted },
-		{ label: '自分以外から見たときは削除済みか判定できない', user: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined },
-		{ label: '削除済み(byAdmin)になっている', user: (): User => userDeletedByAdmin, me: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted },
-		{ label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined },
-		{ label: 'フォロー中になっている', user: (): User => userFollowedByAlice, selector: (user: User): unknown => user.isFollowing },
-		{ label: 'フォローされている', user: (): User => userFollowingAlice, selector: (user: User): unknown => user.isFollowed },
-		{ label: 'ブロック中になっている', user: (): User => userBlockedByAlice, selector: (user: User): unknown => user.isBlocking },
-		{ label: 'ブロックされている', user: (): User => userBlockingAlice, selector: (user: User): unknown => user.isBlocked },
-		{ label: 'ミュート中になっている', user: (): User => userMutedByAlice, selector: (user: User): unknown => user.isMuted },
-		{ label: 'リノートミュート中になっている', user: (): User => userRnMutedByAlice, selector: (user: User): unknown => user.isRenoteMuted },
-		{ label: 'フォローリクエスト中になっている', user: (): User => userFollowRequested, me: (): User => userFollowRequesting, selector: (user: User): unknown => user.hasPendingFollowRequestFromYou },
-		{ label: 'フォローリクエストされている', user: (): User => userFollowRequesting, me: (): User => userFollowRequested, selector: (user: User): unknown => user.hasPendingFollowRequestToYou },
+		{ label: 'Administratorになっている', user: () => userAdmin, me: () => userAdmin, selector: (user: misskey.entities.MeDetailed) => user.isAdmin },
+		// @ts-expect-error UserDetailedNotMe doesn't include isAdmin
+		{ label: '自分以外から見たときはAdministratorか判定できない', user: () => userAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isAdmin, expected: () => undefined },
+		{ label: 'Moderatorになっている', user: () => userModerator, me: () => userModerator, selector: (user: misskey.entities.MeDetailed) => user.isModerator },
+		// @ts-expect-error UserDetailedNotMe doesn't include isModerator
+		{ label: '自分以外から見たときはModeratorか判定できない', user: () => userModerator, selector: (user: misskey.entities.UserDetailedNotMe) => user.isModerator, expected: () => undefined },
+		{ label: 'サイレンスになっている', user: () => userSilenced, selector: (user: misskey.entities.UserDetailed) => user.isSilenced },
+		// FIXME: 落ちる
+		//{ label: 'サスペンドになっている', user: () => userSuspended, selector: (user: misskey.entities.UserDetailed) => user.isSuspended },
+		{ label: '削除済みになっている', user: () => userDeletedBySelf, me: () => userDeletedBySelf, selector: (user: misskey.entities.MeDetailed) => user.isDeleted },
+		// @ts-expect-error UserDetailedNotMe doesn't include isDeleted
+		{ label: '自分以外から見たときは削除済みか判定できない', user: () => userDeletedBySelf, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined },
+		{ label: '削除済み(byAdmin)になっている', user: () => userDeletedByAdmin, me: () => userDeletedByAdmin, selector: (user: misskey.entities.MeDetailed) => user.isDeleted },
+		// @ts-expect-error UserDetailedNotMe doesn't include isDeleted
+		{ label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: () => userDeletedByAdmin, selector: (user: misskey.entities.UserDetailedNotMe) => user.isDeleted, expected: () => undefined },
+		{ label: 'フォロー中になっている', user: () => userFollowedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowing },
+		{ label: 'フォローされている', user: () => userFollowingAlice, selector: (user: misskey.entities.UserDetailed) => user.isFollowed },
+		{ label: 'ブロック中になっている', user: () => userBlockedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isBlocking },
+		{ label: 'ブロックされている', user: () => userBlockingAlice, selector: (user: misskey.entities.UserDetailed) => user.isBlocked },
+		{ label: 'ミュート中になっている', user: () => userMutedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isMuted },
+		{ label: 'リノートミュート中になっている', user: () => userRnMutedByAlice, selector: (user: misskey.entities.UserDetailed) => user.isRenoteMuted },
+		{ label: 'フォローリクエスト中になっている', user: () => userFollowRequested, me: () => userFollowRequesting, selector: (user: misskey.entities.UserDetailed) => user.hasPendingFollowRequestFromYou },
+		{ label: 'フォローリクエストされている', user: () => userFollowRequesting, me: () => userFollowRequested, selector: (user: misskey.entities.UserDetailed) => user.hasPendingFollowRequestToYou },
 	] as const)('を取得することができ、$labelこと', async ({ user, me, selector, expected }) => {
 		const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: user().id }, user: me?.() ?? alice });
-		assert.strictEqual(selector(response), (expected ?? ((): true => true))());
+		assert.strictEqual(selector(response as any), (expected ?? ((): true => true))());
 	});
 	test('を取得することができ、Publicなロールがセットされていること', async () => {
 		const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRolePublic.id }, user: alice });
@@ -694,17 +674,18 @@ describe('ユーザー', () => {
 		assert.deepStrictEqual(response, expected);
 	});
 	test.each([
-		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
-		{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
-		{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
-		{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
-		{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
-		{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
-		{ label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: (): User => userSuspended, me: (): User => root },
+		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
+		{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
+		{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
+		{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
+		{ label: '承認制ユーザーが含まれる', user: () => userLocking },
+		{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
+		{ label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: () => userSuspended, me: () => root },
 		// BUG サスペンドユーザーを一般ユーザーから見るとrootユーザーが返ってくる
-		//{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: (): User => userSuspended, me: (): User => bob, excluded: true },
-		{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
-		{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
+		//{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: () => userSuspended, me: () => bob, excluded: true },
+		{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
+		{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
+		// @ts-expect-error excluded は上でコメントアウトされているので
 	] as const)('をID指定のリスト形式で取得することができ、結果に$label', async ({ user, me, excluded }) => {
 		const parameters = { userIds: [user().id] };
 		const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: me?.() ?? alice });
@@ -729,15 +710,15 @@ describe('ユーザー', () => {
 		assert.deepStrictEqual(response, expected);
 	});
 	test.each([
-		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
-		{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
-		{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
-		{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
-		{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
-		{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
-		{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
-		{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
-		{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
+		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
+		{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
+		{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
+		{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
+		{ label: '承認制ユーザーが含まれる', user: () => userLocking },
+		{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
+		{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
+		{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
+		{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
 	] as const)('を検索することができ、結果に$labelが含まれる', async ({ user, excluded }) => {
 		const parameters = { query: user().username, limit: 1 };
 		const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice });
@@ -751,30 +732,30 @@ describe('ユーザー', () => {
 	//#region ID指定検索(users/search-by-username-and-host)
 
 	test.each([
-		{ label: '自分', parameters: { username: 'alice' }, user: (): User[] => [alice] },
-		{ label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: (): User[] => [alice] },
-		{ label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: (): User[] => [userFollowedByAlice] },
-		{ label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: (): User[] => [] },
-		{ label: 'ローカルの他人1', parameters: { username: 'bob' }, user: (): User[] => [bob] },
-		{ label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: (): User[] => [bob] },
-		{ label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: (): User[] => [bob] },
-		{ label: 'ローカル', parameters: { host: null, limit: 1 }, user: (): User[] => [userFollowedByAlice] },
-		{ label: 'ローカル', parameters: { host: '.', limit: 1 }, user: (): User[] => [userFollowedByAlice] },
+		{ label: '自分', parameters: { username: 'alice' }, user: () => [alice] },
+		{ label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: () => [alice] },
+		{ label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: () => [userFollowedByAlice] },
+		{ label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: () => [] },
+		{ label: 'ローカルの他人1', parameters: { username: 'bob' }, user: () => [bob] },
+		{ label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: () => [bob] },
+		{ label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: () => [bob] },
+		{ label: 'ローカル', parameters: { host: null, limit: 1 }, user: () => [userFollowedByAlice] },
+		{ label: 'ローカル', parameters: { host: '.', limit: 1 }, user: () => [userFollowedByAlice] },
 	])('をID&ホスト指定で検索できる($label)', async ({ parameters, user }) => {
 		const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
 		const expected = await Promise.all(user().map(u => show(u.id, alice)));
 		assert.deepStrictEqual(response, expected);
 	});
 	test.each([
-		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
-		{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
-		{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
-		{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
-		{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
-		{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
-		{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
-		{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
-		{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
+		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
+		{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
+		{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
+		{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
+		{ label: '承認制ユーザーが含まれる', user: () => userLocking },
+		{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
+		{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
+		{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
+		{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
 	] as const)('をID&ホスト指定で検索でき、結果に$label', async ({ user, excluded }) => {
 		const parameters = { username: user().username };
 		const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice });
@@ -796,15 +777,15 @@ describe('ユーザー', () => {
 		assert.deepStrictEqual(response, expected);
 	});
 	test.each([
-		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
-		{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
-		{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
-		{ label: 'ブロックしてきているユーザーが含まれない', user: (): User => userBlockingAlice, excluded: true },
-		{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
-		{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
-		//{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
-		{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
-		{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
+		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
+		{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
+		{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
+		{ label: 'ブロックしてきているユーザーが含まれない', user: () => userBlockingAlice, excluded: true },
+		{ label: '承認制ユーザーが含まれる', user: () => userLocking },
+		{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
+		//{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
+		{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
+		{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
 	] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => {
 		const replyTo = (await successfulApiCall({ endpoint: 'users/notes', parameters: { userId: user().id }, user: undefined }))[0];
 		await post(alice, { text: `@${user().username} test`, replyId: replyTo.id });
@@ -818,12 +799,12 @@ describe('ユーザー', () => {
 	//#region ハッシュタグ(hashtags/users)
 
 	test.each([
-		{ label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
-		{ label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) },
-		{ label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
-		{ label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt },
-		{ label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
-		{ label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) },
+		{ label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
+		{ label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.followersCount) },
+		{ label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
+		{ label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => u.createdAt },
+		{ label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
+		{ label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: misskey.entities.UserDetailedNotMe): string => String(u.updatedAt) },
 	] as const)('をハッシュタグ指定で取得することができる($label)', async ({ sort, selector }) => {
 		const hashtag = 'test_hashtag';
 		await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: alice });
@@ -837,15 +818,15 @@ describe('ユーザー', () => {
 		assert.deepStrictEqual(response, expected);
 	});
 	test.each([
-		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable },
-		{ label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice },
-		{ label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice },
-		{ label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice },
-		{ label: '承認制ユーザーが含まれる', user: (): User => userLocking },
-		{ label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced },
-		{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true },
-		{ label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf },
-		{ label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin },
+		{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
+		{ label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice },
+		{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
+		{ label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice },
+		{ label: '承認制ユーザーが含まれる', user: () => userLocking },
+		{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
+		{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
+		{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
+		{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
 	] as const)('をハッシュタグ指定で取得することができ、結果に$label', async ({ user, excluded }) => {
 		const hashtag = `user_test${user().username}`;
 		if (user() !== userSuspended) {
diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts
index fc358374204c..aa082ff2f280 100644
--- a/packages/backend/test/unit/AnnouncementService.ts
+++ b/packages/backend/test/unit/AnnouncementService.ts
@@ -51,7 +51,7 @@ describe('AnnouncementService', () => {
 
 	function createAnnouncement(data: Partial<MiAnnouncement & { createdAt: Date }> = {}) {
 		return announcementsRepository.insert({
-			id: genAidx(data.createdAt ?? new Date()),
+			id: genAidx(data.createdAt?.getTime() ?? Date.now()),
 			updatedAt: null,
 			title: 'Title',
 			text: 'Text',
diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts
index e6e68ccd6db3..510b84b68036 100644
--- a/packages/backend/test/unit/FetchInstanceMetadataService.ts
+++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts
@@ -19,8 +19,8 @@ import { DI } from '@/di-symbols.js';
 import type { TestingModule } from '@nestjs/testing';
 
 function mockRedis() {
-	const hash = {};
-	const set = jest.fn((key, value) => {
+	const hash = {} as any;
+	const set = jest.fn((key: string, value) => {
 		const ret = hash[key];
 		hash[key] = value;
 		return ret;
@@ -61,7 +61,7 @@ describe('FetchInstanceMetadataService', () => {
 
 		app.enableShutdownHooks();
 
-		fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService);
+		fetchInstanceMetadataService = app.get<FetchInstanceMetadataService>(FetchInstanceMetadataService) as jest.Mocked<FetchInstanceMetadataService>;
 		federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService) as jest.Mocked<FederatedInstanceService>;
 		redisClient = app.get<Redis>(DI.redis) as jest.Mocked<Redis>;
 		httpRequestService = app.get<HttpRequestService>(HttpRequestService) as jest.Mocked<HttpRequestService>;
@@ -74,11 +74,11 @@ describe('FetchInstanceMetadataService', () => {
 	test('Lock and update', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } });
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
-		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' });
+		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
 		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
@@ -88,11 +88,11 @@ describe('FetchInstanceMetadataService', () => {
 	test('Lock and don\'t update', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => now } });
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
-		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' });
+		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
 		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
@@ -101,12 +101,13 @@ describe('FetchInstanceMetadataService', () => {
 
 	test('Do nothing when lock not acquired', async () => {
 		redisClient.set = mockRedis();
-		federatedInstanceService.fetch.mockReturnValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } });
+		const now = Date.now();
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 		await fetchInstanceMetadataService.tryLock('example.com');
-		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' });
+		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
 		expect(tryLockSpy).toHaveBeenCalledTimes(2);
 		expect(unlockSpy).toHaveBeenCalledTimes(0);
 		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index fe5ad31597ad..19d03570e00d 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -228,11 +228,14 @@ describe('RoleService', () => {
 				},
 				target: 'conditional',
 				condFormula: {
+					id: '232a4221-9816-49a6-a967-ae0fac52ec5e',
 					type: 'and',
 					values: [{
+						id: '2a37ef43-2d93-4c4d-87f6-f2fdb7d9b530',
 						type: 'followersMoreThanOrEq',
 						value: 10,
 					}, {
+						id: '1bd67839-b126-4f92-bad0-4e285dab453b',
 						type: 'createdMoreThan',
 						sec: 60 * 60 * 24 * 7,
 					}],
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index cd5dddd68d8e..86814fffe0e4 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -9,11 +9,10 @@ import { basename, isAbsolute } from 'node:path';
 import { randomUUID } from 'node:crypto';
 import { inspect } from 'node:util';
 import WebSocket, { ClientOptions } from 'ws';
-import fetch, { File, RequestInit } from 'node-fetch';
+import fetch, { File, RequestInit, type Headers } from 'node-fetch';
 import { DataSource } from 'typeorm';
 import { JSDOM } from 'jsdom';
 import { DEFAULT_POLICIES } from '@/core/RoleService.js';
-import { Packed } from '@/misc/json-schema.js';
 import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
 import { entities } from '../src/postgres.js';
 import { loadConfig } from '../src/config.js';
@@ -21,7 +20,7 @@ import type * as misskey from 'misskey-js';
 
 export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
 
-interface UserToken {
+export interface UserToken {
 	token: string;
 	bearer?: boolean;
 }
@@ -35,20 +34,15 @@ export const cookie = (me: UserToken): string => {
 	return `token=${me.token};`;
 };
 
-export const api = async (endpoint: string, params: any, me?: UserToken) => {
-	const normalized = endpoint.replace(/^\//, '');
-	return await request(`api/${normalized}`, params, me);
-};
-
-export type ApiRequest = {
-	endpoint: string,
-	parameters: object,
+export type ApiRequest<E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req'] = misskey.Endpoints[E]['req']> = {
+	endpoint: E,
+	parameters: P,
 	user: UserToken | undefined,
 };
 
-export const successfulApiCall = async <T, >(request: ApiRequest, assertion: {
+export const successfulApiCall = async <E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: {
 	status?: number,
-} = {}): Promise<T> => {
+} = {}): Promise<misskey.api.SwitchCaseResponseType<E, P>> => {
 	const { endpoint, parameters, user } = request;
 	const res = await api(endpoint, parameters, user);
 	const status = assertion.status ?? (res.body == null ? 204 : 200);
@@ -56,7 +50,7 @@ export const successfulApiCall = async <T, >(request: ApiRequest, assertion: {
 	return res.body;
 };
 
-export const failedApiCall = async <T, >(request: ApiRequest, assertion: {
+export const failedApiCall = async <T, E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: {
 	status: number,
 	code: string,
 	id: string
@@ -70,7 +64,7 @@ export const failedApiCall = async <T, >(request: ApiRequest, assertion: {
 	return res.body;
 };
 
-const request = async (path: string, params: any, me?: UserToken): Promise<{
+export const api = async <E extends keyof misskey.Endpoints>(path: E, params: misskey.Endpoints[E]['req'], me?: UserToken): Promise<{
 	status: number,
 	headers: Headers,
 	body: any
@@ -86,7 +80,7 @@ const request = async (path: string, params: any, me?: UserToken): Promise<{
 		bodyAuth.i = me.token;
 	}
 
-	const res = await relativeFetch(path, {
+	const res = await relativeFetch(`api/${path}`, {
 		method: 'POST',
 		headers,
 		body: JSON.stringify(Object.assign(bodyAuth, params)),
@@ -141,7 +135,7 @@ export const signup = async (params?: Partial<misskey.Endpoints['signup']['req']
 	return res.body;
 };
 
-export const post = async (user: UserToken, params?: misskey.Endpoints['notes/create']['req']): Promise<misskey.entities.Note> => {
+export const post = async (user: UserToken, params: misskey.Endpoints['notes/create']['req']): Promise<misskey.entities.Note> => {
 	const q = params;
 
 	const res = await api('notes/create', q, user);
@@ -159,8 +153,8 @@ export const createAppToken = async (user: UserToken, permissions: (typeof missk
 };
 
 // 非公開ノートをAPI越しに見たときのノート NoteEntityService.ts
-export const hiddenNote = (note: any): any => {
-	const temp = {
+export const hiddenNote = (note: misskey.entities.Note): misskey.entities.Note => {
+	const temp: misskey.entities.Note = {
 		...note,
 		fileIds: [],
 		files: [],
@@ -173,21 +167,22 @@ export const hiddenNote = (note: any): any => {
 	return temp;
 };
 
-export const react = async (user: UserToken, note: any, reaction: string): Promise<any> => {
+export const react = async (user: UserToken, note: misskey.entities.Note, reaction: string): Promise<void> => {
 	await api('notes/reactions/create', {
 		noteId: note.id,
 		reaction: reaction,
 	}, user);
 };
 
-export const userList = async (user: UserToken, userList: any = {}): Promise<any> => {
+export const userList = async (user: UserToken, userList: Partial<misskey.entities.UserList> = {}): Promise<misskey.entities.UserList> => {
 	const res = await api('users/lists/create', {
 		name: 'test',
+		...userList,
 	}, user);
 	return res.body;
 };
 
-export const page = async (user: UserToken, page: any = {}): Promise<any> => {
+export const page = async (user: UserToken, page: Partial<misskey.entities.Page> = {}): Promise<misskey.entities.Page> => {
 	const res = await api('pages/create', {
 		alignCenter: false,
 		content: [
@@ -198,7 +193,7 @@ export const page = async (user: UserToken, page: any = {}): Promise<any> => {
 			},
 		],
 		eyeCatchingImageId: null,
-		font: 'sans-serif',
+		font: 'sans-serif' as any,
 		hideTitleWhenPinned: false,
 		name: '1678594845072',
 		script: '',
@@ -210,7 +205,7 @@ export const page = async (user: UserToken, page: any = {}): Promise<any> => {
 	return res.body;
 };
 
-export const play = async (user: UserToken, play: any = {}): Promise<any> => {
+export const play = async (user: UserToken, play: Partial<misskey.entities.Flash> = {}): Promise<misskey.entities.Flash> => {
 	const res = await api('flash/create', {
 		permissions: [],
 		script: 'test',
@@ -221,7 +216,7 @@ export const play = async (user: UserToken, play: any = {}): Promise<any> => {
 	return res.body;
 };
 
-export const clip = async (user: UserToken, clip: any = {}): Promise<any> => {
+export const clip = async (user: UserToken, clip: Partial<misskey.entities.Clip> = {}): Promise<misskey.entities.Clip> => {
 	const res = await api('clips/create', {
 		description: null,
 		isPublic: true,
@@ -231,18 +226,18 @@ export const clip = async (user: UserToken, clip: any = {}): Promise<any> => {
 	return res.body;
 };
 
-export const galleryPost = async (user: UserToken, channel: any = {}): Promise<any> => {
+export const galleryPost = async (user: UserToken, galleryPost: Partial<misskey.entities.GalleryPost> = {}): Promise<misskey.entities.GalleryPost> => {
 	const res = await api('gallery/posts/create', {
 		description: null,
 		fileIds: [],
 		isSensitive: false,
 		title: 'test',
-		...channel,
+		...galleryPost,
 	}, user);
 	return res.body;
 };
 
-export const channel = async (user: UserToken, channel: any = {}): Promise<any> => {
+export const channel = async (user: UserToken, channel: Partial<misskey.entities.Channel> = {}): Promise<misskey.entities.Channel> => {
 	const res = await api('channels/create', {
 		bannerId: null,
 		description: null,
@@ -252,7 +247,7 @@ export const channel = async (user: UserToken, channel: any = {}): Promise<any>
 	return res.body;
 };
 
-export const role = async (user: UserToken, role: any = {}, policies: any = {}): Promise<any> => {
+export const role = async (user: UserToken, role: Partial<misskey.entities.Role> = {}, policies: any = {}): Promise<misskey.entities.Role> => {
 	const res = await api('admin/roles/create', {
 		asBadge: false,
 		canEditMembersByModerator: false,
@@ -260,7 +255,7 @@ export const role = async (user: UserToken, role: any = {}, policies: any = {}):
 		condFormula: {
 			id: 'ebef1684-672d-49b6-ad82-1b3ec3784f85',
 			type: 'isRemote',
-		},
+		} as any,
 		description: '',
 		displayOrder: 0,
 		iconUrl: null,
@@ -298,7 +293,7 @@ interface UploadOptions {
 export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadOptions = {}): Promise<{
 	status: number,
 	headers: Headers,
-	body: misskey.Endpoints['drive/files/create']['res'] | null
+	body: misskey.entities.DriveFile | null
 }> => {
 	const absPath = path == null
 		? new URL('resources/Lenna.jpg', import.meta.url)
@@ -335,14 +330,14 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO
 	};
 };
 
-export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'DriveFile'>> => {
+export const uploadUrl = async (user: UserToken, url: string): Promise<misskey.entities.DriveFile> => {
 	const marker = Math.random().toString();
 
 	const catcher = makeStreamCatcher(
 		user,
 		'main',
 		(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
-		(msg) => msg.body.file as Packed<'DriveFile'>,
+		(msg) => msg.body.file,
 		60 * 1000,
 	);
 

From 983480131bf6ac48b3834d334deb97a6b3f2f4f6 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 4 Mar 2024 12:54:13 +0900
Subject: [PATCH 078/266] chore: Automated release (#13075)

* chore: Automated release

* follow
---
 .github/workflows/release-edit-with-push.yml |  40 ++++++
 .github/workflows/release-with-dispatch.yml  | 122 +++++++++++++++++++
 .github/workflows/release-with-ready.yml     |  38 ++++++
 3 files changed, 200 insertions(+)
 create mode 100644 .github/workflows/release-edit-with-push.yml
 create mode 100644 .github/workflows/release-with-dispatch.yml
 create mode 100644 .github/workflows/release-with-ready.yml

diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml
new file mode 100644
index 000000000000..944b98eb7cc2
--- /dev/null
+++ b/.github/workflows/release-edit-with-push.yml
@@ -0,0 +1,40 @@
+name: "Release Manager: sync changelog with PR"
+
+on:
+  push:
+    branches:
+      - release/**
+    paths:
+      - 'CHANGELOG.md'
+
+env:
+  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+permissions:
+  contents: write
+  issues: write
+  pull-requests: write
+
+jobs:
+  edit:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      # headがrelease/かつopenのPRを1つ取得
+      - name: Get PR
+        run: |
+          echo "pr_number=$(gh pr list --limit 1 --head "${{ github.ref_name }}" --json number  --jq '.[] | .number')" >> $GITHUB_OUTPUT
+        id: get_pr
+      - name: Get target version
+        uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v1
+        id: v
+      # CHANGELOG.mdの内容を取得
+      - name: Get changelog
+        uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v1
+        with:
+          version: ${{ steps.v.outputs.target_version }}
+        id: changelog
+      # PRのnotesを更新
+      - name: Update PR
+        run: |
+          gh pr edit ${{ steps.get_pr.outputs.pr_number }} --body "${{ steps.changelog.outputs.changelog }}"
diff --git a/.github/workflows/release-with-dispatch.yml b/.github/workflows/release-with-dispatch.yml
new file mode 100644
index 000000000000..1a954739d959
--- /dev/null
+++ b/.github/workflows/release-with-dispatch.yml
@@ -0,0 +1,122 @@
+name: "Release Manager [Dispatch]"
+
+on:
+  workflow_dispatch:
+    inputs:
+      ## Specify the type of the next release.
+      #version_increment_type:
+      #  type: choice
+      #  description: 'VERSION INCREMENT TYPE'
+      #  default: 'patch'
+      #  required: false
+      #  options:
+      #    - 'major'
+      #    - 'minor'
+      #    - 'patch'
+      merge:
+        type: boolean
+        description: 'MERGE RELEASE BRANCH TO MAIN'
+        default: false
+
+env:
+  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+permissions:
+  contents: write
+  issues: write
+  pull-requests: write
+
+jobs:
+  get-pr:
+    runs-on: ubuntu-latest
+    outputs:
+      pr_number: ${{ steps.get_pr.outputs.pr_number }}
+    steps:
+      - uses: actions/checkout@v4
+      # headがrelease/かつopenのPRを1つ取得
+      - name: Get PRs
+        run: |
+          echo "pr_number=$(gh pr list --limit 1 --search "head:release/ is:open" --json number  --jq '.[] | .number')" >> $GITHUB_OUTPUT
+        id: get_pr
+
+  merge:
+    uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v1
+    needs: get-pr
+    if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }}
+    with:
+      pr_number: ${{ needs.get-pr.outputs.pr_number }}
+      package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
+      # Text to prepend to the changelog
+      # The first line must be `## Unreleased`
+      changes_template: |
+        ## Unreleased
+
+        ### General
+        -
+
+        ### Client
+        -
+
+        ### Server
+        -
+
+      use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+    secrets:
+      RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
+      RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
+      RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
+      RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
+
+  create-prerelease:
+    uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
+    needs: get-pr
+    if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true  }}
+    with:
+      pr_number: ${{ needs.get-pr.outputs.pr_number }}
+      package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
+      use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+    secrets:
+      RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
+      RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
+
+  create-target:
+    uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v1
+    needs: get-pr
+    if: ${{ needs.get-pr.outputs.pr_number == '' }}
+    with:
+      # The script for version increment.
+      # process.env.CURRENT_VERSION: The current version.
+      #
+      # Misskey calender versioning (yyyy.MM.patch) example
+      version_increment_script: |
+        const now = new Date();
+        const year = now.toLocaleDateString('en-US', { year: 'numeric', timeZone: 'Asia/Tokyo' });
+        const month = now.toLocaleDateString('en-US', { month: 'numeric', timeZone: 'Asia/Tokyo' });
+        const [major, minor, _patch] = process.env.CURRENT_VERSION.split('.');
+        const patch = Number(_patch.split('-')[0]);
+        if (Number.isNaN(patch)) {
+          console.error('Invalid patch version', year, month, process.env.CURRENT_VERSION, major, minor, _patch);
+          throw new Error('Invalid patch version');
+        }
+        if (year !== major || month !== minor) {
+          return `${year}.${month}.0`;
+        } else {
+          return `${major}.${minor}.${patch + 1}`;
+        }
+      ##Semver example
+      #version_increment_script: |
+      #  const [major, minor, patch] = process.env.CURRENT_VERSION.split('.');
+      #  if ("${{ inputs.version_increment_type }}" === "major") {
+      #    return `${Number(major) + 1}.0.0`;
+      #  } else if ("${{ inputs.version_increment_type }}" === "minor") {
+      #    return `${major}.${Number(minor) + 1}.0`;
+      #  } else {
+      #    return `${major}.${minor}.${Number(patch) + 1}`;
+      #  }
+      package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
+      use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+    secrets:
+      RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
+      RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
+      RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
+      RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
diff --git a/.github/workflows/release-with-ready.yml b/.github/workflows/release-with-ready.yml
new file mode 100644
index 000000000000..b64ed20791b0
--- /dev/null
+++ b/.github/workflows/release-with-ready.yml
@@ -0,0 +1,38 @@
+name: "Release Manager: release RC when ready for review"
+
+on:
+  pull_request:
+    types: [ready_for_review]
+
+env:
+  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+permissions:
+  contents: write
+  issues: write
+  pull-requests: write
+
+jobs:
+  check:
+    runs-on: ubuntu-latest
+    outputs:
+      ref: ${{ steps.get_pr.outputs.ref }}
+    steps:
+      - uses: actions/checkout@v4
+      # PR情報を取得
+      - name: Get PR
+        run: |
+          pr_json=$(gh pr view ${{ github.event.pull_request.number }} --json isDraft,headRefName)
+          echo "ref=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
+        id: get_pr
+  release:
+    uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
+    needs: check
+    if: startsWith(needs.check.outputs.ref, 'release/')
+    with:
+      pr_number: ${{ github.event.pull_request.number }}
+      package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
+      use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+    secrets:
+      RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
+      RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}

From 9542cb8d6253a93b06a68b9ac3647367f8f7354c Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 4 Mar 2024 13:48:57 +0900
Subject: [PATCH 079/266] =?UTF-8?q?fix(backend):=20=E3=83=AA=E3=83=A2?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=81=AE?=
 =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=81=8C=E6=9B=B4=E6=96=B0=E3=81=A7=E3=81=8D?=
 =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=84=E3=81=9F?=
 =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13507)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): fetchInstanceMetadataのLockが永遠に解除されない問題を修正

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>

* fix test

* fix

* comment

* comment

* improve test

---------

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 .../src/core/FetchInstanceMetadataService.ts  | 28 ++++++++++++++-----
 .../test/unit/FetchInstanceMetadataService.ts | 24 ++++++++++++++--
 2 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts
index bc270bd28fd3..8d173855f33d 100644
--- a/packages/backend/src/core/FetchInstanceMetadataService.ts
+++ b/packages/backend/src/core/FetchInstanceMetadataService.ts
@@ -51,21 +51,35 @@ export class FetchInstanceMetadataService {
 	}
 
 	@bindThis
-	public async tryLock(host: string): Promise<boolean> {
-		const mutex = await this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '1', 'GET');
-		return mutex !== '1';
+	// public for test
+	public async tryLock(host: string): Promise<string | null> {
+		// TODO: マイグレーションなのであとで消す (2024.3.1)
+		this.redisClient.del(`fetchInstanceMetadata:mutex:${host}`);
+
+		return await this.redisClient.set(
+			`fetchInstanceMetadata:mutex:v2:${host}`, '1',
+			'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395
+			'GET' // 古い値を返す(なかったらnull)
+		);
 	}
 
 	@bindThis
-	public unlock(host: string): Promise<'OK'> {
-		return this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '0');
+	// public for test
+	public unlock(host: string): Promise<number> {
+		return this.redisClient.del(`fetchInstanceMetadata:mutex:v2:${host}`);
 	}
 
 	@bindThis
 	public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> {
 		const host = instance.host;
-		// Acquire mutex to ensure no parallel runs
-		if (!await this.tryLock(host)) return;
+
+		// finallyでunlockされてしまうのでtry内でロックチェックをしない
+		// (returnであってもfinallyは実行される)
+		if (!force && await this.tryLock(host) === '1') {
+			// 1が返ってきていたらロックされているという意味なので、何もしない
+			return;
+		}
+
 		try {
 			if (!force) {
 				const _instance = await this.federatedInstanceService.fetch(host);
diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts
index 510b84b68036..bf8f3ab0e306 100644
--- a/packages/backend/test/unit/FetchInstanceMetadataService.ts
+++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts
@@ -56,6 +56,7 @@ describe('FetchInstanceMetadataService', () => {
 				} else if (token === DI.redis) {
 					return mockRedis;
 				}
+				return null;
 			})
 			.compile();
 
@@ -78,6 +79,7 @@ describe('FetchInstanceMetadataService', () => {
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
+
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
@@ -92,6 +94,7 @@ describe('FetchInstanceMetadataService', () => {
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
+
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
@@ -104,13 +107,30 @@ describe('FetchInstanceMetadataService', () => {
 		const now = Date.now();
 		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
+		await fetchInstanceMetadataService.tryLock('example.com');
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
-		await fetchInstanceMetadataService.tryLock('example.com');
+
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
-		expect(tryLockSpy).toHaveBeenCalledTimes(2);
+		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(0);
 		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
 		expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
 	});
+
+	test('Do when lock not acquired but forced', async () => {
+		redisClient.set = mockRedis();
+		const now = Date.now();
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
+		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
+		await fetchInstanceMetadataService.tryLock('example.com');
+		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
+		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
+
+		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true);
+		expect(tryLockSpy).toHaveBeenCalledTimes(0);
+		expect(unlockSpy).toHaveBeenCalledTimes(1);
+		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
+		expect(httpRequestService.getJson).toHaveBeenCalled();
+	});
 });

From 96ab1af03b821ac265a1e8ff492c154b80e02759 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 4 Mar 2024 16:09:24 +0900
Subject: [PATCH 080/266] Update CHANGELOG.md

---
 CHANGELOG.md | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bafee277d259..ca7bf85fd061 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,17 @@
 
 -->
 
+## Unreleased
+
+### General
+-
+
+### Client
+- 
+
+### Server
+-
+
 ## 2024.3.1
 
 ### General

From 13f5fafdbc869207141f2a2f1f75f61c3147372d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 4 Mar 2024 10:39:43 +0000
Subject: [PATCH 081/266] remove template txt

---
 CHANGELOG.md | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ca7bf85fd061..349e99d13399 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,17 +1,3 @@
-<!--
-## 202x.x.x (unreleased)
-
-### General
--
-
-### Client
-- 
-
-### Server
--
-
--->
-
 ## Unreleased
 
 ### General

From 83a5bc0ecd05a352e164bda1f66f48962159c427 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 5 Mar 2024 14:26:16 +0900
Subject: [PATCH 082/266] =?UTF-8?q?=20doc:=20Nest=E3=81=A7=E5=BE=AA?=
 =?UTF-8?q?=E7=92=B0=E4=BE=9D=E5=AD=98=E3=81=8C=E3=81=82=E3=82=8B=E5=A0=B4?=
 =?UTF-8?q?=E5=90=88=E3=81=AECONTRIBUTING.md=E3=81=AB=E6=9B=B8=E3=81=8F=20?=
 =?UTF-8?q?=20(#13522)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* doc: Nestモジュールテストの例をCONTRIBUTING.mdに書く

* rm normal test

* forwardRef
---
 CONTRIBUTING.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a3263bf6aa13..dcb625626d6f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -316,6 +316,98 @@ export const handlers = [
 
 Don't forget to re-run the `.storybook/generate.js` script after adding, editing, or removing the above files.
 
+## Nest
+
+### Nest Service Circular dependency / Nestでサービスの循環参照でエラーが起きた場合
+
+#### forwardRef
+まずは簡単に`forwardRef`を試してみる
+
+```typescript
+export class FooService {
+	constructor(
+		@Inject(forwardRef(() => BarService))
+		private barService: BarService
+	) {
+	}
+}
+```
+
+#### OnModuleInit
+できなければ`OnModuleInit`を使う
+
+```typescript
+import { Injectable, OnModuleInit } from '@nestjs/common';
+import { ModuleRef } from '@nestjs/core';
+import { BarService } from '@/core/BarService';
+
+@Injectable()
+export class FooService implements OnModuleInit {
+	private barService: BarService // constructorから移動してくる
+
+	constructor(
+		private moduleRef: ModuleRef,
+	) {
+	}
+
+	async onModuleInit() {
+		this.barService = this.moduleRef.get(BarService.name);
+	}
+
+	public async niceMethod() {
+		return await this.barService.incredibleMethod({ hoge: 'fuga' });
+	}
+}
+```
+
+##### Service Unit Test
+テストで`onModuleInit`を呼び出す必要がある
+
+```typescript
+// import ...
+
+describe('test', () => {
+	let app: TestingModule;
+	let fooService: FooService; // for test case
+	let barService: BarService; // for test case
+
+	beforeEach(async () => {
+		app = await Test.createTestingModule({
+			imports: ...,
+			providers: [
+				FooService,
+				{ // mockする (mockは必須ではないかもしれない)
+					provide: BarService,
+					useFactory: () => ({
+						incredibleMethod: jest.fn(),
+					}),
+				},
+				{ // Provideにする
+					provide: BarService.name,
+					useExisting: BarService,
+				},
+			],
+		})
+			.useMocker(...
+			.compile();
+	
+		fooService = app.get<FooService>(FooService);
+		barService = app.get<BarService>(BarService) as jest.Mocked<BarService>;
+
+		// onModuleInitを実行する
+		await fooService.onModuleInit();
+	});
+
+	test('nice', () => {
+		await fooService.niceMethod();
+
+		expect(barService.incredibleMethod).toHaveBeenCalled();
+		expect(barService.incredibleMethod.mock.lastCall![0])
+			.toEqual({ hoge: 'fuga' });
+	});
+})
+```
+
 ## Notes
 
 ### Misskeyのドメイン固有の概念は`Mi`をprefixする

From 45672a70f9f8fd932896468f9bfdc6c511013682 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 5 Mar 2024 17:27:33 +0900
Subject: [PATCH 083/266] =?UTF-8?q?fix(frontend):=20router=E9=81=B7?=
 =?UTF-8?q?=E7=A7=BB=E6=99=82=E3=81=ABmatchAll=E3=81=AB=E5=85=A5=E3=81=A3?=
 =?UTF-8?q?=E3=81=9F=E5=A0=B4=E5=90=88=E4=B8=80=E5=BA=A6`location.href`?=
 =?UTF-8?q?=E3=82=92=E7=B5=8C=E7=94=B1=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#13509)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): router遷移時にmatchAllに入った場合一度`location.href`を経由するように

* Update Changelog

* Update CHANGELOG.md

* remove unnecessary args
---
 CHANGELOG.md                               |  2 +-
 packages/frontend/src/nirax.ts             | 20 ++++++++++++--------
 packages/frontend/vite.config.local-dev.ts |  3 +++
 3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 349e99d13399..0cebaabffdc6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
 -
 
 ### Client
-- 
+- Fix: 一部のページ内リンクが正しく動作しない問題を修正
 
 ### Server
 -
diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts
index 616fb104e6c0..6a8ea09ed6af 100644
--- a/packages/frontend/src/nirax.ts
+++ b/packages/frontend/src/nirax.ts
@@ -373,7 +373,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
 		this.currentRoute.value = res.route;
 		this.currentKey = res.route.globalCacheKey ?? key ?? path;
 
-		if (emitChange) {
+		if (emitChange && res.route.path !== '/:(*)') {
 			this.emit('change', {
 				beforePath,
 				path,
@@ -408,13 +408,17 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter {
 			if (cancel) return;
 		}
 		const res = this.navigate(path, null);
-		this.emit('push', {
-			beforePath,
-			path: res._parsedRoute.fullPath,
-			route: res.route,
-			props: res.props,
-			key: this.currentKey,
-		});
+		if (res.route.path === '/:(*)') {
+			location.href = path;
+		} else {
+			this.emit('push', {
+				beforePath,
+				path: res._parsedRoute.fullPath,
+				route: res.route,
+				props: res.props,
+				key: this.currentKey,
+			});
+		}
 	}
 
 	public replace(path: string, key?: string | null) {
diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
index 6d9488797c72..460787fd0589 100644
--- a/packages/frontend/vite.config.local-dev.ts
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -48,6 +48,9 @@ const devConfig = {
 			},
 			'/url': httpUrl,
 			'/proxy': httpUrl,
+			'/_info_card_': httpUrl,
+			'/bios': httpUrl,
+			'/cli': httpUrl,
 		},
 	},
 	build: {

From 08d618bb8b98199a4670313019d6a85ad4cf155b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 5 Mar 2024 18:06:57 +0900
Subject: [PATCH 084/266] =?UTF-8?q?enhance(frontend):=20=E8=87=AA=E5=88=86?=
 =?UTF-8?q?=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E3=81=AE=E6=B7=BB=E4=BB=98?=
 =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=8B=E3=82=89=E7=9B=B4?=
 =?UTF-8?q?=E6=8E=A5=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AE=E8=A9=B3?=
 =?UTF-8?q?=E7=B4=B0=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AB=E9=A3=9B=E3=81=B9?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#1352?=
 =?UTF-8?q?0)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるようにする

* 他のファイルタイプにも対応

* Update Changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                      |  1 +
 .../src/core/entities/DriveFileEntityService.ts   |  2 +-
 packages/frontend/src/components/MkMediaAudio.vue | 15 ++++++++++++---
 packages/frontend/src/components/MkMediaImage.vue |  9 ++++++++-
 packages/frontend/src/components/MkMediaVideo.vue | 15 ++++++++++++---
 5 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0cebaabffdc6..1b9009427929 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 -
 
 ### Client
+- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 
 ### Server
diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts
index 8affe2b3bf84..26bf386cbc4b 100644
--- a/packages/backend/src/core/entities/DriveFileEntityService.ts
+++ b/packages/backend/src/core/entities/DriveFileEntityService.ts
@@ -248,7 +248,7 @@ export class DriveFileEntityService {
 			folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
 				detail: true,
 			}) : null,
-			userId: opts.withUser ? file.userId : null,
+			userId: file.userId,
 			user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null,
 		});
 	}
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index d42146f941f4..96c9b9fd66c8 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -66,7 +66,7 @@ import * as os from '@/os.js';
 import bytes from '@/filters/bytes.js';
 import { hms } from '@/filters/hms.js';
 import MkMediaRange from '@/components/MkMediaRange.vue';
-import { iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/account.js';
 
 const props = defineProps<{
 	audio: Misskey.entities.DriveFile;
@@ -96,8 +96,6 @@ function showMenu(ev: MouseEvent) {
 
 	if (iAmModerator) {
 		menu.push({
-			type: 'divider',
-		}, {
 			text: props.audio.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
 			icon: props.audio.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
 			danger: true,
@@ -105,6 +103,17 @@ function showMenu(ev: MouseEvent) {
 		});
 	}
 
+	if ($i?.id === props.audio.userId) {
+		menu.push({
+			type: 'divider',
+		}, {
+			type: 'link' as const,
+			text: i18n.ts._fileViewer.title,
+			icon: 'ti ti-info-circle',
+			to: `/my/drive/file/${props.audio.id}`,
+		});
+	}
+
 	menuShowing.value = true;
 	os.popupMenu(menu, ev.currentTarget ?? ev.target, {
 		align: 'right',
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 4ba2c7613315..82f36fe5c412 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -59,7 +59,7 @@ import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
-import { iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/account.js';
 
 const props = withDefaults(defineProps<{
 	image: Misskey.entities.DriveFile;
@@ -114,6 +114,13 @@ function showMenu(ev: MouseEvent) {
 		action: () => {
 			os.apiWithDialog('drive/files/update', { fileId: props.image.id, isSensitive: true });
 		},
+	}] : []), ...($i?.id === props.image.userId ? [{
+		type: 'divider' as const,
+	}, {
+		type: 'link' as const,
+		text: i18n.ts._fileViewer.title,
+		icon: 'ti ti-info-circle',
+		to: `/my/drive/file/${props.image.id}`,
 	}] : [])], ev.currentTarget ?? ev.target);
 }
 
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index eab4fdfd6bd3..73c1b6ff9d55 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -94,7 +94,7 @@ import * as os from '@/os.js';
 import { isFullscreenNotSupported } from '@/scripts/device-kind.js';
 import hasAudio from '@/scripts/media-has-audio.js';
 import MkMediaRange from '@/components/MkMediaRange.vue';
-import { iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/account.js';
 
 const props = defineProps<{
 	video: Misskey.entities.DriveFile;
@@ -122,8 +122,6 @@ function showMenu(ev: MouseEvent) {
 
 	if (iAmModerator) {
 		menu.push({
-			type: 'divider',
-		}, {
 			text: props.video.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
 			icon: props.video.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',
 			danger: true,
@@ -131,6 +129,17 @@ function showMenu(ev: MouseEvent) {
 		});
 	}
 
+	if ($i?.id === props.video.userId) {
+		menu.push({
+			type: 'divider',
+		}, {
+			type: 'link' as const,
+			text: i18n.ts._fileViewer.title,
+			icon: 'ti ti-info-circle',
+			to: `/my/drive/file/${props.video.id}`,
+		});
+	}
+
 	menuShowing.value = true;
 	os.popupMenu(menu, ev.currentTarget ?? ev.target, {
 		align: 'right',

From 4457b02db2ca7591afa8683141e4b223170ea367 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Wed, 6 Mar 2024 08:08:32 +0000
Subject: [PATCH 085/266] =?UTF-8?q?fix(frontend)=3F:=20importAppScript?=
 =?UTF-8?q?=E3=81=AFimport=E3=82=92await=E3=81=99=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/server/web/boot.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 59441826b07d..396536948ef4 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -86,8 +86,8 @@
 	//#endregion
 
 	//#region Script
-	function importAppScript() {
-		import(`/vite/${CLIENT_ENTRY}`)
+	async function importAppScript() {
+		await import(`/vite/${CLIENT_ENTRY}`)
 			.catch(async e => {
 				console.error(e);
 				renderError('APP_IMPORT', e);

From 00c1e4eb550c68f43ae44ba9f0c8da9887fc2180 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Wed, 6 Mar 2024 09:40:47 +0000
Subject: [PATCH 086/266] =?UTF-8?q?perf:=20boot.js=E3=81=AE=E8=AA=BF?=
 =?UTF-8?q?=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/server/web/boot.js | 501 ++++++++++++++----------
 1 file changed, 288 insertions(+), 213 deletions(-)

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 396536948ef4..bc7b800d2299 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -7,158 +7,163 @@
  * BOOT LOADER
  * サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで、以下の役割を持ちます。
  * - 翻訳ファイルをフェッチする。
- * - バージョンに基づいて適切なメインスクリプトを読み込む。
+ * - 事前に挿入されたCLIENT_ENTRYを読んで適切なメインスクリプトを読み込む。
  * - キャッシュされたコンパイル済みテーマを適用する。
  * - クライアントの設定値に基づいて対応するHTMLクラス等を設定する。
+ * - もしメインスクリプトの読み込みなどでエラーが発生した場合は、renderErrorでエラーを描画する。
  * テーマをこの段階で設定するのは、メインスクリプトが読み込まれる間もテーマを適用したいためです。
- * 注: webpackは介さないため、このファイルではrequireやimportは使えません。
  */
 
 'use strict';
 
+var misskey_loader = new Set();
+
 // ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
-(async () => {
-	window.onerror = (e) => {
-		console.error(e);
-		renderError('SOMETHING_HAPPENED', e);
-	};
-	window.onunhandledrejection = (e) => {
-		console.error(e);
-		renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
-	};
+function boot() {
+	const defaultSolutions = [
+		'Clear the browser cache / ブラウザのキャッシュをクリアする',
+		'Update your os and browser / ブラウザおよびOSを最新バージョンに更新する',
+		'Disable an adblocker / アドブロッカーを無効にする',
+		'&#40;Tor Browser&#41; Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する'
+	];
+
+	const onErrorStyle = `
+	* {
+		font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
+	}
 
-	let forceError = localStorage.getItem('forceError');
-	if (forceError != null) {
-		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
+	#misskey_app,
+	#splash {
+		display: none !important;
 	}
 
-	//#region Detect language & fetch translations
-	if (!localStorage.hasOwnProperty('locale')) {
-		const supportedLangs = LANGS;
-		let lang = localStorage.getItem('lang');
-		if (lang == null || !supportedLangs.includes(lang)) {
-			if (supportedLangs.includes(navigator.language)) {
-				lang = navigator.language;
-			} else {
-				lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
+	body,
+	html {
+		background-color: #222;
+		color: #dfddcc;
+		justify-content: center;
+		margin: auto;
+		padding: 10px;
+		text-align: center;
+	}
 
-				// Fallback
-				if (lang == null) lang = 'en-US';
-			}
-		}
+	button {
+		border-radius: 999px;
+		padding: 0px 12px 0px 12px;
+		border: none;
+		cursor: pointer;
+		margin-bottom: 12px;
+	}
 
-		const metaRes = await window.fetch('/api/meta', {
-			method: 'POST',
-			body: JSON.stringify({}),
-			credentials: 'omit',
-			cache: 'no-cache',
-			headers: {
-				'Content-Type': 'application/json',
-			},
-		});
-		if (metaRes.status !== 200) {
-			renderError('META_FETCH');
-			return;
-		}
-		const meta = await metaRes.json();
-		const v = meta.version;
-		if (v == null) {
-			renderError('META_FETCH_V');
-			return;
-		}
+	.button-big {
+		background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
+		line-height: 50px;
+	}
 
-		// for https://github.com/misskey-dev/misskey/issues/10202
-		if (lang == null || lang.toString == null || lang.toString() === 'null') {
-			console.error('invalid lang value detected!!!', typeof lang, lang);
-			lang = 'en-US';
-		}
+	.button-big:hover {
+		background: rgb(153, 204, 0);
+	}
 
-		const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
-		if (localRes.status === 200) {
-			localStorage.setItem('lang', lang);
-			localStorage.setItem('locale', await localRes.text());
-			localStorage.setItem('localeVersion', v);
-		} else {
-			renderError('LOCALE_FETCH');
-			return;
-		}
+	.button-small {
+		background: #444;
+		line-height: 40px;
 	}
-	//#endregion
 
-	//#region Script
-	async function importAppScript() {
-		await import(`/vite/${CLIENT_ENTRY}`)
-			.catch(async e => {
-				console.error(e);
-				renderError('APP_IMPORT', e);
-			});
+	.button-small:hover {
+		background: #555;
 	}
 
-	// タイミングによっては、この時点でDOMの構築が済んでいる場合とそうでない場合とがある
-	if (document.readyState !== 'loading') {
-		importAppScript();
-	} else {
-		window.addEventListener('DOMContentLoaded', () => {
-			importAppScript();
-		});
+	.button-label-big {
+		color: #222;
+		font-weight: bold;
+		font-size: 1.2em;
+		padding: 12px;
 	}
-	//#endregion
 
-	//#region Theme
-	const theme = localStorage.getItem('theme');
-	if (theme) {
-		for (const [k, v] of Object.entries(JSON.parse(theme))) {
-			document.documentElement.style.setProperty(`--${k}`, v.toString());
+	.button-label-small {
+		color: rgb(153, 204, 0);
+		font-size: 16px;
+		padding: 12px;
+	}
 
-			// HTMLの theme-color 適用
-			if (k === 'htmlThemeColor') {
-				for (const tag of document.head.children) {
-					if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
-						tag.setAttribute('content', v);
-						break;
-					}
-				}
-			}
-		}
+	a {
+		color: rgb(134, 179, 0);
+		text-decoration: none;
 	}
-	const colorScheme = localStorage.getItem('colorScheme');
-	if (colorScheme) {
-		document.documentElement.style.setProperty('color-scheme', colorScheme);
+
+	p,
+	li {
+		font-size: 16px;
 	}
-	//#endregion
 
-	const fontSize = localStorage.getItem('fontSize');
-	if (fontSize) {
-		document.documentElement.classList.add('f-' + fontSize);
+	.icon-warning {
+		color: #dec340;
+		height: 4rem;
+		padding-top: 2rem;
 	}
 
-	const useSystemFont = localStorage.getItem('useSystemFont');
-	if (useSystemFont) {
-		document.documentElement.classList.add('useSystemFont');
+	h1 {
+		font-size: 1.5em;
+		margin: 1em;
 	}
 
-	const wallpaper = localStorage.getItem('wallpaper');
-	if (wallpaper) {
-		document.documentElement.style.backgroundImage = `url(${wallpaper})`;
+	summary {
+		cursor: pointer;
 	}
 
-	const customCss = localStorage.getItem('customCss');
-	if (customCss && customCss.length > 0) {
-		const style = document.createElement('style');
-		style.innerHTML = customCss;
-		document.head.appendChild(style);
+	code {
+		font-family: Fira, FiraCode, monospace;
+	}
+
+	#errors {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
 	}
 
-	async function addStyle(styleText) {
-		let css = document.createElement('style');
-		css.appendChild(document.createTextNode(styleText));
-		document.head.appendChild(css);
+	.errorInfo {
+		background: #333;
+		width: 40rem;
+		max-width: 100%;
+		border-radius: 10px;
+		justify-content: center;
+		padding: 1rem;
+		margin-bottom: 1rem;
+		box-sizing: border-box;
 	}
 
-	function renderError(code, details) {
+	.errorInfo > pre {
+		text-wrap: auto;
+		text-wrap: balance;
+	}
+	`;
+
+
+	const addStyle = (styleText) => {
+		try {
+			let css = document.createElement('style');
+			css.appendChild(document.createTextNode(styleText));
+			document.head.appendChild(css);
+		} catch (e) {
+			console.error(e);
+		}
+	}
+
+	const renderError = (code, details, solutions = defaultSolutions) => {
+		if (document.readyState === 'loading') {
+			window.addEventListener('DOMContentLoaded', () => {
+				renderError(code, details, solutions);
+			});
+			try {
+				addStyle(onErrorStyle);
+			} catch (e) { }
+			return;
+		}
+
 		let errorsElement = document.getElementById('errors');
 
 		if (!errorsElement) {
+			// エラー描画用のビューになっていない場合は、エラー描画用のビューに切り替える
 			document.body.innerHTML = `
 			<svg class="icon-warning" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-alert-triangle" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
 				<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
@@ -170,10 +175,7 @@
 				<span class="button-label-big">Reload / リロード</span>
 			</button>
 			<p><b>The following actions may solve the problem. / 以下を行うと解決する可能性があります。</b></p>
-			<p>Clear the browser cache / ブラウザのキャッシュをクリアする</p>
-			<p>Update your os and browser / ブラウザおよびOSを最新バージョンに更新する</p>
-			<p>Disable an adblocker / アドブロッカーを無効にする</p>
-	 		<p>&#40;Tor Browser&#41; Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する</p>
+			${solutions.map(x => `<p>${x}</p>`).join('')}
 			<details style="color: #86b300;">
 				<summary>Other options / その他のオプション</summary>
 				<a href="/flush">
@@ -199,121 +201,194 @@
 			`;
 			errorsElement = document.getElementById('errors');
 		}
-		const detailsElement = document.createElement('details');
-		detailsElement.id = 'errorInfo';
-		detailsElement.innerHTML = `
-		<br>
-		<summary>
-			<code>ERROR CODE: ${code}</code>
-		</summary>
-		<code>${JSON.stringify(details)}</code>`;
-		errorsElement.appendChild(detailsElement);
-		addStyle(`
-		* {
-			font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
-		}
 
-		#misskey_app,
-		#splash {
-			display: none !important;
-		}
+		if (typeof details === 'string') {
+			const errorEl = document.createElement('div');
+			errorEl.classList.add('errorInfo');
+
+			const titleCodeElement = document.createElement('code');
+			titleCodeElement.textContent = `ERROR CODE: ${code}`;
+			errorEl.appendChild(titleCodeElement);
+
+			errorEl.appendChild(document.createElement('br'));
+
+			const detailsCodeElement = document.createElement('code');
+			detailsCodeElement.textContent = details;
+			errorEl.appendChild(detailsCodeElement);
+
+			errorsElement.appendChild(errorEl);
+		} else if (details instanceof Error) {
+			const errorEl = document.createElement('details');
+			errorEl.classList.add('errorInfo');
+
+			const summaryElement = document.createElement('summary');
+			const titleCodeElement = document.createElement('code');
+			titleCodeElement.textContent = `ERROR CODE: ${code}`;
+			summaryElement.appendChild(titleCodeElement);
+			errorEl.appendChild(summaryElement);
+
+			const detailsPreElement = document.createElement('pre');
+			const detailsMessageElement = document.createElement('code');
+			detailsMessageElement.textContent = details.message;
+			detailsPreElement.appendChild(detailsMessageElement);
+			detailsPreElement.appendChild(document.createElement('br'));
+			const detailsCodeElement = document.createElement('code');
+			detailsCodeElement.textContent = details.stack;
+			detailsPreElement.appendChild(detailsCodeElement);
+			errorEl.appendChild(detailsPreElement);
+
+			errorsElement.appendChild(errorEl);
+		} else {
+			const errorEl = document.createElement('details');
+			errorEl.classList.add('errorInfo');
 
-		body,
-		html {
-			background-color: #222;
-			color: #dfddcc;
-			justify-content: center;
-			margin: auto;
-			padding: 10px;
-			text-align: center;
-		}
+			const summaryElement = document.createElement('summary');
+			const titleCodeElement = document.createElement('code');
+			titleCodeElement.textContent = `ERROR CODE: ${code}`;
+			summaryElement.appendChild(titleCodeElement);
+			errorEl.appendChild(summaryElement);
 
-		button {
-			border-radius: 999px;
-			padding: 0px 12px 0px 12px;
-			border: none;
-			cursor: pointer;
-			margin-bottom: 12px;
-		}
+			const detailsCodeElement = document.createElement('code');
+			detailsCodeElement.textContent = JSON.stringify(details);
+			errorEl.appendChild(detailsCodeElement);
 
-		.button-big {
-			background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
-			line-height: 50px;
+			errorsElement.appendChild(errorEl);
 		}
 
-		.button-big:hover {
-			background: rgb(153, 204, 0);
-		}
+		addStyle(onErrorStyle);
+	}
 
-		.button-small {
-			background: #444;
-			line-height: 40px;
-		}
+	window.onerror = (e) => {
+		console.error(e);
+		renderError('SOMETHING_HAPPENED', e);
+	};
+	window.onunhandledrejection = (e) => {
+		console.error(e);
+		renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
+	};
 
-		.button-small:hover {
-			background: #555;
-		}
+	let forceError = localStorage.getItem('forceError');
+	if (forceError != null) {
+		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
+		renderError('FORCED_ERROR', Error('This error is forced by having forceError in local storage.'));
+		return;
+	}
 
-		.button-label-big {
-			color: #222;
-			font-weight: bold;
-			font-size: 1.2em;
-			padding: 12px;
+	//#region After DOM loaded
+	async function oncontentload() {
+		const providedMetaEl = document.getElementById('misskey_meta');
+		const meta = providedMetaEl && providedMetaEl.textContent ? JSON.parse(providedMetaEl.textContent) : null;
+		const providedAt = providedMetaEl && providedMetaEl.dataset.generatedAt ? parseInt(providedMetaEl.dataset.generatedAt) : 0;
+		console.log('providedAt', providedAt, 'now', Date.now());
+		if (providedAt < Date.now() - 1000 * 60 * 60 * 24) {
+			// 古いデータがなぜか提供された場合は、エラーを描画する
+			renderError(
+				'META_PROVIDED_AT_TOO_OLD',
+				'This view is too old. Please reload.',
+				[
+					'Reload / リロードする',
+					'Clear the browser cache then reload / ブラウザのキャッシュをクリアしてリロードする',
+					'Disable an adblocker / アドブロッカーを無効にする',
+				]
+			);
+			return;
 		}
 
-		.button-label-small {
-			color: rgb(153, 204, 0);
-			font-size: 16px;
-			padding: 12px;
-		}
+		//#region Detect language & fetch translations on first load
+		if (!localStorage.hasOwnProperty('locale')) {
+			const supportedLangs = LANGS;
+			let lang = localStorage.getItem('lang');
+			if (lang == null || !supportedLangs.includes(lang)) {
+				if (supportedLangs.includes(navigator.language)) {
+					lang = navigator.language;
+				} else {
+					lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
+
+					// Fallback
+					if (lang == null) lang = 'en-US';
+				}
+			}
 
-		a {
-			color: rgb(134, 179, 0);
-			text-decoration: none;
-		}
+			const v = meta?.version;
 
-		p,
-		li {
-			font-size: 16px;
-		}
+			// for https://github.com/misskey-dev/misskey/issues/10202
+			if (lang == null || lang.toString == null || lang.toString() === 'null') {
+				console.warn('invalid lang value detected!!!', typeof lang, lang);
+				lang = 'en-US';
+			}
 
-		.icon-warning {
-			color: #dec340;
-			height: 4rem;
-			padding-top: 2rem;
+			const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
+			if (localRes.status === 200) {
+				localStorage.setItem('lang', lang);
+				localStorage.setItem('locale', await localRes.text());
+				localStorage.setItem('localeVersion', v);
+			} else {
+				renderError('LOCALE_FETCH');
+				return;
+			}
 		}
+		//#endregion
 
-		h1 {
-			font-size: 1.5em;
-			margin: 1em;
-		}
+		await import(`/vite/${CLIENT_ENTRY}`)
+			.catch(async e => {
+				console.error(e);
+				renderError('APP_IMPORT', e);
+			});
+	}
 
-		code {
-			font-family: Fira, FiraCode, monospace;
-		}
+	if (document.readyState !== 'loading') {
+		misskey_loader.add(oncontentload());
+	} else {
+		window.addEventListener('DOMContentLoaded', () => {
+			misskey_loader.add(oncontentload());
+		});
+	}
+	//#endregion
 
-		#errorInfo {
-			background: #333;
-			margin-bottom: 2rem;
-			padding: 0.5rem 1rem;
-			width: 40rem;
-			border-radius: 10px;
-			justify-content: center;
-			margin: auto;
-		}
+	//#region Theme
+	const theme = localStorage.getItem('theme');
+	if (theme) {
+		for (const [k, v] of Object.entries(JSON.parse(theme))) {
+			document.documentElement.style.setProperty(`--${k}`, v.toString());
 
-		#errorInfo summary {
-			cursor: pointer;
+			// HTMLの theme-color 適用
+			if (k === 'htmlThemeColor') {
+				for (const tag of document.head.children) {
+					if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
+						tag.setAttribute('content', v);
+						break;
+					}
+				}
+			}
 		}
+	}
+	const colorScheme = localStorage.getItem('colorScheme');
+	if (colorScheme) {
+		document.documentElement.style.setProperty('color-scheme', colorScheme);
+	}
+	//#endregion
 
-		#errorInfo summary > * {
-			display: inline;
-		}
+	const fontSize = localStorage.getItem('fontSize');
+	if (fontSize) {
+		document.documentElement.classList.add('f-' + fontSize);
+	}
 
-		@media screen and (max-width: 500px) {
-			#errorInfo {
-				width: 50%;
-			}
-		`)
+	const useSystemFont = localStorage.getItem('useSystemFont');
+	if (useSystemFont) {
+		document.documentElement.classList.add('useSystemFont');
 	}
-})();
+
+	const wallpaper = localStorage.getItem('wallpaper');
+	if (wallpaper) {
+		document.documentElement.style.backgroundImage = `url(${wallpaper})`;
+	}
+
+	const customCss = localStorage.getItem('customCss');
+	if (customCss && customCss.length > 0) {
+		const style = document.createElement('style');
+		style.innerHTML = customCss;
+		document.head.appendChild(style);
+	}
+}
+
+boot();

From 62922352b3cddaee9f72261448d862002e55a67c Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Wed, 6 Mar 2024 09:49:01 +0000
Subject: [PATCH 087/266] =?UTF-8?q?Revert=20"perf:=20boot.js=E3=81=AE?=
 =?UTF-8?q?=E8=AA=BF=E6=95=B4"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit 00c1e4eb550c68f43ae44ba9f0c8da9887fc2180.
---
 packages/backend/src/server/web/boot.js | 501 ++++++++++--------------
 1 file changed, 213 insertions(+), 288 deletions(-)

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index bc7b800d2299..396536948ef4 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -7,163 +7,158 @@
  * BOOT LOADER
  * サーバーからレスポンスされるHTMLに埋め込まれるスクリプトで、以下の役割を持ちます。
  * - 翻訳ファイルをフェッチする。
- * - 事前に挿入されたCLIENT_ENTRYを読んで適切なメインスクリプトを読み込む。
+ * - バージョンに基づいて適切なメインスクリプトを読み込む。
  * - キャッシュされたコンパイル済みテーマを適用する。
  * - クライアントの設定値に基づいて対応するHTMLクラス等を設定する。
- * - もしメインスクリプトの読み込みなどでエラーが発生した場合は、renderErrorでエラーを描画する。
  * テーマをこの段階で設定するのは、メインスクリプトが読み込まれる間もテーマを適用したいためです。
+ * 注: webpackは介さないため、このファイルではrequireやimportは使えません。
  */
 
 'use strict';
 
-var misskey_loader = new Set();
-
 // ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
-function boot() {
-	const defaultSolutions = [
-		'Clear the browser cache / ブラウザのキャッシュをクリアする',
-		'Update your os and browser / ブラウザおよびOSを最新バージョンに更新する',
-		'Disable an adblocker / アドブロッカーを無効にする',
-		'&#40;Tor Browser&#41; Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する'
-	];
-
-	const onErrorStyle = `
-	* {
-		font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
-	}
-
-	#misskey_app,
-	#splash {
-		display: none !important;
-	}
-
-	body,
-	html {
-		background-color: #222;
-		color: #dfddcc;
-		justify-content: center;
-		margin: auto;
-		padding: 10px;
-		text-align: center;
-	}
-
-	button {
-		border-radius: 999px;
-		padding: 0px 12px 0px 12px;
-		border: none;
-		cursor: pointer;
-		margin-bottom: 12px;
-	}
+(async () => {
+	window.onerror = (e) => {
+		console.error(e);
+		renderError('SOMETHING_HAPPENED', e);
+	};
+	window.onunhandledrejection = (e) => {
+		console.error(e);
+		renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
+	};
 
-	.button-big {
-		background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
-		line-height: 50px;
+	let forceError = localStorage.getItem('forceError');
+	if (forceError != null) {
+		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
 	}
 
-	.button-big:hover {
-		background: rgb(153, 204, 0);
-	}
+	//#region Detect language & fetch translations
+	if (!localStorage.hasOwnProperty('locale')) {
+		const supportedLangs = LANGS;
+		let lang = localStorage.getItem('lang');
+		if (lang == null || !supportedLangs.includes(lang)) {
+			if (supportedLangs.includes(navigator.language)) {
+				lang = navigator.language;
+			} else {
+				lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
 
-	.button-small {
-		background: #444;
-		line-height: 40px;
-	}
+				// Fallback
+				if (lang == null) lang = 'en-US';
+			}
+		}
 
-	.button-small:hover {
-		background: #555;
-	}
+		const metaRes = await window.fetch('/api/meta', {
+			method: 'POST',
+			body: JSON.stringify({}),
+			credentials: 'omit',
+			cache: 'no-cache',
+			headers: {
+				'Content-Type': 'application/json',
+			},
+		});
+		if (metaRes.status !== 200) {
+			renderError('META_FETCH');
+			return;
+		}
+		const meta = await metaRes.json();
+		const v = meta.version;
+		if (v == null) {
+			renderError('META_FETCH_V');
+			return;
+		}
 
-	.button-label-big {
-		color: #222;
-		font-weight: bold;
-		font-size: 1.2em;
-		padding: 12px;
-	}
+		// for https://github.com/misskey-dev/misskey/issues/10202
+		if (lang == null || lang.toString == null || lang.toString() === 'null') {
+			console.error('invalid lang value detected!!!', typeof lang, lang);
+			lang = 'en-US';
+		}
 
-	.button-label-small {
-		color: rgb(153, 204, 0);
-		font-size: 16px;
-		padding: 12px;
+		const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
+		if (localRes.status === 200) {
+			localStorage.setItem('lang', lang);
+			localStorage.setItem('locale', await localRes.text());
+			localStorage.setItem('localeVersion', v);
+		} else {
+			renderError('LOCALE_FETCH');
+			return;
+		}
 	}
+	//#endregion
 
-	a {
-		color: rgb(134, 179, 0);
-		text-decoration: none;
+	//#region Script
+	async function importAppScript() {
+		await import(`/vite/${CLIENT_ENTRY}`)
+			.catch(async e => {
+				console.error(e);
+				renderError('APP_IMPORT', e);
+			});
 	}
 
-	p,
-	li {
-		font-size: 16px;
+	// タイミングによっては、この時点でDOMの構築が済んでいる場合とそうでない場合とがある
+	if (document.readyState !== 'loading') {
+		importAppScript();
+	} else {
+		window.addEventListener('DOMContentLoaded', () => {
+			importAppScript();
+		});
 	}
+	//#endregion
 
-	.icon-warning {
-		color: #dec340;
-		height: 4rem;
-		padding-top: 2rem;
-	}
+	//#region Theme
+	const theme = localStorage.getItem('theme');
+	if (theme) {
+		for (const [k, v] of Object.entries(JSON.parse(theme))) {
+			document.documentElement.style.setProperty(`--${k}`, v.toString());
 
-	h1 {
-		font-size: 1.5em;
-		margin: 1em;
+			// HTMLの theme-color 適用
+			if (k === 'htmlThemeColor') {
+				for (const tag of document.head.children) {
+					if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
+						tag.setAttribute('content', v);
+						break;
+					}
+				}
+			}
+		}
 	}
-
-	summary {
-		cursor: pointer;
+	const colorScheme = localStorage.getItem('colorScheme');
+	if (colorScheme) {
+		document.documentElement.style.setProperty('color-scheme', colorScheme);
 	}
+	//#endregion
 
-	code {
-		font-family: Fira, FiraCode, monospace;
+	const fontSize = localStorage.getItem('fontSize');
+	if (fontSize) {
+		document.documentElement.classList.add('f-' + fontSize);
 	}
 
-	#errors {
-		display: flex;
-		flex-direction: column;
-		align-items: center;
+	const useSystemFont = localStorage.getItem('useSystemFont');
+	if (useSystemFont) {
+		document.documentElement.classList.add('useSystemFont');
 	}
 
-	.errorInfo {
-		background: #333;
-		width: 40rem;
-		max-width: 100%;
-		border-radius: 10px;
-		justify-content: center;
-		padding: 1rem;
-		margin-bottom: 1rem;
-		box-sizing: border-box;
+	const wallpaper = localStorage.getItem('wallpaper');
+	if (wallpaper) {
+		document.documentElement.style.backgroundImage = `url(${wallpaper})`;
 	}
 
-	.errorInfo > pre {
-		text-wrap: auto;
-		text-wrap: balance;
+	const customCss = localStorage.getItem('customCss');
+	if (customCss && customCss.length > 0) {
+		const style = document.createElement('style');
+		style.innerHTML = customCss;
+		document.head.appendChild(style);
 	}
-	`;
-
 
-	const addStyle = (styleText) => {
-		try {
-			let css = document.createElement('style');
-			css.appendChild(document.createTextNode(styleText));
-			document.head.appendChild(css);
-		} catch (e) {
-			console.error(e);
-		}
+	async function addStyle(styleText) {
+		let css = document.createElement('style');
+		css.appendChild(document.createTextNode(styleText));
+		document.head.appendChild(css);
 	}
 
-	const renderError = (code, details, solutions = defaultSolutions) => {
-		if (document.readyState === 'loading') {
-			window.addEventListener('DOMContentLoaded', () => {
-				renderError(code, details, solutions);
-			});
-			try {
-				addStyle(onErrorStyle);
-			} catch (e) { }
-			return;
-		}
-
+	function renderError(code, details) {
 		let errorsElement = document.getElementById('errors');
 
 		if (!errorsElement) {
-			// エラー描画用のビューになっていない場合は、エラー描画用のビューに切り替える
 			document.body.innerHTML = `
 			<svg class="icon-warning" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-alert-triangle" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
 				<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
@@ -175,7 +170,10 @@ function boot() {
 				<span class="button-label-big">Reload / リロード</span>
 			</button>
 			<p><b>The following actions may solve the problem. / 以下を行うと解決する可能性があります。</b></p>
-			${solutions.map(x => `<p>${x}</p>`).join('')}
+			<p>Clear the browser cache / ブラウザのキャッシュをクリアする</p>
+			<p>Update your os and browser / ブラウザおよびOSを最新バージョンに更新する</p>
+			<p>Disable an adblocker / アドブロッカーを無効にする</p>
+	 		<p>&#40;Tor Browser&#41; Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する</p>
 			<details style="color: #86b300;">
 				<summary>Other options / その他のオプション</summary>
 				<a href="/flush">
@@ -201,194 +199,121 @@ function boot() {
 			`;
 			errorsElement = document.getElementById('errors');
 		}
+		const detailsElement = document.createElement('details');
+		detailsElement.id = 'errorInfo';
+		detailsElement.innerHTML = `
+		<br>
+		<summary>
+			<code>ERROR CODE: ${code}</code>
+		</summary>
+		<code>${JSON.stringify(details)}</code>`;
+		errorsElement.appendChild(detailsElement);
+		addStyle(`
+		* {
+			font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
+		}
 
-		if (typeof details === 'string') {
-			const errorEl = document.createElement('div');
-			errorEl.classList.add('errorInfo');
-
-			const titleCodeElement = document.createElement('code');
-			titleCodeElement.textContent = `ERROR CODE: ${code}`;
-			errorEl.appendChild(titleCodeElement);
-
-			errorEl.appendChild(document.createElement('br'));
-
-			const detailsCodeElement = document.createElement('code');
-			detailsCodeElement.textContent = details;
-			errorEl.appendChild(detailsCodeElement);
-
-			errorsElement.appendChild(errorEl);
-		} else if (details instanceof Error) {
-			const errorEl = document.createElement('details');
-			errorEl.classList.add('errorInfo');
-
-			const summaryElement = document.createElement('summary');
-			const titleCodeElement = document.createElement('code');
-			titleCodeElement.textContent = `ERROR CODE: ${code}`;
-			summaryElement.appendChild(titleCodeElement);
-			errorEl.appendChild(summaryElement);
-
-			const detailsPreElement = document.createElement('pre');
-			const detailsMessageElement = document.createElement('code');
-			detailsMessageElement.textContent = details.message;
-			detailsPreElement.appendChild(detailsMessageElement);
-			detailsPreElement.appendChild(document.createElement('br'));
-			const detailsCodeElement = document.createElement('code');
-			detailsCodeElement.textContent = details.stack;
-			detailsPreElement.appendChild(detailsCodeElement);
-			errorEl.appendChild(detailsPreElement);
-
-			errorsElement.appendChild(errorEl);
-		} else {
-			const errorEl = document.createElement('details');
-			errorEl.classList.add('errorInfo');
-
-			const summaryElement = document.createElement('summary');
-			const titleCodeElement = document.createElement('code');
-			titleCodeElement.textContent = `ERROR CODE: ${code}`;
-			summaryElement.appendChild(titleCodeElement);
-			errorEl.appendChild(summaryElement);
-
-			const detailsCodeElement = document.createElement('code');
-			detailsCodeElement.textContent = JSON.stringify(details);
-			errorEl.appendChild(detailsCodeElement);
+		#misskey_app,
+		#splash {
+			display: none !important;
+		}
 
-			errorsElement.appendChild(errorEl);
+		body,
+		html {
+			background-color: #222;
+			color: #dfddcc;
+			justify-content: center;
+			margin: auto;
+			padding: 10px;
+			text-align: center;
 		}
 
-		addStyle(onErrorStyle);
-	}
+		button {
+			border-radius: 999px;
+			padding: 0px 12px 0px 12px;
+			border: none;
+			cursor: pointer;
+			margin-bottom: 12px;
+		}
 
-	window.onerror = (e) => {
-		console.error(e);
-		renderError('SOMETHING_HAPPENED', e);
-	};
-	window.onunhandledrejection = (e) => {
-		console.error(e);
-		renderError('SOMETHING_HAPPENED_IN_PROMISE', e);
-	};
+		.button-big {
+			background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
+			line-height: 50px;
+		}
 
-	let forceError = localStorage.getItem('forceError');
-	if (forceError != null) {
-		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
-		renderError('FORCED_ERROR', Error('This error is forced by having forceError in local storage.'));
-		return;
-	}
+		.button-big:hover {
+			background: rgb(153, 204, 0);
+		}
 
-	//#region After DOM loaded
-	async function oncontentload() {
-		const providedMetaEl = document.getElementById('misskey_meta');
-		const meta = providedMetaEl && providedMetaEl.textContent ? JSON.parse(providedMetaEl.textContent) : null;
-		const providedAt = providedMetaEl && providedMetaEl.dataset.generatedAt ? parseInt(providedMetaEl.dataset.generatedAt) : 0;
-		console.log('providedAt', providedAt, 'now', Date.now());
-		if (providedAt < Date.now() - 1000 * 60 * 60 * 24) {
-			// 古いデータがなぜか提供された場合は、エラーを描画する
-			renderError(
-				'META_PROVIDED_AT_TOO_OLD',
-				'This view is too old. Please reload.',
-				[
-					'Reload / リロードする',
-					'Clear the browser cache then reload / ブラウザのキャッシュをクリアしてリロードする',
-					'Disable an adblocker / アドブロッカーを無効にする',
-				]
-			);
-			return;
+		.button-small {
+			background: #444;
+			line-height: 40px;
 		}
 
-		//#region Detect language & fetch translations on first load
-		if (!localStorage.hasOwnProperty('locale')) {
-			const supportedLangs = LANGS;
-			let lang = localStorage.getItem('lang');
-			if (lang == null || !supportedLangs.includes(lang)) {
-				if (supportedLangs.includes(navigator.language)) {
-					lang = navigator.language;
-				} else {
-					lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
-
-					// Fallback
-					if (lang == null) lang = 'en-US';
-				}
-			}
+		.button-small:hover {
+			background: #555;
+		}
 
-			const v = meta?.version;
+		.button-label-big {
+			color: #222;
+			font-weight: bold;
+			font-size: 1.2em;
+			padding: 12px;
+		}
 
-			// for https://github.com/misskey-dev/misskey/issues/10202
-			if (lang == null || lang.toString == null || lang.toString() === 'null') {
-				console.warn('invalid lang value detected!!!', typeof lang, lang);
-				lang = 'en-US';
-			}
+		.button-label-small {
+			color: rgb(153, 204, 0);
+			font-size: 16px;
+			padding: 12px;
+		}
 
-			const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
-			if (localRes.status === 200) {
-				localStorage.setItem('lang', lang);
-				localStorage.setItem('locale', await localRes.text());
-				localStorage.setItem('localeVersion', v);
-			} else {
-				renderError('LOCALE_FETCH');
-				return;
-			}
+		a {
+			color: rgb(134, 179, 0);
+			text-decoration: none;
 		}
-		//#endregion
 
-		await import(`/vite/${CLIENT_ENTRY}`)
-			.catch(async e => {
-				console.error(e);
-				renderError('APP_IMPORT', e);
-			});
-	}
+		p,
+		li {
+			font-size: 16px;
+		}
 
-	if (document.readyState !== 'loading') {
-		misskey_loader.add(oncontentload());
-	} else {
-		window.addEventListener('DOMContentLoaded', () => {
-			misskey_loader.add(oncontentload());
-		});
-	}
-	//#endregion
+		.icon-warning {
+			color: #dec340;
+			height: 4rem;
+			padding-top: 2rem;
+		}
 
-	//#region Theme
-	const theme = localStorage.getItem('theme');
-	if (theme) {
-		for (const [k, v] of Object.entries(JSON.parse(theme))) {
-			document.documentElement.style.setProperty(`--${k}`, v.toString());
+		h1 {
+			font-size: 1.5em;
+			margin: 1em;
+		}
 
-			// HTMLの theme-color 適用
-			if (k === 'htmlThemeColor') {
-				for (const tag of document.head.children) {
-					if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
-						tag.setAttribute('content', v);
-						break;
-					}
-				}
-			}
+		code {
+			font-family: Fira, FiraCode, monospace;
 		}
-	}
-	const colorScheme = localStorage.getItem('colorScheme');
-	if (colorScheme) {
-		document.documentElement.style.setProperty('color-scheme', colorScheme);
-	}
-	//#endregion
 
-	const fontSize = localStorage.getItem('fontSize');
-	if (fontSize) {
-		document.documentElement.classList.add('f-' + fontSize);
-	}
+		#errorInfo {
+			background: #333;
+			margin-bottom: 2rem;
+			padding: 0.5rem 1rem;
+			width: 40rem;
+			border-radius: 10px;
+			justify-content: center;
+			margin: auto;
+		}
 
-	const useSystemFont = localStorage.getItem('useSystemFont');
-	if (useSystemFont) {
-		document.documentElement.classList.add('useSystemFont');
-	}
+		#errorInfo summary {
+			cursor: pointer;
+		}
 
-	const wallpaper = localStorage.getItem('wallpaper');
-	if (wallpaper) {
-		document.documentElement.style.backgroundImage = `url(${wallpaper})`;
-	}
+		#errorInfo summary > * {
+			display: inline;
+		}
 
-	const customCss = localStorage.getItem('customCss');
-	if (customCss && customCss.length > 0) {
-		const style = document.createElement('style');
-		style.innerHTML = customCss;
-		document.head.appendChild(style);
+		@media screen and (max-width: 500px) {
+			#errorInfo {
+				width: 50%;
+			}
+		`)
 	}
-}
-
-boot();
+})();

From 7ead98cbe592e6911e4a54550cb7bb507e782d7c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 6 Mar 2024 21:08:42 +0900
Subject: [PATCH 088/266] =?UTF-8?q?enhance(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E7=B7=8F=E6=95=B0?=
 =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#13532)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): リアクションの総数を表示するように

* Update Changelog

* リアクション選択済の色をaccentに
---
 CHANGELOG.md                                  |  2 ++
 locales/index.d.ts                            |  4 +++
 locales/ja-JP.yml                             |  1 +
 .../src/core/entities/NoteEntityService.ts    |  1 +
 .../backend/src/models/json-schema/note.ts    |  4 +++
 packages/frontend/src/components/MkNote.vue   | 25 ++++++++++-----
 .../src/components/MkNoteDetailed.vue         | 31 ++++++++++++-------
 .../src/components/MkNotification.vue         | 27 ++++++++++------
 .../src/components/MkTutorialDialog.Note.vue  |  1 +
 .../components/MkTutorialDialog.PostNote.vue  |  1 +
 .../components/MkTutorialDialog.Sensitive.vue |  1 +
 .../frontend/src/scripts/use-note-capture.ts  |  2 ++
 packages/frontend/src/style.scss              |  7 +++++
 packages/misskey-js/src/autogen/types.ts      |  1 +
 14 files changed, 79 insertions(+), 29 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b9009427929..7bdfa53e9965 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
 
 ### Client
 - Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
+- Enhance: リアクション・いいねの総数を表示するように
+- Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 
 ### Server
diff --git a/locales/index.d.ts b/locales/index.d.ts
index c1aa163f9822..3ac4ff9e74cd 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -8909,6 +8909,10 @@ export interface Locale extends ILocale {
          * {n}人がリアクションしました
          */
         "reactedBySomeUsers": ParameterizedString<"n">;
+        /**
+         * {n}人がいいねしました
+         */
+        "likedBySomeUsers": ParameterizedString<"n">;
         /**
          * {n}人がリノートしました
          */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 51380e49c523..177d6a06c941 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2355,6 +2355,7 @@ _notification:
   sendTestNotification: "テスト通知を送信する"
   notificationWillBeDisplayedLikeThis: "通知はこのように表示されます"
   reactedBySomeUsers: "{n}人がリアクションしました"
+  likedBySomeUsers: "{n}人がいいねしました"
   renotedBySomeUsers: "{n}人がリノートしました"
   followedBySomeUsers: "{n}人にフォローされました"
   flushNotification: "通知の履歴をリセットする"
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 5b6affc6a520..22d01462e773 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -333,6 +333,7 @@ export class NoteEntityService implements OnModuleInit {
 			visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined,
 			renoteCount: note.renoteCount,
 			repliesCount: note.repliesCount,
+			reactionCount: Object.values(note.reactions).reduce((a, b) => a + b, 0),
 			reactions: this.reactionService.convertLegacyReactions(note.reactions),
 			reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host),
 			reactionAndUserPairCache: opts.withReactionAndUserPairCache ? note.reactionAndUserPairCache : undefined,
diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index bb4ccc7ee4db..2641161c8ba2 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -223,6 +223,10 @@ export const packedNoteSchema = {
 				}],
 			},
 		},
+		reactionCount: {
+			type: 'number',
+			optional: false, nullable: false,
+		},
 		renoteCount: {
 			type: 'number',
 			optional: false, nullable: false,
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 03a283cab33b..656ccc7959bd 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -93,7 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 				<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
 			</div>
-			<MkReactionsViewer :note="appearNote" :maxNumber="16" @mockUpdateMyReaction="emitUpdReaction">
+			<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @mockUpdateMyReaction="emitUpdReaction">
 				<template #more>
 					<div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div>
 				</template>
@@ -101,7 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<footer :class="$style.footer">
 				<button :class="$style.footerButton" class="_button" @click="reply()">
 					<i class="ti ti-arrow-back-up"></i>
-					<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p>
+					<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p>
 				</button>
 				<button
 					v-if="canRenote"
@@ -111,17 +111,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 					@mousedown="renote()"
 				>
 					<i class="ti ti-repeat"></i>
-					<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p>
+					<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.renoteCount) }}</p>
 				</button>
 				<button v-else :class="$style.footerButton" class="_button" disabled>
 					<i class="ti ti-ban"></i>
 				</button>
-				<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()">
-					<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
+				<button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()">
+					<i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i>
+					<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
+					<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
 					<i v-else class="ti ti-plus"></i>
-				</button>
-				<button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)">
-					<i class="ti ti-minus"></i>
+					<p v-if="appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
 				</button>
 				<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
 					<i class="ti ti-paperclip"></i>
@@ -175,6 +175,7 @@ import { pleaseLogin } from '@/scripts/please-login.js';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
 import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
+import number from '@/filters/number.js';
 import * as os from '@/os.js';
 import * as sound from '@/scripts/sound.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
@@ -420,6 +421,14 @@ function undoReact(targetNote: Misskey.entities.Note): void {
 	});
 }
 
+function toggleReact() {
+	if (appearNote.value.myReaction == null) {
+		react();
+	} else {
+		undoReact(appearNote.value);
+	}
+}
+
 function onContextmenu(ev: MouseEvent): void {
 	if (props.mock) {
 		return;
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index e3ef14120f79..2d2930ee7c55 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -106,10 +106,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkTime :time="appearNote.createdAt" mode="detail" colored/>
 				</MkA>
 			</div>
-			<MkReactionsViewer ref="reactionsViewer" :note="appearNote"/>
+			<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/>
 			<button class="_button" :class="$style.noteFooterButton" @click="reply()">
 				<i class="ti ti-arrow-back-up"></i>
-				<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.repliesCount }}</p>
+				<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
 			</button>
 			<button
 				v-if="canRenote"
@@ -119,17 +119,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 				@mousedown="renote()"
 			>
 				<i class="ti ti-repeat"></i>
-				<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.renoteCount }}</p>
+				<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
 			</button>
 			<button v-else class="_button" :class="$style.noteFooterButton" disabled>
 				<i class="ti ti-ban"></i>
 			</button>
-			<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
-				<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
+			<button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()">
+				<i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i>
+				<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
+				<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
 				<i v-else class="ti ti-plus"></i>
-			</button>
-			<button v-if="appearNote.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(appearNote)">
-				<i class="ti ti-minus"></i>
+				<p v-if="appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
 			</button>
 			<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
 				<i class="ti ti-paperclip"></i>
@@ -209,6 +209,7 @@ import { pleaseLogin } from '@/scripts/please-login.js';
 import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import { notePage } from '@/filters/note.js';
+import number from '@/filters/number.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import * as sound from '@/scripts/sound.js';
@@ -401,14 +402,22 @@ function react(viaKeyboard = false): void {
 	}
 }
 
-function undoReact(note): void {
-	const oldReaction = note.myReaction;
+function undoReact(targetNote: Misskey.entities.Note): void {
+	const oldReaction = targetNote.myReaction;
 	if (!oldReaction) return;
 	misskeyApi('notes/reactions/delete', {
-		noteId: note.id,
+		noteId: targetNote.id,
 	});
 }
 
+function toggleReact() {
+	if (appearNote.value.myReaction == null) {
+		react();
+	} else {
+		undoReact(appearNote.value);
+	}
+}
+
 function onContextmenu(ev: MouseEvent): void {
 	const isLink = (el: HTMLElement): boolean => {
 		if (el.tagName === 'A') return true;
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 322b9400beb9..0d3a5c13ba2b 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div :class="$style.head">
 		<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/>
 		<MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
+		<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div>
 		<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
 		<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
 		<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
@@ -57,6 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
 			<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
 			<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
+			<span v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'">{{ i18n.tsx._notification.likedBySomeUsers({ n: notification.reactions.length }) }}</span>
 			<span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: notification.reactions.length }) }}</span>
 			<span v-else-if="notification.type === 'renote:grouped'">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span>
 			<span v-else-if="notification.type === 'app'">{{ notification.header }}</span>
@@ -201,6 +203,7 @@ const rejectFollowRequest = () => {
 }
 
 .icon_reactionGroup,
+.icon_reactionGroupHeart,
 .icon_renoteGroup {
 	display: grid;
 	align-items: center;
@@ -213,11 +216,15 @@ const rejectFollowRequest = () => {
 }
 
 .icon_reactionGroup {
-	background: #e99a0b;
+	background: var(--eventReaction);
+}
+
+.icon_reactionGroupHeart {
+	background: var(--eventReactionHeart);
 }
 
 .icon_renoteGroup {
-	background: #36d298;
+	background: var(--eventRenote);
 }
 
 .icon_app {
@@ -246,49 +253,49 @@ const rejectFollowRequest = () => {
 
 .t_follow, .t_followRequestAccepted, .t_receiveFollowRequest {
 	padding: 3px;
-	background: #36aed2;
+	background: var(--eventFollow);
 	pointer-events: none;
 }
 
 .t_renote {
 	padding: 3px;
-	background: #36d298;
+	background: var(--eventRenote);
 	pointer-events: none;
 }
 
 .t_quote {
 	padding: 3px;
-	background: #36d298;
+	background: var(--eventRenote);
 	pointer-events: none;
 }
 
 .t_reply {
 	padding: 3px;
-	background: #007aff;
+	background: var(--eventReply);
 	pointer-events: none;
 }
 
 .t_mention {
 	padding: 3px;
-	background: #88a6b7;
+	background: var(--eventOther);
 	pointer-events: none;
 }
 
 .t_pollEnded {
 	padding: 3px;
-	background: #88a6b7;
+	background: var(--eventOther);
 	pointer-events: none;
 }
 
 .t_achievementEarned {
 	padding: 3px;
-	background: #cb9a11;
+	background: var(--eventAchievement);
 	pointer-events: none;
 }
 
 .t_roleAssigned {
 	padding: 3px;
-	background: #88a6b7;
+	background: var(--eventOther);
 	pointer-events: none;
 }
 
diff --git a/packages/frontend/src/components/MkTutorialDialog.Note.vue b/packages/frontend/src/components/MkTutorialDialog.Note.vue
index f03a83293b69..2a26d22dc27c 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Note.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Note.vue
@@ -63,6 +63,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
 	reactionAcceptance: null,
 	renoteCount: 0,
 	repliesCount: 1,
+	reactionCount: 0,
 	reactions: {},
 	reactionEmojis: {},
 	fileIds: [],
diff --git a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
index 2b8c586dacce..e1d88b5e5c22 100644
--- a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
@@ -68,6 +68,7 @@ const exampleCWNote = reactive<Misskey.entities.Note>({
 	reactionAcceptance: null,
 	renoteCount: 0,
 	repliesCount: 1,
+	reactionCount: 0,
 	reactions: {},
 	reactionEmojis: {},
 	fileIds: [],
diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
index b17ec6646185..7ae48dcd151a 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
@@ -58,6 +58,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
 	reactionAcceptance: null,
 	renoteCount: 0,
 	repliesCount: 1,
+	reactionCount: 0,
 	reactions: {},
 	reactionEmojis: {},
 	fileIds: ['0000000002'],
diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index 524ac5d3fe75..542d8ab52b18 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -35,6 +35,7 @@ export function useNoteCapture(props: {
 				const currentCount = (note.value.reactions || {})[reaction] || 0;
 
 				note.value.reactions[reaction] = currentCount + 1;
+				note.value.reactionCount += 1;
 
 				if ($i && (body.userId === $i.id)) {
 					note.value.myReaction = reaction;
@@ -49,6 +50,7 @@ export function useNoteCapture(props: {
 				const currentCount = (note.value.reactions || {})[reaction] || 0;
 
 				note.value.reactions[reaction] = Math.max(0, currentCount - 1);
+				note.value.reactionCount = Math.max(0, note.value.reactionCount - 1);
 				if (note.value.reactions[reaction] === 0) delete note.value.reactions[reaction];
 
 				if ($i && (body.userId === $i.id)) {
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 0951a7d98d5d..187d9027339f 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -22,6 +22,13 @@
 	}
 
 	//--ad: rgb(255 169 0 / 10%);
+	--eventFollow: #36aed2;
+	--eventRenote: #36d298;
+	--eventReply: #007aff;
+	--eventReactionHeart: #dd2e44;
+	--eventReaction: #e99a0b;
+	--eventAchievement: #cb9a11;
+	--eventOther: #88a6b7;
 }
 
 ::selection {
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index b1e6a194f9df..41c3f5013568 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3987,6 +3987,7 @@ export type components = {
       reactions: {
         [key: string]: number;
       };
+      reactionCount: number;
       renoteCount: number;
       repliesCount: number;
       uri?: string;

From 412e9f284dce4167304a57c905f3cd8e9438b128 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 7 Mar 2024 09:51:57 +0900
Subject: [PATCH 089/266] test(backend): enable typecheck by workflow (#13526)

---
 packages/backend/package.json                 |  2 +-
 packages/backend/test/e2e/note.ts             |  8 ++++----
 packages/backend/test/e2e/timelines.ts        | 20 ++++++++++++++++++-
 packages/backend/test/global.d.ts             |  7 +++++++
 .../backend/test/prelude/get-api-validator.ts |  4 ++--
 packages/backend/test/tsconfig.json           |  3 ++-
 packages/backend/test/unit/RelayService.ts    |  3 ++-
 7 files changed, 37 insertions(+), 10 deletions(-)
 create mode 100644 packages/backend/test/global.d.ts

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 8680610441b3..eaad96d5f6ee 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -19,7 +19,7 @@
 		"watch": "node watch.mjs",
 		"restart": "pnpm build && pnpm start",
 		"dev": "nodemon -w src -e ts,js,mjs,cjs,json --exec \"cross-env NODE_ENV=development pnpm run restart\"",
-		"typecheck": "tsc --noEmit",
+		"typecheck": "tsc --noEmit && tsc -p test --noEmit",
 		"eslint": "eslint --quiet \"src/**/*.ts\"",
 		"lint": "pnpm typecheck && pnpm eslint",
 		"jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --config jest.config.unit.cjs",
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 973bcbd750cf..11016f58aef3 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -472,7 +472,7 @@ describe('Note', () => {
 						priority: 0,
 						value: true,
 					},
-				},
+				} as any,
 			}, alice);
 
 			assert.strictEqual(res.status, 200);
@@ -784,7 +784,7 @@ describe('Note', () => {
 						priority: 1,
 						value: 0,
 					},
-				},
+				} as any,
 			}, alice);
 
 			assert.strictEqual(res.status, 200);
@@ -838,7 +838,7 @@ describe('Note', () => {
 						priority: 1,
 						value: 0,
 					},
-				},
+				} as any,
 			}, alice);
 
 			assert.strictEqual(res.status, 200);
@@ -894,7 +894,7 @@ describe('Note', () => {
 						priority: 1,
 						value: 1,
 					},
-				},
+				} as any,
 			}, alice);
 
 			assert.strictEqual(res.status, 200);
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index d413703edeee..5487292afcd5 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -890,17 +890,35 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
 			await sleep(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
 			await waitForPushToTl();
 
-			const res = await api('notes/user-list-timeline', { listId: list.id, withReplies: false }, alice);
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
 		});
 
+		test.concurrent('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
+			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
+			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
+			await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
+			await sleep(1000);
+			const carolNote = await post(carol, { text: 'hi' });
+			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+
+			await waitForPushToTl();
+
+			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
+
+			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+		});
+
 		test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
diff --git a/packages/backend/test/global.d.ts b/packages/backend/test/global.d.ts
new file mode 100644
index 000000000000..0363073356ce
--- /dev/null
+++ b/packages/backend/test/global.d.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type FIXME = any;
diff --git a/packages/backend/test/prelude/get-api-validator.ts b/packages/backend/test/prelude/get-api-validator.ts
index b86a7a978def..7aa7a92702d8 100644
--- a/packages/backend/test/prelude/get-api-validator.ts
+++ b/packages/backend/test/prelude/get-api-validator.ts
@@ -4,10 +4,10 @@
  */
 
 import Ajv from 'ajv';
-import { Schema } from '@/misc/schema';
+import { Schema } from '@/misc/json-schema.js';
 
 export const getValidator = (paramDef: Schema) => {
-	const ajv = new Ajv({
+	const ajv = new Ajv.default({
 		useDefaults: true,
 	});
 	ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/);
diff --git a/packages/backend/test/tsconfig.json b/packages/backend/test/tsconfig.json
index 4597ff87803e..2b562acda81a 100644
--- a/packages/backend/test/tsconfig.json
+++ b/packages/backend/test/tsconfig.json
@@ -5,7 +5,7 @@
 		"noImplicitAny": true,
 		"noImplicitReturns": true,
 		"noUnusedParameters": false,
-		"noUnusedLocals": true,
+		"noUnusedLocals": false,
 		"noFallthroughCasesInSwitch": true,
 		"declaration": false,
 		"sourceMap": true,
@@ -18,6 +18,7 @@
 		"strict": true,
 		"strictNullChecks": true,
 		"strictPropertyInitialization": false,
+		"skipLibCheck": true,
 		"experimentalDecorators": true,
 		"emitDecoratorMetadata": true,
 		"resolveJsonModule": true,
diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts
index f2a67dba46b4..9676abf07b74 100644
--- a/packages/backend/test/unit/RelayService.ts
+++ b/packages/backend/test/unit/RelayService.ts
@@ -90,7 +90,8 @@ describe('RelayService', () => {
 
 		expect(queueService.deliver).toHaveBeenCalled();
 		expect(queueService.deliver.mock.lastCall![1]?.type).toBe('Undo');
-		expect(queueService.deliver.mock.lastCall![1]?.object.type).toBe('Follow');
+		expect(typeof queueService.deliver.mock.lastCall![1]?.object).toBe('object');
+		expect((queueService.deliver.mock.lastCall![1]?.object as any).type).toBe('Follow');
 		expect(queueService.deliver.mock.lastCall![2]).toBe('https://example.com');
 		//expect(queueService.deliver.mock.lastCall![0].username).toBe('relay.actor');
 

From c680e35aa0e91ee66794c3d419c282aaea0ef05a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 7 Mar 2024 16:36:06 +0900
Subject: [PATCH 090/266] =?UTF-8?q?enhance(frontend):=20=E5=BA=83=E5=91=8A?=
 =?UTF-8?q?=E3=81=8C=E5=90=8C=E4=B8=80=E3=83=89=E3=83=A1=E3=82=A4=E3=83=B3?=
 =?UTF-8?q?=E3=81=AE=E5=A0=B4=E5=90=88=E3=81=AFRouter=E3=81=A7=E9=81=B7?=
 =?UTF-8?q?=E7=A7=BB=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1351?=
 =?UTF-8?q?0)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 広告が同一ドメインの場合はRouterで遷移するように

* Update Changelog

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 .../frontend/src/components/global/MkAd.vue   | 21 +++++++++++++++----
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bdfa53e9965..66b41ca7716f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 
 ### Client
 - Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
+- Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
 - Enhance: リアクション・いいねの総数を表示するように
 - Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue
index 8f5ed760d586..bdaa8a809ffd 100644
--- a/packages/frontend/src/components/global/MkAd.vue
+++ b/packages/frontend/src/components/global/MkAd.vue
@@ -14,10 +14,20 @@ SPDX-License-Identifier: AGPL-3.0-only
 			[$style.form_vertical]: chosen.place === 'vertical',
 		}]"
 	>
-		<a :href="chosen.url" target="_blank" :class="$style.link">
+		<component
+			:is="self ? 'MkA' : 'a'"
+			:class="$style.link"
+			v-bind="self ? {
+				to: chosen.url.substring(local.length),
+			} : {
+				href: chosen.url,
+				rel: 'nofollow noopener',
+				target: '_blank',
+			}"
+		>
 			<img :src="chosen.imageUrl" :class="$style.img">
 			<button class="_button" :class="$style.i" @click.prevent.stop="toggleMenu"><i :class="$style.iIcon" class="ti ti-info-circle"></i></button>
-		</a>
+		</component>
 	</div>
 	<div v-else :class="$style.menu">
 		<div :class="$style.menuContainer">
@@ -32,10 +42,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
-import { host } from '@/config.js';
+import { url as local, host } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
 import { defaultStore } from '@/store.js';
 import * as os from '@/os.js';
@@ -96,6 +106,9 @@ const choseAd = (): Ad | null => {
 };
 
 const chosen = ref(choseAd());
+
+const self = computed(() => chosen.value?.url.startsWith(local));
+
 const shouldHide = ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null));
 
 function reduceFrequency(): void {

From f4a57404120be713d5fec4fc8e882b09927deca2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 7 Mar 2024 17:21:57 +0900
Subject: [PATCH 091/266] =?UTF-8?q?fix(frontend):=20=E5=91=A8=E5=B9=B4?=
 =?UTF-8?q?=E3=81=AE=E5=AE=9F=E7=B8=BE=E3=81=8C=E9=96=8F=E5=B9=B4=E3=82=92?=
 =?UTF-8?q?=E8=80=83=E6=85=AE=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#13525)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 周年の実績が閏年を考慮するように

* まちがえた

* Update Changelog

* 変数の定義回数を減らす
---
 CHANGELOG.md                            |  1 +
 packages/frontend/src/boot/main-boot.ts | 28 ++++++++++++++++++-------
 2 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 66b41ca7716f..be621e1ebd18 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 - Enhance: リアクション・いいねの総数を表示するように
 - Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
+- Fix: 周年の実績が閏年を考慮しない問題を修正
 
 ### Server
 -
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 61f04678bff0..8016e8b0e0a0 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -187,14 +187,26 @@ export async function mainBoot() {
 		if ($i.followersCount >= 500) claimAchievement('followers500');
 		if ($i.followersCount >= 1000) claimAchievement('followers1000');
 
-		if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365) {
-			claimAchievement('passedSinceAccountCreated1');
-		}
-		if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 2) {
-			claimAchievement('passedSinceAccountCreated2');
-		}
-		if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 3) {
+		const createdAt = new Date($i.createdAt);
+		const createdAtThreeYearsLater = new Date($i.createdAt);
+		createdAtThreeYearsLater.setFullYear(createdAtThreeYearsLater.getFullYear() + 3);
+		if (now >= createdAtThreeYearsLater) {
 			claimAchievement('passedSinceAccountCreated3');
+			claimAchievement('passedSinceAccountCreated2');
+			claimAchievement('passedSinceAccountCreated1');
+		} else {
+			const createdAtTwoYearsLater = new Date($i.createdAt);
+			createdAtTwoYearsLater.setFullYear(createdAtTwoYearsLater.getFullYear() + 2);
+			if (now >= createdAtTwoYearsLater) {
+				claimAchievement('passedSinceAccountCreated2');
+				claimAchievement('passedSinceAccountCreated1');
+			} else {
+				const createdAtOneYearLater = new Date($i.createdAt);
+				createdAtOneYearLater.setFullYear(createdAtOneYearLater.getFullYear() + 1);
+				if (now >= createdAtOneYearLater) {
+					claimAchievement('passedSinceAccountCreated1');
+				}
+			}
 		}
 
 		if (claimedAchievements.length >= 30) {
@@ -229,7 +241,7 @@ export async function mainBoot() {
 
 		const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt');
 		const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo');
-		if (neverShowDonationInfo !== 'true' && (new Date($i.createdAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) {
+		if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) {
 			if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) {
 				popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {}, 'closed');
 			}

From 27f823e8823225ce33f464c1d12acf7c37834426 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 8 Mar 2024 18:13:09 +0900
Subject: [PATCH 092/266] =?UTF-8?q?enhance(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E7=B7=8F=E6=95=B0?=
 =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=81=8B=E8=A8=AD?=
 =?UTF-8?q?=E5=AE=9A=E3=81=A7=E9=81=B8=E3=81=B9=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#13539)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): リプライ・リノート・リアクションの総数を表示するか設定で選べるように (MisskeyIO#512)

(cherry picked from commit 3c8475e5ac217f055eab0f6d0aedcbbcb2a2f27c)

* fix: いいねのみの場合は強制的にカウント表示

* make `showReactionsCount` default false

* リアクションだけ

* けしわすれ

* けしわすれ2

---------

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 locales/index.d.ts                                        | 4 ++++
 locales/ja-JP.yml                                         | 1 +
 packages/frontend/src/components/MkNote.vue               | 2 +-
 packages/frontend/src/components/MkNoteDetailed.vue       | 2 +-
 packages/frontend/src/pages/settings/general.vue          | 2 ++
 .../frontend/src/pages/settings/preferences-backups.vue   | 1 +
 packages/frontend/src/store.ts                            | 8 ++++++--
 7 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 3ac4ff9e74cd..53c3159da661 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1992,6 +1992,10 @@ export interface Locale extends ILocale {
      * ノートのアクションをホバー時のみ表示する
      */
     "showNoteActionsOnlyHover": string;
+    /**
+     * ノートのリアクション数を表示する
+     */
+    "showReactionsCount": string;
     /**
      * 履歴はありません
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 177d6a06c941..64705868b930 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -494,6 +494,7 @@ emojiStyle: "絵文字のスタイル"
 native: "ネイティブ"
 disableDrawer: "メニューをドロワーで表示しない"
 showNoteActionsOnlyHover: "ノートのアクションをホバー時のみ表示する"
+showReactionsCount: "ノートのリアクション数を表示する"
 noHistory: "履歴はありません"
 signinHistory: "ログイン履歴"
 enableAdvancedMfm: "高度なMFMを有効にする"
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 656ccc7959bd..5ca0eae01221 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -121,7 +121,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
 					<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
 					<i v-else class="ti ti-plus"></i>
-					<p v-if="appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
+					<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
 				</button>
 				<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
 					<i class="ti ti-paperclip"></i>
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 2d2930ee7c55..e27121551645 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -129,7 +129,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
 				<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
 				<i v-else class="ti ti-plus"></i>
-				<p v-if="appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
+				<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
 			</button>
 			<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
 				<i class="ti ti-paperclip"></i>
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index d13b6884bdd9..285f874dde1e 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -56,6 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
 				<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
 				<MkSwitch v-if="advancedMfm" v-model="enableQuickAddMfmFunction">{{ i18n.ts.enableQuickAddMfmFunction }}</MkSwitch>
+				<MkSwitch v-model="showReactionsCount">{{ i18n.ts.showReactionsCount }}</MkSwitch>
 				<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
 				<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
 				<MkRadios v-model="reactionsDisplaySize">
@@ -281,6 +282,7 @@ const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
 const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'));
 const animatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm'));
 const advancedMfm = computed(defaultStore.makeGetterSetter('advancedMfm'));
+const showReactionsCount = computed(defaultStore.makeGetterSetter('showReactionsCount'));
 const enableQuickAddMfmFunction = computed(defaultStore.makeGetterSetter('enableQuickAddMfmFunction'));
 const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle'));
 const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 942de19d82af..b6f1043154d3 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -70,6 +70,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
 	'animation',
 	'animatedMfm',
 	'advancedMfm',
+	'showReactionsCount',
 	'loadRawImages',
 	'imageNewTab',
 	'dataSaver',
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index dfc4169a54bc..a335ed33bb9c 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -227,6 +227,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: true,
 	},
+	showReactionsCount: {
+		where: 'device',
+		default: false,
+	},
 	enableQuickAddMfmFunction: {
 		where: 'device',
 		default: false,
@@ -431,10 +435,10 @@ export const defaultStore = markRaw(new Storage('base', {
 			sfxVolume: 1,
 		},
 	},
-  hemisphere: {
+	hemisphere: {
 		where: 'device',
 		default: hemisphere as 'N' | 'S',
-  },
+	},
 	enableHorizontalSwipe: {
 		where: 'device',
 		default: true,

From 1b064d7e30899827a59535ffa5fe6c90512a836d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sat, 9 Mar 2024 04:10:17 +0000
Subject: [PATCH 093/266] =?UTF-8?q?chore(backend):=20validateNote=E3=81=AE?=
 =?UTF-8?q?=E7=B5=90=E6=9E=9CError=E3=81=AF=E3=81=9D=E3=81=AE=E3=81=BE?=
 =?UTF-8?q?=E3=81=BEthrow=E3=81=99=E3=82=8B=20=E7=90=86=E7=94=B1=E3=81=8C?=
 =?UTF-8?q?=E3=82=8F=E3=81=8B=E3=82=89=E3=81=AA=E3=81=84=E3=81=9F=E3=82=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/core/activitypub/models/ApNoteService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index b2fd435f93a0..4d64b08e15c4 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -129,7 +129,7 @@ export class ApNoteService {
 				value,
 				object,
 			});
-			throw new Error('invalid note');
+			throw err;
 		}
 
 		const note = object as IPost;

From db29680e749fae8ce93f82b527bf8e8597fa0526 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 9 Mar 2024 15:31:21 +0900
Subject: [PATCH 094/266] chore(dev): remove deprecated vscode plugins

---
 .devcontainer/devcontainer.json | 1 -
 .vscode/extensions.json         | 1 -
 2 files changed, 2 deletions(-)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index e409adf64473..f8d9905ecdf6 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -19,7 +19,6 @@
 				"editorconfig.editorconfig",
 				"dbaeumer.vscode-eslint",
 				"Vue.volar",
-				"Vue.vscode-typescript-vue-plugin",
 				"Orta.vscode-jest",
 				"dbaeumer.vscode-eslint",
 				"mrmlnc.vscode-json5"
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index baca8db24671..d08109477c59 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -3,7 +3,6 @@
 		"editorconfig.editorconfig",
 		"dbaeumer.vscode-eslint",
 		"Vue.volar",
-		"Vue.vscode-typescript-vue-plugin",
 		"Orta.vscode-jest",
 		"dbaeumer.vscode-eslint",
 		"mrmlnc.vscode-json5"

From dbc4fd3e93a7b73be9e0bc3e863cf1d553edd80c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 9 Mar 2024 15:40:21 +0900
Subject: [PATCH 095/266] Update about-misskey.vue

---
 packages/frontend/src/pages/about-misskey.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 1a49dbf1d512..9aaa2d8fbad3 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -324,6 +324,7 @@ const patrons = [
 	'てば',
 	'たっくん',
 	'SHO SEKIGUCHI',
+	'塩キャベツ',
 ];
 
 const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));

From e4eaf1220e0d23b056f3203b3b24d8e3827134b9 Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Sat, 9 Mar 2024 17:55:41 +0900
Subject: [PATCH 096/266] Update example.yml (#13551)

---
 .config/example.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.config/example.yml b/.config/example.yml
index 7fea9293746e..b0b7f140593d 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -38,7 +38,7 @@
 # Option 3: If neither of the above applies to you.
 #           (In this case, the source code should be published
 #           on the Misskey interface.  IT IS NOT ENOUGH TO
-#           DISCLOSE THE SOURCE CODE WEHN A USER REQUESTS IT BY
+#           DISCLOSE THE SOURCE CODE WHEN A USER REQUESTS IT BY
 #           E-MAIL OR OTHER MEANS.  If you are not satisfied
 #           with this, it is recommended that you read the
 #           license again carefully.  Anyway, enabling this

From 6b676a928d3e167c98f2a1854a432adf5c125d65 Mon Sep 17 00:00:00 2001
From: yupix <yupi0982@outlook.jp>
Date: Sun, 10 Mar 2024 17:31:39 +0900
Subject: [PATCH 097/266] =?UTF-8?q?enhance(backend):=20antennas/update?=
 =?UTF-8?q?=E3=81=AE=E5=BF=85=E9=A0=88=E9=A0=85=E7=9B=AE=E3=82=92antennaId?=
 =?UTF-8?q?=E3=81=AE=E3=81=BF=E3=81=AB=20(#13542)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: antennas/updateの必須項目を最小限に

* fix: userListIdがnullにできない
---
 CHANGELOG.md                                   |  2 +-
 .../server/api/endpoints/antennas/update.ts    | 12 +++++++-----
 packages/misskey-js/src/autogen/types.ts       | 18 +++++++++---------
 3 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index be621e1ebd18..8c5f05dbe5c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,7 +26,7 @@
 - Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487
 
 ### Server
--
+- Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
 
 ## 2024.3.0
 
diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts
index 459729f61f2e..76a34924a0f0 100644
--- a/packages/backend/src/server/api/endpoints/antennas/update.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/update.ts
@@ -67,7 +67,7 @@ export const paramDef = {
 		withFile: { type: 'boolean' },
 		notify: { type: 'boolean' },
 	},
-	required: ['antennaId', 'name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'],
+	required: ['antennaId'],
 } as const;
 
 @Injectable()
@@ -83,8 +83,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) {
-				throw new Error('either keywords or excludeKeywords is required.');
+			if (ps.keywords && ps.excludeKeywords) {
+				if (ps.keywords.flat().every(x => x === '') && ps.excludeKeywords.flat().every(x => x === '')) {
+					throw new Error('either keywords or excludeKeywords is required.');
+				}
 			}
 			// Fetch the antenna
 			const antenna = await this.antennasRepository.findOneBy({
@@ -98,7 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			let userList;
 
-			if (ps.src === 'list' && ps.userListId) {
+			if ((ps.src === 'list' || antenna.src === 'list') && ps.userListId) {
 				userList = await this.userListsRepository.findOneBy({
 					id: ps.userListId,
 					userId: me.id,
@@ -112,7 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			await this.antennasRepository.update(antenna.id, {
 				name: ps.name,
 				src: ps.src,
-				userListId: userList ? userList.id : null,
+				userListId: ps.userListId !== undefined ? userList ? userList.id : null : undefined,
 				keywords: ps.keywords,
 				excludeKeywords: ps.excludeKeywords,
 				users: ps.users,
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 41c3f5013568..be3e86bd7886 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -9925,19 +9925,19 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           antennaId: string;
-          name: string;
+          name?: string;
           /** @enum {string} */
-          src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist';
+          src?: 'home' | 'all' | 'users' | 'list' | 'users_blacklist';
           /** Format: misskey:id */
           userListId?: string | null;
-          keywords: string[][];
-          excludeKeywords: string[][];
-          users: string[];
-          caseSensitive: boolean;
+          keywords?: string[][];
+          excludeKeywords?: string[][];
+          users?: string[];
+          caseSensitive?: boolean;
           localOnly?: boolean;
-          withReplies: boolean;
-          withFile: boolean;
-          notify: boolean;
+          withReplies?: boolean;
+          withFile?: boolean;
+          notify?: boolean;
         };
       };
     };

From e23e2f4ae9368754e4eadcd0afaa2bb1f984ba6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 12 Mar 2024 12:09:26 +0900
Subject: [PATCH 098/266] Fix Changelog

---
 CHANGELOG.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c5f05dbe5c2..9c2a32aa56c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 
 ### Server
--
+- Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
 
 ## 2024.3.1
 
@@ -26,7 +26,7 @@
 - Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487
 
 ### Server
-- Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
+-
 
 ## 2024.3.0
 

From b280faa8e72c29036ef65af7fd8949538ab43dbd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 12 Mar 2024 13:48:14 +0900
Subject: [PATCH 099/266] =?UTF-8?q?enhance(frontend):=20=E5=90=84=E3=82=B5?=
 =?UTF-8?q?=E3=83=BC=E3=83=90=E3=83=BC=E3=81=AFMisskey=E3=82=92=E5=88=A9?=
 =?UTF-8?q?=E7=94=A8=E3=81=97=E3=81=9F=E3=82=B5=E3=83=BC=E3=83=93=E3=82=B9?=
 =?UTF-8?q?=E3=81=A7=E3=81=82=E3=82=8B=E3=81=93=E3=81=A8=E3=82=92=E5=BC=B7?=
 =?UTF-8?q?=E8=AA=BF=20(#13559)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ロゴ周りを追加

* 調整

---------

Co-authored-by: uboar <10250330+uboar@users.noreply.github.com>
---
 .../frontend/src/pages/welcome.entrance.a.vue | 27 ++++++++++++++-----
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 89bb010dd669..6c05aad24f22 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -9,7 +9,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<XTimeline class="tl"/>
 	<div class="shape1"></div>
 	<div class="shape2"></div>
-	<img :src="misskeysvg" class="misskey"/>
+	<div class="logo-wrapper">
+		<div class="powered-by">Powered by</div>
+		<img :src="misskeysvg" class="misskey"/>
+	</div>
 	<div class="emojis">
 		<MkEmoji :normal="true" :noStyle="true" emoji="👍"/>
 		<MkEmoji :normal="true" :noStyle="true" emoji="❤"/>
@@ -113,14 +116,24 @@ misskeyApiGet('federation/instances', {
 		opacity: 0.5;
 	}
 
-	> .misskey {
+	> .logo-wrapper {
 		position: fixed;
-		top: 42px;
-		left: 42px;
-		width: 140px;
+		top: 36px;
+		left: 36px;
+		flex: auto;
+		color: #fff;
+		user-select: none;
+		pointer-events: none;
+
+		> .powered-by {
+			margin-bottom: 2px;
+		}
 
-		@media (max-width: 450px) {
-			width: 130px;
+		> .misskey {
+			width: 140px;
+			@media (max-width: 450px) {
+				width: 130px;
+			}
 		}
 	}
 

From 6d9c234cb6d3ddfaa3266255e7c305b329a556b6 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 12 Mar 2024 13:50:24 +0900
Subject: [PATCH 100/266] fix: URL preview popup for local URL appears in the
 upper left corner (#13555)

---
 CHANGELOG.md                                      | 1 +
 packages/frontend/src/components/MkLink.vue       | 4 ++--
 packages/frontend/src/components/global/MkA.vue   | 8 ++++++--
 packages/frontend/src/components/global/MkUrl.vue | 2 +-
 4 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c2a32aa56c0..83d0a3f7d2ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 - Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
+- Fix: ローカルURLのプレビューポップアップが左上に表示される
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index a5abbeceac49..3f7aba2fe4e1 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -29,13 +29,13 @@ const self = props.url.startsWith(local);
 const attr = self ? 'to' : 'href';
 const target = self ? null : '_blank';
 
-const el = ref<HTMLElement>();
+const el = ref<HTMLElement | { $el: HTMLElement }>();
 
 useTooltip(el, (showing) => {
 	os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
 		showing,
 		url: props.url,
-		source: el.value,
+		source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
 	}, {}, 'closed');
 });
 </script>
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 61d7ac17d9e9..1ba7cb20229f 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -4,13 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<a :href="to" :class="active ? activeClass : null" @click.prevent="nav" @contextmenu.prevent.stop="onContextmenu">
+<a ref="el" :href="to" :class="active ? activeClass : null" @click.prevent="nav" @contextmenu.prevent.stop="onContextmenu">
 	<slot></slot>
 </a>
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, shallowRef } from 'vue';
 import * as os from '@/os.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
@@ -26,6 +26,10 @@ const props = withDefaults(defineProps<{
 	behavior: null,
 });
 
+const el = shallowRef<HTMLElement>();
+
+defineExpose({ $el: el });
+
 const router = useRouter();
 
 const active = computed(() => {
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 0c3eee63ff84..8d29a4da8c3a 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -49,7 +49,7 @@ if (props.showUrlPreview) {
 		os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
 			showing,
 			url: props.url,
-			source: el.value,
+			source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
 		}, {}, 'closed');
 	});
 }

From 5c1d86b796d6ab878bc4f9bd2faf4207998e71cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 12 Mar 2024 14:31:34 +0900
Subject: [PATCH 101/266] =?UTF-8?q?refactor(backend):=20UserEntityService.?=
 =?UTF-8?q?packMany()=E3=81=AE=E9=AB=98=E9=80=9F=E5=8C=96=20(#13550)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(backend): UserEntityService.packMany()の高速化

* 修正
---
 .../src/core/entities/UserEntityService.ts    | 229 +++++++-
 .../server/api/endpoints/users/relation.ts    |   8 +-
 .../test/unit/entities/UserEntityService.ts   | 528 ++++++++++++++++++
 3 files changed, 729 insertions(+), 36 deletions(-)
 create mode 100644 packages/backend/test/unit/entities/UserEntityService.ts

diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index 14761357a5a8..df2b27d70974 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import * as Redis from 'ioredis';
 import _Ajv from 'ajv';
 import { ModuleRef } from '@nestjs/core';
+import { In } from 'typeorm';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import type { Packed } from '@/misc/json-schema.js';
@@ -14,9 +15,30 @@ import type { Promiseable } from '@/misc/prelude/await-all.js';
 import { awaitAll } from '@/misc/prelude/await-all.js';
 import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
 import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
-import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/User.js';
-import { MiNotification } from '@/models/Notification.js';
-import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, UserNotePiningsRepository, UserProfilesRepository, AnnouncementReadsRepository, AnnouncementsRepository, MiUserProfile, RenoteMutingsRepository, UserMemoRepository } from '@/models/_.js';
+import {
+	birthdaySchema,
+	descriptionSchema,
+	localUsernameSchema,
+	locationSchema,
+	nameSchema,
+	passwordSchema,
+} from '@/models/User.js';
+import type {
+	BlockingsRepository,
+	FollowingsRepository,
+	FollowRequestsRepository,
+	MiFollowing,
+	MiUserNotePining,
+	MiUserProfile,
+	MutingsRepository,
+	NoteUnreadsRepository,
+	RenoteMutingsRepository,
+	UserMemoRepository,
+	UserNotePiningsRepository,
+	UserProfilesRepository,
+	UserSecurityKeysRepository,
+	UsersRepository,
+} from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
@@ -46,11 +68,23 @@ function isRemoteUser(user: MiUser | { host: MiUser['host'] }): boolean {
 	return !isLocalUser(user);
 }
 
+export type UserRelation = {
+	id: MiUser['id']
+	following: MiFollowing | null,
+	isFollowing: boolean
+	isFollowed: boolean
+	hasPendingFollowRequestFromYou: boolean
+	hasPendingFollowRequestToYou: boolean
+	isBlocking: boolean
+	isBlocked: boolean
+	isMuted: boolean
+	isRenoteMuted: boolean
+}
+
 @Injectable()
 export class UserEntityService implements OnModuleInit {
 	private apPersonService: ApPersonService;
 	private noteEntityService: NoteEntityService;
-	private driveFileEntityService: DriveFileEntityService;
 	private pageEntityService: PageEntityService;
 	private customEmojiService: CustomEmojiService;
 	private announcementService: AnnouncementService;
@@ -89,9 +123,6 @@ export class UserEntityService implements OnModuleInit {
 		@Inject(DI.renoteMutingsRepository)
 		private renoteMutingsRepository: RenoteMutingsRepository,
 
-		@Inject(DI.driveFilesRepository)
-		private driveFilesRepository: DriveFilesRepository,
-
 		@Inject(DI.noteUnreadsRepository)
 		private noteUnreadsRepository: NoteUnreadsRepository,
 
@@ -101,12 +132,6 @@ export class UserEntityService implements OnModuleInit {
 		@Inject(DI.userProfilesRepository)
 		private userProfilesRepository: UserProfilesRepository,
 
-		@Inject(DI.announcementReadsRepository)
-		private announcementReadsRepository: AnnouncementReadsRepository,
-
-		@Inject(DI.announcementsRepository)
-		private announcementsRepository: AnnouncementsRepository,
-
 		@Inject(DI.userMemosRepository)
 		private userMemosRepository: UserMemoRepository,
 	) {
@@ -115,7 +140,6 @@ export class UserEntityService implements OnModuleInit {
 	onModuleInit() {
 		this.apPersonService = this.moduleRef.get('ApPersonService');
 		this.noteEntityService = this.moduleRef.get('NoteEntityService');
-		this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService');
 		this.pageEntityService = this.moduleRef.get('PageEntityService');
 		this.customEmojiService = this.moduleRef.get('CustomEmojiService');
 		this.announcementService = this.moduleRef.get('AnnouncementService');
@@ -138,7 +162,7 @@ export class UserEntityService implements OnModuleInit {
 	public isRemoteUser = isRemoteUser;
 
 	@bindThis
-	public async getRelation(me: MiUser['id'], target: MiUser['id']) {
+	public async getRelation(me: MiUser['id'], target: MiUser['id']): Promise<UserRelation> {
 		const [
 			following,
 			isFollowed,
@@ -211,6 +235,59 @@ export class UserEntityService implements OnModuleInit {
 		};
 	}
 
+	@bindThis
+	public async getRelations(me: MiUser['id'], targets: MiUser['id'][]): Promise<Map<MiUser['id'], UserRelation>> {
+		const [
+			followers,
+			followees,
+			followersRequests,
+			followeesRequests,
+			blockers,
+			blockees,
+			muters,
+			renoteMuters,
+		] = await Promise.all([
+			this.followingsRepository.findBy({ followerId: me })
+				.then(f => new Map(f.map(it => [it.followeeId, it]))),
+			this.followingsRepository.findBy({ followeeId: me })
+				.then(it => it.map(it => it.followerId)),
+			this.followRequestsRepository.findBy({ followerId: me })
+				.then(it => it.map(it => it.followeeId)),
+			this.followRequestsRepository.findBy({ followeeId: me })
+				.then(it => it.map(it => it.followerId)),
+			this.blockingsRepository.findBy({ blockerId: me })
+				.then(it => it.map(it => it.blockeeId)),
+			this.blockingsRepository.findBy({ blockeeId: me })
+				.then(it => it.map(it => it.blockerId)),
+			this.mutingsRepository.findBy({ muterId: me })
+				.then(it => it.map(it => it.muteeId)),
+			this.renoteMutingsRepository.findBy({ muterId: me })
+				.then(it => it.map(it => it.muteeId)),
+		]);
+
+		return new Map(
+			targets.map(target => {
+				const following = followers.get(target) ?? null;
+
+				return [
+					target,
+					{
+						id: target,
+						following: following,
+						isFollowing: following != null,
+						isFollowed: followees.includes(target),
+						hasPendingFollowRequestFromYou: followersRequests.includes(target),
+						hasPendingFollowRequestToYou: followeesRequests.includes(target),
+						isBlocking: blockers.includes(target),
+						isBlocked: blockees.includes(target),
+						isMuted: muters.includes(target),
+						isRenoteMuted: renoteMuters.includes(target),
+					},
+				];
+			}),
+		);
+	}
+
 	@bindThis
 	public async getHasUnreadAntenna(userId: MiUser['id']): Promise<boolean> {
 		/*
@@ -303,6 +380,9 @@ export class UserEntityService implements OnModuleInit {
 			schema?: S,
 			includeSecrets?: boolean,
 			userProfile?: MiUserProfile,
+			userRelations?: Map<MiUser['id'], UserRelation>,
+			userMemos?: Map<MiUser['id'], string | null>,
+			pinNotes?: Map<MiUser['id'], MiUserNotePining[]>,
 		},
 	): Promise<Packed<S>> {
 		const opts = Object.assign({
@@ -317,13 +397,41 @@ export class UserEntityService implements OnModuleInit {
 		const isMe = meId === user.id;
 		const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false;
 
-		const relation = meId && !isMe && isDetailed ? await this.getRelation(meId, user.id) : null;
-		const pins = isDetailed ? await this.userNotePiningsRepository.createQueryBuilder('pin')
-			.where('pin.userId = :userId', { userId: user.id })
-			.innerJoinAndSelect('pin.note', 'note')
-			.orderBy('pin.id', 'DESC')
-			.getMany() : [];
-		const profile = isDetailed ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null;
+		const profile = isDetailed
+			? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }))
+			: null;
+
+		let relation: UserRelation | null = null;
+		if (meId && !isMe && isDetailed) {
+			if (opts.userRelations) {
+				relation = opts.userRelations.get(user.id) ?? null;
+			} else {
+				relation = await this.getRelation(meId, user.id);
+			}
+		}
+
+		let memo: string | null = null;
+		if (isDetailed && meId) {
+			if (opts.userMemos) {
+				memo = opts.userMemos.get(user.id) ?? null;
+			} else {
+				memo = await this.userMemosRepository.findOneBy({ userId: meId, targetUserId: user.id })
+					.then(row => row?.memo ?? null);
+			}
+		}
+
+		let pins: MiUserNotePining[] = [];
+		if (isDetailed) {
+			if (opts.pinNotes) {
+				pins = opts.pinNotes.get(user.id) ?? [];
+			} else {
+				pins = await this.userNotePiningsRepository.createQueryBuilder('pin')
+					.where('pin.userId = :userId', { userId: user.id })
+					.innerJoinAndSelect('pin.note', 'note')
+					.orderBy('pin.id', 'DESC')
+					.getMany();
+			}
+		}
 
 		const followingCount = profile == null ? null :
 			(profile.followingVisibility === 'public') || isMe ? user.followingCount :
@@ -416,9 +524,7 @@ export class UserEntityService implements OnModuleInit {
 				twoFactorEnabled: profile!.twoFactorEnabled,
 				usePasswordLessLogin: profile!.usePasswordLessLogin,
 				securityKeys: profile!.twoFactorEnabled
-					? this.userSecurityKeysRepository.countBy({
-						userId: user.id,
-					}).then(result => result >= 1)
+					? this.userSecurityKeysRepository.countBy({ userId: user.id }).then(result => result >= 1)
 					: false,
 				roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
 					id: role.id,
@@ -430,10 +536,7 @@ export class UserEntityService implements OnModuleInit {
 					isAdministrator: role.isAdministrator,
 					displayOrder: role.displayOrder,
 				}))),
-				memo: meId == null ? null : await this.userMemosRepository.findOneBy({
-					userId: meId,
-					targetUserId: user.id,
-				}).then(row => row?.memo ?? null),
+				memo: memo,
 				moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined,
 			} : {}),
 
@@ -514,7 +617,7 @@ export class UserEntityService implements OnModuleInit {
 		return await awaitAll(packed);
 	}
 
-	public packMany<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>(
+	public async packMany<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>(
 		users: (MiUser['id'] | MiUser)[],
 		me?: { id: MiUser['id'] } | null | undefined,
 		options?: {
@@ -522,6 +625,70 @@ export class UserEntityService implements OnModuleInit {
 			includeSecrets?: boolean,
 		},
 	): Promise<Packed<S>[]> {
-		return Promise.all(users.map(u => this.pack(u, me, options)));
+		// -- IDのみの要素を補完して完全なエンティティ一覧を作る
+
+		const _users = users.filter((user): user is MiUser => typeof user !== 'string');
+		if (_users.length !== users.length) {
+			_users.push(
+				...await this.usersRepository.findBy({
+					id: In(users.filter((user): user is string => typeof user === 'string')),
+				}),
+			);
+		}
+		const _userIds = _users.map(u => u.id);
+
+		// -- 特に前提条件のない値群を取得
+
+		const profilesMap = await this.userProfilesRepository.findBy({ userId: In(_userIds) })
+			.then(profiles => new Map(profiles.map(p => [p.userId, p])));
+
+		// -- 実行者の有無や指定スキーマの種別によって要否が異なる値群を取得
+
+		let userRelations: Map<MiUser['id'], UserRelation> = new Map();
+		let userMemos: Map<MiUser['id'], string | null> = new Map();
+		let pinNotes: Map<MiUser['id'], MiUserNotePining[]> = new Map();
+
+		if (options?.schema !== 'UserLite') {
+			const meId = me ? me.id : null;
+			if (meId) {
+				userMemos = await this.userMemosRepository.findBy({ userId: meId })
+					.then(memos => new Map(memos.map(memo => [memo.targetUserId, memo.memo])));
+
+				if (_userIds.length > 0) {
+					userRelations = await this.getRelations(meId, _userIds);
+					pinNotes = await this.userNotePiningsRepository.createQueryBuilder('pin')
+						.where('pin.userId IN (:...userIds)', { userIds: _userIds })
+						.innerJoinAndSelect('pin.note', 'note')
+						.getMany()
+						.then(pinsNotes => {
+							const map = new Map<MiUser['id'], MiUserNotePining[]>();
+							for (const note of pinsNotes) {
+								const notes = map.get(note.userId) ?? [];
+								notes.push(note);
+								map.set(note.userId, notes);
+							}
+							for (const [, notes] of map.entries()) {
+								// pack側ではDESCで取得しているので、それに合わせて降順に並び替えておく
+								notes.sort((a, b) => b.id.localeCompare(a.id));
+							}
+							return map;
+						});
+				}
+			}
+		}
+
+		return Promise.all(
+			_users.map(u => this.pack(
+				u,
+				me,
+				{
+					...options,
+					userProfile: profilesMap.get(u.id),
+					userRelations: userRelations,
+					userMemos: userMemos,
+					pinNotes: pinNotes,
+				},
+			)),
+		);
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts
index 6a5b2262fa64..1d75437b81df 100644
--- a/packages/backend/src/server/api/endpoints/users/relation.ts
+++ b/packages/backend/src/server/api/endpoints/users/relation.ts
@@ -132,11 +132,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private userEntityService: UserEntityService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId];
-
-			const relations = await Promise.all(ids.map(id => this.userEntityService.getRelation(me.id, id)));
-
-			return Array.isArray(ps.userId) ? relations : relations[0];
+			return Array.isArray(ps.userId)
+				? await this.userEntityService.getRelations(me.id, ps.userId).then(it => [...it.values()])
+				: await this.userEntityService.getRelation(me.id, ps.userId).then(it => [it]);
 		});
 	}
 }
diff --git a/packages/backend/test/unit/entities/UserEntityService.ts b/packages/backend/test/unit/entities/UserEntityService.ts
new file mode 100644
index 000000000000..ee16d421c4c9
--- /dev/null
+++ b/packages/backend/test/unit/entities/UserEntityService.ts
@@ -0,0 +1,528 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Test, TestingModule } from '@nestjs/testing';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { CoreModule } from '@/core/CoreModule.js';
+import type { MiUser } from '@/models/User.js';
+import { secureRndstr } from '@/misc/secure-rndstr.js';
+import { genAidx } from '@/misc/id/aidx.js';
+import {
+	BlockingsRepository,
+	FollowingsRepository, FollowRequestsRepository,
+	MiUserProfile, MutingsRepository, RenoteMutingsRepository,
+	UserMemoRepository,
+	UserProfilesRepository,
+	UsersRepository,
+} from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
+import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
+import { PageEntityService } from '@/core/entities/PageEntityService.js';
+import { CustomEmojiService } from '@/core/CustomEmojiService.js';
+import { AnnouncementService } from '@/core/AnnouncementService.js';
+import { RoleService } from '@/core/RoleService.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
+import { IdService } from '@/core/IdService.js';
+import { UtilityService } from '@/core/UtilityService.js';
+import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
+import { MetaService } from '@/core/MetaService.js';
+import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
+import { CacheService } from '@/core/CacheService.js';
+import { ApResolverService } from '@/core/activitypub/ApResolverService.js';
+import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
+import { ApImageService } from '@/core/activitypub/models/ApImageService.js';
+import { ApMfmService } from '@/core/activitypub/ApMfmService.js';
+import { MfmService } from '@/core/MfmService.js';
+import { HashtagService } from '@/core/HashtagService.js';
+import UsersChart from '@/core/chart/charts/users.js';
+import { ChartLoggerService } from '@/core/chart/ChartLoggerService.js';
+import InstanceChart from '@/core/chart/charts/instance.js';
+import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js';
+import { AccountMoveService } from '@/core/AccountMoveService.js';
+import { ReactionService } from '@/core/ReactionService.js';
+import { NotificationService } from '@/core/NotificationService.js';
+
+process.env.NODE_ENV = 'test';
+
+describe('UserEntityService', () => {
+	describe('pack/packMany', () => {
+		let app: TestingModule;
+		let service: UserEntityService;
+		let usersRepository: UsersRepository;
+		let userProfileRepository: UserProfilesRepository;
+		let userMemosRepository: UserMemoRepository;
+		let followingRepository: FollowingsRepository;
+		let followingRequestRepository: FollowRequestsRepository;
+		let blockingRepository: BlockingsRepository;
+		let mutingRepository: MutingsRepository;
+		let renoteMutingsRepository: RenoteMutingsRepository;
+
+		async function createUser(userData: Partial<MiUser> = {}, profileData: Partial<MiUserProfile> = {}) {
+			const un = secureRndstr(16);
+			const user = await usersRepository
+				.insert({
+					...userData,
+					id: genAidx(Date.now()),
+					username: un,
+					usernameLower: un,
+				})
+				.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+
+			await userProfileRepository.insert({
+				...profileData,
+				userId: user.id,
+			});
+
+			return user;
+		}
+
+		async function memo(writer: MiUser, target: MiUser, memo: string) {
+			await userMemosRepository.insert({
+				id: genAidx(Date.now()),
+				userId: writer.id,
+				targetUserId: target.id,
+				memo,
+			});
+		}
+
+		async function follow(follower: MiUser, followee: MiUser) {
+			await followingRepository.insert({
+				id: genAidx(Date.now()),
+				followerId: follower.id,
+				followeeId: followee.id,
+			});
+		}
+
+		async function requestFollow(requester: MiUser, requestee: MiUser) {
+			await followingRequestRepository.insert({
+				id: genAidx(Date.now()),
+				followerId: requester.id,
+				followeeId: requestee.id,
+			});
+		}
+
+		async function block(blocker: MiUser, blockee: MiUser) {
+			await blockingRepository.insert({
+				id: genAidx(Date.now()),
+				blockerId: blocker.id,
+				blockeeId: blockee.id,
+			});
+		}
+
+		async function mute(mutant: MiUser, mutee: MiUser) {
+			await mutingRepository.insert({
+				id: genAidx(Date.now()),
+				muterId: mutant.id,
+				muteeId: mutee.id,
+			});
+		}
+
+		async function muteRenote(mutant: MiUser, mutee: MiUser) {
+			await renoteMutingsRepository.insert({
+				id: genAidx(Date.now()),
+				muterId: mutant.id,
+				muteeId: mutee.id,
+			});
+		}
+
+		function randomIntRange(weight = 10) {
+			return [...Array(Math.floor(Math.random() * weight))].map((it, idx) => idx);
+		}
+
+		beforeAll(async () => {
+			const services = [
+				UserEntityService,
+				ApPersonService,
+				NoteEntityService,
+				PageEntityService,
+				CustomEmojiService,
+				AnnouncementService,
+				RoleService,
+				FederatedInstanceService,
+				IdService,
+				AvatarDecorationService,
+				UtilityService,
+				EmojiEntityService,
+				ModerationLogService,
+				GlobalEventService,
+				DriveFileEntityService,
+				MetaService,
+				FetchInstanceMetadataService,
+				CacheService,
+				ApResolverService,
+				ApNoteService,
+				ApImageService,
+				ApMfmService,
+				MfmService,
+				HashtagService,
+				UsersChart,
+				ChartLoggerService,
+				InstanceChart,
+				ApLoggerService,
+				AccountMoveService,
+				ReactionService,
+				NotificationService,
+			];
+
+			app = await Test.createTestingModule({
+				imports: [GlobalModule, CoreModule],
+				providers: [
+					...services,
+					...services.map(x => ({ provide: x.name, useExisting: x })),
+				],
+			}).compile();
+			await app.init();
+			app.enableShutdownHooks();
+
+			service = app.get<UserEntityService>(UserEntityService);
+			usersRepository = app.get<UsersRepository>(DI.usersRepository);
+			userProfileRepository = app.get<UserProfilesRepository>(DI.userProfilesRepository);
+			userMemosRepository = app.get<UserMemoRepository>(DI.userMemosRepository);
+			followingRepository = app.get<FollowingsRepository>(DI.followingsRepository);
+			followingRequestRepository = app.get<FollowRequestsRepository>(DI.followRequestsRepository);
+			blockingRepository = app.get<BlockingsRepository>(DI.blockingsRepository);
+			mutingRepository = app.get<MutingsRepository>(DI.mutingsRepository);
+			renoteMutingsRepository = app.get<RenoteMutingsRepository>(DI.renoteMutingsRepository);
+		});
+
+		afterAll(async () => {
+			await app.close();
+		});
+
+		test('UserLite', async() => {
+			const me = await createUser();
+			const who = await createUser();
+
+			await memo(me, who, 'memo');
+
+			const actual = await service.pack(who, me, { schema: 'UserLite' }) as any;
+			// no detail
+			expect(actual.memo).toBeUndefined();
+			// no detail and me
+			expect(actual.birthday).toBeUndefined();
+			// no detail and me
+			expect(actual.achievements).toBeUndefined();
+		});
+
+		test('UserDetailedNotMe', async() => {
+			const me = await createUser();
+			const who = await createUser({}, { birthday: '2000-01-01' });
+
+			await memo(me, who, 'memo');
+
+			const actual = await service.pack(who, me, { schema: 'UserDetailedNotMe' }) as any;
+			// is detail
+			expect(actual.memo).toBe('memo');
+			// is detail
+			expect(actual.birthday).toBe('2000-01-01');
+			// no detail and me
+			expect(actual.achievements).toBeUndefined();
+		});
+
+		test('MeDetailed', async() => {
+			const achievements = [{ name: 'achievement', unlockedAt: new Date().getTime() }];
+			const me = await createUser({}, {
+				birthday: '2000-01-01',
+				achievements: achievements,
+			});
+			await memo(me, me, 'memo');
+
+			const actual = await service.pack(me, me, { schema: 'MeDetailed' }) as any;
+			// is detail
+			expect(actual.memo).toBe('memo');
+			// is detail
+			expect(actual.birthday).toBe('2000-01-01');
+			// is detail and me
+			expect(actual.achievements).toEqual(achievements);
+		});
+
+		describe('packManyによるpreloadがある時、preloadが無い時とpackの結果が同じになるか見たい', () => {
+			test('no-preload', async() => {
+				const me = await createUser();
+				// meがフォローしてる人たち
+				const followeeMe = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of followeeMe) {
+					await follow(me, who);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(true);
+					expect(actual.isFollowed).toBe(false);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+					expect(actual.hasPendingFollowRequestToYou).toBe(false);
+					expect(actual.isBlocking).toBe(false);
+					expect(actual.isBlocked).toBe(false);
+					expect(actual.isMuted).toBe(false);
+					expect(actual.isRenoteMuted).toBe(false);
+				}
+
+				// meをフォローしてる人たち
+				const followerMe = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of followerMe) {
+					await follow(who, me);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(false);
+					expect(actual.isFollowed).toBe(true);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+					expect(actual.hasPendingFollowRequestToYou).toBe(false);
+					expect(actual.isBlocking).toBe(false);
+					expect(actual.isBlocked).toBe(false);
+					expect(actual.isMuted).toBe(false);
+					expect(actual.isRenoteMuted).toBe(false);
+				}
+
+				// meがフォローリクエストを送った人たち
+				const requestsFromYou = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of requestsFromYou) {
+					await requestFollow(me, who);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(false);
+					expect(actual.isFollowed).toBe(false);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(true);
+					expect(actual.hasPendingFollowRequestToYou).toBe(false);
+					expect(actual.isBlocking).toBe(false);
+					expect(actual.isBlocked).toBe(false);
+					expect(actual.isMuted).toBe(false);
+					expect(actual.isRenoteMuted).toBe(false);
+				}
+
+				// meにフォローリクエストを送った人たち
+				const requestsToYou = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of requestsToYou) {
+					await requestFollow(who, me);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(false);
+					expect(actual.isFollowed).toBe(false);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+					expect(actual.hasPendingFollowRequestToYou).toBe(true);
+					expect(actual.isBlocking).toBe(false);
+					expect(actual.isBlocked).toBe(false);
+					expect(actual.isMuted).toBe(false);
+					expect(actual.isRenoteMuted).toBe(false);
+				}
+
+				// meがブロックしてる人たち
+				const blockingYou = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of blockingYou) {
+					await block(me, who);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(false);
+					expect(actual.isFollowed).toBe(false);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+					expect(actual.hasPendingFollowRequestToYou).toBe(false);
+					expect(actual.isBlocking).toBe(true);
+					expect(actual.isBlocked).toBe(false);
+					expect(actual.isMuted).toBe(false);
+					expect(actual.isRenoteMuted).toBe(false);
+				}
+
+				// meをブロックしてる人たち
+				const blockingMe = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of blockingMe) {
+					await block(who, me);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(false);
+					expect(actual.isFollowed).toBe(false);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+					expect(actual.hasPendingFollowRequestToYou).toBe(false);
+					expect(actual.isBlocking).toBe(false);
+					expect(actual.isBlocked).toBe(true);
+					expect(actual.isMuted).toBe(false);
+					expect(actual.isRenoteMuted).toBe(false);
+				}
+
+				// meがミュートしてる人たち
+				const muters = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of muters) {
+					await mute(me, who);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(false);
+					expect(actual.isFollowed).toBe(false);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+					expect(actual.hasPendingFollowRequestToYou).toBe(false);
+					expect(actual.isBlocking).toBe(false);
+					expect(actual.isBlocked).toBe(false);
+					expect(actual.isMuted).toBe(true);
+					expect(actual.isRenoteMuted).toBe(false);
+				}
+
+				// meがリノートミュートしてる人たち
+				const renoteMuters = await Promise.all(randomIntRange().map(() => createUser()));
+				for (const who of renoteMuters) {
+					await muteRenote(me, who);
+					const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any;
+					expect(actual.isFollowing).toBe(false);
+					expect(actual.isFollowed).toBe(false);
+					expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+					expect(actual.hasPendingFollowRequestToYou).toBe(false);
+					expect(actual.isBlocking).toBe(false);
+					expect(actual.isBlocked).toBe(false);
+					expect(actual.isMuted).toBe(false);
+					expect(actual.isRenoteMuted).toBe(true);
+				}
+			});
+
+			test('preload', async() => {
+				const me = await createUser();
+
+				{
+					// meがフォローしてる人たち
+					const followeeMe = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of followeeMe) {
+						await follow(me, who);
+					}
+					const actualList = await service.packMany(followeeMe, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(true);
+						expect(actual.isFollowed).toBe(false);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+						expect(actual.hasPendingFollowRequestToYou).toBe(false);
+						expect(actual.isBlocking).toBe(false);
+						expect(actual.isBlocked).toBe(false);
+						expect(actual.isMuted).toBe(false);
+						expect(actual.isRenoteMuted).toBe(false);
+					}
+				}
+
+				{
+					// meをフォローしてる人たち
+					const followerMe = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of followerMe) {
+						await follow(who, me);
+					}
+					const actualList = await service.packMany(followerMe, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(false);
+						expect(actual.isFollowed).toBe(true);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+						expect(actual.hasPendingFollowRequestToYou).toBe(false);
+						expect(actual.isBlocking).toBe(false);
+						expect(actual.isBlocked).toBe(false);
+						expect(actual.isMuted).toBe(false);
+						expect(actual.isRenoteMuted).toBe(false);
+					}
+				}
+
+				{
+					// meがフォローリクエストを送った人たち
+					const requestsFromYou = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of requestsFromYou) {
+						await requestFollow(me, who);
+					}
+					const actualList = await service.packMany(requestsFromYou, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(false);
+						expect(actual.isFollowed).toBe(false);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(true);
+						expect(actual.hasPendingFollowRequestToYou).toBe(false);
+						expect(actual.isBlocking).toBe(false);
+						expect(actual.isBlocked).toBe(false);
+						expect(actual.isMuted).toBe(false);
+						expect(actual.isRenoteMuted).toBe(false);
+					}
+				}
+
+				{
+					// meにフォローリクエストを送った人たち
+					const requestsToYou = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of requestsToYou) {
+						await requestFollow(who, me);
+					}
+					const actualList = await service.packMany(requestsToYou, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(false);
+						expect(actual.isFollowed).toBe(false);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+						expect(actual.hasPendingFollowRequestToYou).toBe(true);
+						expect(actual.isBlocking).toBe(false);
+						expect(actual.isBlocked).toBe(false);
+						expect(actual.isMuted).toBe(false);
+						expect(actual.isRenoteMuted).toBe(false);
+					}
+				}
+
+				{
+					// meがブロックしてる人たち
+					const blockingYou = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of blockingYou) {
+						await block(me, who);
+					}
+					const actualList = await service.packMany(blockingYou, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(false);
+						expect(actual.isFollowed).toBe(false);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+						expect(actual.hasPendingFollowRequestToYou).toBe(false);
+						expect(actual.isBlocking).toBe(true);
+						expect(actual.isBlocked).toBe(false);
+						expect(actual.isMuted).toBe(false);
+						expect(actual.isRenoteMuted).toBe(false);
+					}
+				}
+
+				{
+					// meをブロックしてる人たち
+					const blockingMe = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of blockingMe) {
+						await block(who, me);
+					}
+					const actualList = await service.packMany(blockingMe, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(false);
+						expect(actual.isFollowed).toBe(false);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+						expect(actual.hasPendingFollowRequestToYou).toBe(false);
+						expect(actual.isBlocking).toBe(false);
+						expect(actual.isBlocked).toBe(true);
+						expect(actual.isMuted).toBe(false);
+						expect(actual.isRenoteMuted).toBe(false);
+					}
+				}
+
+				{
+					// meがミュートしてる人たち
+					const muters = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of muters) {
+						await mute(me, who);
+					}
+					const actualList = await service.packMany(muters, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(false);
+						expect(actual.isFollowed).toBe(false);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+						expect(actual.hasPendingFollowRequestToYou).toBe(false);
+						expect(actual.isBlocking).toBe(false);
+						expect(actual.isBlocked).toBe(false);
+						expect(actual.isMuted).toBe(true);
+						expect(actual.isRenoteMuted).toBe(false);
+					}
+				}
+
+				{
+					// meがリノートミュートしてる人たち
+					const renoteMuters = await Promise.all(randomIntRange().map(() => createUser()));
+					for (const who of renoteMuters) {
+						await muteRenote(me, who);
+					}
+					const actualList = await service.packMany(renoteMuters, me, { schema: 'UserDetailed' }) as any;
+					for (const actual of actualList) {
+						expect(actual.isFollowing).toBe(false);
+						expect(actual.isFollowed).toBe(false);
+						expect(actual.hasPendingFollowRequestFromYou).toBe(false);
+						expect(actual.hasPendingFollowRequestToYou).toBe(false);
+						expect(actual.isBlocking).toBe(false);
+						expect(actual.isBlocked).toBe(false);
+						expect(actual.isMuted).toBe(false);
+						expect(actual.isRenoteMuted).toBe(true);
+					}
+				}
+			});
+		});
+	});
+});

From 29f6ba6310e162d6e24c9075ddd6176c155a10e7 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Wed, 13 Mar 2024 22:37:18 +0900
Subject: [PATCH 102/266] chore: add missing SPDX ID and workflow check
 (#13570)

* chore: add workflow which checks if SPDX ID exists

* chore: add missing SPDX ID in some files

* chore: change trigger condition

* chore: trigger on push

* lint
---
 .github/workflows/check-spdx-license-id.yml   | 75 +++++++++++++++++++
 cypress/e2e/basic.cy.js                       |  5 ++
 cypress/e2e/router.cy.js                      |  5 ++
 cypress/e2e/widgets.cy.js                     |  5 ++
 packages/backend/generate_api_json.js         |  7 +-
 .../1689325027964-UserBlacklistAnntena.js     |  5 ++
 .../1690417561185-fix-renote-muting.js        |  5 ++
 ...417561186-ChangeCacheRemoteFilesDefault.js |  5 ++
 .../backend/migration/1690417561187-Fix.js    |  5 ++
 .../1690569881926-user-2fa-backup-codes.js    |  5 ++
 .../1691649257651-refine-announcement.js      |  5 ++
 .../1691657412740-refine-announcement-2.js    |  5 ++
 .../migration/1695260774117-verified-links.js |  5 ++
 .../1695288787870-following-notify.js         |  5 ++
 .../migration/1695440131671-short-name.js     |  5 ++
 .../1695605508898-mutingNotificationTypes.js  |  5 ++
 .../1695901659683-note-updated-at.js          |  5 ++
 .../1696323464251-user-list-membership.js     |  5 ++
 .../migration/1696331570827-hibernation.js    |  5 ++
 .../backend/migration/1696332072038-clean.js  |  5 ++
 .../migration/1700383825690-hard-mute.js      |  5 ++
 .../src/core/ChannelFollowingService.ts       |  5 ++
 .../backend/src/misc/fastify-hook-handlers.ts |  5 ++
 packages/backend/src/misc/is-pure-renote.ts   |  5 ++
 packages/backend/src/misc/loader.ts           |  5 ++
 packages/backend/src/models/ReversiGame.ts    |  5 ++
 .../backend/src/models/json-schema/signin.ts  |  5 ++
 packages/backend/test/jest.setup.ts           |  5 ++
 packages/backend/test/unit/ApMfmService.ts    |  5 ++
 packages/backend/test/unit/misc/loader.ts     |  5 ++
 .../frontend/.storybook/preview-head.html     |  5 ++
 .../frontend/src/components/global/I18n.vue   |  5 ++
 packages/frontend/src/filters/kmg.ts          |  5 ++
 .../src/scripts/check-reaction-permissions.ts |  5 ++
 packages/frontend/src/scripts/clear-cache.ts  |  5 ++
 .../frontend/src/scripts/code-highlighter.ts  |  5 ++
 .../frontend/src/scripts/media-has-audio.ts   |  5 ++
 packages/frontend/src/type.ts                 |  5 ++
 scripts/changelog-checker/src/checker.ts      |  5 ++
 scripts/changelog-checker/src/index.ts        |  5 ++
 scripts/changelog-checker/src/parser.ts       |  5 ++
 .../changelog-checker/test/checker.test.ts    |  7 +-
 scripts/tarball.mjs                           |  5 ++
 43 files changed, 287 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/check-spdx-license-id.yml

diff --git a/.github/workflows/check-spdx-license-id.yml b/.github/workflows/check-spdx-license-id.yml
new file mode 100644
index 000000000000..6cd8bf60d520
--- /dev/null
+++ b/.github/workflows/check-spdx-license-id.yml
@@ -0,0 +1,75 @@
+name: Check SPDX-License-Identifier
+
+on:
+  push:
+    branches:
+      - master
+      - develop
+  pull_request:
+
+jobs:
+  check-spdx-license-id:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4.1.1
+      - name: Check
+        run: |
+          counter=0
+
+          search() {
+            local directory="$1"
+            find "$directory" -type f \
+              '(' \
+                -name "*.cjs" -and -not -name '*.config.cjs' -o \
+                -name "*.html" -o \
+                -name "*.js" -and -not -name '*.config.js' -o \
+                -name "*.mjs" -and -not -name '*.config.mjs' -o \
+                -name "*.scss" -o \
+                -name "*.ts" -and -not -name '*.config.ts' -o \
+                -name "*.vue" \
+              ')' -and \
+              -not -name '*eslint*'
+          }
+
+          check() {
+            local file="$1"
+            if ! (
+              grep -q "SPDX-FileCopyrightText: syuilo and misskey-project" "$file" ||
+              grep -q "SPDX-License-Identifier: AGPL-3.0-only" "$file"
+            ); then
+              echo "Missing: $file"
+              ((counter++))
+            fi
+          }
+
+          directories=(
+            "cypress/e2e"
+            "packages/backend/migration"
+            "packages/backend/src"
+            "packages/backend/test"
+            "packages/frontend/.storybook"
+            "packages/frontend/@types"
+            "packages/frontend/lib"
+            "packages/frontend/public"
+            "packages/frontend/src"
+            "packages/frontend/test"
+            "packages/misskey-bubble-game/src"
+            "packages/misskey-reversi/src"
+            "packages/sw/src"
+            "scripts"
+          )
+
+          for directory in "${directories[@]}"; do
+            for file in $(search $directory); do
+              check "$file"
+            done
+          done
+
+          if [ $counter -gt 0 ]; then
+            echo "SPDX-License-Identifier is missing in $counter files."
+            exit 1
+          else
+            echo "SPDX-License-Identifier is certainly described in all target files!"
+            exit 0
+          fi
diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.js
index 604241d13cc6..d2525e0a7d05 100644
--- a/cypress/e2e/basic.cy.js
+++ b/cypress/e2e/basic.cy.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 describe('Before setup instance', () => {
 	beforeEach(() => {
 		cy.resetState();
diff --git a/cypress/e2e/router.cy.js b/cypress/e2e/router.cy.js
index 6de27be5f429..8d8fb3af318f 100644
--- a/cypress/e2e/router.cy.js
+++ b/cypress/e2e/router.cy.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 describe('Router transition', () => {
 	describe('Redirect', () => {
 		// サーバの初期化。ルートのテストに関しては各describeごとに1度だけ実行で十分だと思う(使いまわした方が早い)
diff --git a/cypress/e2e/widgets.cy.js b/cypress/e2e/widgets.cy.js
index df6ec8357d2f..847801a69fb5 100644
--- a/cypress/e2e/widgets.cy.js
+++ b/cypress/e2e/widgets.cy.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 /* flaky
 describe('After user signed in', () => {
 	beforeEach(() => {
diff --git a/packages/backend/generate_api_json.js b/packages/backend/generate_api_json.js
index 4079b3bb0aac..602ced1d7553 100644
--- a/packages/backend/generate_api_json.js
+++ b/packages/backend/generate_api_json.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { loadConfig } from './built/config.js'
 import { genOpenapiSpec } from './built/server/api/openapi/gen-spec.js'
 import { writeFileSync } from "node:fs";
@@ -5,4 +10,4 @@ import { writeFileSync } from "node:fs";
 const config = loadConfig();
 const spec = genOpenapiSpec(config, true);
 
-writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8');
\ No newline at end of file
+writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8');
diff --git a/packages/backend/migration/1689325027964-UserBlacklistAnntena.js b/packages/backend/migration/1689325027964-UserBlacklistAnntena.js
index ce246b20f887..2dc777449383 100644
--- a/packages/backend/migration/1689325027964-UserBlacklistAnntena.js
+++ b/packages/backend/migration/1689325027964-UserBlacklistAnntena.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class UserBlacklistAnntena1689325027964 {
     name = 'UserBlacklistAnntena1689325027964'
 
diff --git a/packages/backend/migration/1690417561185-fix-renote-muting.js b/packages/backend/migration/1690417561185-fix-renote-muting.js
index 14150b036237..d9604ca26c04 100644
--- a/packages/backend/migration/1690417561185-fix-renote-muting.js
+++ b/packages/backend/migration/1690417561185-fix-renote-muting.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class FixRenoteMuting1690417561185 {
     name = 'FixRenoteMuting1690417561185'
 
diff --git a/packages/backend/migration/1690417561186-ChangeCacheRemoteFilesDefault.js b/packages/backend/migration/1690417561186-ChangeCacheRemoteFilesDefault.js
index 7eda5debe542..9bccdb3bb5fb 100644
--- a/packages/backend/migration/1690417561186-ChangeCacheRemoteFilesDefault.js
+++ b/packages/backend/migration/1690417561186-ChangeCacheRemoteFilesDefault.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class ChangeCacheRemoteFilesDefault1690417561186 {
     name = 'ChangeCacheRemoteFilesDefault1690417561186'
 
diff --git a/packages/backend/migration/1690417561187-Fix.js b/packages/backend/migration/1690417561187-Fix.js
index e780e66d7b33..7f1d62d68cf8 100644
--- a/packages/backend/migration/1690417561187-Fix.js
+++ b/packages/backend/migration/1690417561187-Fix.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class Fix1690417561187 {
     name = 'Fix1690417561187'
 
diff --git a/packages/backend/migration/1690569881926-user-2fa-backup-codes.js b/packages/backend/migration/1690569881926-user-2fa-backup-codes.js
index 2049df8ea2e4..a3ef8dcf08f3 100644
--- a/packages/backend/migration/1690569881926-user-2fa-backup-codes.js
+++ b/packages/backend/migration/1690569881926-user-2fa-backup-codes.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class User2faBackupCodes1690569881926 {
 	name = 'User2faBackupCodes1690569881926'
 
diff --git a/packages/backend/migration/1691649257651-refine-announcement.js b/packages/backend/migration/1691649257651-refine-announcement.js
index d8d63f310332..ac621155d5f6 100644
--- a/packages/backend/migration/1691649257651-refine-announcement.js
+++ b/packages/backend/migration/1691649257651-refine-announcement.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class RefineAnnouncement1691649257651 {
     name = 'RefineAnnouncement1691649257651'
 
diff --git a/packages/backend/migration/1691657412740-refine-announcement-2.js b/packages/backend/migration/1691657412740-refine-announcement-2.js
index 8791f99f44e8..67edf196590d 100644
--- a/packages/backend/migration/1691657412740-refine-announcement-2.js
+++ b/packages/backend/migration/1691657412740-refine-announcement-2.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class RefineAnnouncement21691657412740 {
     name = 'RefineAnnouncement21691657412740'
 
diff --git a/packages/backend/migration/1695260774117-verified-links.js b/packages/backend/migration/1695260774117-verified-links.js
index 18e0571d81d0..64c8a9ad8f85 100644
--- a/packages/backend/migration/1695260774117-verified-links.js
+++ b/packages/backend/migration/1695260774117-verified-links.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class VerifiedLinks1695260774117 {
     name = 'VerifiedLinks1695260774117'
 
diff --git a/packages/backend/migration/1695288787870-following-notify.js b/packages/backend/migration/1695288787870-following-notify.js
index e7e2194b156f..b3f78d5f2a32 100644
--- a/packages/backend/migration/1695288787870-following-notify.js
+++ b/packages/backend/migration/1695288787870-following-notify.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class FollowingNotify1695288787870 {
     name = 'FollowingNotify1695288787870'
 
diff --git a/packages/backend/migration/1695440131671-short-name.js b/packages/backend/migration/1695440131671-short-name.js
index 2c37297fc109..fdc256caf88a 100644
--- a/packages/backend/migration/1695440131671-short-name.js
+++ b/packages/backend/migration/1695440131671-short-name.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class ShortName1695440131671 {
     name = 'ShortName1695440131671'
 
diff --git a/packages/backend/migration/1695605508898-mutingNotificationTypes.js b/packages/backend/migration/1695605508898-mutingNotificationTypes.js
index 8c0e52a2f00c..67d4243142e7 100644
--- a/packages/backend/migration/1695605508898-mutingNotificationTypes.js
+++ b/packages/backend/migration/1695605508898-mutingNotificationTypes.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class MutingNotificationTypes1695605508898 {
     name = 'MutingNotificationTypes1695605508898'
 
diff --git a/packages/backend/migration/1695901659683-note-updated-at.js b/packages/backend/migration/1695901659683-note-updated-at.js
index d8a151a1f70e..e828fb1a6fab 100644
--- a/packages/backend/migration/1695901659683-note-updated-at.js
+++ b/packages/backend/migration/1695901659683-note-updated-at.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class NoteUpdatedAt1695901659683 {
     name = 'NoteUpdatedAt1695901659683'
 
diff --git a/packages/backend/migration/1696323464251-user-list-membership.js b/packages/backend/migration/1696323464251-user-list-membership.js
index 7534040c4c9b..dc1d438dd7ef 100644
--- a/packages/backend/migration/1696323464251-user-list-membership.js
+++ b/packages/backend/migration/1696323464251-user-list-membership.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class UserListMembership1696323464251 {
     name = 'UserListMembership1696323464251'
 
diff --git a/packages/backend/migration/1696331570827-hibernation.js b/packages/backend/migration/1696331570827-hibernation.js
index 119d35913f7b..1487ece77c5f 100644
--- a/packages/backend/migration/1696331570827-hibernation.js
+++ b/packages/backend/migration/1696331570827-hibernation.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class Hibernation1696331570827 {
     name = 'Hibernation1696331570827'
 
diff --git a/packages/backend/migration/1696332072038-clean.js b/packages/backend/migration/1696332072038-clean.js
index 97dba655f46c..92a6810d6a9e 100644
--- a/packages/backend/migration/1696332072038-clean.js
+++ b/packages/backend/migration/1696332072038-clean.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class Clean1696332072038 {
     name = 'Clean1696332072038'
 
diff --git a/packages/backend/migration/1700383825690-hard-mute.js b/packages/backend/migration/1700383825690-hard-mute.js
index afd3247f5cae..92c3ada4a1f2 100644
--- a/packages/backend/migration/1700383825690-hard-mute.js
+++ b/packages/backend/migration/1700383825690-hard-mute.js
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export class HardMute1700383825690 {
     name = 'HardMute1700383825690'
 
diff --git a/packages/backend/src/core/ChannelFollowingService.ts b/packages/backend/src/core/ChannelFollowingService.ts
index 75843b9773e0..12251595e2fa 100644
--- a/packages/backend/src/core/ChannelFollowingService.ts
+++ b/packages/backend/src/core/ChannelFollowingService.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
 import Redis from 'ioredis';
 import { DI } from '@/di-symbols.js';
diff --git a/packages/backend/src/misc/fastify-hook-handlers.ts b/packages/backend/src/misc/fastify-hook-handlers.ts
index 49a48f6a6b00..3e1c099e000c 100644
--- a/packages/backend/src/misc/fastify-hook-handlers.ts
+++ b/packages/backend/src/misc/fastify-hook-handlers.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import type { onRequestHookHandler } from 'fastify';
 
 export const handleRequestRedirectToOmitSearch: onRequestHookHandler = (request, reply, done) => {
diff --git a/packages/backend/src/misc/is-pure-renote.ts b/packages/backend/src/misc/is-pure-renote.ts
index 994d98152244..f9c2243a06ff 100644
--- a/packages/backend/src/misc/is-pure-renote.ts
+++ b/packages/backend/src/misc/is-pure-renote.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import type { MiNote } from '@/models/Note.js';
 
 export function isPureRenote(note: MiNote): note is MiNote & { renoteId: NonNullable<MiNote['renoteId']> } {
diff --git a/packages/backend/src/misc/loader.ts b/packages/backend/src/misc/loader.ts
index 25f7b54d31c6..7f29b9db10e4 100644
--- a/packages/backend/src/misc/loader.ts
+++ b/packages/backend/src/misc/loader.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export type FetchFunction<K, V> = (key: K) => Promise<V>;
 
 type ResolveReject<V> = Parameters<ConstructorParameters<typeof Promise<V>>[0]>;
diff --git a/packages/backend/src/models/ReversiGame.ts b/packages/backend/src/models/ReversiGame.ts
index c03335dd6305..6b29a0ce8c59 100644
--- a/packages/backend/src/models/ReversiGame.ts
+++ b/packages/backend/src/models/ReversiGame.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
 import { id } from './util/id.js';
 import { MiUser } from './User.js';
diff --git a/packages/backend/src/models/json-schema/signin.ts b/packages/backend/src/models/json-schema/signin.ts
index d27d2490c579..45732a742b1d 100644
--- a/packages/backend/src/models/json-schema/signin.ts
+++ b/packages/backend/src/models/json-schema/signin.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export const packedSigninSchema = {
 	type: 'object',
 	properties: {
diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts
index cf5b9bf24ddb..861bc6db669b 100644
--- a/packages/backend/test/jest.setup.ts
+++ b/packages/backend/test/jest.setup.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { initTestDb, sendEnvResetRequest } from './utils.js';
 
 beforeAll(async () => {
diff --git a/packages/backend/test/unit/ApMfmService.ts b/packages/backend/test/unit/ApMfmService.ts
index 2b79041c86d7..79cb81f5c9ff 100644
--- a/packages/backend/test/unit/ApMfmService.ts
+++ b/packages/backend/test/unit/ApMfmService.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import * as assert from 'assert';
 import { Test } from '@nestjs/testing';
 
diff --git a/packages/backend/test/unit/misc/loader.ts b/packages/backend/test/unit/misc/loader.ts
index fa3795095118..2cf54e15559a 100644
--- a/packages/backend/test/unit/misc/loader.ts
+++ b/packages/backend/test/unit/misc/loader.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { DebounceLoader } from '@/misc/loader.js';
 
 class Mock {
diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html
index 30f3ebfb64cf..e50c48824332 100644
--- a/packages/frontend/.storybook/preview-head.html
+++ b/packages/frontend/.storybook/preview-head.html
@@ -1,3 +1,8 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
 <link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.44.0/tabler-icons.min.css">
diff --git a/packages/frontend/src/components/global/I18n.vue b/packages/frontend/src/components/global/I18n.vue
index 162aa2bcf811..6b7723e6ac9b 100644
--- a/packages/frontend/src/components/global/I18n.vue
+++ b/packages/frontend/src/components/global/I18n.vue
@@ -1,3 +1,8 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
 <template>
 <render/>
 </template>
diff --git a/packages/frontend/src/filters/kmg.ts b/packages/frontend/src/filters/kmg.ts
index 4dcb5c580027..9608e420f602 100644
--- a/packages/frontend/src/filters/kmg.ts
+++ b/packages/frontend/src/filters/kmg.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export default (v, fractionDigits = 0) => {
 	if (v == null) return 'N/A';
 	if (v === 0) return '0';
diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts
index e7b473dd7588..8fc857f84fce 100644
--- a/packages/frontend/src/scripts/check-reaction-permissions.ts
+++ b/packages/frontend/src/scripts/check-reaction-permissions.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import * as Misskey from 'misskey-js';
 import { UnicodeEmojiDef } from './emojilist.js';
 
diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts
index b20109ec72bf..71d123271033 100644
--- a/packages/frontend/src/scripts/clear-cache.ts
+++ b/packages/frontend/src/scripts/clear-cache.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { unisonReload } from '@/scripts/unison-reload.js';
 import * as os from '@/os.js';
 import { miLocalStorage } from '@/local-storage.js';
diff --git a/packages/frontend/src/scripts/code-highlighter.ts b/packages/frontend/src/scripts/code-highlighter.ts
index 2733897bab10..5dd0a3be7895 100644
--- a/packages/frontend/src/scripts/code-highlighter.ts
+++ b/packages/frontend/src/scripts/code-highlighter.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { bundledThemesInfo } from 'shiki';
 import { getHighlighterCore, loadWasm } from 'shiki/core';
 import darkPlus from 'shiki/themes/dark-plus.mjs';
diff --git a/packages/frontend/src/scripts/media-has-audio.ts b/packages/frontend/src/scripts/media-has-audio.ts
index 3421a38a7655..4bf3ee5d97c8 100644
--- a/packages/frontend/src/scripts/media-has-audio.ts
+++ b/packages/frontend/src/scripts/media-has-audio.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export default async function hasAudio(media: HTMLMediaElement) {
 	const cloned = media.cloneNode() as HTMLMediaElement;
 	cloned.muted = (cloned as typeof cloned & Partial<HTMLVideoElement>).playsInline = true;
diff --git a/packages/frontend/src/type.ts b/packages/frontend/src/type.ts
index 9c0fc2a11e6d..5ff27158d249 100644
--- a/packages/frontend/src/type.ts
+++ b/packages/frontend/src/type.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
 
 export type WithNonNullable<T, K extends keyof T> = T & { [P in K]-?: NonNullable<T[P]> };
diff --git a/scripts/changelog-checker/src/checker.ts b/scripts/changelog-checker/src/checker.ts
index bbd5b2270a76..8fd6ff5629d1 100644
--- a/scripts/changelog-checker/src/checker.ts
+++ b/scripts/changelog-checker/src/checker.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { Release } from './parser.js';
 
 export class Result {
diff --git a/scripts/changelog-checker/src/index.ts b/scripts/changelog-checker/src/index.ts
index 8cbeb297d905..0e2c5ce05705 100644
--- a/scripts/changelog-checker/src/index.ts
+++ b/scripts/changelog-checker/src/index.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import * as process from 'process';
 import * as fs from 'fs';
 import { parseChangeLog } from './parser.js';
diff --git a/scripts/changelog-checker/src/parser.ts b/scripts/changelog-checker/src/parser.ts
index d6a9ddeda8c1..894d60d6aff3 100644
--- a/scripts/changelog-checker/src/parser.ts
+++ b/scripts/changelog-checker/src/parser.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import * as fs from 'node:fs';
 import { unified } from 'unified';
 import remarkParse from 'remark-parse';
diff --git a/scripts/changelog-checker/test/checker.test.ts b/scripts/changelog-checker/test/checker.test.ts
index bc73e5622b12..9ca8fa85f2e0 100644
--- a/scripts/changelog-checker/test/checker.test.ts
+++ b/scripts/changelog-checker/test/checker.test.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import {expect, suite, test} from "vitest";
 import {Release, ReleaseCategory} from "../src/parser";
 import {checkNewRelease, checkNewTopic} from "../src/checker";
@@ -411,4 +416,4 @@ suite('checkNewTopic', () => {
 		console.log(result.message)
 		expect(result.success).toBe(false)
 	})
-})
\ No newline at end of file
+})
diff --git a/scripts/tarball.mjs b/scripts/tarball.mjs
index 936a43d27017..b1862ad289b0 100644
--- a/scripts/tarball.mjs
+++ b/scripts/tarball.mjs
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
 import { createWriteStream } from 'node:fs';
 import { mkdir } from 'node:fs/promises';
 import { resolve } from 'node:path';

From 88d47ab0245bf5990096e59ced280f62cb7e7a60 Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Wed, 13 Mar 2024 22:38:26 +0900
Subject: [PATCH 103/266] =?UTF-8?q?=E3=83=97=E3=83=A9=E3=82=B0=E3=82=A4?=
 =?UTF-8?q?=E3=83=B3=E3=81=AE=E7=B0=A1=E6=98=93=E7=9A=84=E3=81=AA=E3=83=AD?=
 =?UTF-8?q?=E3=82=B0=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E6=A9=9F?=
 =?UTF-8?q?=E8=83=BD=20(#13564)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* add plugin logging

* change variable name

* Update plugin.ts

* Update CHANGELOG.md
---
 CHANGELOG.md                                  |  2 ++
 locales/ja-JP.yml                             |  1 +
 .../frontend/src/pages/settings/plugin.vue    | 20 ++++++++++++---
 packages/frontend/src/plugin.ts               | 25 +++++++++++++------
 4 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83d0a3f7d2ed..f1e863a8f2b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
 - Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
 - Enhance: リアクション・いいねの総数を表示するように
 - Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
+- Enhance: 設定>プラグインのページからプラグインの簡易的なログやエラーを見られるように
+  - 実装の都合により、プラグインは1つエラーを起こした時に即時停止するようになりました
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 64705868b930..f42fd6587a1c 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1772,6 +1772,7 @@ _plugin:
   installWarn: "信頼できないプラグインはインストールしないでください。"
   manage: "プラグインの管理"
   viewSource: "ソースを表示"
+  viewLog: "ログを表示"
 
 _preferencesBackups:
   list: "作成したバックアップ"
diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue
index 0ab75b95a28c..9804454e6605 100644
--- a/packages/frontend/src/pages/settings/plugin.vue
+++ b/packages/frontend/src/pages/settings/plugin.vue
@@ -41,13 +41,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkButton inline danger @click="uninstall(plugin)"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton>
 				</div>
 
+				<MkFolder>
+					<template #icon><i class="ti ti-terminal-2"></i></template>
+					<template #label>{{ i18n.ts._plugin.viewLog }}</template>
+
+					<div class="_gaps_s">
+						<div class="_buttons">
+							<MkButton inline @click="copy(pluginLogs.get(plugin.id)?.join('\n'))"><i class="ti ti-copy"></i> {{ i18n.ts.copy }}</MkButton>
+						</div>
+
+						<MkCode :code="pluginLogs.get(plugin.id)?.join('\n') ?? ''"/>
+					</div>
+				</MkFolder>
+
 				<MkFolder>
 					<template #icon><i class="ti ti-code"></i></template>
 					<template #label>{{ i18n.ts._plugin.viewSource }}</template>
 
 					<div class="_gaps_s">
 						<div class="_buttons">
-							<MkButton inline @click="copy(plugin)"><i class="ti ti-copy"></i> {{ i18n.ts.copy }}</MkButton>
+							<MkButton inline @click="copy(plugin.src)"><i class="ti ti-copy"></i> {{ i18n.ts.copy }}</MkButton>
 						</div>
 
 						<MkCode :code="plugin.src ?? ''" lang="is"/>
@@ -74,6 +87,7 @@ import { ColdDeviceStorage } from '@/store.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { pluginLogs } from '@/plugin.js';
 
 const plugins = ref(ColdDeviceStorage.get('plugins'));
 
@@ -87,8 +101,8 @@ async function uninstall(plugin) {
 	});
 }
 
-function copy(plugin) {
-	copyToClipboard(plugin.src ?? '');
+function copy(text) {
+	copyToClipboard(text ?? '');
 	os.success();
 }
 
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index 743cadc36a82..81233a5a5e45 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { ref } from 'vue';
 import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
 import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import { inputText } from '@/os.js';
@@ -10,6 +11,7 @@ import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFo
 
 const parser = new Parser();
 const pluginContexts = new Map<string, Interpreter>();
+export const pluginLogs = ref(new Map<string, string[]>());
 
 export async function install(plugin: Plugin): Promise<void> {
 	// 後方互換性のため
@@ -22,21 +24,27 @@ export async function install(plugin: Plugin): Promise<void> {
 		in: aiScriptReadline,
 		out: (value): void => {
 			console.log(value);
+			pluginLogs.value.get(plugin.id).push(utils.reprValue(value));
 		},
 		log: (): void => {
 		},
+		err: (err): void => {
+			pluginLogs.value.get(plugin.id).push(`${err}`);
+			throw err; // install時のtry-catchに反応させる
+		},
 	});
 
 	initPlugin({ plugin, aiscript });
 
-	try {
-		await aiscript.exec(parser.parse(plugin.src));
-	} catch (err) {
-		console.error('Plugin install failed:', plugin.name, 'v' + plugin.version);
-		return;
-	}
-
-	console.info('Plugin installed:', plugin.name, 'v' + plugin.version);
+	aiscript.exec(parser.parse(plugin.src)).then(
+		() => {
+			console.info('Plugin installed:', plugin.name, 'v' + plugin.version);
+		},
+		(err) => {
+			console.error('Plugin install failed:', plugin.name, 'v' + plugin.version);
+			throw err;
+		},
+	);
 }
 
 function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<string, values.Value> {
@@ -92,6 +100,7 @@ function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<s
 
 function initPlugin({ plugin, aiscript }): void {
 	pluginContexts.set(plugin.id, aiscript);
+	pluginLogs.value.set(plugin.id, []);
 }
 
 function registerPostFormAction({ pluginId, title, handler }): void {

From 75fa43bc59348c59a6e1a95823b5977bff75d78e Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 14 Mar 2024 17:39:38 +0900
Subject: [PATCH 104/266] fix(dev): fix duplication in .vscode/extensions.json

---
 .vscode/extensions.json | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index d08109477c59..3cdf81e339e2 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -4,7 +4,6 @@
 		"dbaeumer.vscode-eslint",
 		"Vue.volar",
 		"Orta.vscode-jest",
-		"dbaeumer.vscode-eslint",
 		"mrmlnc.vscode-json5"
 	]
 }

From 8604bd98078b3e31d8b53de1f9e54f5dab1f22bd Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 14 Mar 2024 17:42:30 +0900
Subject: [PATCH 105/266] fix(dev): vscode-jest: Deprecated: Please use
 jest.runMode instead.

---
 .vscode/settings.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.vscode/settings.json b/.vscode/settings.json
index e2a82b1ffed0..0ceec23acd8b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -7,7 +7,7 @@
 		"*.test.ts": "typescript"
 	},
 	"jest.jestCommandLine": "pnpm run jest",
-	"jest.autoRun": "off",
+	"jest.runMode": "on-demand",
 	"editor.codeActionsOnSave": {
 		"source.fixAll": "explicit"
 	},

From 71d0538647684d67de1b15467f890bd65ffc770d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 14 Mar 2024 18:18:32 +0900
Subject: [PATCH 106/266] fix(frontend): update locales/index.d.ts

---
 locales/index.d.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 53c3159da661..87065e1d374c 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -6805,6 +6805,10 @@ export interface Locale extends ILocale {
          * ソースを表示
          */
         "viewSource": string;
+        /**
+         * ログを表示
+         */
+        "viewLog": string;
     };
     "_preferencesBackups": {
         /**

From 4b1ca9ef619903e9ff1de10101c18569efbe099c Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Fri, 15 Mar 2024 22:02:57 +0900
Subject: [PATCH 107/266] =?UTF-8?q?fix(general):=20`flash/create`=E3=81=A7?=
 =?UTF-8?q?Play=E3=81=AE=E5=85=AC=E9=96=8B=E7=AF=84=E5=9B=B2=E3=82=92?=
 =?UTF-8?q?=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=E3=81=AE=E4=BF=AE=E6=AD=A3=E3=81=A8=E7=B7=A8=E9=9B=86?=
 =?UTF-8?q?=E7=94=BB=E9=9D=A2=E3=81=AE=E8=AA=BF=E6=95=B4=20(#13574)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): param `visibility` wasn't included in `flash/create`

* fix(frontend): tweak flash editor ui

* Update CHANGELOG.md
---
 CHANGELOG.md                                       |  2 +-
 locales/index.d.ts                                 |  4 ++++
 locales/ja-JP.yml                                  |  1 +
 .../src/server/api/endpoints/flash/create.ts       |  2 ++
 packages/frontend/src/pages/flash/flash-edit.vue   | 14 ++++++++------
 packages/misskey-js/src/autogen/types.ts           |  5 +++++
 6 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1e863a8f2b2..fa56f1a268ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
 ## Unreleased
 
 ### General
--
+- Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 
 ### Client
 - Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 87065e1d374c..7f4ec7ecb091 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -8635,6 +8635,10 @@ export interface Locale extends ILocale {
          * 説明
          */
         "summary": string;
+        /**
+         * 非公開に設定するとプロフィールに表示されなくなりますが、URLを知っている人は引き続きアクセスできます。
+         */
+        "visibilityDescription": string;
     };
     "_pages": {
         /**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index f42fd6587a1c..8b44ac2121cb 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2280,6 +2280,7 @@ _play:
   title: "タイトル"
   script: "スクリプト"
   summary: "説明"
+  visibilityDescription: "非公開に設定するとプロフィールに表示されなくなりますが、URLを知っている人は引き続きアクセスできます。"
 
 _pages:
   newPage: "ページの作成"
diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts
index 584d167a2900..361496e17e12 100644
--- a/packages/backend/src/server/api/endpoints/flash/create.ts
+++ b/packages/backend/src/server/api/endpoints/flash/create.ts
@@ -44,6 +44,7 @@ export const paramDef = {
 		permissions: { type: 'array', items: {
 			type: 'string',
 		} },
+		visibility: { type: 'string', enum: ['public', 'private'], default: 'public' },
 	},
 	required: ['title', 'summary', 'script', 'permissions'],
 } as const;
@@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				summary: ps.summary,
 				script: ps.script,
 				permissions: ps.permissions,
+				visibility: ps.visibility,
 			}).then(x => this.flashsRepository.findOneByOrFail(x.identifiers[0]));
 
 			return await this.flashEntityService.pack(flash);
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 4418172e629c..3625bc11649a 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -18,16 +18,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkCodeEditor v-model="script" lang="is">
 				<template #label>{{ i18n.ts._play.script }}</template>
 			</MkCodeEditor>
-			<div class="_buttons">
-				<MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
-				<MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton>
-				<MkButton v-if="flash" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
-			</div>
 			<MkSelect v-model="visibility">
 				<template #label>{{ i18n.ts.visibility }}</template>
+				<template #caption>{{ i18n.ts._play.visibilityDescription }}</template>
 				<option :key="'public'" :value="'public'">{{ i18n.ts.public }}</option>
 				<option :key="'private'" :value="'private'">{{ i18n.ts.private }}</option>
 			</MkSelect>
+			<div class="_buttons">
+				<MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+				<MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton>
+				<MkButton v-if="flash" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+			</div>
 		</div>
 	</MkSpacer>
 </MkStickyContainer>
@@ -367,7 +368,7 @@ const props = defineProps<{
 }>();
 
 const flash = ref<Misskey.entities.Flash | null>(null);
-const visibility = ref<Misskey.entities.FlashUpdateRequest['visibility']>('public');
+const visibility = ref<'private' | 'public'>('public');
 
 if (props.id) {
 	flash.value = await misskeyApi('flash/show', {
@@ -420,6 +421,7 @@ async function save() {
 			summary: summary.value,
 			permissions: permissions.value,
 			script: script.value,
+			visibility: visibility.value,
 		});
 		router.push('/play/' + created.id + '/edit');
 	}
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index be3e86bd7886..3c862f690e5f 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -22686,6 +22686,11 @@ export type operations = {
           summary: string;
           script: string;
           permissions: string[];
+          /**
+           * @default public
+           * @enum {string}
+           */
+          visibility?: 'public' | 'private';
         };
       };
     };

From 7e63ab0f5628bea33e8867d8257779b9ffeb8dd6 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 17 Mar 2024 10:34:13 +0900
Subject: [PATCH 108/266] refactor(backend): refactor chart engine

---
 packages/backend/src/core/chart/core.ts | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts
index aa0cb9dc2bd5..f10e30ef1048 100644
--- a/packages/backend/src/core/chart/core.ts
+++ b/packages/backend/src/core/chart/core.ts
@@ -459,13 +459,15 @@ export default abstract class Chart<T extends Schema> {
 				}
 			}
 
-			// bake unique count
+			// bake cardinality
 			for (const [k, v] of Object.entries(finalDiffs)) {
 				if (this.schema[k].uniqueIncrement) {
 					const name = COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof Columns<T>;
 					const tempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique<T>;
-					queryForHour[name] = new Set([...(v as string[]), ...(logHour[tempColumnName] as unknown as string[])]).size;
-					queryForDay[name] = new Set([...(v as string[]), ...(logDay[tempColumnName] as unknown as string[])]).size;
+					const cardinalityOfHour = new Set([...(v as string[]), ...(logHour[tempColumnName] as unknown as string[])]).size;
+					const cardinalityOfDay = new Set([...(v as string[]), ...(logDay[tempColumnName] as unknown as string[])]).size;
+					queryForHour[name] = cardinalityOfHour;
+					queryForDay[name] = cardinalityOfDay;
 				}
 			}
 
@@ -637,7 +639,7 @@ export default abstract class Chart<T extends Schema> {
 		// 要求された範囲にログがひとつもなかったら
 		if (logs.length === 0) {
 			// もっとも新しいログを持ってくる
-			// (すくなくともひとつログが無いと隙間埋めできないため)
+			// (すくなくともひとつログが無いと補間できないため)
 			const recentLog = await repository.findOne({
 				where: group ? {
 					group: group,
@@ -654,7 +656,7 @@ export default abstract class Chart<T extends Schema> {
 		// 要求された範囲の最も古い箇所に位置するログが存在しなかったら
 		} else if (!isTimeSame(new Date(logs.at(-1)!.date * 1000), gt)) {
 			// 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する
-			// (隙間埋めできないため)
+			// (補間できないため)
 			const outdatedLog = await repository.findOne({
 				where: {
 					date: LessThan(Chart.dateToTimestamp(gt)),
@@ -683,7 +685,7 @@ export default abstract class Chart<T extends Schema> {
 			if (log) {
 				chart.unshift(this.convertRawRecord(log));
 			} else {
-				// 隙間埋め
+				// 補間
 				const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current));
 				const data = latest ? this.convertRawRecord(latest) : null;
 				chart.unshift(this.getNewLog(data));

From dcfab918e9885ffd533f12d7d62e06a5072baa5c Mon Sep 17 00:00:00 2001
From: BackRunner <dev@backrunner.top>
Date: Sun, 17 Mar 2024 17:47:29 +0800
Subject: [PATCH 109/266] feat: send heartbeat right after visibility changed
 to 'visible' (#13581)

---
 packages/frontend/src/stream.ts | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts
index 0c5ee06197ba..0d5bd78b09af 100644
--- a/packages/frontend/src/stream.ts
+++ b/packages/frontend/src/stream.ts
@@ -8,7 +8,12 @@ import { markRaw } from 'vue';
 import { $i } from '@/account.js';
 import { wsOrigin } from '@/config.js';
 
+// heart beat interval in ms
+const HEART_BEAT_INTERVAL = 1000 * 60;
+
 let stream: Misskey.Stream | null = null;
+let timeoutHeartBeat: ReturnType<typeof setTimeout> | null = null;
+let lastHeartbeatCall = 0;
 
 export function useStream(): Misskey.Stream {
 	if (stream) return stream;
@@ -17,7 +22,18 @@ export function useStream(): Misskey.Stream {
 		token: $i.token,
 	} : null));
 
-	window.setTimeout(heartbeat, 1000 * 60);
+	if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat);
+	timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL);
+
+	// send heartbeat right now when last send time is over HEART_BEAT_INTERVAL
+	document.addEventListener('visibilitychange', () => {
+		if (
+			!stream
+			|| document.visibilityState !== 'visible'
+			|| Date.now() - lastHeartbeatCall < HEART_BEAT_INTERVAL
+		) return;
+		heartbeat();
+	});
 
 	return stream;
 }
@@ -26,5 +42,7 @@ function heartbeat(): void {
 	if (stream != null && document.visibilityState === 'visible') {
 		stream.heartbeat();
 	}
-	window.setTimeout(heartbeat, 1000 * 60);
+	lastHeartbeatCall = Date.now();
+	if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat);
+	timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL);
 }

From b65203c9f852a29a3a6e7ce81c6761e9ac228bf3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 17 Mar 2024 20:33:33 +0900
Subject: [PATCH 110/266] =?UTF-8?q?fix(frontend):=20WebGL2=E3=82=B3?=
 =?UTF-8?q?=E3=83=B3=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=81=AE=E5=88=9D?=
 =?UTF-8?q?=E6=9C=9F=E5=8C=96=E3=81=AB=E5=A4=B1=E6=95=97=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=81=A8Misskey=E3=81=8C=E8=B5=B7=E5=8B=95=E3=81=A7=E3=81=8D?=
 =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#13587)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fixed startup crash with seasonal effects

(cherry picked from commit eba0c2cc61512db22109e2f15604eb65f5b7d2f2)

* Update Changelog

* Update Changelog

---------

Co-authored-by: Leah <kevinlukej@gmail.com>
---
 CHANGELOG.md                                  |  2 +
 packages/frontend/src/boot/main-boot.ts       | 44 ++++++++++---------
 .../frontend/src/scripts/snowfall-effect.ts   |  4 +-
 3 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa56f1a268ad..cbd190d714f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
+- Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正  
+  (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459)
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 8016e8b0e0a0..5cb19f388aee 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -75,27 +75,31 @@ export async function mainBoot() {
 			mainRouter.push('/search');
 		},
 	};
-
-	if (defaultStore.state.enableSeasonalScreenEffect) {
-		const month = new Date().getMonth() + 1;
-		if (defaultStore.state.hemisphere === 'S') {
-			// ▼南半球
-			if (month === 7 || month === 8) {
-				const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
-				new SnowfallEffect({}).render();
-			}
-		} else {
-			// ▼北半球
-			if (month === 12 || month === 1) {
-				const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
-				new SnowfallEffect({}).render();
-			} else if (month === 3 || month === 4) {
-				const SakuraEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
-				new SakuraEffect({
-					sakura: true,
-				}).render();
+	try {
+		if (defaultStore.state.enableSeasonalScreenEffect) {
+			const month = new Date().getMonth() + 1;
+			if (defaultStore.state.hemisphere === 'S') {
+				// ▼南半球
+				if (month === 7 || month === 8) {
+					const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
+					new SnowfallEffect({}).render();
+				}
+			} else {
+				// ▼北半球
+				if (month === 12 || month === 1) {
+					const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
+					new SnowfallEffect({}).render();
+				} else if (month === 3 || month === 4) {
+					const SakuraEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
+					new SakuraEffect({
+						sakura: true,
+					}).render();
+				}
 			}
-		}
+		}	
+	} catch (error) {
+		// console.error(error);
+		console.error('Failed to initialise the seasonal screen effect canvas context:', error);
 	}
 
 	if ($i) {
diff --git a/packages/frontend/src/scripts/snowfall-effect.ts b/packages/frontend/src/scripts/snowfall-effect.ts
index 11fcaa07163d..d88bdb666099 100644
--- a/packages/frontend/src/scripts/snowfall-effect.ts
+++ b/packages/frontend/src/scripts/snowfall-effect.ts
@@ -155,7 +155,9 @@ export class SnowfallEffect {
 		max: 0.125,
 		easing: 0.0005,
 	};
-
+	/**
+	 * @throws {Error} - Thrown when it fails to get WebGL context for the canvas 
+	 */
 	constructor(options: {
 		sakura?: boolean;
 	}) {

From a38646bd0f732c3f71bf9e8174baa7d66f8eae9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 18 Mar 2024 14:20:28 +0900
Subject: [PATCH 111/266] =?UTF-8?q?fix(backend):=20=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88?=
 =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90=E3=81=99=E3=82=8B=E9=9A=9B=E3=81=AB?=
 =?UTF-8?q?=E6=97=A2=E5=AD=98=E3=81=AE=E3=82=82=E3=81=AE=E3=81=AF=E5=89=8A?=
 =?UTF-8?q?=E9=99=A4=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1358?=
 =?UTF-8?q?8)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: delete old follow request (if exists) before creating new

(cherry picked from commit ea948ccadc7eace1fcace176c9c070b2a9b46f56)

* Update Changelog

* Update Changelog

---------

Co-authored-by: Kaity A <kaity@atikayda.au>
---
 CHANGELOG.md                                      | 2 ++
 packages/backend/src/core/UserFollowingService.ts | 6 ++++++
 2 files changed, 8 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cbd190d714f2..09f7bba18bb0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,8 @@
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
+- Fix: フォローリクエストを作成する際に既存のものは削除するように  
+  (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index 0a492c06e4b1..deeecdeb1f12 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -511,6 +511,12 @@ export class UserFollowingService implements OnModuleInit {
 		if (blocking) throw new Error('blocking');
 		if (blocked) throw new Error('blocked');
 
+		// Remove old follow requests before creating a new one.
+		await this.followRequestsRepository.delete({
+			followeeId: followee.id,
+			followerId: follower.id,
+		});
+
 		const followRequest = await this.followRequestsRepository.insert({
 			id: this.idService.gen(),
 			followerId: follower.id,

From 067cdf3ce422f46535c3f70be91c3b55e03248ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 18 Mar 2024 18:21:27 +0900
Subject: [PATCH 112/266] =?UTF-8?q?enhance(frontend):=20=E3=83=9A=E3=83=BC?=
 =?UTF-8?q?=E3=82=B8=E3=81=AE=E3=83=87=E3=82=B6=E3=82=A4=E3=83=B3=E3=82=92?=
 =?UTF-8?q?=E8=AA=BF=E6=95=B4=20(#13590)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): ページのデザインを調整

* 共有ボタンを直感的な導線に変更

* Update Changelog

* Update packages/frontend/src/components/page/page.image.vue

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 .../src/components/page/page.image.vue        |  24 +-
 .../src/components/page/page.note.vue         |  13 +-
 .../src/components/page/page.text.vue         |   8 +-
 .../frontend/src/components/page/page.vue     |   2 +-
 .../page-editor/els/page-editor.el.note.vue   |   2 +-
 packages/frontend/src/pages/page.vue          | 374 ++++++++++++------
 7 files changed, 288 insertions(+), 136 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09f7bba18bb0..5d74090b3593 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 - Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
 - Enhance: 設定>プラグインのページからプラグインの簡易的なログやエラーを見られるように
   - 実装の都合により、プラグインは1つエラーを起こした時に即時停止するようになりました
+- Enhance: ページのデザインを変更	
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue
index ced02943db13..fc1ce9fc7b39 100644
--- a/packages/frontend/src/components/page/page.image.vue
+++ b/packages/frontend/src/components/page/page.image.vue
@@ -4,19 +4,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div>
-	<MediaImage
-		v-if="image"
-		:image="image"
-		:disableImageLink="true"
-	/>
+<div :class="$style.root">
+	<MkMediaList v-if="image" :mediaList="[image]" :class="$style.mediaList"/>
 </div>
 </template>
 
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import MediaImage from '@/components/MkMediaImage.vue';
+import MkMediaList from '@/components/MkMediaList.vue';
 
 const props = defineProps<{
 	block: Misskey.entities.PageBlock,
@@ -28,5 +24,17 @@ const image = ref<Misskey.entities.DriveFile | null>(null);
 onMounted(() => {
 	image.value = props.page.attachedFiles.find(x => x.id === props.block.fileId) ?? null;
 });
-
 </script>
+
+<style lang="scss" module>
+.root {
+	border: 1px solid var(--divider);
+	border-radius: var(--radius);
+	overflow: hidden;
+}
+.mediaList {
+	// MkMediaList 内の上部マージン 4px
+	margin-top: -4px;
+	height: calc(100% + 4px);
+}
+</style>
diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue
index 7b56494a6e8a..b5ba4078065c 100644
--- a/packages/frontend/src/components/page/page.note.vue
+++ b/packages/frontend/src/components/page/page.note.vue
@@ -4,9 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div style="margin: 1em 0;">
-	<MkNote v-if="note && !block.detailed" :key="note.id + ':normal'" v-model:note="note"/>
-	<MkNoteDetailed v-if="note && block.detailed" :key="note.id + ':detail'" v-model:note="note"/>
+<div :class="$style.root">
+	<MkNote v-if="note && !block.detailed" :key="note.id + ':normal'" :note="note"/>
+	<MkNoteDetailed v-if="note && block.detailed" :key="note.id + ':detail'" :note="note"/>
 </div>
 </template>
 
@@ -32,3 +32,10 @@ onMounted(() => {
 		});
 });
 </script>
+
+<style lang="scss" module>
+.root {
+	border: 1px solid var(--divider);
+	border-radius: var(--radius);
+}
+</style>
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index 81a4c4fa93a6..61247b381fa9 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div class="_gaps">
+<div class="_gaps" :class="$style.textRoot">
 	<Mfm :text="block.text ?? ''" :isNote="false"/>
 	<MkUrlPreview v-for="url in urls" :key="url" :url="url"/>
 </div>
@@ -25,3 +25,9 @@ const props = defineProps<{
 
 const urls = props.block.text ? extractUrlFromMfm(mfm.parse(props.block.text)) : [];
 </script>
+
+<style lang="scss" module>
+.textRoot {
+	font-size: 1.1rem;
+}
+</style>
diff --git a/packages/frontend/src/components/page/page.vue b/packages/frontend/src/components/page/page.vue
index 53c70b01f4ab..a31c5eff2879 100644
--- a/packages/frontend/src/components/page/page.vue
+++ b/packages/frontend/src/components/page/page.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps_s">
+<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps">
 	<XBlock v-for="child in page.content" :key="child.id" :page="page" :block="child" :h="2"/>
 </div>
 </template>
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
index 194a276f89fd..0a28386986dc 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <XContainer :draggable="true" @remove="() => $emit('remove')">
 	<template #header><i class="ti ti-note"></i> {{ i18n.ts._pages.blocks.note }}</template>
 
-	<section style="padding: 0 16px 0 16px;">
+	<section style="padding: 16px;" class="_gaps_s">
 		<MkInput v-model="id">
 			<template #label>{{ i18n.ts._pages.blocks._note.id }}</template>
 			<template #caption>{{ i18n.ts._pages.blocks._note.idDescription }}</template>
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index bece32fc11c7..ab44533b8103 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -6,48 +6,73 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
-	<MkSpacer :contentMax="700">
-		<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
-			<div v-if="page" :key="page.id" class="xcukqgmh">
-				<div class="main">
-					<!--
-				<div class="header">
-					<h1>{{ page.title }}</h1>
-				</div>
-				-->
-					<div class="banner">
-						<MkMediaImage
-							v-if="page.eyeCatchingImageId"
-							:image="page.eyeCatchingImage"
-							:cover="true"
-							:disableImageLink="true"
-							class="thumbnail"
-						/>
+	<MkSpacer :contentMax="800">
+		<Transition
+			:enterActiveClass="defaultStore.state.animation ? $style.fadeEnterActive : ''"
+			:leaveActiveClass="defaultStore.state.animation ? $style.fadeLeaveActive : ''"
+			:enterFromClass="defaultStore.state.animation ? $style.fadeEnterFrom : ''"
+			:leaveToClass="defaultStore.state.animation ? $style.fadeLeaveTo : ''"
+		>
+			<div v-if="page" :key="page.id" class="_gaps">
+				<div :class="$style.pageMain">
+					<div :class="$style.pageBanner">
+						<div :class="$style.pageBannerBgRoot">
+							<MkImgWithBlurhash
+								v-if="page.eyeCatchingImageId"
+								:class="$style.pageBannerBg"
+								:hash="page.eyeCatchingImage?.blurhash"
+								:cover="true"
+								:forceBlurhash="true"
+							/>
+							<img
+								v-else-if="instance.backgroundImageUrl || instance.bannerUrl"
+								:class="[$style.pageBannerBg, $style.pageBannerBgFallback1]"
+								:src="getStaticImageUrl(instance.backgroundImageUrl ?? instance.bannerUrl!)"
+							/>
+							<div v-else :class="[$style.pageBannerBg, $style.pageBannerBgFallback2]"></div>
+						</div>
+						<div v-if="page.eyeCatchingImageId" :class="$style.pageBannerImage">
+							<MkMediaImage
+								:image="page.eyeCatchingImage!"
+								:cover="true"
+								:disableImageLink="true"
+								:class="$style.thumbnail"
+							/>
+						</div>
+						<div :class="$style.pageBannerTitle" class="_gaps_s">
+							<h1>{{ page.title || page.name }}</h1>
+							<div v-if="page.user" :class="$style.pageBannerTitleUser">
+								<MkAvatar :user="page.user" :class="$style.avatar" indicator link preview/> <MkA :to="`/@${username}`"><MkUserName :user="page.user" :nowrap="false"/></MkA>
+							</div>
+						</div>
 					</div>
-					<div class="content">
+					<div :class="$style.pageContent">
 						<XPage :page="page"/>
 					</div>
-					<div class="actions">
-						<div class="like">
+					<div :class="$style.pageActions">
+						<div>
 							<MkButton v-if="page.isLiked" v-tooltip="i18n.ts._pages.unlike" class="button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
 							<MkButton v-else v-tooltip="i18n.ts._pages.like" class="button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
 						</div>
-						<div class="other">
-							<button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></button>
-							<button v-tooltip="i18n.ts.copyLink" v-click-anime class="_button" @click="copyLink"><i class="ti ti-link ti-fw"></i></button>
-							<button v-if="isSupportShare()" v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ti ti-share ti-fw"></i></button>
+						<div :class="$style.other">
+							<button v-tooltip="i18n.ts.copyLink" class="_button" :class="$style.generalActionButton" @click="copyLink"><i class="ti ti-link ti-fw"></i></button>
+							<button v-tooltip="i18n.ts.share" class="_button" :class="$style.generalActionButton" @click="share"><i class="ti ti-share ti-fw"></i></button>
 						</div>
 					</div>
-					<div class="user">
-						<MkAvatar :user="page.user" class="avatar" link preview/>
-						<div class="name">
-							<MkUserName :user="page.user" style="display: block;"/>
-							<MkAcct :user="page.user"/>
-						</div>
-						<MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
+					<div :class="$style.pageUser">
+						<MkAvatar :user="page.user" :class="$style.avatar" link preview/>
+						<MkA :to="`/@${username}`">
+							<MkUserName :user="page.user" :class="$style.name"/>
+							<MkAcct :user="page.user" :class="$style.acct"/>
+						</MkA>
+						<MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user!" :inline="true" :transparent="false" :full="true" :class="$style.follow"/>
 					</div>
-					<div class="links">
-						<MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA>
+					<div :class="$style.pageDate">
+						<div><i class="ti ti-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
+						<div v-if="page.createdAt != page.updatedAt"><i class="ti ti-clock-edit"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
+					</div>
+					<div :class="$style.pageLinks">
+						<MkA v-if="!$i || $i.id !== page.userId" :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA>
 						<template v-if="$i && $i.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>
@@ -55,10 +80,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</template>
 					</div>
 				</div>
-				<div class="footer">
-					<div><i class="ti ti-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
-					<div v-if="page.createdAt != page.updatedAt"><i class="ti ti-clock"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
-				</div>
 				<MkAd :prefer="['horizontal', 'horizontal-big']"/>
 				<MkContainer :max-height="300" :foldable="true" class="other">
 					<template #icon><i class="ti ti-clock"></i></template>
@@ -84,6 +105,7 @@ import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { url } from '@/config.js';
 import MkMediaImage from '@/components/MkMediaImage.vue';
+import MkImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import MkContainer from '@/components/MkContainer.vue';
 import MkPagination from '@/components/MkPagination.vue';
@@ -94,6 +116,8 @@ import { pageViewInterruptors, defaultStore } from '@/store.js';
 import { deepClone } from '@/scripts/clone.js';
 import { $i } from '@/account.js';
 import { isSupportShare } from '@/scripts/navigator.js';
+import { instance } from '@/instance.js';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
 const props = defineProps<{
@@ -133,35 +157,63 @@ function fetchPage() {
 	});
 }
 
-function share() {
-	navigator.share({
-		title: page.value.title ?? page.value.name,
-		text: page.value.summary,
-		url: `${url}/@${page.value.user.username}/pages/${page.value.name}`,
-	});
+function share(ev: MouseEvent) {
+	if (!page.value) return;
+
+	os.popupMenu([
+		{
+			text: i18n.ts.shareWithNote,
+			icon: 'ti ti-pencil',
+			action: shareWithNote,
+		},
+		...(isSupportShare() ? [{
+			text: i18n.ts.share,
+			icon: 'ti ti-share',
+			action: shareWithNavigator,
+		}] : []),
+	], ev.currentTarget ?? ev.target);
 }
 
 function copyLink() {
+	if (!page.value) return;
+
 	copyToClipboard(`${url}/@${page.value.user.username}/pages/${page.value.name}`);
 	os.success();
 }
 
 function shareWithNote() {
+	if (!page.value) return;
+
 	os.post({
-		initialText: `${page.value.title || page.value.name} ${url}/@${page.value.user.username}/pages/${page.value.name}`,
+		initialText: `${page.value.title || page.value.name}\n${url}/@${page.value.user.username}/pages/${page.value.name}`,
+		instant: true,
+	});
+}
+
+function shareWithNavigator() {
+	if (!page.value) return;
+
+	navigator.share({
+		title: page.value.title ?? page.value.name,
+		text: page.value.summary ?? undefined,
+		url: `${url}/@${page.value.user.username}/pages/${page.value.name}`,
 	});
 }
 
 function like() {
+	if (!page.value) return;
+
 	os.apiWithDialog('pages/like', {
 		pageId: page.value.id,
 	}).then(() => {
-		page.value.isLiked = true;
-		page.value.likedCount++;
+		page.value!.isLiked = true;
+		page.value!.likedCount++;
 	});
 }
 
 async function unlike() {
+	if (!page.value) return;
+
 	const confirm = await os.confirm({
 		type: 'warning',
 		text: i18n.ts.unlikeConfirm,
@@ -170,12 +222,14 @@ async function unlike() {
 	os.apiWithDialog('pages/unlike', {
 		pageId: page.value.id,
 	}).then(() => {
-		page.value.isLiked = false;
-		page.value.likedCount--;
+		page.value!.isLiked = false;
+		page.value!.likedCount--;
 	});
 }
 
 function pin(pin) {
+	if (!page.value) return;
+
 	os.apiWithDialog('i/update', {
 		pinnedPageId: pin ? page.value.id : null,
 	});
@@ -200,109 +254,185 @@ definePageMetadata(() => ({
 }));
 </script>
 
-<style lang="scss" scoped>
-.fade-enter-active,
-.fade-leave-active {
+<style lang="scss" module>
+.fadeEnterActive,
+.fadeLeaveActive {
 	transition: opacity 0.125s ease;
 }
-.fade-enter-from,
-.fade-leave-to {
+.fadeEnterFrom,
+.fadeLeaveTo {
 	opacity: 0;
 }
 
-.xcukqgmh {
-	> .main {
-		padding: 32px;
+.generalActionButton {
+	height: 2.5rem;
+	width: 2.5rem;
+	text-align: center;
+	border-radius: 99rem;
 
-		> .header {
-			padding: 16px;
+	& :global(.ti) {
+		line-height: 2.5rem;
+	}
 
-			> h1 {
-				margin: 0;
-			}
-		}
+	&:hover,
+	&:focus-visible {
+		background-color: var(--accentedBg);
+		color: var(--accent);
+		text-decoration: none;
+	}
+}
 
-		> .banner {
-			> .thumbnail {
-				// TODO: 良い感じのアスペクト比で表示
-				display: block;
-				width: 100%;
-				height: auto;
-				aspect-ratio: 3/1;
-				border-radius: var(--radius);
-				overflow: hidden;
-				object-fit: cover;
-			}
+.pageMain {
+	border-radius: var(--radius);
+	padding: 2rem;
+	background: var(--panel);
+	box-sizing: border-box;
+}
+
+.pageBanner {
+	width: calc(100% + 4rem);
+	margin: -2rem -2rem 1.5rem;
+	border-radius: var(--radius) var(--radius) 0 0;
+	overflow: hidden;
+	position: relative;
+
+	> .pageBannerBgRoot {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		overflow: hidden;
+
+		.pageBannerBg {
+			width: 100%;
+			height: 100%;
+			object-fit: cover;
+			opacity: .2;
+			filter: brightness(1.2);
 		}
 
-		> .content {
-			margin-top: 16px;
-			padding: 16px 0 0 0;
+		.pageBannerBgFallback1 {
+			filter: blur(20px);
 		}
 
-		> .actions {
-			display: flex;
-			align-items: center;
-			margin-top: 16px;
-			padding: 16px 0 0 0;
-			border-top: solid 0.5px var(--divider);
+		.pageBannerBgFallback2 {
+			background-color: var(--accentedBg);
+		}
 
-			> .other {
-				margin-left: auto;
+		&::after {
+			content: '';
+			position: absolute;
+			left: 0;
+			bottom: 0;
+			width: 100%;
+			height: 100px;
+			background: linear-gradient(0deg, var(--panel), transparent);
+		}
+	}
 
-				> button {
-					padding: 8px;
-					margin: 0 8px;
+	> .pageBannerImage {
+		position: relative;
+		padding-top: 56.25%;
 
-					&:hover {
-						color: var(--fgHighlighted);
-					}
-				}
-			}
+		> .thumbnail {
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			height: 100%;
 		}
+	}
 
-		> .user {
-			margin-top: 16px;
-			padding: 16px 0 0 0;
-			border-top: solid 0.5px var(--divider);
-			display: flex;
-			align-items: center;
+	> .pageBannerTitle {
+		position: relative;
+		padding: 1.5rem 2rem;
 
-			> .avatar {
-				width: 52px;
-				height: 52px;
-			}
+		h1 {
+			font-size: 2rem;
+			font-weight: 700;
+			color: var(--fg);
+			margin: 0;
+		}
 
-			> .name {
-				margin: 0 0 0 12px;
-				font-size: 90%;
-			}
+		.pageBannerTitleUser {
+			--height: 32px;
 
-			> .koudoku {
-				margin-left: auto;
+			.avatar {
+				height: var(--height);
+				width: var(--height);
 			}
+
+			line-height: var(--height);
 		}
+	}
+}
+
+.pageContent {
+	margin-bottom: 1.5rem;
+}
 
-		> .links {
-			margin-top: 16px;
-			padding: 24px 0 0 0;
-			border-top: solid 0.5px var(--divider);
+.pageActions {
+	display: flex;
+	align-items: center;
 
-			> .link {
-				margin-right: 0.75em;
-			}
-		}
+	border-top: 1px solid var(--divider);
+	padding-top: 1.5rem;
+	margin-bottom: 1.5rem;
+
+	> .other {
+		margin-left: auto;
+		display: flex;
+		gap: var(--marginHalf);
+	}
+}
+
+.pageUser {
+	display: flex;
+	align-items: center;
+
+	border-top: 1px solid var(--divider);
+	padding-top: 1.5rem;
+	margin-bottom: 1.5rem;
+
+	.avatar,
+	.name,
+	.acct {
+		display: block;
+	}
+
+	.avatar {
+		width: 4rem;
+		height: 4rem;
+		margin-right: 1rem;
+	}
+
+	.name {
+		font-size: 110%;
+		font-weight: 700;
+	}
+
+	.acct {
+		font-size: 90%;
+		opacity: 0.7;
 	}
 
-	> .footer {
-		margin: var(--margin) 0 var(--margin) 0;
-		font-size: 85%;
-		opacity: 0.75;
+	.follow {
+		margin-left: auto;
 	}
 }
-</style>
 
-<style module>
+.pageDate {
+	margin-bottom: 1.5rem;
+}
+
+.pageLinks {
+	display: flex;
+	align-items: center;
+	flex-wrap: wrap;
+	gap: var(--marginHalf);
+}
+
 .relatedPagesRoot {
 	padding: var(--margin);
 }

From 0226a670ddb0a38dfd8b8f479885ee5e83cf970f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 18 Mar 2024 18:34:31 +0900
Subject: [PATCH 113/266] =?UTF-8?q?fix(backend):=20=E3=83=A6=E3=83=BC?=
 =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=82=84=E3=83=8E=E3=83=BC=E3=83=88=E3=81=AE?=
 =?UTF-8?q?OGP=E3=81=A7=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB=E3=81=A8?=
 =?UTF-8?q?=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88=E3=83=A6=E3=83=BC=E3=82=B6?=
 =?UTF-8?q?=E3=83=BC=E3=81=AE=E8=A6=8B=E5=88=86=E3=81=91=E3=81=8C=E4=BB=98?=
 =?UTF-8?q?=E3=81=8B=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#13586)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(OGP): ユーザーやノートのOGPでローカルとリモートユーザーの見分けが付かない問題を修正 (MisskeyIO#528)

(cherry picked from commit 0c3de462d99c47297bebc162581bac6f78f21b49)

* Update Changelog

---------

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 CHANGELOG.md                                   | 1 +
 packages/backend/src/server/web/views/note.pug | 4 ++--
 packages/backend/src/server/web/views/user.pug | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d74090b3593..91539a6a9e58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
 - Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459)
+- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug
index 9bc652b6a108..fb659ce17162 100644
--- a/packages/backend/src/server/web/views/note.pug
+++ b/packages/backend/src/server/web/views/note.pug
@@ -2,7 +2,7 @@ extends ./base
 
 block vars
 	- const user = note.user;
-	- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
+	- const title = user.name ? `${user.name} (@${user.username}${user.host ? `@${user.host}` : ''})` : `@${user.username}${user.host ? `@${user.host}` : ''}`;
 	- const url = `${config.url}/notes/${note.id}`;
 	- const isRenote = note.renote && note.text == null && note.fileIds.length == 0 && note.poll == null;
 	- const images = (note.files || []).filter(file => file.type.startsWith('image/') && !file.isSensitive)
@@ -28,7 +28,7 @@ block og
 			// FIXME: add embed player for Twitter
 	if images.length
 		meta(property='twitter:card' content='summary_large_image')
-		each image in images	
+		each image in images
 			meta(property='og:image'     content= image.url)
 	else
 		meta(property='twitter:card' content='summary')
diff --git a/packages/backend/src/server/web/views/user.pug b/packages/backend/src/server/web/views/user.pug
index 83d57349a64d..2b0a7bab5c18 100644
--- a/packages/backend/src/server/web/views/user.pug
+++ b/packages/backend/src/server/web/views/user.pug
@@ -1,7 +1,7 @@
 extends ./base
 
 block vars
-	- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
+	- const title = user.name ? `${user.name} (@${user.username}${user.host ? `@${user.host}` : ''})` : `@${user.username}${user.host ? `@${user.host}` : ''}`;
 	- const url = `${config.url}/@${(user.host ? `${user.username}@${user.host}` : user.username)}`;
 
 block title

From 5f6863b77e9e955b2e82b9f44a63df4c1eb6e4c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 18 Mar 2024 19:04:20 +0900
Subject: [PATCH 114/266] Add missing credit (for #13586)

---
 CHANGELOG.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91539a6a9e58..d948127d06e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,7 +16,8 @@
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
 - Fix: WebGL2をサポートしないブラウザで「季節に応じた画面の演出」が有効になっているとき、Misskeyが起動できなくなる問題を修正  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459)
-- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正
+- Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正  
+  (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/528)
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに

From 115d91812e4bc25a56126f23b4ad13b07d451552 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Wed, 20 Mar 2024 10:30:45 +0900
Subject: [PATCH 115/266] =?UTF-8?q?fix(frontend):=20shiki=E3=81=AE?=
 =?UTF-8?q?=E8=A8=80=E8=AA=9E=E3=83=BB=E3=83=86=E3=83=BC=E3=83=9E=E3=81=AE?=
 =?UTF-8?q?=E5=AE=9A=E7=BE=A9=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92?=
 =?UTF-8?q?CDN(esm.sh)=E3=81=8B=E3=82=89=E5=8F=96=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#13598)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): shikiの言語・テーマの定義ファイルをCDN(esm.sh)から取るようにする

* fix CHANGELOG.md
---
 CHANGELOG.md                                  |  2 ++
 packages/frontend/package.json                |  2 +-
 .../frontend/src/components/MkCode.core.vue   | 10 +++----
 packages/frontend/src/index.html              |  2 +-
 .../frontend/src/scripts/code-highlighter.ts  | 18 ++++++------
 packages/frontend/src/scripts/theme.ts        |  4 +--
 packages/frontend/src/store.ts                |  1 -
 packages/frontend/vite.config.ts              | 29 +++++++++++++++++++
 pnpm-lock.yaml                                | 20 ++++++++-----
 9 files changed, 61 insertions(+), 27 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d948127d06e8..18dd07f1c323 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,8 @@
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/459)
 - Fix: ページタイトルでローカルユーザーとリモートユーザーの区別がつかない問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/528)
+- Fix: コードブロックのシンタックスハイライトで使用される定義ファイルをCDNから取得するように #13177
+  - CDNから取得せずMisskey本体にバンドルする場合は`pacakges/frontend/vite.config.ts`を修正してください。
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 682def8e8d7e..db7f7b76f649 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -60,7 +60,7 @@
 		"rollup": "4.12.0",
 		"sanitize-html": "2.12.1",
 		"sass": "1.71.1",
-		"shiki": "1.1.7",
+		"shiki": "1.2.0",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
 		"three": "0.162.0",
diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue
index 872517b6aaab..c0e7df5dac27 100644
--- a/packages/frontend/src/components/MkCode.core.vue
+++ b/packages/frontend/src/components/MkCode.core.vue
@@ -9,9 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref, computed, watch } from 'vue';
-import { bundledLanguagesInfo } from 'shiki';
-import type { BuiltinLanguage } from 'shiki';
+import { computed, ref, watch } from 'vue';
+import { bundledLanguagesInfo } from 'shiki/langs';
+import type { BundledLanguage } from 'shiki/langs';
 import { getHighlighter, getTheme } from '@/scripts/code-highlighter.js';
 import { defaultStore } from '@/store.js';
 
@@ -23,7 +23,7 @@ const props = defineProps<{
 
 const highlighter = await getHighlighter();
 const darkMode = defaultStore.reactiveState.darkMode;
-const codeLang = ref<BuiltinLanguage | 'aiscript'>('js');
+const codeLang = ref<BundledLanguage | 'aiscript'>('js');
 
 const [lightThemeName, darkThemeName] = await Promise.all([
 	getTheme('light', true),
@@ -42,7 +42,7 @@ const html = computed(() => highlighter.codeToHtml(props.code, {
 }));
 
 async function fetchLanguage(to: string): Promise<void> {
-	const language = to as BuiltinLanguage;
+	const language = to as BundledLanguage;
 
 	// Check for the loaded languages, and load the language if it's not loaded yet.
 	if (!highlighter.getLoadedLanguages().includes(language)) {
diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html
index cd84145f40f6..08ff0c58dd3a 100644
--- a/packages/frontend/src/index.html
+++ b/packages/frontend/src/index.html
@@ -18,7 +18,7 @@
 		http-equiv="Content-Security-Policy"
 		content="default-src 'self' https://newassets.hcaptcha.com/ https://challenges.cloudflare.com/ http://localhost:7493/;
 			worker-src 'self';
-			script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com;
+			script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com https://esm.sh;
 			style-src 'self' 'unsafe-inline';
 			img-src 'self' data: blob: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
 			media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
diff --git a/packages/frontend/src/scripts/code-highlighter.ts b/packages/frontend/src/scripts/code-highlighter.ts
index 5dd0a3be7895..e94027d3022a 100644
--- a/packages/frontend/src/scripts/code-highlighter.ts
+++ b/packages/frontend/src/scripts/code-highlighter.ts
@@ -3,18 +3,19 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { bundledThemesInfo } from 'shiki';
 import { getHighlighterCore, loadWasm } from 'shiki/core';
 import darkPlus from 'shiki/themes/dark-plus.mjs';
+import { bundledThemesInfo } from 'shiki/themes';
+import { bundledLanguagesInfo } from 'shiki/langs';
 import { unique } from './array.js';
 import { deepClone } from './clone.js';
 import { deepMerge } from './merge.js';
-import type { Highlighter, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki';
+import type { HighlighterCore, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki/core';
 import { ColdDeviceStorage } from '@/store.js';
 import lightTheme from '@/themes/_light.json5';
 import darkTheme from '@/themes/_dark.json5';
 
-let _highlighter: Highlighter | null = null;
+let _highlighter: HighlighterCore | null = null;
 
 export async function getTheme(mode: 'light' | 'dark', getName: true): Promise<string>;
 export async function getTheme(mode: 'light' | 'dark', getName?: false): Promise<ThemeRegistration | ThemeRegistrationRaw>;
@@ -51,16 +52,14 @@ export async function getTheme(mode: 'light' | 'dark', getName = false): Promise
 	return darkPlus;
 }
 
-export async function getHighlighter(): Promise<Highlighter> {
+export async function getHighlighter(): Promise<HighlighterCore> {
 	if (!_highlighter) {
 		return await initHighlighter();
 	}
 	return _highlighter;
 }
 
-export async function initHighlighter() {
-	const aiScriptGrammar = await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json');
-
+async function initHighlighter() {
 	await loadWasm(import('shiki/onig.wasm?init'));
 
 	// テーマの重複を消す
@@ -69,11 +68,12 @@ export async function initHighlighter() {
 		...(await Promise.all([getTheme('light'), getTheme('dark')])),
 	]);
 
+	const jsLangInfo = bundledLanguagesInfo.find(t => t.id === 'javascript');
 	const highlighter = await getHighlighterCore({
 		themes,
 		langs: [
-			import('shiki/langs/javascript.mjs'),
-			aiScriptGrammar.default as unknown as LanguageRegistration,
+			...(jsLangInfo ? [async () => await jsLangInfo.import()] : []),
+			async () => (await import('aiscript-vscode/aiscript/syntaxes/aiscript.tmLanguage.json')).default as unknown as LanguageRegistration,
 		],
 	});
 
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index 5f7e88bd9fef..c7f8b3d59663 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -6,7 +6,7 @@
 import { ref } from 'vue';
 import tinycolor from 'tinycolor2';
 import { deepClone } from './clone.js';
-import type { BuiltinTheme } from 'shiki';
+import type { BundledTheme } from 'shiki/themes';
 import { globalEvents } from '@/events.js';
 import lightTheme from '@/themes/_light.json5';
 import darkTheme from '@/themes/_dark.json5';
@@ -20,7 +20,7 @@ export type Theme = {
 	base?: 'dark' | 'light';
 	props: Record<string, string>;
 	codeHighlighter?: {
-		base: BuiltinTheme;
+		base: BundledTheme;
 		overrides?: Record<string, any>;
 	} | {
 		base: '_none_';
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index a335ed33bb9c..7742acc60d58 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -7,7 +7,6 @@ import { markRaw, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { miLocalStorage } from './local-storage.js';
 import type { SoundType } from '@/scripts/sound.js';
-import type { BuiltinTheme as ShikiBuiltinTheme } from 'shiki';
 import { Storage } from '@/pizzax.js';
 import { hemisphere } from '@/scripts/intl-const.js';
 
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index 35d112f6ecf2..82eb2af46410 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -5,11 +5,30 @@ import { type UserConfig, defineConfig } from 'vite';
 
 import locales from '../../locales/index.js';
 import meta from '../../package.json';
+import packageInfo from './package.json' assert { type: 'json' };
 import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
 import pluginJson5 from './vite.json5.js';
 
 const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue'];
 
+/**
+ * Misskeyのフロントエンドにバンドルせず、CDNなどから別途読み込むリソースを記述する。
+ * CDNを使わずにバンドルしたい場合、以下の配列から該当要素を削除orコメントアウトすればOK
+ */
+const externalPackages = [
+	// shiki(コードブロックのシンタックスハイライトで使用中)はテーマ・言語の定義の容量が大きいため、それらはCDNから読み込む
+	{
+		name: 'shiki',
+		match: /^shiki\/(?<subPkg>(langs|themes))$/,
+		path(id: string, pattern: RegExp): string {
+			const match = pattern.exec(id)?.groups;
+			return match
+				? `https://esm.sh/shiki@${packageInfo.dependencies.shiki}/${match['subPkg']}`
+				: id;
+		},
+	},
+];
+
 const hash = (str: string, seed = 0): number => {
 	let h1 = 0xdeadbeef ^ seed,
 		h2 = 0x41c6ce57 ^ seed;
@@ -112,6 +131,7 @@ export function getConfig(): UserConfig {
 				input: {
 					app: './src/_boot_.ts',
 				},
+				external: externalPackages.map(p => p.match),
 				output: {
 					manualChunks: {
 						vue: ['vue'],
@@ -119,6 +139,15 @@ export function getConfig(): UserConfig {
 					},
 					chunkFileNames: process.env.NODE_ENV === 'production' ? '[hash:8].js' : '[name]-[hash:8].js',
 					assetFileNames: process.env.NODE_ENV === 'production' ? '[hash:8][extname]' : '[name]-[hash:8][extname]',
+					paths(id) {
+						for (const p of externalPackages) {
+							if (p.match.test(id)) {
+								return p.path(id, p.match);
+							}
+						}
+
+						return id;
+					},
 				},
 			},
 			cssCodeSplit: true,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5e29c1162be4..2906eef4d8ef 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -806,8 +806,8 @@ importers:
         specifier: 1.71.1
         version: 1.71.1
       shiki:
-        specifier: 1.1.7
-        version: 1.1.7
+        specifier: 1.2.0
+        version: 1.2.0
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -5324,8 +5324,8 @@ packages:
       string-argv: 0.3.1
     dev: true
 
-  /@shikijs/core@1.1.7:
-    resolution: {integrity: sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==}
+  /@shikijs/core@1.2.0:
+    resolution: {integrity: sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==}
     dev: false
 
   /@sideway/address@4.1.4:
@@ -6646,7 +6646,7 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.21(typescript@5.3.3)
-      vue-component-type-helpers: 1.8.27
+      vue-component-type-helpers: 2.0.6
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -17658,10 +17658,10 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
-  /shiki@1.1.7:
-    resolution: {integrity: sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==}
+  /shiki@1.2.0:
+    resolution: {integrity: sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==}
     dependencies:
-      '@shikijs/core': 1.1.7
+      '@shikijs/core': 1.2.0
     dev: false
 
   /side-channel@1.0.4:
@@ -19445,6 +19445,10 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
+  /vue-component-type-helpers@2.0.6:
+    resolution: {integrity: sha512-qdGXCtoBrwqk1BT6r2+1Wcvl583ZVkuSZ3or7Y1O2w5AvWtlvvxwjGhmz5DdPJS9xqRdDlgTJ/38ehWnEi0tFA==}
+    dev: true
+
   /vue-demi@0.14.7(vue@3.4.21):
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
     engines: {node: '>=12'}

From d7bb6c88d3e4878486fb1f4d1655379896a5d976 Mon Sep 17 00:00:00 2001
From: Gianni Ceccarelli <dakkar@thenautilus.net>
Date: Wed, 20 Mar 2024 02:25:49 +0000
Subject: [PATCH 116/266] Cypress typescript (#13591)

* convert Cypress tests to TypeScript

this work was done by @lunaisnotaboy https://github.com/lunaisnotaboy
for their fork https://github.com/cutiekey/cutiekey/pull/7

I just repacked their changes into a minimal set

* fix call to `window` in cypress tests

this error was spotted thanks to the TypeScript compiler:

```
support/commands.ts:33:12 - error TS2559: Type '(win: any) => void'
has no properties in common with type 'Partial<Loggable &
Timeoutable>'.

33  cy.window(win => {
              ~~~~~~~~

Found 1 error in support/commands.ts:33
```

(again, @lunaisnotaboy did the actual work)
---
 cypress/e2e/{basic.cy.js => basic.cy.ts}     |  0
 cypress/e2e/{router.cy.js => router.cy.ts}   |  0
 cypress/e2e/{widgets.cy.js => widgets.cy.ts} |  0
 cypress/support/{commands.js => commands.ts} |  2 +-
 cypress/support/{e2e.js => e2e.ts}           |  0
 cypress/support/index.ts                     | 19 +++++++++++++++++++
 cypress/tsconfig.json                        |  8 ++++++++
 package.json                                 |  1 +
 pnpm-lock.yaml                               |  9 +++++++++
 9 files changed, 38 insertions(+), 1 deletion(-)
 rename cypress/e2e/{basic.cy.js => basic.cy.ts} (100%)
 rename cypress/e2e/{router.cy.js => router.cy.ts} (100%)
 rename cypress/e2e/{widgets.cy.js => widgets.cy.ts} (100%)
 rename cypress/support/{commands.js => commands.ts} (98%)
 rename cypress/support/{e2e.js => e2e.ts} (100%)
 create mode 100644 cypress/support/index.ts
 create mode 100644 cypress/tsconfig.json

diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.ts
similarity index 100%
rename from cypress/e2e/basic.cy.js
rename to cypress/e2e/basic.cy.ts
diff --git a/cypress/e2e/router.cy.js b/cypress/e2e/router.cy.ts
similarity index 100%
rename from cypress/e2e/router.cy.js
rename to cypress/e2e/router.cy.ts
diff --git a/cypress/e2e/widgets.cy.js b/cypress/e2e/widgets.cy.ts
similarity index 100%
rename from cypress/e2e/widgets.cy.js
rename to cypress/e2e/widgets.cy.ts
diff --git a/cypress/support/commands.js b/cypress/support/commands.ts
similarity index 98%
rename from cypress/support/commands.js
rename to cypress/support/commands.ts
index 91a4d7abe645..c2d92e1663e8 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.ts
@@ -30,7 +30,7 @@ Cypress.Commands.add('visitHome', () => {
 })
 
 Cypress.Commands.add('resetState', () => {
-	cy.window(win => {
+	cy.window().then(win => {
 		win.indexedDB.deleteDatabase('keyval-store');
 	});
 	cy.request('POST', '/api/reset-db', {}).as('reset');
diff --git a/cypress/support/e2e.js b/cypress/support/e2e.ts
similarity index 100%
rename from cypress/support/e2e.js
rename to cypress/support/e2e.ts
diff --git a/cypress/support/index.ts b/cypress/support/index.ts
new file mode 100644
index 000000000000..c1bed2197994
--- /dev/null
+++ b/cypress/support/index.ts
@@ -0,0 +1,19 @@
+declare global {
+	namespace Cypress {
+		interface Chainable {
+			login(username: string, password: string): Chainable<void>;
+
+			registerUser(
+				username: string,
+				password: string,
+				isAdmin?: boolean
+			): Chainable<void>;
+
+			resetState(): Chainable<void>;
+
+			visitHome(): Chainable<void>;
+		}
+	}
+}
+
+export {}
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
new file mode 100644
index 000000000000..6fe7f32cc44a
--- /dev/null
+++ b/cypress/tsconfig.json
@@ -0,0 +1,8 @@
+{
+	"compilerOptions": {
+		"lib": ["dom", "es5"],
+		"target": "es5",
+		"types": ["cypress", "node"]
+	},
+	"include": ["./**/*.ts"]
+}
diff --git a/package.json b/package.json
index 41b865456dbb..8f5ab0b12425 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
 		"typescript": "5.3.3"
 	},
 	"devDependencies": {
+		"@types/node": "^20.11.28",
 		"@typescript-eslint/eslint-plugin": "7.1.0",
 		"@typescript-eslint/parser": "7.1.0",
 		"cross-env": "7.0.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2906eef4d8ef..4c1e228a9514 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -44,6 +44,9 @@ importers:
         specifier: 4.4.0
         version: 4.4.0
     devDependencies:
+      '@types/node':
+        specifier: ^20.11.28
+        version: 20.11.28
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
         version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
@@ -7650,6 +7653,12 @@ packages:
     dependencies:
       undici-types: 5.26.5
 
+  /@types/node@20.11.28:
+    resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==}
+    dependencies:
+      undici-types: 5.26.5
+    dev: true
+
   /@types/node@20.11.5:
     resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==}
     dependencies:

From ca2df14a8f4e2d0d7eef699b44a2dd9580842a2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 20 Mar 2024 13:10:09 +0900
Subject: [PATCH 117/266] =?UTF-8?q?fix(frontend):=20woodenPanel=E3=81=AE?=
 =?UTF-8?q?=E9=85=8D=E8=89=B2=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13561)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): woodenPanelの配色を修正

* fix
---
 packages/frontend/src/boot/common.ts |  3 +++
 packages/frontend/src/style.scss     | 11 ++++++-----
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 681beaf00f96..d86ae18ffeac 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -145,8 +145,11 @@ export async function common(createVue: () => App<Element>) {
 	// NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため)
 	watch(defaultStore.reactiveState.darkMode, (darkMode) => {
 		applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme'));
+		document.documentElement.dataset.colorMode = darkMode ? 'dark' : 'light';
 	}, { immediate: miLocalStorage.getItem('theme') == null });
 
+	document.documentElement.dataset.colorMode = defaultStore.state.darkMode ? 'dark' : 'light';
+
 	const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme'));
 	const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme'));
 
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 187d9027339f..250a2616a75f 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -431,12 +431,13 @@ rt {
 	border-radius: 10px;
 
 	--bg: #F1E8DC;
-	--panel: #fff;
 	--fg: #693410;
-	--switchOffBg: rgba(0, 0, 0, 0.1);
-	--switchOffFg: rgb(255, 255, 255);
-	--switchOnBg: var(--accent);
-	--switchOnFg: rgb(255, 255, 255);
+}
+
+html[data-color-mode=dark] ._woodenFrame {
+	--bg: #1d0c02;
+	--fg: #F1E8DC;
+	--panel: #192320;
 }
 
 ._woodenFrameH {

From 7795045b23a95032a15a980ad28cc27ce5423bbe Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 20 Mar 2024 20:01:56 +0900
Subject: [PATCH 118/266] Update about-misskey.vue

---
 packages/frontend/src/pages/about-misskey.vue | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 9aaa2d8fbad3..92accd0117c5 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -222,6 +222,15 @@ const patronsWithIcon = [{
 }, {
 	name: '有栖かずみ',
 	icon: 'https://assets.misskey-hub.net/patrons/9240e8e0ba294a8884143e99ac7ed6a0.jpg',
+}, {
+	name: 'イカロ(コアラ)',
+	icon: 'https://assets.misskey-hub.net/patrons/50b9bdc03735412c80807dbdf32cecb6.jpg',
+}, {
+	name: 'ハチノス3号',
+	icon: 'https://assets.misskey-hub.net/patrons/030347a6f8ce4e82bc5184b5aad09a18.jpg',
+}, {
+	name: 'Takeno',
+	icon: 'https://assets.misskey-hub.net/patrons/6fba81536aea48fe94a30909c502dfa1.jpg',
 }];
 
 const patrons = [
@@ -325,6 +334,7 @@ const patrons = [
 	'たっくん',
 	'SHO SEKIGUCHI',
 	'塩キャベツ',
+	'はとぽぷさん',
 ];
 
 const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));

From f4838e50b4043f917020dd1cfa7b75da087ff8f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 21 Mar 2024 07:51:01 +0900
Subject: [PATCH 119/266] =?UTF-8?q?enhance(antenna):=20Bot=E3=81=AE?=
 =?UTF-8?q?=E6=8A=95=E7=A8=BF=E3=82=92=E9=99=A4=E5=A4=96=E3=81=A7=E3=81=8D?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13603)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(antenna): Botの投稿を除外できるように (MisskeyIO#545)

(cherry picked from commit a95ce067c6cf0a93647e358aabc984bdbe99e952)

* Update Changelog

* remove translations

* spdx

---------

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 CHANGELOG.md                                     |  2 ++
 locales/index.d.ts                               |  4 ++++
 locales/ja-JP.yml                                |  1 +
 .../1710919614510-antenna-exclude-bots.js        | 16 ++++++++++++++++
 packages/backend/src/core/AntennaService.ts      |  6 ++++--
 .../src/core/entities/AntennaEntityService.ts    |  1 +
 packages/backend/src/models/Antenna.ts           |  5 +++++
 .../backend/src/models/json-schema/antenna.ts    |  5 +++++
 .../processors/ExportAntennasProcessorService.ts |  1 +
 .../processors/ImportAntennasProcessorService.ts |  2 ++
 .../src/server/api/endpoints/antennas/create.ts  |  2 ++
 .../src/server/api/endpoints/antennas/update.ts  |  2 ++
 packages/backend/test/e2e/antennas.ts            |  2 ++
 .../frontend/src/pages/my-antennas/create.vue    |  1 +
 .../frontend/src/pages/my-antennas/editor.vue    |  3 +++
 packages/misskey-js/src/autogen/types.ts         |  4 ++++
 16 files changed, 55 insertions(+), 2 deletions(-)
 create mode 100644 packages/backend/migration/1710919614510-antenna-exclude-bots.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18dd07f1c323..0dce1a049681 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
 ## Unreleased
 
 ### General
+- Enhance: アンテナでBotによるノートを除外できるように  
+  (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 
 ### Client
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 7f4ec7ecb091..afb4adac6cb5 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1616,6 +1616,10 @@ export interface Locale extends ILocale {
      * 除外キーワード
      */
     "antennaExcludeKeywords": string;
+    /**
+     * Botアカウントを除外
+     */
+    "antennaExcludeBots": string;
     /**
      * スペースで区切るとAND指定になり、改行で区切るとOR指定になります
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 8b44ac2121cb..a64c83b10fa7 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -400,6 +400,7 @@ name: "名前"
 antennaSource: "受信ソース"
 antennaKeywords: "受信キーワード"
 antennaExcludeKeywords: "除外キーワード"
+antennaExcludeBots: "Botアカウントを除外"
 antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
 notifyAntenna: "新しいノートを通知する"
 withFileAntenna: "ファイルが添付されたノートのみ"
diff --git a/packages/backend/migration/1710919614510-antenna-exclude-bots.js b/packages/backend/migration/1710919614510-antenna-exclude-bots.js
new file mode 100644
index 000000000000..fac84317ccac
--- /dev/null
+++ b/packages/backend/migration/1710919614510-antenna-exclude-bots.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class AntennaExcludeBots1710919614510 {
+    name = 'AntennaExcludeBots1710919614510'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "antenna" ADD "excludeBots" boolean NOT NULL DEFAULT false`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "excludeBots"`);
+    }
+}
diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts
index 4f956a43ed77..793d8974b34b 100644
--- a/packages/backend/src/core/AntennaService.ts
+++ b/packages/backend/src/core/AntennaService.ts
@@ -92,7 +92,7 @@ export class AntennaService implements OnApplicationShutdown {
 	}
 
 	@bindThis
-	public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<void> {
+	public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; isBot: boolean; }): Promise<void> {
 		const antennas = await this.getAntennas();
 		const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const)));
 		const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna);
@@ -110,10 +110,12 @@ export class AntennaService implements OnApplicationShutdown {
 	// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
 
 	@bindThis
-	public async checkHitAntenna(antenna: MiAntenna, note: (MiNote | Packed<'Note'>), noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<boolean> {
+	public async checkHitAntenna(antenna: MiAntenna, note: (MiNote | Packed<'Note'>), noteUser: { id: MiUser['id']; username: string; host: string | null; isBot: boolean; }): Promise<boolean> {
 		if (note.visibility === 'specified') return false;
 		if (note.visibility === 'followers') return false;
 
+		if (antenna.excludeBots && noteUser.isBot) return false;
+
 		if (antenna.localOnly && noteUser.host != null) return false;
 
 		if (!antenna.withReplies && note.replyId != null) return false;
diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts
index 64d6a3c97841..3ec8efa6bfd0 100644
--- a/packages/backend/src/core/entities/AntennaEntityService.ts
+++ b/packages/backend/src/core/entities/AntennaEntityService.ts
@@ -39,6 +39,7 @@ export class AntennaEntityService {
 			caseSensitive: antenna.caseSensitive,
 			localOnly: antenna.localOnly,
 			notify: antenna.notify,
+			excludeBots: antenna.excludeBots,
 			withReplies: antenna.withReplies,
 			withFile: antenna.withFile,
 			isActive: antenna.isActive,
diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts
index 332a8997683f..f5e819059e03 100644
--- a/packages/backend/src/models/Antenna.ts
+++ b/packages/backend/src/models/Antenna.ts
@@ -72,6 +72,11 @@ export class MiAntenna {
 	})
 	public caseSensitive: boolean;
 
+	@Column('boolean', {
+		default: false,
+	})
+	public excludeBots: boolean;
+
 	@Column('boolean', {
 		default: false,
 	})
diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts
index 74622b619398..78cf6d3ba27f 100644
--- a/packages/backend/src/models/json-schema/antenna.ts
+++ b/packages/backend/src/models/json-schema/antenna.ts
@@ -76,6 +76,11 @@ export const packedAntennaSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		excludeBots: {
+			type: 'boolean',
+			optional: false, nullable: false,
+			default: false,
+		},
 		withReplies: {
 			type: 'boolean',
 			optional: false, nullable: false,
diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
index af48bad4177e..1d8e90f367f0 100644
--- a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
@@ -81,6 +81,7 @@ export class ExportAntennasProcessorService {
 					}) : null,
 					caseSensitive: antenna.caseSensitive,
 					localOnly: antenna.localOnly,
+					excludeBots: antenna.excludeBots,
 					withReplies: antenna.withReplies,
 					withFile: antenna.withFile,
 					notify: antenna.notify,
diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
index 951b56059787..ff1c04de06c5 100644
--- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
@@ -44,6 +44,7 @@ const validate = new Ajv().compile({
 		} },
 		caseSensitive: { type: 'boolean' },
 		localOnly: { type: 'boolean' },
+		excludeBots: { type: 'boolean' },
 		withReplies: { type: 'boolean' },
 		withFile: { type: 'boolean' },
 		notify: { type: 'boolean' },
@@ -88,6 +89,7 @@ export class ImportAntennasProcessorService {
 					users: (antenna.src === 'list' && antenna.userListAccts !== null ? antenna.userListAccts : antenna.users).filter(Boolean),
 					caseSensitive: antenna.caseSensitive,
 					localOnly: antenna.localOnly,
+					excludeBots: antenna.excludeBots,
 					withReplies: antenna.withReplies,
 					withFile: antenna.withFile,
 					notify: antenna.notify,
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index 191de8f833e6..57c8eb495850 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -64,6 +64,7 @@ export const paramDef = {
 		} },
 		caseSensitive: { type: 'boolean' },
 		localOnly: { type: 'boolean' },
+		excludeBots: { type: 'boolean' },
 		withReplies: { type: 'boolean' },
 		withFile: { type: 'boolean' },
 		notify: { type: 'boolean' },
@@ -124,6 +125,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				users: ps.users,
 				caseSensitive: ps.caseSensitive,
 				localOnly: ps.localOnly,
+				excludeBots: ps.excludeBots,
 				withReplies: ps.withReplies,
 				withFile: ps.withFile,
 				notify: ps.notify,
diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts
index 76a34924a0f0..e6720aacf82e 100644
--- a/packages/backend/src/server/api/endpoints/antennas/update.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/update.ts
@@ -63,6 +63,7 @@ export const paramDef = {
 		} },
 		caseSensitive: { type: 'boolean' },
 		localOnly: { type: 'boolean' },
+		excludeBots: { type: 'boolean' },
 		withReplies: { type: 'boolean' },
 		withFile: { type: 'boolean' },
 		notify: { type: 'boolean' },
@@ -120,6 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				users: ps.users,
 				caseSensitive: ps.caseSensitive,
 				localOnly: ps.localOnly,
+				excludeBots: ps.excludeBots,
 				withReplies: ps.withReplies,
 				withFile: ps.withFile,
 				notify: ps.notify,
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index 7370b1963ca3..cf5c7dd130ea 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -44,6 +44,7 @@ describe('アンテナ', () => {
 		users: [''],
 		withFile: false,
 		withReplies: false,
+		excludeBots: false,
 	};
 
 	let root: User;
@@ -156,6 +157,7 @@ describe('アンテナ', () => {
 			users: [''],
 			withFile: false,
 			withReplies: false,
+			excludeBots: false,
 			localOnly: false,
 		};
 		assert.deepStrictEqual(response, expected);
diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue
index 8b3b3cfbfd40..2d026d2fa976 100644
--- a/packages/frontend/src/pages/my-antennas/create.vue
+++ b/packages/frontend/src/pages/my-antennas/create.vue
@@ -26,6 +26,7 @@ const draft = ref({
 	users: [],
 	keywords: [],
 	excludeKeywords: [],
+	excludeBots: false,
 	withReplies: false,
 	caseSensitive: false,
 	localOnly: false,
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index c6dcbadd9b65..97edbc44cefa 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -26,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.users }}</template>
 				<template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template>
 			</MkTextarea>
+			<MkSwitch v-model="excludeBots">{{ i18n.ts.antennaExcludeBots }}</MkSwitch>
 			<MkSwitch v-model="withReplies">{{ i18n.ts.withReplies }}</MkSwitch>
 			<MkTextarea v-model="keywords">
 				<template #label>{{ i18n.ts.antennaKeywords }}</template>
@@ -78,6 +79,7 @@ const keywords = ref<string>(props.antenna.keywords.map(x => x.join(' ')).join('
 const excludeKeywords = ref<string>(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
 const caseSensitive = ref<boolean>(props.antenna.caseSensitive);
 const localOnly = ref<boolean>(props.antenna.localOnly);
+const excludeBots = ref<boolean>(props.antenna.excludeBots);
 const withReplies = ref<boolean>(props.antenna.withReplies);
 const withFile = ref<boolean>(props.antenna.withFile);
 const notify = ref<boolean>(props.antenna.notify);
@@ -94,6 +96,7 @@ async function saveAntenna() {
 		name: name.value,
 		src: src.value,
 		userListId: userListId.value,
+		excludeBots: excludeBots.value,
 		withReplies: withReplies.value,
 		withFile: withFile.value,
 		notify: notify.value,
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 3c862f690e5f..636bc62aaaa2 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4434,6 +4434,8 @@ export type components = {
       localOnly: boolean;
       notify: boolean;
       /** @default false */
+      excludeBots: boolean;
+      /** @default false */
       withReplies: boolean;
       withFile: boolean;
       isActive: boolean;
@@ -9654,6 +9656,7 @@ export type operations = {
           users: string[];
           caseSensitive: boolean;
           localOnly?: boolean;
+          excludeBots?: boolean;
           withReplies: boolean;
           withFile: boolean;
           notify: boolean;
@@ -9935,6 +9938,7 @@ export type operations = {
           users?: string[];
           caseSensitive?: boolean;
           localOnly?: boolean;
+          excludeBots?: boolean;
           withReplies?: boolean;
           withFile?: boolean;
           notify?: boolean;

From 831c74a25b2db0ba3f6d43a9a1a9072d342b2822 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 21 Mar 2024 18:46:42 +0900
Subject: [PATCH 120/266] =?UTF-8?q?fix:=20URL=E3=83=97=E3=83=AC=E3=83=93?=
 =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=81=AE=E5=8B=95=E4=BD=9C=E6=94=B9=E5=96=84?=
 =?UTF-8?q?=EF=BC=8B=E5=8B=95=E4=BD=9C=E8=A8=AD=E5=AE=9A=E3=82=92=E5=8F=AF?=
 =?UTF-8?q?=E8=83=BD=E3=81=AB=E3=81=99=E3=82=8B=20(#13579)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* wip

* support new version

* URLプレビュー無効化時、フロント側も非表示にしてリクエストをしないようにする

* fix lint

* fix lint

* tweak preview request error handles

* fix: CHANGELOG.md

* fix

* fix

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  5 ++
 locales/index.d.ts                            | 58 +++++++++++++++
 locales/ja-JP.yml                             | 15 ++++
 .../1710512074000-url-preview-meta.js         | 42 +++++++++++
 packages/backend/package.json                 |  2 +-
 .../src/core/entities/MetaEntityService.ts    |  1 +
 packages/backend/src/models/Meta.ts           | 38 ++++++++--
 .../backend/src/models/json-schema/meta.ts    |  4 +
 .../src/server/api/endpoints/admin/meta.ts    | 34 ++++++++-
 .../server/api/endpoints/admin/update-meta.ts | 41 ++++++++--
 .../src/server/web/UrlPreviewService.ts       | 67 +++++++++++++----
 packages/frontend/src/components/MkLink.vue   | 17 +++--
 packages/frontend/src/components/MkNote.vue   |  7 +-
 .../src/components/MkNoteDetailed.vue         |  5 +-
 .../frontend/src/components/MkUrlPreview.vue  | 12 +--
 .../frontend/src/components/global/MkUrl.vue  |  3 +-
 .../src/components/page/page.text.vue         |  5 +-
 packages/frontend/src/instance.ts             |  2 +
 .../frontend/src/pages/admin/security.vue     | 16 ----
 .../frontend/src/pages/admin/settings.vue     | 74 ++++++++++++++++++-
 packages/misskey-js/src/autogen/types.ts      | 20 ++++-
 pnpm-lock.yaml                                | 18 ++++-
 22 files changed, 420 insertions(+), 66 deletions(-)
 create mode 100644 packages/backend/migration/1710512074000-url-preview-meta.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0dce1a049681..188e146cddb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
 ## Unreleased
 
+### Note
+- コントロールパネル内にあるサマリープロキシの設定個所がセキュリティから全般へ変更となります。
+
 ### General
+- Enhance: URLプレビューの有効化・無効化を設定できるように #13569
 - Enhance: アンテナでBotによるノートを除外できるように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
@@ -25,6 +29,7 @@
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
+- Enhance: misskey-dev/summaly@5.1.0の取り込み(プレビュー生成処理の効率化)
 - Fix: フォローリクエストを作成する際に既存のものは削除するように  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
 
diff --git a/locales/index.d.ts b/locales/index.d.ts
index afb4adac6cb5..70586d7a8758 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4916,6 +4916,10 @@ export interface Locale extends ILocale {
      * リトライ
      */
     "gameRetry": string;
+    /**
+     * 使用しない場合は空欄にしてください
+     */
+    "notUsePleaseLeaveBlank": string;
     "_bubbleGame": {
         /**
          * 遊び方
@@ -9768,6 +9772,60 @@ export interface Locale extends ILocale {
          */
         "header": string;
     };
+    "_urlPreviewSetting": {
+        /**
+         * URLプレビューの設定
+         */
+        "title": string;
+        /**
+         * URLプレビューを有効にする
+         */
+        "enable": string;
+        /**
+         * プレビュー取得時のタイムアウト(ms)
+         */
+        "timeout": string;
+        /**
+         * プレビュー取得の所要時間がこの値を超えた場合、プレビューは生成されません。
+         */
+        "timeoutDescription": string;
+        /**
+         * Content-Lengthの最大値(byte)
+         */
+        "maximumContentLength": string;
+        /**
+         * Content-Lengthがこの値を超えた場合、プレビューは生成されません。
+         */
+        "maximumContentLengthDescription": string;
+        /**
+         * Content-Lengthが取得できた場合のみプレビューを生成
+         */
+        "requireContentLength": string;
+        /**
+         * 相手サーバがContent-Lengthを返さない場合、プレビューは生成されません。
+         */
+        "requireContentLengthDescription": string;
+        /**
+         * User-Agent
+         */
+        "userAgent": string;
+        /**
+         * プレビュー取得時に使用されるUser-Agentを設定します。空欄の場合、デフォルトのUser-Agentが使用されます。
+         */
+        "userAgentDescription": string;
+        /**
+         * プレビューを生成するプロキシのエンドポイント
+         */
+        "summaryProxy": string;
+        /**
+         * Misskey本体ではなく、サマリープロキシを使用してプレビューを生成します。
+         */
+        "summaryProxyDescription": string;
+        /**
+         * プロキシには下記パラメータがクエリ文字列として連携されます。プロキシ側がこれらをサポートしない場合、設定値は無視されます。
+         */
+        "summaryProxyDescription2": string;
+    };
 }
 declare const locales: {
     [lang: string]: Locale;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index a64c83b10fa7..cada6d855f70 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1225,6 +1225,7 @@ enableHorizontalSwipe: "スワイプしてタブを切り替える"
 loading: "読み込み中"
 surrender: "やめる"
 gameRetry: "リトライ"
+notUsePleaseLeaveBlank: "使用しない場合は空欄にしてください"
 
 _bubbleGame:
   howToPlay: "遊び方"
@@ -2602,3 +2603,17 @@ _offlineScreen:
   title: "オフライン - サーバーに接続できません"
   header: "サーバーに接続できません"
 
+_urlPreviewSetting:
+  title: "URLプレビューの設定"
+  enable: "URLプレビューを有効にする"
+  timeout: "プレビュー取得時のタイムアウト(ms)"
+  timeoutDescription: "プレビュー取得の所要時間がこの値を超えた場合、プレビューは生成されません。"
+  maximumContentLength: "Content-Lengthの最大値(byte)"
+  maximumContentLengthDescription: "Content-Lengthがこの値を超えた場合、プレビューは生成されません。"
+  requireContentLength: "Content-Lengthが取得できた場合のみプレビューを生成"
+  requireContentLengthDescription: "相手サーバがContent-Lengthを返さない場合、プレビューは生成されません。"
+  userAgent: "User-Agent"
+  userAgentDescription: "プレビュー取得時に使用されるUser-Agentを設定します。空欄の場合、デフォルトのUser-Agentが使用されます。"
+  summaryProxy: "プレビューを生成するプロキシのエンドポイント"
+  summaryProxyDescription: "Misskey本体ではなく、サマリープロキシを使用してプレビューを生成します。"
+  summaryProxyDescription2: "プロキシには下記パラメータがクエリ文字列として連携されます。プロキシ側がこれらをサポートしない場合、設定値は無視されます。"
diff --git a/packages/backend/migration/1710512074000-url-preview-meta.js b/packages/backend/migration/1710512074000-url-preview-meta.js
new file mode 100644
index 000000000000..8af521bbf4a9
--- /dev/null
+++ b/packages/backend/migration/1710512074000-url-preview-meta.js
@@ -0,0 +1,42 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class UrlPreviewMeta1710512074000 {
+    name = 'UrlPreviewMeta1710512074000'
+
+    async up(queryRunner) {
+        await queryRunner.query(`
+					alter table meta
+						rename column "summalyProxy" to "urlPreviewSummaryProxyUrl";
+					alter table meta
+						add "urlPreviewEnabled" boolean default true not null;
+					alter table meta
+						add "urlPreviewTimeout" integer default 10000 not null;
+					alter table meta
+						add "urlPreviewMaximumContentLength" bigint default 10485760 not null;
+					alter table meta
+						add "urlPreviewRequireContentLength" boolean default false not null;
+					alter table meta
+						add "urlPreviewUserAgent" varchar(1024) default null;
+				`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`
+					alter table meta
+						rename column "urlPreviewSummaryProxyUrl" to "summalyProxy";
+					alter table meta
+						drop column "urlPreviewEnabled";
+					alter table meta
+						drop column "urlPreviewTimeout";
+					alter table meta
+						drop column "urlPreviewMaximumContentLength";
+					alter table meta
+						drop column "urlPreviewRequireContentLength";
+					alter table meta
+						drop column "urlPreviewUserAgent";
+				`);
+    }
+}
diff --git a/packages/backend/package.json b/packages/backend/package.json
index eaad96d5f6ee..d64fcc3d2a05 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -80,7 +80,7 @@
 		"@fastify/static": "6.12.0",
 		"@fastify/view": "8.2.0",
 		"@misskey-dev/sharp-read-bmp": "1.2.0",
-		"@misskey-dev/summaly": "5.0.3",
+		"@misskey-dev/summaly": "5.1.0",
 		"@nestjs/common": "10.3.3",
 		"@nestjs/core": "10.3.3",
 		"@nestjs/testing": "10.3.3",
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index b50d76288f27..9d054ab6a156 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -111,6 +111,7 @@ export class MetaEntityService {
 			policies: { ...DEFAULT_POLICIES, ...instance.policies },
 
 			mediaProxy: this.config.mediaProxy,
+			enableUrlPreview: instance.urlPreviewEnabled,
 		};
 
 		return packed;
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 66f19ce1975c..04a34bbbb42b 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -277,12 +277,6 @@ export class MiMeta {
 	})
 	public enableSensitiveMediaDetectionForVideos: boolean;
 
-	@Column('varchar', {
-		length: 1024,
-		nullable: true,
-	})
-	public summalyProxy: string | null;
-
 	@Column('boolean', {
 		default: false,
 	})
@@ -588,4 +582,36 @@ export class MiMeta {
 		default: 0,
 	})
 	public notesPerOneAd: number;
+
+	@Column('boolean', {
+		default: true,
+	})
+	public urlPreviewEnabled: boolean;
+
+	@Column('integer', {
+		default: 10000,
+	})
+	public urlPreviewTimeout: number;
+
+	@Column('bigint', {
+		default: 1024 * 1024 * 10,
+	})
+	public urlPreviewMaximumContentLength: number;
+
+	@Column('boolean', {
+		default: true,
+	})
+	public urlPreviewRequireContentLength: boolean;
+
+	@Column('varchar', {
+		length: 1024,
+		nullable: true,
+	})
+	public urlPreviewSummaryProxyUrl: string | null;
+
+	@Column('varchar', {
+		length: 1024,
+		nullable: true,
+	})
+	public urlPreviewUserAgent: string | null;
 }
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
index 17789f3b46ce..473339a1add5 100644
--- a/packages/backend/src/models/json-schema/meta.ts
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -207,6 +207,10 @@ export const packedMetaLiteSchema = {
 			type: 'string',
 			optional: false, nullable: false,
 		},
+		enableUrlPreview: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
 		backgroundImageUrl: {
 			type: 'string',
 			optional: false, nullable: true,
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 88c5907bcc80..f4ff5732715d 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -434,6 +434,8 @@ export const meta = {
 			summalyProxy: {
 				type: 'string',
 				optional: false, nullable: true,
+				deprecated: true,
+				description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
 			},
 			themeColor: {
 				type: 'string',
@@ -451,6 +453,30 @@ export const meta = {
 				type: 'string',
 				optional: false, nullable: false,
 			},
+			urlPreviewEnabled: {
+				type: 'boolean',
+				optional: false, nullable: false,
+			},
+			urlPreviewTimeout: {
+				type: 'number',
+				optional: false, nullable: false,
+			},
+			urlPreviewMaximumContentLength: {
+				type: 'number',
+				optional: false, nullable: false,
+			},
+			urlPreviewRequireContentLength: {
+				type: 'boolean',
+				optional: false, nullable: false,
+			},
+			urlPreviewUserAgent: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			urlPreviewSummaryProxyUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
 		},
 	},
 } as const;
@@ -533,7 +559,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically,
 				enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos,
 				proxyAccountId: instance.proxyAccountId,
-				summalyProxy: instance.summalyProxy,
 				email: instance.email,
 				smtpSecure: instance.smtpSecure,
 				smtpHost: instance.smtpHost,
@@ -577,6 +602,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax,
 				perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax,
 				notesPerOneAd: instance.notesPerOneAd,
+				summalyProxy: instance.urlPreviewSummaryProxyUrl,
+				urlPreviewEnabled: instance.urlPreviewEnabled,
+				urlPreviewTimeout: instance.urlPreviewTimeout,
+				urlPreviewMaximumContentLength: instance.urlPreviewMaximumContentLength,
+				urlPreviewRequireContentLength: instance.urlPreviewRequireContentLength,
+				urlPreviewUserAgent: instance.urlPreviewUserAgent,
+				urlPreviewSummaryProxyUrl: instance.urlPreviewSummaryProxyUrl,
 			};
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index bffceef8151d..2f62d30ada77 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -90,7 +90,6 @@ export const paramDef = {
 				type: 'string',
 			},
 		},
-		summalyProxy: { type: 'string', nullable: true },
 		deeplAuthKey: { type: 'string', nullable: true },
 		deeplIsPro: { type: 'boolean' },
 		enableEmail: { type: 'boolean' },
@@ -150,6 +149,16 @@ export const paramDef = {
 				type: 'string',
 			},
 		},
+		summalyProxy: {
+			type: 'string', nullable: true,
+			description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
+		},
+		urlPreviewEnabled: { type: 'boolean' },
+		urlPreviewTimeout: { type: 'integer' },
+		urlPreviewMaximumContentLength: { type: 'integer' },
+		urlPreviewRequireContentLength: { type: 'boolean' },
+		urlPreviewUserAgent: { type: 'string', nullable: true },
+		urlPreviewSummaryProxyUrl: { type: 'string', nullable: true },
 	},
 	required: [],
 } as const;
@@ -353,10 +362,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.langs = ps.langs.filter(Boolean);
 			}
 
-			if (ps.summalyProxy !== undefined) {
-				set.summalyProxy = ps.summalyProxy;
-			}
-
 			if (ps.enableEmail !== undefined) {
 				set.enableEmail = ps.enableEmail;
 			}
@@ -581,6 +586,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.bannedEmailDomains = ps.bannedEmailDomains;
 			}
 
+			if (ps.urlPreviewEnabled !== undefined) {
+				set.urlPreviewEnabled = ps.urlPreviewEnabled;
+			}
+
+			if (ps.urlPreviewTimeout !== undefined) {
+				set.urlPreviewTimeout = ps.urlPreviewTimeout;
+			}
+
+			if (ps.urlPreviewMaximumContentLength !== undefined) {
+				set.urlPreviewMaximumContentLength = ps.urlPreviewMaximumContentLength;
+			}
+
+			if (ps.urlPreviewRequireContentLength !== undefined) {
+				set.urlPreviewRequireContentLength = ps.urlPreviewRequireContentLength;
+			}
+
+			if (ps.urlPreviewUserAgent !== undefined) {
+				const value = (ps.urlPreviewUserAgent ?? '').trim();
+				set.urlPreviewUserAgent = value === '' ? null : ps.urlPreviewUserAgent;
+			}
+
+			if (ps.summalyProxy !== undefined || ps.urlPreviewSummaryProxyUrl !== undefined) {
+				const value = ((ps.urlPreviewSummaryProxyUrl ?? ps.summalyProxy) ?? '').trim();
+				set.urlPreviewSummaryProxyUrl = value === '' ? null : value;
+			}
+
 			const before = await this.metaService.fetch(true);
 
 			await this.metaService.update(set);
diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts
index c6a96e94cbbd..8f8f08a3051b 100644
--- a/packages/backend/src/server/web/UrlPreviewService.ts
+++ b/packages/backend/src/server/web/UrlPreviewService.ts
@@ -5,6 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { summaly } from '@misskey-dev/summaly';
+import { SummalyResult } from '@misskey-dev/summaly/built/summary.js';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -14,6 +15,7 @@ import { query } from '@/misc/prelude/url.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
 import { ApiError } from '@/server/api/error.js';
+import { MiMeta } from '@/models/Meta.js';
 import type { FastifyRequest, FastifyReply } from 'fastify';
 
 @Injectable()
@@ -62,24 +64,25 @@ export class UrlPreviewService {
 
 		const meta = await this.metaService.fetch();
 
-		this.logger.info(meta.summalyProxy
+		if (!meta.urlPreviewEnabled) {
+			reply.code(403);
+			return {
+				error: new ApiError({
+					message: 'URL preview is disabled',
+					code: 'URL_PREVIEW_DISABLED',
+					id: '58b36e13-d2f5-0323-b0c6-76aa9dabefb8',
+				}),
+			};
+		}
+
+		this.logger.info(meta.urlPreviewSummaryProxyUrl
 			? `(Proxy) Getting preview of ${url}@${lang} ...`
 			: `Getting preview of ${url}@${lang} ...`);
+
 		try {
-			const summary = meta.summalyProxy ?
-				await this.httpRequestService.getJson<ReturnType<typeof summaly>>(`${meta.summalyProxy}?${query({
-					url: url,
-					lang: lang ?? 'ja-JP',
-				})}`)
-				:
-				await summaly(url, {
-					followRedirects: false,
-					lang: lang ?? 'ja-JP',
-					agent: this.config.proxy ? {
-						http: this.httpRequestService.httpAgent,
-						https: this.httpRequestService.httpsAgent,
-					} : undefined,
-				});
+			const summary = meta.urlPreviewSummaryProxyUrl
+				? await this.fetchSummaryFromProxy(url, meta, lang)
+				: await this.fetchSummary(url, meta, lang);
 
 			this.logger.succ(`Got preview of ${url}: ${summary.title}`);
 
@@ -100,6 +103,7 @@ export class UrlPreviewService {
 			return summary;
 		} catch (err) {
 			this.logger.warn(`Failed to get preview of ${url}: ${err}`);
+
 			reply.code(422);
 			reply.header('Cache-Control', 'max-age=86400, immutable');
 			return {
@@ -111,4 +115,37 @@ export class UrlPreviewService {
 			};
 		}
 	}
+
+	private fetchSummary(url: string, meta: MiMeta, lang?: string): Promise<SummalyResult> {
+		const agent = this.config.proxy
+			? {
+				http: this.httpRequestService.httpAgent,
+				https: this.httpRequestService.httpsAgent,
+			}
+			: undefined;
+
+		return summaly(url, {
+			followRedirects: false,
+			lang: lang ?? 'ja-JP',
+			agent: agent,
+			userAgent: meta.urlPreviewUserAgent ?? undefined,
+			operationTimeout: meta.urlPreviewTimeout,
+			contentLengthLimit: meta.urlPreviewMaximumContentLength,
+			contentLengthRequired: meta.urlPreviewRequireContentLength,
+		});
+	}
+
+	private fetchSummaryFromProxy(url: string, meta: MiMeta, lang?: string): Promise<SummalyResult> {
+		const proxy = meta.urlPreviewSummaryProxyUrl!;
+		const queryStr = query({
+			url: url,
+			lang: lang ?? 'ja-JP',
+			userAgent: meta.urlPreviewUserAgent ?? undefined,
+			operationTimeout: meta.urlPreviewTimeout,
+			contentLengthLimit: meta.urlPreviewMaximumContentLength,
+			contentLengthRequired: meta.urlPreviewRequireContentLength,
+		});
+
+		return this.httpRequestService.getJson<SummalyResult>(`${proxy}?${queryStr}`);
+	}
 }
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index 3f7aba2fe4e1..ca875242b443 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -18,6 +18,7 @@ import { defineAsyncComponent, ref } from 'vue';
 import { url as local } from '@/config.js';
 import { useTooltip } from '@/scripts/use-tooltip.js';
 import * as os from '@/os.js';
+import { isEnabledUrlPreview } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	url: string;
@@ -31,13 +32,15 @@ const target = self ? null : '_blank';
 
 const el = ref<HTMLElement | { $el: HTMLElement }>();
 
-useTooltip(el, (showing) => {
-	os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
-		showing,
-		url: props.url,
-		source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
-	}, {}, 'closed');
-});
+if (isEnabledUrlPreview.value) {
+	useTooltip(el, (showing) => {
+		os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
+			showing,
+			url: props.url,
+			source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
+		}, {}, 'closed');
+	});
+}
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 5ca0eae01221..50741f2cb7b1 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -82,7 +82,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<MkMediaList :mediaList="appearNote.files"/>
 					</div>
 					<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/>
-					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/>
+					<div v-if="isEnabledUrlPreview">
+						<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/>
+					</div>
 					<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
 					<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false">
 						<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
@@ -194,6 +196,7 @@ import { MenuItem } from '@/types/menu.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import { shouldCollapsed } from '@/scripts/collapsed.js';
+import { isEnabledUrlPreview } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -268,7 +271,7 @@ const renoteCollapsed = ref(
 	defaultStore.state.collapseRenotes && isRenote && (
 		($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
 		(appearNote.value.myReaction != null)
-	)
+	),
 );
 
 /* Overload FunctionにLintが対応していないのでコメントアウト
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index e27121551645..1b7dcda409c7 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -95,7 +95,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkMediaList :mediaList="appearNote.files"/>
 				</div>
 				<MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/>
-				<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/>
+				<div v-if="isEnabledUrlPreview">
+					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/>
+				</div>
 				<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
 			</div>
 			<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
@@ -229,6 +231,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkPagination, { type Paging } from '@/components/MkPagination.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkButton from '@/components/MkButton.vue';
+import { isEnabledUrlPreview } from '@/instance.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index efc58b7e292b..b3dc4926165f 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -110,7 +110,6 @@ const MOBILE_THRESHOLD = 500;
 const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
 
 const self = props.url.startsWith(local);
-const attr = self ? 'to' : 'href';
 const target = self ? null : '_blank';
 const fetching = ref(true);
 const title = ref<string | null>(null);
@@ -152,15 +151,16 @@ requestUrl.hash = '';
 window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`)
 	.then(res => {
 		if (!res.ok) {
-			fetching.value = false;
-			unknownUrl.value = true;
-			return;
+			if (_DEV_) {
+				console.warn(`[HTTP${res.status}] Failed to fetch url preview`);
+			}
+			return null;
 		}
 
 		return res.json();
 	})
-	.then((info: SummalyResult) => {
-		if (info.url == null) {
+	.then((info: SummalyResult | null) => {
+		if (!info || info.url == null) {
 			fetching.value = false;
 			unknownUrl.value = true;
 			return;
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 8d29a4da8c3a..d2945a78b950 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -30,6 +30,7 @@ import { url as local } from '@/config.js';
 import * as os from '@/os.js';
 import { useTooltip } from '@/scripts/use-tooltip.js';
 import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
+import { isEnabledUrlPreview } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	url: string;
@@ -44,7 +45,7 @@ const url = new URL(props.url);
 if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url');
 const el = ref();
 
-if (props.showUrlPreview) {
+if (props.showUrlPreview && isEnabledUrlPreview.value) {
 	useTooltip(el, (showing) => {
 		os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
 			showing,
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index 61247b381fa9..4e501bd6999e 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -6,7 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div class="_gaps" :class="$style.textRoot">
 	<Mfm :text="block.text ?? ''" :isNote="false"/>
-	<MkUrlPreview v-for="url in urls" :key="url" :url="url"/>
+	<div v-if="isEnabledUrlPreview">
+		<MkUrlPreview v-for="url in urls" :key="url" :url="url"/>
+	</div>
 </div>
 </template>
 
@@ -15,6 +17,7 @@ import { defineAsyncComponent } from 'vue';
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
+import { isEnabledUrlPreview } from '@/instance.js';
 
 const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
 
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index 4232cbcd78d5..22337e7eb958 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -36,6 +36,8 @@ export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO
 
 export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
 
+export const isEnabledUrlPreview = computed(() => instance.enableUrlPreview ?? true);
+
 export async function fetchInstance(force = false): Promise<void> {
 	if (!force) {
 		const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index c4745978dfb6..9bccee89a50f 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -118,19 +118,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</MkSwitch>
 					</div>
 				</MkFolder>
-
-				<MkFolder>
-					<template #label>Summaly Proxy</template>
-
-					<div class="_gaps_m">
-						<MkInput v-model="summalyProxy">
-							<template #prefix><i class="ti ti-link"></i></template>
-							<template #label>Summaly Proxy URL</template>
-						</MkInput>
-
-						<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
-					</div>
-				</MkFolder>
 			</div>
 		</FormSuspense>
 	</MkSpacer>
@@ -155,7 +142,6 @@ import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const summalyProxy = ref<string>('');
 const enableHcaptcha = ref<boolean>(false);
 const enableMcaptcha = ref<boolean>(false);
 const enableRecaptcha = ref<boolean>(false);
@@ -175,7 +161,6 @@ const bannedEmailDomains = ref<string>('');
 
 async function init() {
 	const meta = await misskeyApi('admin/meta');
-	summalyProxy.value = meta.summalyProxy;
 	enableHcaptcha.value = meta.enableHcaptcha;
 	enableMcaptcha.value = meta.enableMcaptcha;
 	enableRecaptcha.value = meta.enableRecaptcha;
@@ -201,7 +186,6 @@ async function init() {
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		summalyProxy: summalyProxy.value,
 		sensitiveMediaDetection: sensitiveMediaDetection.value,
 		sensitiveMediaDetectionSensitivity:
 			sensitiveMediaDetectionSensitivity.value === 0 ? 'veryLow' :
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 9a198ee8a343..6f45c212ece1 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -143,6 +143,53 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</div>
 						</div>
 					</FormSection>
+
+					<FormSection>
+						<template #label>{{ i18n.ts._urlPreviewSetting.title }}</template>
+
+						<div class="_gaps_m">
+							<MkSwitch v-model="urlPreviewEnabled">
+								<template #label>{{ i18n.ts._urlPreviewSetting.enable }}</template>
+							</MkSwitch>
+
+							<MkSwitch v-model="urlPreviewRequireContentLength">
+								<template #label>{{ i18n.ts._urlPreviewSetting.requireContentLength }}</template>
+								<template #caption>{{ i18n.ts._urlPreviewSetting.requireContentLengthDescription }}</template>
+							</MkSwitch>
+
+							<MkInput v-model="urlPreviewMaximumContentLength" type="number">
+								<template #label>{{ i18n.ts._urlPreviewSetting.maximumContentLength }}</template>
+								<template #caption>{{ i18n.ts._urlPreviewSetting.maximumContentLengthDescription }}</template>
+							</MkInput>
+
+							<MkInput v-model="urlPreviewTimeout" type="number">
+								<template #label>{{ i18n.ts._urlPreviewSetting.timeout }}</template>
+								<template #caption>{{ i18n.ts._urlPreviewSetting.timeoutDescription }}</template>
+							</MkInput>
+
+							<MkInput v-model="urlPreviewUserAgent" type="text">
+								<template #label>{{ i18n.ts._urlPreviewSetting.userAgent }}</template>
+								<template #caption>{{ i18n.ts._urlPreviewSetting.userAgentDescription }}</template>
+							</MkInput>
+
+							<div>
+								<MkInput v-model="urlPreviewSummaryProxyUrl" type="text">
+									<template #label>{{ i18n.ts._urlPreviewSetting.summaryProxy }}</template>
+									<template #caption>[{{ i18n.ts.notUsePleaseLeaveBlank }}] {{ i18n.ts._urlPreviewSetting.summaryProxyDescription }}</template>
+								</MkInput>
+
+								<div :class="$style.subCaption">
+									{{ i18n.ts._urlPreviewSetting.summaryProxyDescription2 }}
+									<ul style="padding-left: 20px; margin: 4px 0">
+										<li>{{ i18n.ts._urlPreviewSetting.timeout }} / key:timeout</li>
+										<li>{{ i18n.ts._urlPreviewSetting.maximumContentLength }} / key:contentLengthLimit</li>
+										<li>{{ i18n.ts._urlPreviewSetting.requireContentLength }} / key:contentLengthRequired</li>
+										<li>{{ i18n.ts._urlPreviewSetting.userAgent }} / key:userAgent</li>
+									</ul>
+								</div>
+							</div>
+						</div>
+					</FormSection>
 				</div>
 			</FormSuspense>
 		</MkSpacer>
@@ -173,6 +220,8 @@ import { fetchInstance, instance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import MkSelect from '@/components/MkSelect.vue';
 
 const name = ref<string | null>(null);
 const shortName = ref<string | null>(null);
@@ -194,6 +243,12 @@ const perRemoteUserUserTimelineCacheMax = ref<number>(0);
 const perUserHomeTimelineCacheMax = ref<number>(0);
 const perUserListTimelineCacheMax = ref<number>(0);
 const notesPerOneAd = ref<number>(0);
+const urlPreviewEnabled = ref<boolean>(true);
+const urlPreviewTimeout = ref<number>(10000);
+const urlPreviewMaximumContentLength = ref<number>(1024 * 1024 * 10);
+const urlPreviewRequireContentLength = ref<boolean>(true);
+const urlPreviewUserAgent = ref<string | null>(null);
+const urlPreviewSummaryProxyUrl = ref<string | null>(null);
 
 async function init(): Promise<void> {
 	const meta = await misskeyApi('admin/meta');
@@ -217,9 +272,15 @@ async function init(): Promise<void> {
 	perUserHomeTimelineCacheMax.value = meta.perUserHomeTimelineCacheMax;
 	perUserListTimelineCacheMax.value = meta.perUserListTimelineCacheMax;
 	notesPerOneAd.value = meta.notesPerOneAd;
+	urlPreviewEnabled.value = meta.urlPreviewEnabled;
+	urlPreviewTimeout.value = meta.urlPreviewTimeout;
+	urlPreviewMaximumContentLength.value = meta.urlPreviewMaximumContentLength;
+	urlPreviewRequireContentLength.value = meta.urlPreviewRequireContentLength;
+	urlPreviewUserAgent.value = meta.urlPreviewUserAgent;
+	urlPreviewSummaryProxyUrl.value = meta.urlPreviewSummaryProxyUrl;
 }
 
-async function save(): void {
+async function save() {
 	await os.apiWithDialog('admin/update-meta', {
 		name: name.value,
 		shortName: shortName.value === '' ? null : shortName.value,
@@ -241,6 +302,12 @@ async function save(): void {
 		perUserHomeTimelineCacheMax: perUserHomeTimelineCacheMax.value,
 		perUserListTimelineCacheMax: perUserListTimelineCacheMax.value,
 		notesPerOneAd: notesPerOneAd.value,
+		urlPreviewEnabled: urlPreviewEnabled.value,
+		urlPreviewTimeout: urlPreviewTimeout.value,
+		urlPreviewMaximumContentLength: urlPreviewMaximumContentLength.value,
+		urlPreviewRequireContentLength: urlPreviewRequireContentLength.value,
+		urlPreviewUserAgent: urlPreviewUserAgent.value,
+		urlPreviewSummaryProxyUrl: urlPreviewSummaryProxyUrl.value,
 	});
 
 	fetchInstance(true);
@@ -259,4 +326,9 @@ definePageMetadata(() => ({
 	-webkit-backdrop-filter: var(--blur, blur(15px));
 	backdrop-filter: var(--blur, blur(15px));
 }
+
+.subCaption {
+	font-size: 0.85em;
+	color: var(--fgTransparentWeak);
+}
 </style>
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 636bc62aaaa2..c6e36d80aaed 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4810,6 +4810,7 @@ export type components = {
       enableServiceWorker: boolean;
       translatorAvailable: boolean;
       mediaProxy: string;
+      enableUrlPreview: boolean;
       backgroundImageUrl: string | null;
       impressumUrl: string | null;
       logoImageUrl: string | null;
@@ -4962,11 +4963,21 @@ export type operations = {
             objectStorageS3ForcePathStyle: boolean;
             privacyPolicyUrl: string | null;
             repositoryUrl: string | null;
+            /**
+             * @deprecated
+             * @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead.
+             */
             summalyProxy: string | null;
             themeColor: string | null;
             tosUrl: string | null;
             uri: string;
             version: string;
+            urlPreviewEnabled: boolean;
+            urlPreviewTimeout: number;
+            urlPreviewMaximumContentLength: number;
+            urlPreviewRequireContentLength: boolean;
+            urlPreviewUserAgent: string | null;
+            urlPreviewSummaryProxyUrl: string | null;
           };
         };
       };
@@ -8862,7 +8873,6 @@ export type operations = {
           maintainerName?: string | null;
           maintainerEmail?: string | null;
           langs?: string[];
-          summalyProxy?: string | null;
           deeplAuthKey?: string | null;
           deeplIsPro?: boolean;
           enableEmail?: boolean;
@@ -8916,6 +8926,14 @@ export type operations = {
           perUserListTimelineCacheMax?: number;
           notesPerOneAd?: number;
           silencedHosts?: string[] | null;
+          /** @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead. */
+          summalyProxy?: string | null;
+          urlPreviewEnabled?: boolean;
+          urlPreviewTimeout?: number;
+          urlPreviewMaximumContentLength?: number;
+          urlPreviewRequireContentLength?: boolean;
+          urlPreviewUserAgent?: string | null;
+          urlPreviewSummaryProxyUrl?: string | null;
         };
       };
     };
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4c1e228a9514..383b31b1f361 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -117,8 +117,8 @@ importers:
         specifier: 1.2.0
         version: 1.2.0
       '@misskey-dev/summaly':
-        specifier: 5.0.3
-        version: 5.0.3
+        specifier: 5.1.0
+        version: 5.1.0
       '@nestjs/common':
         specifier: 10.3.3
         version: 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
@@ -4772,6 +4772,20 @@ packages:
       jschardet: 3.0.0
       private-ip: 2.3.3
       trace-redirect: 1.0.6
+    dev: true
+
+  /@misskey-dev/summaly@5.1.0:
+    resolution: {integrity: sha512-WAUrgX3/z4h4aI8Y/WVwmJcJ6Fa1Zf2LJCSS651t9MHoWVGABLsQ2KCXRGmlpk4i+cMDNIwweObUroosE7j8rg==}
+    dependencies:
+      cheerio: 1.0.0-rc.12
+      escape-regexp: 0.0.1
+      got: 12.6.1
+      html-entities: 2.3.2
+      iconv-lite: 0.6.3
+      jschardet: 3.0.0
+      private-ip: 2.3.3
+      trace-redirect: 1.0.6
+    dev: false
 
   /@mole-inc/bin-wrapper@8.0.1:
     resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==}

From 40bb6069ec04bc0461ac407da7d03c6910c23d6d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Fri, 22 Mar 2024 08:54:34 +0900
Subject: [PATCH 121/266] =?UTF-8?q?fix(frontend):=20URL=E3=83=97=E3=83=AC?=
 =?UTF-8?q?=E3=83=93=E3=83=A5=E3=83=BC=E3=81=AEto/href=E3=81=8C=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20Fix=20?=
 =?UTF-8?q?of=20https://github.com/misskey-dev/misskey/pull/13579/files/9a?=
 =?UTF-8?q?e577871b10f6231acc3451188cd69ede9443ed#diff-cfa02e203bdbd03dbf3?=
 =?UTF-8?q?12a889f009ca7f9ebd8376334ebd74c4961b716b22d93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkUrlPreview.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index b3dc4926165f..6954f1f6ff88 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -110,6 +110,7 @@ const MOBILE_THRESHOLD = 500;
 const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
 
 const self = props.url.startsWith(local);
+const attr = self ? 'to' : 'href';
 const target = self ? null : '_blank';
 const fetching = ref(true);
 const title = ref<string | null>(null);

From c9c6424205f1d05813735a8f9c0e53a1ccc35e0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 22 Mar 2024 15:03:21 +0900
Subject: [PATCH 122/266] =?UTF-8?q?enhance(frontend):=20TOTP=E3=81=AE?=
 =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0?=
 =?UTF-8?q?=E3=82=92=E6=94=B9=E8=89=AF=20(#13607)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): TOTPの入力ダイアログを改良

* Update Changelog
---
 CHANGELOG.md                                  |  3 ++-
 locales/index.d.ts                            |  8 ++++++
 locales/ja-JP.yml                             |  2 ++
 packages/frontend/src/components/MkInput.vue  |  2 ++
 .../src/components/MkPasswordDialog.vue       | 26 +++++++++++--------
 packages/frontend/src/components/MkSignin.vue | 11 ++++----
 .../src/pages/settings/2fa.qrdialog.vue       |  2 +-
 packages/frontend/src/pages/settings/2fa.vue  |  6 ++++-
 8 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 188e146cddb6..d90b1425c13d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,7 +16,8 @@
 - Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
 - Enhance: 設定>プラグインのページからプラグインの簡易的なログやエラーを見られるように
   - 実装の都合により、プラグインは1つエラーを起こした時に即時停止するようになりました
-- Enhance: ページのデザインを変更	
+- Enhance: ページのデザインを変更
+- Enhance: 2要素認証(ワンタイムパスワード)の入力欄を改善
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 70586d7a8758..e1250946f35f 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4920,6 +4920,14 @@ export interface Locale extends ILocale {
      * 使用しない場合は空欄にしてください
      */
     "notUsePleaseLeaveBlank": string;
+    /**
+     * ワンタイムパスワードを使う
+     */
+    "useTotp": string;
+    /**
+     * バックアップコードを使う
+     */
+    "useBackupCode": string;
     "_bubbleGame": {
         /**
          * 遊び方
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index cada6d855f70..1c4df27d92ad 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1226,6 +1226,8 @@ loading: "読み込み中"
 surrender: "やめる"
 gameRetry: "リトライ"
 notUsePleaseLeaveBlank: "使用しない場合は空欄にしてください"
+useTotp: "ワンタイムパスワードを使う"
+useBackupCode: "バックアップコードを使う"
 
 _bubbleGame:
   howToPlay: "遊び方"
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index d3cddad15b29..88ef4635e6e4 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:autocomplete="autocomplete"
 			:autocapitalize="autocapitalize"
 			:spellcheck="spellcheck"
+			:inputmode="inputmode"
 			:step="step"
 			:list="id"
 			:min="min"
@@ -63,6 +64,7 @@ const props = defineProps<{
 	mfmAutocomplete?: boolean | SuggestionType[],
 	autocapitalize?: string;
 	spellcheck?: boolean;
+	inputmode?: 'none' | 'text' | 'search' | 'email' | 'url' | 'numeric' | 'tel' | 'decimal';
 	step?: any;
 	datalist?: string[];
 	min?: number;
diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
index c49526d8e25f..e749725feaaa 100644
--- a/packages/frontend/src/components/MkPasswordDialog.vue
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -19,18 +19,21 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div style="margin-top: 16px;">{{ i18n.ts.authenticationRequiredToContinue }}</div>
 		</div>
 
-		<div class="_gaps">
-			<MkInput ref="passwordInput" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" :withPasswordToggle="true">
-				<template #prefix><i class="ti ti-password"></i></template>
-			</MkInput>
+		<form @submit.prevent="done">
+			<div class="_gaps">
+				<MkInput ref="passwordInput" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" required :withPasswordToggle="true">
+					<template #prefix><i class="ti ti-password"></i></template>
+				</MkInput>
 
-			<MkInput v-if="$i.twoFactorEnabled" v-model="token" type="text" pattern="^([0-9]{6}|[A-Z0-9]{32})$" autocomplete="one-time-code" :spellcheck="false">
-				<template #label>{{ i18n.ts.token }} ({{ i18n.ts['2fa'] }})</template>
-				<template #prefix><i class="ti ti-123"></i></template>
-			</MkInput>
+				<MkInput v-if="$i.twoFactorEnabled" v-model="token" type="text" :pattern="isBackupCode ? '^[A-Z0-9]{32}$' :'^[0-9]{6}$'" autocomplete="one-time-code" required :spellcheck="false" :inputmode="isBackupCode ? undefined : 'numeric'">
+					<template #label>{{ i18n.ts.token }} ({{ i18n.ts['2fa'] }})</template>
+					<template #prefix><i v-if="isBackupCode" class="ti ti-key"></i><i v-else class="ti ti-123"></i></template>
+					<template #caption><button class="_textButton" type="button" @click="isBackupCode = !isBackupCode">{{ isBackupCode ? i18n.ts.useTotp : i18n.ts.useBackupCode }}</button></template>
+				</MkInput>
 
-			<MkButton :disabled="(password ?? '') == '' || ($i.twoFactorEnabled && (token ?? '') == '')" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-lock-open"></i> {{ i18n.ts.continue }}</MkButton>
-		</div>
+				<MkButton :disabled="(password ?? '') == '' || ($i.twoFactorEnabled && (token ?? '') == '')" type="submit" primary rounded style="margin: 0 auto;"><i class="ti ti-lock-open"></i> {{ i18n.ts.continue }}</MkButton>
+			</div>
+		</form>
 	</MkSpacer>
 </MkModalWindow>
 </template>
@@ -54,6 +57,7 @@ const emit = defineEmits<{
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 const passwordInput = shallowRef<InstanceType<typeof MkInput>>();
 const password = ref('');
+const isBackupCode = ref(false);
 const token = ref<string | null>(null);
 
 function onClose() {
@@ -61,7 +65,7 @@ function onClose() {
 	if (dialog.value) dialog.value.close();
 }
 
-function done(res) {
+function done() {
 	emit('done', { password: password.value, token: token.value });
 	if (dialog.value) dialog.value.close();
 }
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 852af01b5a3d..970aff825d2e 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -31,15 +31,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div v-if="user && user.securityKeys" class="or-hr">
 				<p class="or-msg">{{ i18n.ts.or }}</p>
 			</div>
-			<div class="twofa-group totp-group">
-				<p style="margin-bottom:0;">{{ i18n.ts['2fa'] }}</p>
+			<div class="twofa-group totp-group _gaps">
 				<MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" autocomplete="current-password" :withPasswordToggle="true" required>
 					<template #label>{{ i18n.ts.password }}</template>
 					<template #prefix><i class="ti ti-lock"></i></template>
 				</MkInput>
-				<MkInput v-model="token" type="text" pattern="^([0-9]{6}|[A-Z0-9]{32})$" autocomplete="one-time-code" :spellcheck="false" required>
-					<template #label>{{ i18n.ts.token }}</template>
-					<template #prefix><i class="ti ti-123"></i></template>
+				<MkInput v-model="token" type="text" :pattern="isBackupCode ? '^[A-Z0-9]{32}$' :'^[0-9]{6}$'" autocomplete="one-time-code" required :spellcheck="false" :inputmode="isBackupCode ? undefined : 'numeric'">
+					<template #label>{{ i18n.ts.token }} ({{ i18n.ts['2fa'] }})</template>
+					<template #prefix><i v-if="isBackupCode" class="ti ti-key"></i><i v-else class="ti ti-123"></i></template>
+					<template #caption><button class="_textButton" type="button" @click="isBackupCode = !isBackupCode">{{ isBackupCode ? i18n.ts.useTotp : i18n.ts.useBackupCode }}</button></template>
 				</MkInput>
 				<MkButton type="submit" :disabled="signing" large primary rounded style="margin: 0 auto;">{{ signing ? i18n.ts.loggingIn : i18n.ts.login }}</MkButton>
 			</div>
@@ -70,6 +70,7 @@ const password = ref('');
 const token = ref('');
 const host = ref(toUnicode(configHost));
 const totpLogin = ref(false);
+const isBackupCode = ref(false);
 const queryingKey = ref(false);
 const credentialRequest = ref<CredentialRequestOptions | null>(null);
 
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
index 2608560cc4e9..2ef664b9a363 100644
--- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkSpacer :marginMin="20" :marginMax="28">
 						<div class="_gaps">
 							<div>{{ i18n.ts._2fa.step3Title }}</div>
-							<MkInput v-model="token" autocomplete="one-time-code"></MkInput>
+							<MkInput v-model="token" autocomplete="one-time-code" inputmode="numeric"></MkInput>
 							<div>{{ i18n.ts._2fa.step3 }}</div>
 						</div>
 						<div class="_buttonsCenter" style="margin-top: 16px;">
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index d8c5f848feff..975f23cdd1de 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -80,7 +80,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import * as os from '@/os.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired, updateAccount } from '@/account.js';
 import { i18n } from '@/i18n.js';
 
 const $i = signinRequired();
@@ -116,6 +116,10 @@ async function unregisterTOTP(): Promise<void> {
 	os.apiWithDialog('i/2fa/unregister', {
 		password: auth.result.password,
 		token: auth.result.token,
+	}).then(res => {
+		updateAccount({
+			twoFactorEnabled: false,
+		});
 	}).catch(error => {
 		os.alert({
 			type: 'error',

From 6bd78770de06bd3694127da17ccd051f05057329 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 22 Mar 2024 18:21:14 +0900
Subject: [PATCH 123/266] =?UTF-8?q?enhance(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E5=8F=97=E3=81=91=E5=85=A5?=
 =?UTF-8?q?=E3=82=8C=E3=81=8C=E3=81=84=E3=81=84=E3=81=AD=E3=81=AE=E3=81=BF?=
 =?UTF-8?q?=E3=81=AE=E5=A0=B4=E5=90=88=E3=81=AF=E3=83=9C=E3=82=BF=E3=83=B3?=
 =?UTF-8?q?=E3=83=9B=E3=83=90=E3=83=BC=E3=81=A7=E3=83=84=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=83=81=E3=83=83=E3=83=97=E3=81=8C=E5=87=BA=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#13613)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkNote.vue   | 25 ++++++++++++++++++-
 .../src/components/MkNoteDetailed.vue         | 25 ++++++++++++++++++-
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 50741f2cb7b1..4add3aa6dd14 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -167,6 +167,7 @@ import MkNoteSub from '@/components/MkNoteSub.vue';
 import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkNoteSimple from '@/components/MkNoteSimple.vue';
 import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
+import MkReactionsViewerDetails from '@/components/MkReactionsViewer.details.vue';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
 import MkPoll from '@/components/MkPoll.vue';
@@ -180,7 +181,7 @@ import { userPage } from '@/filters/user.js';
 import number from '@/filters/number.js';
 import * as os from '@/os.js';
 import * as sound from '@/scripts/sound.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import { defaultStore, noteViewInterruptors } from '@/store.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
@@ -340,6 +341,28 @@ if (!props.mock) {
 			targetElement: renoteButton.value,
 		}, {}, 'closed');
 	});
+
+	if (appearNote.value.reactionAcceptance === 'likeOnly') {
+		useTooltip(reactButton, async (showing) => {
+			const reactions = await misskeyApiGet('notes/reactions', {
+				noteId: appearNote.value.id,
+				limit: 10,
+				_cacheKey_: appearNote.value.reactionCount,
+			});
+
+			const users = reactions.map(x => x.user);
+
+			if (users.length < 1) return;
+
+			os.popup(MkReactionsViewerDetails, {
+				showing,
+				reaction: '❤️',
+				users,
+				count: appearNote.value.reactionCount,
+				targetElement: reactButton.value!,
+			}, {}, 'closed');
+		});
+	}
 }
 
 function renote(viaKeyboard = false) {
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 1b7dcda409c7..ebccd6098608 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -201,6 +201,7 @@ import * as Misskey from 'misskey-js';
 import MkNoteSub from '@/components/MkNoteSub.vue';
 import MkNoteSimple from '@/components/MkNoteSimple.vue';
 import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
+import MkReactionsViewerDetails from '@/components/MkReactionsViewer.details.vue';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
 import MkPoll from '@/components/MkPoll.vue';
@@ -213,7 +214,7 @@ import { userPage } from '@/filters/user.js';
 import { notePage } from '@/filters/note.js';
 import number from '@/filters/number.js';
 import * as os from '@/os.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import * as sound from '@/scripts/sound.js';
 import { defaultStore, noteViewInterruptors } from '@/store.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
@@ -348,6 +349,28 @@ useTooltip(renoteButton, async (showing) => {
 	}, {}, 'closed');
 });
 
+if (appearNote.value.reactionAcceptance === 'likeOnly') {
+	useTooltip(reactButton, async (showing) => {
+		const reactions = await misskeyApiGet('notes/reactions', {
+			noteId: appearNote.value.id,
+			limit: 10,
+			_cacheKey_: appearNote.value.reactionCount,
+		});
+
+		const users = reactions.map(x => x.user);
+
+		if (users.length < 1) return;
+
+		os.popup(MkReactionsViewerDetails, {
+			showing,
+			reaction: '❤️',
+			users,
+			count: appearNote.value.reactionCount,
+			targetElement: reactButton.value!,
+		}, {}, 'closed');
+	});
+}
+
 function renote(viaKeyboard = false) {
 	pleaseLogin();
 	showMovedDialog();

From 3db26f2b94af6cc981f1305ddd4da20401aa2910 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 23 Mar 2024 20:43:29 +0900
Subject: [PATCH 124/266] fix(backend): fix openAPI operationId format

---
 packages/backend/src/server/api/openapi/gen-spec.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index 7679a9b4648a..2a14270a2497 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -93,7 +93,7 @@ export function genOpenapiSpec(config: Config, includeSelfRef = false) {
 		const hasBody = (schema.type === 'object' && schema.properties && Object.keys(schema.properties).length >= 1);
 
 		const info = {
-			operationId: endpoint.name,
+			operationId: endpoint.name.replaceAll('/', '___'), // NOTE: スラッシュは使えない
 			summary: endpoint.name,
 			description: desc,
 			externalDocs: {

From 539718f6a86cac21bd47106e206b888135e2a89d Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 24 Mar 2024 16:46:15 +0900
Subject: [PATCH 125/266] fix(misskey-js): fix ESLint error in generator due to
 `operationId` change (#13619)

---
 packages/misskey-js/generator/src/generator.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/misskey-js/generator/src/generator.ts b/packages/misskey-js/generator/src/generator.ts
index f091e599a921..49dcd4c1edab 100644
--- a/packages/misskey-js/generator/src/generator.ts
+++ b/packages/misskey-js/generator/src/generator.ts
@@ -98,6 +98,8 @@ async function generateEndpoints(
 
 	const entitiesOutputLine: string[] = [];
 
+	entitiesOutputLine.push('/* eslint @typescript-eslint/naming-convention: 0 */');
+
 	entitiesOutputLine.push(`import { operations } from '${toImportPath(typeFileName)}';`);
 	entitiesOutputLine.push('');
 

From a1bc8fa77b0820907399d010f56c2169f6898e8b Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 24 Mar 2024 16:46:52 +0900
Subject: [PATCH 126/266] test(backend): fix streaming test error when replying
 to followers-only note (#13618)

---
 packages/backend/test/e2e/streaming.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index edb930617fff..26bb68ec6c83 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -158,19 +158,17 @@ describe('Streaming', () => {
 				assert.strictEqual(fired, true);
 			});
 
-			/* なんか失敗する
 			test('フォローしているユーザーの visibility: followers な投稿への返信が流れる', async () => {
-				const note = await api('notes/create', { text: 'foo', visibility: 'followers' }, kyoko);
+				const note = await post(kyoko, { text: 'foo', visibility: 'followers' });
 
 				const fired = await waitFire(
 					ayano, 'homeTimeline',		// ayano:home
-					() => api('notes/create', { text: 'bar', visibility: 'followers', replyId: note.body.id }, kyoko),	// kyoko posts
+					() => api('notes/create', { text: 'bar', visibility: 'followers', replyId: note.id }, kyoko),	// kyoko posts
 					msg => msg.type === 'note' && msg.body.userId === kyoko.id && msg.body.reply.text === 'foo',
 				);
 
 				assert.strictEqual(fired, true);
 			});
-			*/
 
 			test('フォローしているユーザーのフォローしていないユーザーの visibility: followers な投稿への返信が流れない', async () => {
 				const chitoseNote = await post(chitose, { text: 'followers-only post', visibility: 'followers' });

From 8f415d69cd2e459c6a8ac46034d5eb09b91e441f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 25 Mar 2024 12:11:10 +0900
Subject: [PATCH 127/266] =?UTF-8?q?fix(generator):=20API=E3=82=AF=E3=83=A9?=
 =?UTF-8?q?=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88=E3=81=AE=E3=83=91=E3=82=B9?=
 =?UTF-8?q?=E3=81=ABoperationId=E3=81=8C=E4=BD=BF=E3=82=8F=E3=82=8C?=
 =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1362?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/misskey-js/etc/misskey-js.api.md     | 1036 ++++++------
 .../misskey-js/generator/src/generator.ts     |   39 +-
 packages/misskey-js/src/autogen/entities.ts   | 1037 ++++++------
 packages/misskey-js/src/autogen/types.ts      | 1410 ++++++++---------
 4 files changed, 1770 insertions(+), 1752 deletions(-)

diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 2237d278f46f..360724d2a9e3 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -29,298 +29,298 @@ type Ad = components['schemas']['Ad'];
 // Warning: (ae-forgotten-export) The symbol "operations" needs to be exported by the entry point index.d.ts
 //
 // @public (undocumented)
-type AdminAbuseUserReportsRequest = operations['admin/abuse-user-reports']['requestBody']['content']['application/json'];
+type AdminAbuseUserReportsRequest = operations['admin___abuse-user-reports']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAbuseUserReportsResponse = operations['admin/abuse-user-reports']['responses']['200']['content']['application/json'];
+type AdminAbuseUserReportsResponse = operations['admin___abuse-user-reports']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAccountsCreateRequest = operations['admin/accounts/create']['requestBody']['content']['application/json'];
+type AdminAccountsCreateRequest = operations['admin___accounts___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAccountsCreateResponse = operations['admin/accounts/create']['responses']['200']['content']['application/json'];
+type AdminAccountsCreateResponse = operations['admin___accounts___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBody']['content']['application/json'];
+type AdminAccountsDeleteRequest = operations['admin___accounts___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json'];
+type AdminAccountsFindByEmailRequest = operations['admin___accounts___find-by-email']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAccountsFindByEmailResponse = operations['admin/accounts/find-by-email']['responses']['200']['content']['application/json'];
+type AdminAccountsFindByEmailResponse = operations['admin___accounts___find-by-email']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json'];
+type AdminAdCreateRequest = operations['admin___ad___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAdCreateResponse = operations['admin/ad/create']['responses']['200']['content']['application/json'];
+type AdminAdCreateResponse = operations['admin___ad___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json'];
+type AdminAdDeleteRequest = operations['admin___ad___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json'];
+type AdminAdListRequest = operations['admin___ad___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAdListResponse = operations['admin/ad/list']['responses']['200']['content']['application/json'];
+type AdminAdListResponse = operations['admin___ad___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json'];
+type AdminAdUpdateRequest = operations['admin___ad___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAnnouncementsCreateRequest = operations['admin/announcements/create']['requestBody']['content']['application/json'];
+type AdminAnnouncementsCreateRequest = operations['admin___announcements___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAnnouncementsCreateResponse = operations['admin/announcements/create']['responses']['200']['content']['application/json'];
+type AdminAnnouncementsCreateResponse = operations['admin___announcements___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAnnouncementsDeleteRequest = operations['admin/announcements/delete']['requestBody']['content']['application/json'];
+type AdminAnnouncementsDeleteRequest = operations['admin___announcements___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAnnouncementsListRequest = operations['admin/announcements/list']['requestBody']['content']['application/json'];
+type AdminAnnouncementsListRequest = operations['admin___announcements___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAnnouncementsListResponse = operations['admin/announcements/list']['responses']['200']['content']['application/json'];
+type AdminAnnouncementsListResponse = operations['admin___announcements___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAnnouncementsUpdateRequest = operations['admin/announcements/update']['requestBody']['content']['application/json'];
+type AdminAnnouncementsUpdateRequest = operations['admin___announcements___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAvatarDecorationsCreateRequest = operations['admin/avatar-decorations/create']['requestBody']['content']['application/json'];
+type AdminAvatarDecorationsCreateRequest = operations['admin___avatar-decorations___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAvatarDecorationsDeleteRequest = operations['admin/avatar-decorations/delete']['requestBody']['content']['application/json'];
+type AdminAvatarDecorationsDeleteRequest = operations['admin___avatar-decorations___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAvatarDecorationsListRequest = operations['admin/avatar-decorations/list']['requestBody']['content']['application/json'];
+type AdminAvatarDecorationsListRequest = operations['admin___avatar-decorations___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAvatarDecorationsListResponse = operations['admin/avatar-decorations/list']['responses']['200']['content']['application/json'];
+type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminAvatarDecorationsUpdateRequest = operations['admin/avatar-decorations/update']['requestBody']['content']['application/json'];
+type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminDeleteAccountRequest = operations['admin/delete-account']['requestBody']['content']['application/json'];
+type AdminDeleteAccountRequest = operations['admin___delete-account']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminDeleteAllFilesOfAUserRequest = operations['admin/delete-all-files-of-a-user']['requestBody']['content']['application/json'];
+type AdminDeleteAllFilesOfAUserRequest = operations['admin___delete-all-files-of-a-user']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminDriveFilesRequest = operations['admin/drive/files']['requestBody']['content']['application/json'];
+type AdminDriveFilesRequest = operations['admin___drive___files']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminDriveFilesResponse = operations['admin/drive/files']['responses']['200']['content']['application/json'];
+type AdminDriveFilesResponse = operations['admin___drive___files']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminDriveShowFileRequest = operations['admin/drive/show-file']['requestBody']['content']['application/json'];
+type AdminDriveShowFileRequest = operations['admin___drive___show-file']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json'];
+type AdminDriveShowFileResponse = operations['admin___drive___show-file']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json'];
+type AdminEmojiAddAliasesBulkRequest = operations['admin___emoji___add-aliases-bulk']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
+type AdminEmojiAddRequest = operations['admin___emoji___add']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json'];
+type AdminEmojiAddResponse = operations['admin___emoji___add']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
+type AdminEmojiCopyRequest = operations['admin___emoji___copy']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
+type AdminEmojiCopyResponse = operations['admin___emoji___copy']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
+type AdminEmojiDeleteBulkRequest = operations['admin___emoji___delete-bulk']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
+type AdminEmojiDeleteRequest = operations['admin___emoji___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiImportZipRequest = operations['admin/emoji/import-zip']['requestBody']['content']['application/json'];
+type AdminEmojiImportZipRequest = operations['admin___emoji___import-zip']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
+type AdminEmojiListRemoteRequest = operations['admin___emoji___list-remote']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiListRemoteResponse = operations['admin/emoji/list-remote']['responses']['200']['content']['application/json'];
+type AdminEmojiListRemoteResponse = operations['admin___emoji___list-remote']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiListRequest = operations['admin/emoji/list']['requestBody']['content']['application/json'];
+type AdminEmojiListRequest = operations['admin___emoji___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiListResponse = operations['admin/emoji/list']['responses']['200']['content']['application/json'];
+type AdminEmojiListResponse = operations['admin___emoji___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiRemoveAliasesBulkRequest = operations['admin/emoji/remove-aliases-bulk']['requestBody']['content']['application/json'];
+type AdminEmojiRemoveAliasesBulkRequest = operations['admin___emoji___remove-aliases-bulk']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiSetAliasesBulkRequest = operations['admin/emoji/set-aliases-bulk']['requestBody']['content']['application/json'];
+type AdminEmojiSetAliasesBulkRequest = operations['admin___emoji___set-aliases-bulk']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiSetCategoryBulkRequest = operations['admin/emoji/set-category-bulk']['requestBody']['content']['application/json'];
+type AdminEmojiSetCategoryBulkRequest = operations['admin___emoji___set-category-bulk']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiSetLicenseBulkRequest = operations['admin/emoji/set-license-bulk']['requestBody']['content']['application/json'];
+type AdminEmojiSetLicenseBulkRequest = operations['admin___emoji___set-license-bulk']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminEmojiUpdateRequest = operations['admin/emoji/update']['requestBody']['content']['application/json'];
+type AdminEmojiUpdateRequest = operations['admin___emoji___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminFederationDeleteAllFilesRequest = operations['admin/federation/delete-all-files']['requestBody']['content']['application/json'];
+type AdminFederationDeleteAllFilesRequest = operations['admin___federation___delete-all-files']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
+type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin___federation___refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json'];
+type AdminFederationRemoveAllFollowingRequest = operations['admin___federation___remove-all-following']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
+type AdminFederationUpdateInstanceRequest = operations['admin___federation___update-instance']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminGetIndexStatsResponse = operations['admin/get-index-stats']['responses']['200']['content']['application/json'];
+type AdminGetIndexStatsResponse = operations['admin___get-index-stats']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
+type AdminGetTableStatsResponse = operations['admin___get-table-stats']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
+type AdminGetUserIpsRequest = operations['admin___get-user-ips']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminGetUserIpsResponse = operations['admin/get-user-ips']['responses']['200']['content']['application/json'];
+type AdminGetUserIpsResponse = operations['admin___get-user-ips']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
+type AdminInviteCreateRequest = operations['admin___invite___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminInviteCreateResponse = operations['admin/invite/create']['responses']['200']['content']['application/json'];
+type AdminInviteCreateResponse = operations['admin___invite___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminInviteListRequest = operations['admin/invite/list']['requestBody']['content']['application/json'];
+type AdminInviteListRequest = operations['admin___invite___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminInviteListResponse = operations['admin/invite/list']['responses']['200']['content']['application/json'];
+type AdminInviteListResponse = operations['admin___invite___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminMetaResponse = operations['admin/meta']['responses']['200']['content']['application/json'];
+type AdminMetaResponse = operations['admin___meta']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminPromoCreateRequest = operations['admin/promo/create']['requestBody']['content']['application/json'];
+type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminQueueDeliverDelayedResponse = operations['admin/queue/deliver-delayed']['responses']['200']['content']['application/json'];
+type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminQueueInboxDelayedResponse = operations['admin/queue/inbox-delayed']['responses']['200']['content']['application/json'];
+type AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminQueuePromoteRequest = operations['admin/queue/promote']['requestBody']['content']['application/json'];
+type AdminQueuePromoteRequest = operations['admin___queue___promote']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminQueueStatsResponse = operations['admin/queue/stats']['responses']['200']['content']['application/json'];
+type AdminQueueStatsResponse = operations['admin___queue___stats']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRelaysAddRequest = operations['admin/relays/add']['requestBody']['content']['application/json'];
+type AdminRelaysAddRequest = operations['admin___relays___add']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRelaysAddResponse = operations['admin/relays/add']['responses']['200']['content']['application/json'];
+type AdminRelaysAddResponse = operations['admin___relays___add']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRelaysListResponse = operations['admin/relays/list']['responses']['200']['content']['application/json'];
+type AdminRelaysListResponse = operations['admin___relays___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRelaysRemoveRequest = operations['admin/relays/remove']['requestBody']['content']['application/json'];
+type AdminRelaysRemoveRequest = operations['admin___relays___remove']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminResetPasswordRequest = operations['admin/reset-password']['requestBody']['content']['application/json'];
+type AdminResetPasswordRequest = operations['admin___reset-password']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminResetPasswordResponse = operations['admin/reset-password']['responses']['200']['content']['application/json'];
+type AdminResetPasswordResponse = operations['admin___reset-password']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminResolveAbuseUserReportRequest = operations['admin/resolve-abuse-user-report']['requestBody']['content']['application/json'];
+type AdminResolveAbuseUserReportRequest = operations['admin___resolve-abuse-user-report']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesAssignRequest = operations['admin/roles/assign']['requestBody']['content']['application/json'];
+type AdminRolesAssignRequest = operations['admin___roles___assign']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json'];
+type AdminRolesCreateRequest = operations['admin___roles___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesCreateResponse = operations['admin/roles/create']['responses']['200']['content']['application/json'];
+type AdminRolesCreateResponse = operations['admin___roles___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesDeleteRequest = operations['admin/roles/delete']['requestBody']['content']['application/json'];
+type AdminRolesDeleteRequest = operations['admin___roles___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesListResponse = operations['admin/roles/list']['responses']['200']['content']['application/json'];
+type AdminRolesListResponse = operations['admin___roles___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesShowRequest = operations['admin/roles/show']['requestBody']['content']['application/json'];
+type AdminRolesShowRequest = operations['admin___roles___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesShowResponse = operations['admin/roles/show']['responses']['200']['content']['application/json'];
+type AdminRolesShowResponse = operations['admin___roles___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
+type AdminRolesUnassignRequest = operations['admin___roles___unassign']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesUpdateDefaultPoliciesRequest = operations['admin/roles/update-default-policies']['requestBody']['content']['application/json'];
+type AdminRolesUpdateDefaultPoliciesRequest = operations['admin___roles___update-default-policies']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody']['content']['application/json'];
+type AdminRolesUpdateRequest = operations['admin___roles___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json'];
+type AdminRolesUsersRequest = operations['admin___roles___users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminRolesUsersResponse = operations['admin/roles/users']['responses']['200']['content']['application/json'];
+type AdminRolesUsersResponse = operations['admin___roles___users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminSendEmailRequest = operations['admin/send-email']['requestBody']['content']['application/json'];
+type AdminSendEmailRequest = operations['admin___send-email']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminServerInfoResponse = operations['admin/server-info']['responses']['200']['content']['application/json'];
+type AdminServerInfoResponse = operations['admin___server-info']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminShowModerationLogsRequest = operations['admin/show-moderation-logs']['requestBody']['content']['application/json'];
+type AdminShowModerationLogsRequest = operations['admin___show-moderation-logs']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminShowModerationLogsResponse = operations['admin/show-moderation-logs']['responses']['200']['content']['application/json'];
+type AdminShowModerationLogsResponse = operations['admin___show-moderation-logs']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminShowUserRequest = operations['admin/show-user']['requestBody']['content']['application/json'];
+type AdminShowUserRequest = operations['admin___show-user']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminShowUserResponse = operations['admin/show-user']['responses']['200']['content']['application/json'];
+type AdminShowUserResponse = operations['admin___show-user']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['content']['application/json'];
+type AdminShowUsersRequest = operations['admin___show-users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json'];
+type AdminShowUsersResponse = operations['admin___show-users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json'];
+type AdminSuspendUserRequest = operations['admin___suspend-user']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminUnsetUserAvatarRequest = operations['admin/unset-user-avatar']['requestBody']['content']['application/json'];
+type AdminUnsetUserAvatarRequest = operations['admin___unset-user-avatar']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminUnsetUserBannerRequest = operations['admin/unset-user-banner']['requestBody']['content']['application/json'];
+type AdminUnsetUserBannerRequest = operations['admin___unset-user-banner']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json'];
+type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['content']['application/json'];
+type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json'];
+type AdminUpdateUserNoteRequest = operations['admin___update-user-note']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type Announcement = components['schemas']['Announcement'];
@@ -340,40 +340,40 @@ type AnnouncementsResponse = operations['announcements']['responses']['200']['co
 type Antenna = components['schemas']['Antenna'];
 
 // @public (undocumented)
-type AntennasCreateRequest = operations['antennas/create']['requestBody']['content']['application/json'];
+type AntennasCreateRequest = operations['antennas___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasCreateResponse = operations['antennas/create']['responses']['200']['content']['application/json'];
+type AntennasCreateResponse = operations['antennas___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasDeleteRequest = operations['antennas/delete']['requestBody']['content']['application/json'];
+type AntennasDeleteRequest = operations['antennas___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasListResponse = operations['antennas/list']['responses']['200']['content']['application/json'];
+type AntennasListResponse = operations['antennas___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasNotesRequest = operations['antennas/notes']['requestBody']['content']['application/json'];
+type AntennasNotesRequest = operations['antennas___notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasNotesResponse = operations['antennas/notes']['responses']['200']['content']['application/json'];
+type AntennasNotesResponse = operations['antennas___notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasShowRequest = operations['antennas/show']['requestBody']['content']['application/json'];
+type AntennasShowRequest = operations['antennas___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasShowResponse = operations['antennas/show']['responses']['200']['content']['application/json'];
+type AntennasShowResponse = operations['antennas___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasUpdateRequest = operations['antennas/update']['requestBody']['content']['application/json'];
+type AntennasUpdateRequest = operations['antennas___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AntennasUpdateResponse = operations['antennas/update']['responses']['200']['content']['application/json'];
+type AntennasUpdateResponse = operations['antennas___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ApGetRequest = operations['ap/get']['requestBody']['content']['application/json'];
+type ApGetRequest = operations['ap___get']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ApGetResponse = operations['ap/get']['responses']['200']['content']['application/json'];
+type ApGetResponse = operations['ap___get']['responses']['200']['content']['application/json'];
 
 declare namespace api {
     export {
@@ -414,73 +414,73 @@ type APIError = {
 type App = components['schemas']['App'];
 
 // @public (undocumented)
-type AppCreateRequest = operations['app/create']['requestBody']['content']['application/json'];
+type AppCreateRequest = operations['app___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AppCreateResponse = operations['app/create']['responses']['200']['content']['application/json'];
+type AppCreateResponse = operations['app___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AppShowRequest = operations['app/show']['requestBody']['content']['application/json'];
+type AppShowRequest = operations['app___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AppShowResponse = operations['app/show']['responses']['200']['content']['application/json'];
+type AppShowResponse = operations['app___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ApShowRequest = operations['ap/show']['requestBody']['content']['application/json'];
+type ApShowRequest = operations['ap___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ApShowResponse = operations['ap/show']['responses']['200']['content']['application/json'];
+type ApShowResponse = operations['ap___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AuthAcceptRequest = operations['auth/accept']['requestBody']['content']['application/json'];
+type AuthAcceptRequest = operations['auth___accept']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
+type AuthSessionGenerateRequest = operations['auth___session___generate']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AuthSessionGenerateResponse = operations['auth/session/generate']['responses']['200']['content']['application/json'];
+type AuthSessionGenerateResponse = operations['auth___session___generate']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AuthSessionShowRequest = operations['auth/session/show']['requestBody']['content']['application/json'];
+type AuthSessionShowRequest = operations['auth___session___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AuthSessionShowResponse = operations['auth/session/show']['responses']['200']['content']['application/json'];
+type AuthSessionShowResponse = operations['auth___session___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type AuthSessionUserkeyRequest = operations['auth/session/userkey']['requestBody']['content']['application/json'];
+type AuthSessionUserkeyRequest = operations['auth___session___userkey']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AuthSessionUserkeyResponse = operations['auth/session/userkey']['responses']['200']['content']['application/json'];
+type AuthSessionUserkeyResponse = operations['auth___session___userkey']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type Blocking = components['schemas']['Blocking'];
 
 // @public (undocumented)
-type BlockingCreateRequest = operations['blocking/create']['requestBody']['content']['application/json'];
+type BlockingCreateRequest = operations['blocking___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type BlockingCreateResponse = operations['blocking/create']['responses']['200']['content']['application/json'];
+type BlockingCreateResponse = operations['blocking___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type BlockingDeleteRequest = operations['blocking/delete']['requestBody']['content']['application/json'];
+type BlockingDeleteRequest = operations['blocking___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type BlockingDeleteResponse = operations['blocking/delete']['responses']['200']['content']['application/json'];
+type BlockingDeleteResponse = operations['blocking___delete']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type BlockingListRequest = operations['blocking/list']['requestBody']['content']['application/json'];
+type BlockingListRequest = operations['blocking___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type BlockingListResponse = operations['blocking/list']['responses']['200']['content']['application/json'];
+type BlockingListResponse = operations['blocking___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type BubbleGameRankingRequest = operations['bubble-game/ranking']['requestBody']['content']['application/json'];
+type BubbleGameRankingRequest = operations['bubble-game___ranking']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type BubbleGameRankingResponse = operations['bubble-game/ranking']['responses']['200']['content']['application/json'];
+type BubbleGameRankingResponse = operations['bubble-game___ranking']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type BubbleGameRegisterRequest = operations['bubble-game/register']['requestBody']['content']['application/json'];
+type BubbleGameRegisterRequest = operations['bubble-game___register']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type Channel = components['schemas']['Channel'];
@@ -732,184 +732,184 @@ export type Channels = {
 };
 
 // @public (undocumented)
-type ChannelsCreateRequest = operations['channels/create']['requestBody']['content']['application/json'];
+type ChannelsCreateRequest = operations['channels___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsCreateResponse = operations['channels/create']['responses']['200']['content']['application/json'];
+type ChannelsCreateResponse = operations['channels___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsFavoriteRequest = operations['channels/favorite']['requestBody']['content']['application/json'];
+type ChannelsFavoriteRequest = operations['channels___favorite']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsFeaturedResponse = operations['channels/featured']['responses']['200']['content']['application/json'];
+type ChannelsFeaturedResponse = operations['channels___featured']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsFollowedRequest = operations['channels/followed']['requestBody']['content']['application/json'];
+type ChannelsFollowedRequest = operations['channels___followed']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsFollowedResponse = operations['channels/followed']['responses']['200']['content']['application/json'];
+type ChannelsFollowedResponse = operations['channels___followed']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsFollowRequest = operations['channels/follow']['requestBody']['content']['application/json'];
+type ChannelsFollowRequest = operations['channels___follow']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsMyFavoritesResponse = operations['channels/my-favorites']['responses']['200']['content']['application/json'];
+type ChannelsMyFavoritesResponse = operations['channels___my-favorites']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsOwnedRequest = operations['channels/owned']['requestBody']['content']['application/json'];
+type ChannelsOwnedRequest = operations['channels___owned']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsOwnedResponse = operations['channels/owned']['responses']['200']['content']['application/json'];
+type ChannelsOwnedResponse = operations['channels___owned']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsSearchRequest = operations['channels/search']['requestBody']['content']['application/json'];
+type ChannelsSearchRequest = operations['channels___search']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsSearchResponse = operations['channels/search']['responses']['200']['content']['application/json'];
+type ChannelsSearchResponse = operations['channels___search']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsShowRequest = operations['channels/show']['requestBody']['content']['application/json'];
+type ChannelsShowRequest = operations['channels___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsShowResponse = operations['channels/show']['responses']['200']['content']['application/json'];
+type ChannelsShowResponse = operations['channels___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsTimelineRequest = operations['channels/timeline']['requestBody']['content']['application/json'];
+type ChannelsTimelineRequest = operations['channels___timeline']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsTimelineResponse = operations['channels/timeline']['responses']['200']['content']['application/json'];
+type ChannelsTimelineResponse = operations['channels___timeline']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsUnfavoriteRequest = operations['channels/unfavorite']['requestBody']['content']['application/json'];
+type ChannelsUnfavoriteRequest = operations['channels___unfavorite']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsUnfollowRequest = operations['channels/unfollow']['requestBody']['content']['application/json'];
+type ChannelsUnfollowRequest = operations['channels___unfollow']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsUpdateRequest = operations['channels/update']['requestBody']['content']['application/json'];
+type ChannelsUpdateRequest = operations['channels___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChannelsUpdateResponse = operations['channels/update']['responses']['200']['content']['application/json'];
+type ChannelsUpdateResponse = operations['channels___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsActiveUsersRequest = operations['charts/active-users']['requestBody']['content']['application/json'];
+type ChartsActiveUsersRequest = operations['charts___active-users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsActiveUsersResponse = operations['charts/active-users']['responses']['200']['content']['application/json'];
+type ChartsActiveUsersResponse = operations['charts___active-users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsApRequestRequest = operations['charts/ap-request']['requestBody']['content']['application/json'];
+type ChartsApRequestRequest = operations['charts___ap-request']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsApRequestResponse = operations['charts/ap-request']['responses']['200']['content']['application/json'];
+type ChartsApRequestResponse = operations['charts___ap-request']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsDriveRequest = operations['charts/drive']['requestBody']['content']['application/json'];
+type ChartsDriveRequest = operations['charts___drive']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsDriveResponse = operations['charts/drive']['responses']['200']['content']['application/json'];
+type ChartsDriveResponse = operations['charts___drive']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsFederationRequest = operations['charts/federation']['requestBody']['content']['application/json'];
+type ChartsFederationRequest = operations['charts___federation']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsFederationResponse = operations['charts/federation']['responses']['200']['content']['application/json'];
+type ChartsFederationResponse = operations['charts___federation']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsInstanceRequest = operations['charts/instance']['requestBody']['content']['application/json'];
+type ChartsInstanceRequest = operations['charts___instance']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsInstanceResponse = operations['charts/instance']['responses']['200']['content']['application/json'];
+type ChartsInstanceResponse = operations['charts___instance']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsNotesRequest = operations['charts/notes']['requestBody']['content']['application/json'];
+type ChartsNotesRequest = operations['charts___notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsNotesResponse = operations['charts/notes']['responses']['200']['content']['application/json'];
+type ChartsNotesResponse = operations['charts___notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserDriveRequest = operations['charts/user/drive']['requestBody']['content']['application/json'];
+type ChartsUserDriveRequest = operations['charts___user___drive']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserDriveResponse = operations['charts/user/drive']['responses']['200']['content']['application/json'];
+type ChartsUserDriveResponse = operations['charts___user___drive']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserFollowingRequest = operations['charts/user/following']['requestBody']['content']['application/json'];
+type ChartsUserFollowingRequest = operations['charts___user___following']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserFollowingResponse = operations['charts/user/following']['responses']['200']['content']['application/json'];
+type ChartsUserFollowingResponse = operations['charts___user___following']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserNotesRequest = operations['charts/user/notes']['requestBody']['content']['application/json'];
+type ChartsUserNotesRequest = operations['charts___user___notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserNotesResponse = operations['charts/user/notes']['responses']['200']['content']['application/json'];
+type ChartsUserNotesResponse = operations['charts___user___notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserPvRequest = operations['charts/user/pv']['requestBody']['content']['application/json'];
+type ChartsUserPvRequest = operations['charts___user___pv']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserPvResponse = operations['charts/user/pv']['responses']['200']['content']['application/json'];
+type ChartsUserPvResponse = operations['charts___user___pv']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserReactionsRequest = operations['charts/user/reactions']['requestBody']['content']['application/json'];
+type ChartsUserReactionsRequest = operations['charts___user___reactions']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUserReactionsResponse = operations['charts/user/reactions']['responses']['200']['content']['application/json'];
+type ChartsUserReactionsResponse = operations['charts___user___reactions']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUsersRequest = operations['charts/users']['requestBody']['content']['application/json'];
+type ChartsUsersRequest = operations['charts___users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ChartsUsersResponse = operations['charts/users']['responses']['200']['content']['application/json'];
+type ChartsUsersResponse = operations['charts___users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type Clip = components['schemas']['Clip'];
 
 // @public (undocumented)
-type ClipsAddNoteRequest = operations['clips/add-note']['requestBody']['content']['application/json'];
+type ClipsAddNoteRequest = operations['clips___add-note']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsCreateRequest = operations['clips/create']['requestBody']['content']['application/json'];
+type ClipsCreateRequest = operations['clips___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsCreateResponse = operations['clips/create']['responses']['200']['content']['application/json'];
+type ClipsCreateResponse = operations['clips___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsDeleteRequest = operations['clips/delete']['requestBody']['content']['application/json'];
+type ClipsDeleteRequest = operations['clips___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsFavoriteRequest = operations['clips/favorite']['requestBody']['content']['application/json'];
+type ClipsFavoriteRequest = operations['clips___favorite']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsListResponse = operations['clips/list']['responses']['200']['content']['application/json'];
+type ClipsListResponse = operations['clips___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsMyFavoritesResponse = operations['clips/my-favorites']['responses']['200']['content']['application/json'];
+type ClipsMyFavoritesResponse = operations['clips___my-favorites']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsNotesRequest = operations['clips/notes']['requestBody']['content']['application/json'];
+type ClipsNotesRequest = operations['clips___notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsNotesResponse = operations['clips/notes']['responses']['200']['content']['application/json'];
+type ClipsNotesResponse = operations['clips___notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsRemoveNoteRequest = operations['clips/remove-note']['requestBody']['content']['application/json'];
+type ClipsRemoveNoteRequest = operations['clips___remove-note']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsShowRequest = operations['clips/show']['requestBody']['content']['application/json'];
+type ClipsShowRequest = operations['clips___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsShowResponse = operations['clips/show']['responses']['200']['content']['application/json'];
+type ClipsShowResponse = operations['clips___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsUnfavoriteRequest = operations['clips/unfavorite']['requestBody']['content']['application/json'];
+type ClipsUnfavoriteRequest = operations['clips___unfavorite']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsUpdateRequest = operations['clips/update']['requestBody']['content']['application/json'];
+type ClipsUpdateRequest = operations['clips___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ClipsUpdateResponse = operations['clips/update']['responses']['200']['content']['application/json'];
+type ClipsUpdateResponse = operations['clips___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type DateString = string;
@@ -918,109 +918,109 @@ type DateString = string;
 type DriveFile = components['schemas']['DriveFile'];
 
 // @public (undocumented)
-type DriveFilesAttachedNotesRequest = operations['drive/files/attached-notes']['requestBody']['content']['application/json'];
+type DriveFilesAttachedNotesRequest = operations['drive___files___attached-notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesAttachedNotesResponse = operations['drive/files/attached-notes']['responses']['200']['content']['application/json'];
+type DriveFilesAttachedNotesResponse = operations['drive___files___attached-notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesCheckExistenceRequest = operations['drive/files/check-existence']['requestBody']['content']['application/json'];
+type DriveFilesCheckExistenceRequest = operations['drive___files___check-existence']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesCheckExistenceResponse = operations['drive/files/check-existence']['responses']['200']['content']['application/json'];
+type DriveFilesCheckExistenceResponse = operations['drive___files___check-existence']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesCreateRequest = operations['drive/files/create']['requestBody']['content']['multipart/form-data'];
+type DriveFilesCreateRequest = operations['drive___files___create']['requestBody']['content']['multipart/form-data'];
 
 // @public (undocumented)
-type DriveFilesCreateResponse = operations['drive/files/create']['responses']['200']['content']['application/json'];
+type DriveFilesCreateResponse = operations['drive___files___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesDeleteRequest = operations['drive/files/delete']['requestBody']['content']['application/json'];
+type DriveFilesDeleteRequest = operations['drive___files___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesFindByHashRequest = operations['drive/files/find-by-hash']['requestBody']['content']['application/json'];
+type DriveFilesFindByHashRequest = operations['drive___files___find-by-hash']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesFindByHashResponse = operations['drive/files/find-by-hash']['responses']['200']['content']['application/json'];
+type DriveFilesFindByHashResponse = operations['drive___files___find-by-hash']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesFindRequest = operations['drive/files/find']['requestBody']['content']['application/json'];
+type DriveFilesFindRequest = operations['drive___files___find']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesFindResponse = operations['drive/files/find']['responses']['200']['content']['application/json'];
+type DriveFilesFindResponse = operations['drive___files___find']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesRequest = operations['drive/files']['requestBody']['content']['application/json'];
+type DriveFilesRequest = operations['drive___files']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesResponse = operations['drive/files']['responses']['200']['content']['application/json'];
+type DriveFilesResponse = operations['drive___files']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesShowRequest = operations['drive/files/show']['requestBody']['content']['application/json'];
+type DriveFilesShowRequest = operations['drive___files___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesShowResponse = operations['drive/files/show']['responses']['200']['content']['application/json'];
+type DriveFilesShowResponse = operations['drive___files___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesUpdateRequest = operations['drive/files/update']['requestBody']['content']['application/json'];
+type DriveFilesUpdateRequest = operations['drive___files___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesUpdateResponse = operations['drive/files/update']['responses']['200']['content']['application/json'];
+type DriveFilesUpdateResponse = operations['drive___files___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFilesUploadFromUrlRequest = operations['drive/files/upload-from-url']['requestBody']['content']['application/json'];
+type DriveFilesUploadFromUrlRequest = operations['drive___files___upload-from-url']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type DriveFolder = components['schemas']['DriveFolder'];
 
 // @public (undocumented)
-type DriveFoldersCreateRequest = operations['drive/folders/create']['requestBody']['content']['application/json'];
+type DriveFoldersCreateRequest = operations['drive___folders___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersCreateResponse = operations['drive/folders/create']['responses']['200']['content']['application/json'];
+type DriveFoldersCreateResponse = operations['drive___folders___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersDeleteRequest = operations['drive/folders/delete']['requestBody']['content']['application/json'];
+type DriveFoldersDeleteRequest = operations['drive___folders___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersFindRequest = operations['drive/folders/find']['requestBody']['content']['application/json'];
+type DriveFoldersFindRequest = operations['drive___folders___find']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersFindResponse = operations['drive/folders/find']['responses']['200']['content']['application/json'];
+type DriveFoldersFindResponse = operations['drive___folders___find']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersRequest = operations['drive/folders']['requestBody']['content']['application/json'];
+type DriveFoldersRequest = operations['drive___folders']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersResponse = operations['drive/folders']['responses']['200']['content']['application/json'];
+type DriveFoldersResponse = operations['drive___folders']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersShowRequest = operations['drive/folders/show']['requestBody']['content']['application/json'];
+type DriveFoldersShowRequest = operations['drive___folders___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersShowResponse = operations['drive/folders/show']['responses']['200']['content']['application/json'];
+type DriveFoldersShowResponse = operations['drive___folders___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersUpdateRequest = operations['drive/folders/update']['requestBody']['content']['application/json'];
+type DriveFoldersUpdateRequest = operations['drive___folders___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFoldersUpdateResponse = operations['drive/folders/update']['responses']['200']['content']['application/json'];
+type DriveFoldersUpdateResponse = operations['drive___folders___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type DriveResponse = operations['drive']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type DriveStreamRequest = operations['drive/stream']['requestBody']['content']['application/json'];
+type DriveStreamRequest = operations['drive___stream']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveStreamResponse = operations['drive/stream']['responses']['200']['content']['application/json'];
+type DriveStreamResponse = operations['drive___stream']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type EmailAddressAvailableRequest = operations['email-address/available']['requestBody']['content']['application/json'];
+type EmailAddressAvailableRequest = operations['email-address___available']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type EmailAddressAvailableResponse = operations['email-address/available']['responses']['200']['content']['application/json'];
+type EmailAddressAvailableResponse = operations['email-address___available']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type EmojiAdded = {
@@ -1733,46 +1733,46 @@ export { entities }
 type Error_2 = components['schemas']['Error'];
 
 // @public (undocumented)
-type FederationFollowersRequest = operations['federation/followers']['requestBody']['content']['application/json'];
+type FederationFollowersRequest = operations['federation___followers']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FederationFollowersResponse = operations['federation/followers']['responses']['200']['content']['application/json'];
+type FederationFollowersResponse = operations['federation___followers']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FederationFollowingRequest = operations['federation/following']['requestBody']['content']['application/json'];
+type FederationFollowingRequest = operations['federation___following']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FederationFollowingResponse = operations['federation/following']['responses']['200']['content']['application/json'];
+type FederationFollowingResponse = operations['federation___following']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type FederationInstance = components['schemas']['FederationInstance'];
 
 // @public (undocumented)
-type FederationInstancesRequest = operations['federation/instances']['requestBody']['content']['application/json'];
+type FederationInstancesRequest = operations['federation___instances']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FederationInstancesResponse = operations['federation/instances']['responses']['200']['content']['application/json'];
+type FederationInstancesResponse = operations['federation___instances']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FederationShowInstanceRequest = operations['federation/show-instance']['requestBody']['content']['application/json'];
+type FederationShowInstanceRequest = operations['federation___show-instance']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FederationShowInstanceResponse = operations['federation/show-instance']['responses']['200']['content']['application/json'];
+type FederationShowInstanceResponse = operations['federation___show-instance']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json'];
+type FederationStatsRequest = operations['federation___stats']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FederationStatsResponse = operations['federation/stats']['responses']['200']['content']['application/json'];
+type FederationStatsResponse = operations['federation___stats']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FederationUpdateRemoteUserRequest = operations['federation/update-remote-user']['requestBody']['content']['application/json'];
+type FederationUpdateRemoteUserRequest = operations['federation___update-remote-user']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FederationUsersRequest = operations['federation/users']['requestBody']['content']['application/json'];
+type FederationUsersRequest = operations['federation___users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FederationUsersResponse = operations['federation/users']['responses']['200']['content']['application/json'];
+type FederationUsersResponse = operations['federation___users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json'];
@@ -1804,43 +1804,43 @@ type FetchRssResponse = operations['fetch-rss']['responses']['200']['content']['
 type Flash = components['schemas']['Flash'];
 
 // @public (undocumented)
-type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
+type FlashCreateRequest = operations['flash___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FlashCreateResponse = operations['flash/create']['responses']['200']['content']['application/json'];
+type FlashCreateResponse = operations['flash___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
+type FlashDeleteRequest = operations['flash___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
+type FlashFeaturedResponse = operations['flash___featured']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
+type FlashLikeRequest = operations['flash___like']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FlashMyLikesRequest = operations['flash/my-likes']['requestBody']['content']['application/json'];
+type FlashMyLikesRequest = operations['flash___my-likes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FlashMyLikesResponse = operations['flash/my-likes']['responses']['200']['content']['application/json'];
+type FlashMyLikesResponse = operations['flash___my-likes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FlashMyRequest = operations['flash/my']['requestBody']['content']['application/json'];
+type FlashMyRequest = operations['flash___my']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FlashMyResponse = operations['flash/my']['responses']['200']['content']['application/json'];
+type FlashMyResponse = operations['flash___my']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FlashShowRequest = operations['flash/show']['requestBody']['content']['application/json'];
+type FlashShowRequest = operations['flash___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FlashShowResponse = operations['flash/show']['responses']['200']['content']['application/json'];
+type FlashShowResponse = operations['flash___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FlashUnlikeRequest = operations['flash/unlike']['requestBody']['content']['application/json'];
+type FlashUnlikeRequest = operations['flash___unlike']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FlashUpdateRequest = operations['flash/update']['requestBody']['content']['application/json'];
+type FlashUpdateRequest = operations['flash___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 export const followersVisibilities: readonly ["public", "followers", "private"];
@@ -1849,97 +1849,97 @@ export const followersVisibilities: readonly ["public", "followers", "private"];
 type Following = components['schemas']['Following'];
 
 // @public (undocumented)
-type FollowingCreateRequest = operations['following/create']['requestBody']['content']['application/json'];
+type FollowingCreateRequest = operations['following___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingCreateResponse = operations['following/create']['responses']['200']['content']['application/json'];
+type FollowingCreateResponse = operations['following___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingDeleteRequest = operations['following/delete']['requestBody']['content']['application/json'];
+type FollowingDeleteRequest = operations['following___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingDeleteResponse = operations['following/delete']['responses']['200']['content']['application/json'];
+type FollowingDeleteResponse = operations['following___delete']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingInvalidateRequest = operations['following/invalidate']['requestBody']['content']['application/json'];
+type FollowingInvalidateRequest = operations['following___invalidate']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingInvalidateResponse = operations['following/invalidate']['responses']['200']['content']['application/json'];
+type FollowingInvalidateResponse = operations['following___invalidate']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingRequestsAcceptRequest = operations['following/requests/accept']['requestBody']['content']['application/json'];
+type FollowingRequestsAcceptRequest = operations['following___requests___accept']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingRequestsCancelRequest = operations['following/requests/cancel']['requestBody']['content']['application/json'];
+type FollowingRequestsCancelRequest = operations['following___requests___cancel']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingRequestsCancelResponse = operations['following/requests/cancel']['responses']['200']['content']['application/json'];
+type FollowingRequestsCancelResponse = operations['following___requests___cancel']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingRequestsListRequest = operations['following/requests/list']['requestBody']['content']['application/json'];
+type FollowingRequestsListRequest = operations['following___requests___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingRequestsListResponse = operations['following/requests/list']['responses']['200']['content']['application/json'];
+type FollowingRequestsListResponse = operations['following___requests___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingRequestsRejectRequest = operations['following/requests/reject']['requestBody']['content']['application/json'];
+type FollowingRequestsRejectRequest = operations['following___requests___reject']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingUpdateAllRequest = operations['following/update-all']['requestBody']['content']['application/json'];
+type FollowingUpdateAllRequest = operations['following___update-all']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingUpdateRequest = operations['following/update']['requestBody']['content']['application/json'];
+type FollowingUpdateRequest = operations['following___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingUpdateResponse = operations['following/update']['responses']['200']['content']['application/json'];
+type FollowingUpdateResponse = operations['following___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 export const followingVisibilities: readonly ["public", "followers", "private"];
 
 // @public (undocumented)
-type GalleryFeaturedRequest = operations['gallery/featured']['requestBody']['content']['application/json'];
+type GalleryFeaturedRequest = operations['gallery___featured']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryFeaturedResponse = operations['gallery/featured']['responses']['200']['content']['application/json'];
+type GalleryFeaturedResponse = operations['gallery___featured']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPopularResponse = operations['gallery/popular']['responses']['200']['content']['application/json'];
+type GalleryPopularResponse = operations['gallery___popular']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type GalleryPost = components['schemas']['GalleryPost'];
 
 // @public (undocumented)
-type GalleryPostsCreateRequest = operations['gallery/posts/create']['requestBody']['content']['application/json'];
+type GalleryPostsCreateRequest = operations['gallery___posts___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsCreateResponse = operations['gallery/posts/create']['responses']['200']['content']['application/json'];
+type GalleryPostsCreateResponse = operations['gallery___posts___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsDeleteRequest = operations['gallery/posts/delete']['requestBody']['content']['application/json'];
+type GalleryPostsDeleteRequest = operations['gallery___posts___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsLikeRequest = operations['gallery/posts/like']['requestBody']['content']['application/json'];
+type GalleryPostsLikeRequest = operations['gallery___posts___like']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsRequest = operations['gallery/posts']['requestBody']['content']['application/json'];
+type GalleryPostsRequest = operations['gallery___posts']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsResponse = operations['gallery/posts']['responses']['200']['content']['application/json'];
+type GalleryPostsResponse = operations['gallery___posts']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsShowRequest = operations['gallery/posts/show']['requestBody']['content']['application/json'];
+type GalleryPostsShowRequest = operations['gallery___posts___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsShowResponse = operations['gallery/posts/show']['responses']['200']['content']['application/json'];
+type GalleryPostsShowResponse = operations['gallery___posts___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsUnlikeRequest = operations['gallery/posts/unlike']['requestBody']['content']['application/json'];
+type GalleryPostsUnlikeRequest = operations['gallery___posts___unlike']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsUpdateRequest = operations['gallery/posts/update']['requestBody']['content']['application/json'];
+type GalleryPostsUpdateRequest = operations['gallery___posts___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses']['200']['content']['application/json'];
+type GalleryPostsUpdateResponse = operations['gallery___posts___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json'];
@@ -1951,280 +1951,280 @@ type GetOnlineUsersCountResponse = operations['get-online-users-count']['respons
 type Hashtag = components['schemas']['Hashtag'];
 
 // @public (undocumented)
-type HashtagsListRequest = operations['hashtags/list']['requestBody']['content']['application/json'];
+type HashtagsListRequest = operations['hashtags___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsListResponse = operations['hashtags/list']['responses']['200']['content']['application/json'];
+type HashtagsListResponse = operations['hashtags___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsSearchRequest = operations['hashtags/search']['requestBody']['content']['application/json'];
+type HashtagsSearchRequest = operations['hashtags___search']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsSearchResponse = operations['hashtags/search']['responses']['200']['content']['application/json'];
+type HashtagsSearchResponse = operations['hashtags___search']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsShowRequest = operations['hashtags/show']['requestBody']['content']['application/json'];
+type HashtagsShowRequest = operations['hashtags___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsShowResponse = operations['hashtags/show']['responses']['200']['content']['application/json'];
+type HashtagsShowResponse = operations['hashtags___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsTrendResponse = operations['hashtags/trend']['responses']['200']['content']['application/json'];
+type HashtagsTrendResponse = operations['hashtags___trend']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content']['application/json'];
+type HashtagsUsersRequest = operations['hashtags___users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
+type HashtagsUsersResponse = operations['hashtags___users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json'];
+type I2faDoneRequest = operations['i___2fa___done']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type I2faDoneResponse = operations['i/2fa/done']['responses']['200']['content']['application/json'];
+type I2faDoneResponse = operations['i___2fa___done']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
+type I2faKeyDoneRequest = operations['i___2fa___key-done']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type I2faKeyDoneResponse = operations['i/2fa/key-done']['responses']['200']['content']['application/json'];
+type I2faKeyDoneResponse = operations['i___2fa___key-done']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
+type I2faPasswordLessRequest = operations['i___2fa___password-less']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
+type I2faRegisterKeyRequest = operations['i___2fa___register-key']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type I2faRegisterKeyResponse = operations['i/2fa/register-key']['responses']['200']['content']['application/json'];
+type I2faRegisterKeyResponse = operations['i___2fa___register-key']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
+type I2faRegisterRequest = operations['i___2fa___register']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type I2faRegisterResponse = operations['i/2fa/register']['responses']['200']['content']['application/json'];
+type I2faRegisterResponse = operations['i___2fa___register']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
+type I2faRemoveKeyRequest = operations['i___2fa___remove-key']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type I2faUnregisterRequest = operations['i/2fa/unregister']['requestBody']['content']['application/json'];
+type I2faUnregisterRequest = operations['i___2fa___unregister']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['content']['application/json'];
+type I2faUpdateKeyRequest = operations['i___2fa___update-key']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
+type IAppsRequest = operations['i___apps']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IAppsResponse = operations['i/apps']['responses']['200']['content']['application/json'];
+type IAppsResponse = operations['i___apps']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
+type IAuthorizedAppsRequest = operations['i___authorized-apps']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IAuthorizedAppsResponse = operations['i/authorized-apps']['responses']['200']['content']['application/json'];
+type IAuthorizedAppsResponse = operations['i___authorized-apps']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
+type IChangePasswordRequest = operations['i___change-password']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
+type IClaimAchievementRequest = operations['i___claim-achievement']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type ID = string;
 
 // @public (undocumented)
-type IDeleteAccountRequest = operations['i/delete-account']['requestBody']['content']['application/json'];
+type IDeleteAccountRequest = operations['i___delete-account']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IExportFollowingRequest = operations['i/export-following']['requestBody']['content']['application/json'];
+type IExportFollowingRequest = operations['i___export-following']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
+type IFavoritesRequest = operations['i___favorites']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IFavoritesResponse = operations['i/favorites']['responses']['200']['content']['application/json'];
+type IFavoritesResponse = operations['i___favorites']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IGalleryLikesRequest = operations['i/gallery/likes']['requestBody']['content']['application/json'];
+type IGalleryLikesRequest = operations['i___gallery___likes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IGalleryLikesResponse = operations['i/gallery/likes']['responses']['200']['content']['application/json'];
+type IGalleryLikesResponse = operations['i___gallery___likes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['content']['application/json'];
+type IGalleryPostsRequest = operations['i___gallery___posts']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
+type IGalleryPostsResponse = operations['i___gallery___posts']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IImportAntennasRequest = operations['i/import-antennas']['requestBody']['content']['application/json'];
+type IImportAntennasRequest = operations['i___import-antennas']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IImportBlockingRequest = operations['i/import-blocking']['requestBody']['content']['application/json'];
+type IImportBlockingRequest = operations['i___import-blocking']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IImportFollowingRequest = operations['i/import-following']['requestBody']['content']['application/json'];
+type IImportFollowingRequest = operations['i___import-following']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json'];
+type IImportMutingRequest = operations['i___import-muting']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json'];
+type IImportUserListsRequest = operations['i___import-user-lists']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
+type IMoveRequest = operations['i___move']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IMoveResponse = operations['i/move']['responses']['200']['content']['application/json'];
+type IMoveResponse = operations['i___move']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
+type INotificationsGroupedRequest = operations['i___notifications-grouped']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type INotificationsGroupedResponse = operations['i/notifications-grouped']['responses']['200']['content']['application/json'];
+type INotificationsGroupedResponse = operations['i___notifications-grouped']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type INotificationsRequest = operations['i/notifications']['requestBody']['content']['application/json'];
+type INotificationsRequest = operations['i___notifications']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type INotificationsResponse = operations['i/notifications']['responses']['200']['content']['application/json'];
+type INotificationsResponse = operations['i___notifications']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type InviteCode = components['schemas']['InviteCode'];
 
 // @public (undocumented)
-type InviteCreateResponse = operations['invite/create']['responses']['200']['content']['application/json'];
+type InviteCreateResponse = operations['invite___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type InviteDeleteRequest = operations['invite/delete']['requestBody']['content']['application/json'];
+type InviteDeleteRequest = operations['invite___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type InviteLimitResponse = operations['invite/limit']['responses']['200']['content']['application/json'];
+type InviteLimitResponse = operations['invite___limit']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type InviteListRequest = operations['invite/list']['requestBody']['content']['application/json'];
+type InviteListRequest = operations['invite___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type InviteListResponse = operations['invite/list']['responses']['200']['content']['application/json'];
+type InviteListResponse = operations['invite___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IPageLikesRequest = operations['i/page-likes']['requestBody']['content']['application/json'];
+type IPageLikesRequest = operations['i___page-likes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IPageLikesResponse = operations['i/page-likes']['responses']['200']['content']['application/json'];
+type IPageLikesResponse = operations['i___page-likes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IPagesRequest = operations['i/pages']['requestBody']['content']['application/json'];
+type IPagesRequest = operations['i___pages']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IPagesResponse = operations['i/pages']['responses']['200']['content']['application/json'];
+type IPagesResponse = operations['i___pages']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IPinRequest = operations['i/pin']['requestBody']['content']['application/json'];
+type IPinRequest = operations['i___pin']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IPinResponse = operations['i/pin']['responses']['200']['content']['application/json'];
+type IPinResponse = operations['i___pin']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
+type IReadAnnouncementRequest = operations['i___read-announcement']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json'];
+type IRegenerateTokenRequest = operations['i___regenerate-token']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
+type IRegistryGetAllRequest = operations['i___registry___get-all']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryGetAllResponse = operations['i/registry/get-all']['responses']['200']['content']['application/json'];
+type IRegistryGetAllResponse = operations['i___registry___get-all']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
+type IRegistryGetDetailRequest = operations['i___registry___get-detail']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryGetDetailResponse = operations['i/registry/get-detail']['responses']['200']['content']['application/json'];
+type IRegistryGetDetailResponse = operations['i___registry___get-detail']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
+type IRegistryGetRequest = operations['i___registry___get']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryGetResponse = operations['i/registry/get']['responses']['200']['content']['application/json'];
+type IRegistryGetResponse = operations['i___registry___get']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
+type IRegistryKeysRequest = operations['i___registry___keys']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryKeysResponse = operations['i/registry/keys']['responses']['200']['content']['application/json'];
+type IRegistryKeysResponse = operations['i___registry___keys']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json'];
+type IRegistryKeysWithTypeRequest = operations['i___registry___keys-with-type']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryKeysWithTypeResponse = operations['i/registry/keys-with-type']['responses']['200']['content']['application/json'];
+type IRegistryKeysWithTypeResponse = operations['i___registry___keys-with-type']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
+type IRegistryRemoveRequest = operations['i___registry___remove']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistryScopesWithDomainResponse = operations['i/registry/scopes-with-domain']['responses']['200']['content']['application/json'];
+type IRegistryScopesWithDomainResponse = operations['i___registry___scopes-with-domain']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
+type IRegistrySetRequest = operations['i___registry___set']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type IResponse = operations['i']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json'];
+type IRevokeTokenRequest = operations['i___revoke-token']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 function isAPIError(reason: Record<PropertyKey, unknown>): reason is APIError;
 
 // @public (undocumented)
-type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json'];
+type ISigninHistoryRequest = operations['i___signin-history']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ISigninHistoryResponse = operations['i/signin-history']['responses']['200']['content']['application/json'];
+type ISigninHistoryResponse = operations['i___signin-history']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
+type IUnpinRequest = operations['i___unpin']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
+type IUnpinResponse = operations['i___unpin']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
+type IUpdateEmailRequest = operations['i___update-email']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IUpdateEmailResponse = operations['i/update-email']['responses']['200']['content']['application/json'];
+type IUpdateEmailResponse = operations['i___update-email']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
+type IUpdateRequest = operations['i___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
+type IUpdateResponse = operations['i___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
+type IWebhooksCreateRequest = operations['i___webhooks___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IWebhooksCreateResponse = operations['i/webhooks/create']['responses']['200']['content']['application/json'];
+type IWebhooksCreateResponse = operations['i___webhooks___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json'];
+type IWebhooksDeleteRequest = operations['i___webhooks___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IWebhooksListResponse = operations['i/webhooks/list']['responses']['200']['content']['application/json'];
+type IWebhooksListResponse = operations['i___webhooks___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
+type IWebhooksShowRequest = operations['i___webhooks___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200']['content']['application/json'];
+type IWebhooksShowResponse = operations['i___webhooks___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
+type IWebhooksUpdateRequest = operations['i___webhooks___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type MeDetailed = components['schemas']['MeDetailed'];
@@ -2248,10 +2248,10 @@ type MetaRequest = operations['meta']['requestBody']['content']['application/jso
 type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type MiauthGenTokenRequest = operations['miauth/gen-token']['requestBody']['content']['application/json'];
+type MiauthGenTokenRequest = operations['miauth___gen-token']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type MiauthGenTokenResponse = operations['miauth/gen-token']['responses']['200']['content']['application/json'];
+type MiauthGenTokenResponse = operations['miauth___gen-token']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type ModerationLog = {
@@ -2379,28 +2379,28 @@ type ModerationLog = {
 export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
 
 // @public (undocumented)
-type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
+type MuteCreateRequest = operations['mute___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type MuteDeleteRequest = operations['mute/delete']['requestBody']['content']['application/json'];
+type MuteDeleteRequest = operations['mute___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
 
 // @public (undocumented)
-type MuteListRequest = operations['mute/list']['requestBody']['content']['application/json'];
+type MuteListRequest = operations['mute___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type MuteListResponse = operations['mute/list']['responses']['200']['content']['application/json'];
+type MuteListResponse = operations['mute___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type Muting = components['schemas']['Muting'];
 
 // @public (undocumented)
-type MyAppsRequest = operations['my/apps']['requestBody']['content']['application/json'];
+type MyAppsRequest = operations['my___apps']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type MyAppsResponse = operations['my/apps']['responses']['200']['content']['application/json'];
+type MyAppsResponse = operations['my___apps']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type Note = components['schemas']['Note'];
@@ -2412,100 +2412,100 @@ type NoteFavorite = components['schemas']['NoteFavorite'];
 type NoteReaction = components['schemas']['NoteReaction'];
 
 // @public (undocumented)
-type NotesChildrenRequest = operations['notes/children']['requestBody']['content']['application/json'];
+type NotesChildrenRequest = operations['notes___children']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesChildrenResponse = operations['notes/children']['responses']['200']['content']['application/json'];
+type NotesChildrenResponse = operations['notes___children']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesClipsRequest = operations['notes/clips']['requestBody']['content']['application/json'];
+type NotesClipsRequest = operations['notes___clips']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesClipsResponse = operations['notes/clips']['responses']['200']['content']['application/json'];
+type NotesClipsResponse = operations['notes___clips']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesConversationRequest = operations['notes/conversation']['requestBody']['content']['application/json'];
+type NotesConversationRequest = operations['notes___conversation']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesConversationResponse = operations['notes/conversation']['responses']['200']['content']['application/json'];
+type NotesConversationResponse = operations['notes___conversation']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesCreateRequest = operations['notes/create']['requestBody']['content']['application/json'];
+type NotesCreateRequest = operations['notes___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesCreateResponse = operations['notes/create']['responses']['200']['content']['application/json'];
+type NotesCreateResponse = operations['notes___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesDeleteRequest = operations['notes/delete']['requestBody']['content']['application/json'];
+type NotesDeleteRequest = operations['notes___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesFavoritesCreateRequest = operations['notes/favorites/create']['requestBody']['content']['application/json'];
+type NotesFavoritesCreateRequest = operations['notes___favorites___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesFavoritesDeleteRequest = operations['notes/favorites/delete']['requestBody']['content']['application/json'];
+type NotesFavoritesDeleteRequest = operations['notes___favorites___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesFeaturedRequest = operations['notes/featured']['requestBody']['content']['application/json'];
+type NotesFeaturedRequest = operations['notes___featured']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesFeaturedResponse = operations['notes/featured']['responses']['200']['content']['application/json'];
+type NotesFeaturedResponse = operations['notes___featured']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesGlobalTimelineRequest = operations['notes/global-timeline']['requestBody']['content']['application/json'];
+type NotesGlobalTimelineRequest = operations['notes___global-timeline']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesGlobalTimelineResponse = operations['notes/global-timeline']['responses']['200']['content']['application/json'];
+type NotesGlobalTimelineResponse = operations['notes___global-timeline']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBody']['content']['application/json'];
+type NotesHybridTimelineRequest = operations['notes___hybrid-timeline']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json'];
+type NotesHybridTimelineResponse = operations['notes___hybrid-timeline']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json'];
+type NotesLocalTimelineRequest = operations['notes___local-timeline']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesLocalTimelineResponse = operations['notes/local-timeline']['responses']['200']['content']['application/json'];
+type NotesLocalTimelineResponse = operations['notes___local-timeline']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesMentionsRequest = operations['notes/mentions']['requestBody']['content']['application/json'];
+type NotesMentionsRequest = operations['notes___mentions']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesMentionsResponse = operations['notes/mentions']['responses']['200']['content']['application/json'];
+type NotesMentionsResponse = operations['notes___mentions']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesPollsRecommendationRequest = operations['notes/polls/recommendation']['requestBody']['content']['application/json'];
+type NotesPollsRecommendationRequest = operations['notes___polls___recommendation']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesPollsRecommendationResponse = operations['notes/polls/recommendation']['responses']['200']['content']['application/json'];
+type NotesPollsRecommendationResponse = operations['notes___polls___recommendation']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesPollsVoteRequest = operations['notes/polls/vote']['requestBody']['content']['application/json'];
+type NotesPollsVoteRequest = operations['notes___polls___vote']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesReactionsCreateRequest = operations['notes/reactions/create']['requestBody']['content']['application/json'];
+type NotesReactionsCreateRequest = operations['notes___reactions___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesReactionsDeleteRequest = operations['notes/reactions/delete']['requestBody']['content']['application/json'];
+type NotesReactionsDeleteRequest = operations['notes___reactions___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesReactionsRequest = operations['notes/reactions']['requestBody']['content']['application/json'];
+type NotesReactionsRequest = operations['notes___reactions']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesReactionsResponse = operations['notes/reactions']['responses']['200']['content']['application/json'];
+type NotesReactionsResponse = operations['notes___reactions']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesRenotesRequest = operations['notes/renotes']['requestBody']['content']['application/json'];
+type NotesRenotesRequest = operations['notes___renotes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesRenotesResponse = operations['notes/renotes']['responses']['200']['content']['application/json'];
+type NotesRenotesResponse = operations['notes___renotes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesRepliesRequest = operations['notes/replies']['requestBody']['content']['application/json'];
+type NotesRepliesRequest = operations['notes___replies']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesRepliesResponse = operations['notes/replies']['responses']['200']['content']['application/json'];
+type NotesRepliesResponse = operations['notes___replies']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type NotesRequest = operations['notes']['requestBody']['content']['application/json'];
@@ -2514,55 +2514,55 @@ type NotesRequest = operations['notes']['requestBody']['content']['application/j
 type NotesResponse = operations['notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesSearchByTagRequest = operations['notes/search-by-tag']['requestBody']['content']['application/json'];
+type NotesSearchByTagRequest = operations['notes___search-by-tag']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesSearchByTagResponse = operations['notes/search-by-tag']['responses']['200']['content']['application/json'];
+type NotesSearchByTagResponse = operations['notes___search-by-tag']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesSearchRequest = operations['notes/search']['requestBody']['content']['application/json'];
+type NotesSearchRequest = operations['notes___search']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesSearchResponse = operations['notes/search']['responses']['200']['content']['application/json'];
+type NotesSearchResponse = operations['notes___search']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesShowRequest = operations['notes/show']['requestBody']['content']['application/json'];
+type NotesShowRequest = operations['notes___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesShowResponse = operations['notes/show']['responses']['200']['content']['application/json'];
+type NotesShowResponse = operations['notes___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesStateRequest = operations['notes/state']['requestBody']['content']['application/json'];
+type NotesStateRequest = operations['notes___state']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesStateResponse = operations['notes/state']['responses']['200']['content']['application/json'];
+type NotesStateResponse = operations['notes___state']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesThreadMutingCreateRequest = operations['notes/thread-muting/create']['requestBody']['content']['application/json'];
+type NotesThreadMutingCreateRequest = operations['notes___thread-muting___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesThreadMutingDeleteRequest = operations['notes/thread-muting/delete']['requestBody']['content']['application/json'];
+type NotesThreadMutingDeleteRequest = operations['notes___thread-muting___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesTimelineRequest = operations['notes/timeline']['requestBody']['content']['application/json'];
+type NotesTimelineRequest = operations['notes___timeline']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesTimelineResponse = operations['notes/timeline']['responses']['200']['content']['application/json'];
+type NotesTimelineResponse = operations['notes___timeline']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesTranslateRequest = operations['notes/translate']['requestBody']['content']['application/json'];
+type NotesTranslateRequest = operations['notes___translate']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesTranslateResponse = operations['notes/translate']['responses']['200']['content']['application/json'];
+type NotesTranslateResponse = operations['notes___translate']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['content']['application/json'];
+type NotesUnrenoteRequest = operations['notes___unrenote']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json'];
+type NotesUserListTimelineRequest = operations['notes___user-list-timeline']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
+type NotesUserListTimelineResponse = operations['notes___user-list-timeline']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 export const noteVisibilities: readonly ["public", "home", "followers", "specified"];
@@ -2571,7 +2571,7 @@ export const noteVisibilities: readonly ["public", "home", "followers", "specifi
 type Notification_2 = components['schemas']['Notification'];
 
 // @public (undocumented)
-type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
+type NotificationsCreateRequest = operations['notifications___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned"];
@@ -2595,31 +2595,31 @@ type PageEvent = {
 type PagePushRequest = operations['page-push']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
+type PagesCreateRequest = operations['pages___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type PagesCreateResponse = operations['pages/create']['responses']['200']['content']['application/json'];
+type PagesCreateResponse = operations['pages___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type PagesDeleteRequest = operations['pages/delete']['requestBody']['content']['application/json'];
+type PagesDeleteRequest = operations['pages___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type PagesFeaturedResponse = operations['pages/featured']['responses']['200']['content']['application/json'];
+type PagesFeaturedResponse = operations['pages___featured']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type PagesLikeRequest = operations['pages/like']['requestBody']['content']['application/json'];
+type PagesLikeRequest = operations['pages___like']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type PagesShowRequest = operations['pages/show']['requestBody']['content']['application/json'];
+type PagesShowRequest = operations['pages___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type PagesShowResponse = operations['pages/show']['responses']['200']['content']['application/json'];
+type PagesShowResponse = operations['pages___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type PagesUnlikeRequest = operations['pages/unlike']['requestBody']['content']['application/json'];
+type PagesUnlikeRequest = operations['pages___unlike']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['application/json'];
+type PagesUpdateRequest = operations['pages___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 function parse(acct: string): Acct;
@@ -2634,7 +2634,7 @@ type PingResponse = operations['ping']['responses']['200']['content']['applicati
 type PinnedUsersResponse = operations['pinned-users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type PromoReadRequest = operations['promo/read']['requestBody']['content']['application/json'];
+type PromoReadRequest = operations['promo___read']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type QueueCount = components['schemas']['QueueCount'];
@@ -2659,16 +2659,16 @@ type QueueStats = {
 type QueueStatsLog = QueueStats[];
 
 // @public (undocumented)
-type RenoteMuteCreateRequest = operations['renote-mute/create']['requestBody']['content']['application/json'];
+type RenoteMuteCreateRequest = operations['renote-mute___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type RenoteMuteDeleteRequest = operations['renote-mute/delete']['requestBody']['content']['application/json'];
+type RenoteMuteDeleteRequest = operations['renote-mute___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type RenoteMuteListRequest = operations['renote-mute/list']['requestBody']['content']['application/json'];
+type RenoteMuteListRequest = operations['renote-mute___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type RenoteMuteListResponse = operations['renote-mute/list']['responses']['200']['content']['application/json'];
+type RenoteMuteListResponse = operations['renote-mute___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type RenoteMuting = components['schemas']['RenoteMuting'];
@@ -2683,7 +2683,7 @@ type ResetPasswordRequest = operations['reset-password']['requestBody']['content
 type RetentionResponse = operations['retention']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiCancelMatchRequest = operations['reversi/cancel-match']['requestBody']['content']['application/json'];
+type ReversiCancelMatchRequest = operations['reversi___cancel-match']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
@@ -2692,34 +2692,34 @@ type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
 type ReversiGameLite = components['schemas']['ReversiGameLite'];
 
 // @public (undocumented)
-type ReversiGamesRequest = operations['reversi/games']['requestBody']['content']['application/json'];
+type ReversiGamesRequest = operations['reversi___games']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiGamesResponse = operations['reversi/games']['responses']['200']['content']['application/json'];
+type ReversiGamesResponse = operations['reversi___games']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiInvitationsResponse = operations['reversi/invitations']['responses']['200']['content']['application/json'];
+type ReversiInvitationsResponse = operations['reversi___invitations']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiMatchRequest = operations['reversi/match']['requestBody']['content']['application/json'];
+type ReversiMatchRequest = operations['reversi___match']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiMatchResponse = operations['reversi/match']['responses']['200']['content']['application/json'];
+type ReversiMatchResponse = operations['reversi___match']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiShowGameRequest = operations['reversi/show-game']['requestBody']['content']['application/json'];
+type ReversiShowGameRequest = operations['reversi___show-game']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiShowGameResponse = operations['reversi/show-game']['responses']['200']['content']['application/json'];
+type ReversiShowGameResponse = operations['reversi___show-game']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiSurrenderRequest = operations['reversi/surrender']['requestBody']['content']['application/json'];
+type ReversiSurrenderRequest = operations['reversi___surrender']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiVerifyRequest = operations['reversi/verify']['requestBody']['content']['application/json'];
+type ReversiVerifyRequest = operations['reversi___verify']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type ReversiVerifyResponse = operations['reversi/verify']['responses']['200']['content']['application/json'];
+type ReversiVerifyResponse = operations['reversi___verify']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type Role = components['schemas']['Role'];
@@ -2752,25 +2752,25 @@ type RoleLite = components['schemas']['RoleLite'];
 type RolePolicies = components['schemas']['RolePolicies'];
 
 // @public (undocumented)
-type RolesListResponse = operations['roles/list']['responses']['200']['content']['application/json'];
+type RolesListResponse = operations['roles___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
+type RolesNotesRequest = operations['roles___notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type RolesNotesResponse = operations['roles/notes']['responses']['200']['content']['application/json'];
+type RolesNotesResponse = operations['roles___notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
+type RolesShowRequest = operations['roles___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type RolesShowResponse = operations['roles/show']['responses']['200']['content']['application/json'];
+type RolesShowResponse = operations['roles___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
+type RolesUsersRequest = operations['roles___users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type RolesUsersResponse = operations['roles/users']['responses']['200']['content']['application/json'];
+type RolesUsersResponse = operations['roles___users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type ServerInfoResponse = operations['server-info']['responses']['200']['content']['application/json'];
@@ -2889,25 +2889,25 @@ export class Stream extends EventEmitter<StreamEvents> {
 type SwitchCaseResponseType<E extends keyof Endpoints, P extends Endpoints[E]['req']> = Endpoints[E]['res'] extends SwitchCase ? IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> : IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> : IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> : IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> : IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> : IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> : IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> : IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> : IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> : IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> : Endpoints[E]['res']['$switch']['$default'] : Endpoints[E]['res'];
 
 // @public (undocumented)
-type SwRegisterRequest = operations['sw/register']['requestBody']['content']['application/json'];
+type SwRegisterRequest = operations['sw___register']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type SwRegisterResponse = operations['sw/register']['responses']['200']['content']['application/json'];
+type SwRegisterResponse = operations['sw___register']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type SwShowRegistrationRequest = operations['sw/show-registration']['requestBody']['content']['application/json'];
+type SwShowRegistrationRequest = operations['sw___show-registration']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type SwShowRegistrationResponse = operations['sw/show-registration']['responses']['200']['content']['application/json'];
+type SwShowRegistrationResponse = operations['sw___show-registration']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type SwUnregisterRequest = operations['sw/unregister']['requestBody']['content']['application/json'];
+type SwUnregisterRequest = operations['sw___unregister']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type SwUpdateRegistrationRequest = operations['sw/update-registration']['requestBody']['content']['application/json'];
+type SwUpdateRegistrationRequest = operations['sw___update-registration']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type SwUpdateRegistrationResponse = operations['sw/update-registration']['responses']['200']['content']['application/json'];
+type SwUpdateRegistrationResponse = operations['sw___update-registration']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type TestRequest = operations['test']['requestBody']['content']['application/json'];
@@ -2937,145 +2937,145 @@ type UserList = components['schemas']['UserList'];
 type UserLite = components['schemas']['UserLite'];
 
 // @public (undocumented)
-type UsernameAvailableRequest = operations['username/available']['requestBody']['content']['application/json'];
+type UsernameAvailableRequest = operations['username___available']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsernameAvailableResponse = operations['username/available']['responses']['200']['content']['application/json'];
+type UsernameAvailableResponse = operations['username___available']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json'];
+type UsersAchievementsRequest = operations['users___achievements']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersAchievementsResponse = operations['users/achievements']['responses']['200']['content']['application/json'];
+type UsersAchievementsResponse = operations['users___achievements']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersClipsRequest = operations['users/clips']['requestBody']['content']['application/json'];
+type UsersClipsRequest = operations['users___clips']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersClipsResponse = operations['users/clips']['responses']['200']['content']['application/json'];
+type UsersClipsResponse = operations['users___clips']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFeaturedNotesRequest = operations['users/featured-notes']['requestBody']['content']['application/json'];
+type UsersFeaturedNotesRequest = operations['users___featured-notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFeaturedNotesResponse = operations['users/featured-notes']['responses']['200']['content']['application/json'];
+type UsersFeaturedNotesResponse = operations['users___featured-notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFlashsRequest = operations['users/flashs']['requestBody']['content']['application/json'];
+type UsersFlashsRequest = operations['users___flashs']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFlashsResponse = operations['users/flashs']['responses']['200']['content']['application/json'];
+type UsersFlashsResponse = operations['users___flashs']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFollowersRequest = operations['users/followers']['requestBody']['content']['application/json'];
+type UsersFollowersRequest = operations['users___followers']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFollowersResponse = operations['users/followers']['responses']['200']['content']['application/json'];
+type UsersFollowersResponse = operations['users___followers']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFollowingRequest = operations['users/following']['requestBody']['content']['application/json'];
+type UsersFollowingRequest = operations['users___following']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersFollowingResponse = operations['users/following']['responses']['200']['content']['application/json'];
+type UsersFollowingResponse = operations['users___following']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersGalleryPostsRequest = operations['users/gallery/posts']['requestBody']['content']['application/json'];
+type UsersGalleryPostsRequest = operations['users___gallery___posts']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersGalleryPostsResponse = operations['users/gallery/posts']['responses']['200']['content']['application/json'];
+type UsersGalleryPostsResponse = operations['users___gallery___posts']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersGetFrequentlyRepliedUsersRequest = operations['users/get-frequently-replied-users']['requestBody']['content']['application/json'];
+type UsersGetFrequentlyRepliedUsersRequest = operations['users___get-frequently-replied-users']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersGetFrequentlyRepliedUsersResponse = operations['users/get-frequently-replied-users']['responses']['200']['content']['application/json'];
+type UsersGetFrequentlyRepliedUsersResponse = operations['users___get-frequently-replied-users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsCreateFromPublicRequest = operations['users/lists/create-from-public']['requestBody']['content']['application/json'];
+type UsersListsCreateFromPublicRequest = operations['users___lists___create-from-public']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsCreateFromPublicResponse = operations['users/lists/create-from-public']['responses']['200']['content']['application/json'];
+type UsersListsCreateFromPublicResponse = operations['users___lists___create-from-public']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsCreateRequest = operations['users/lists/create']['requestBody']['content']['application/json'];
+type UsersListsCreateRequest = operations['users___lists___create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsCreateResponse = operations['users/lists/create']['responses']['200']['content']['application/json'];
+type UsersListsCreateResponse = operations['users___lists___create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsDeleteRequest = operations['users/lists/delete']['requestBody']['content']['application/json'];
+type UsersListsDeleteRequest = operations['users___lists___delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsFavoriteRequest = operations['users/lists/favorite']['requestBody']['content']['application/json'];
+type UsersListsFavoriteRequest = operations['users___lists___favorite']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json'];
+type UsersListsGetMembershipsRequest = operations['users___lists___get-memberships']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsGetMembershipsResponse = operations['users/lists/get-memberships']['responses']['200']['content']['application/json'];
+type UsersListsGetMembershipsResponse = operations['users___lists___get-memberships']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsListRequest = operations['users/lists/list']['requestBody']['content']['application/json'];
+type UsersListsListRequest = operations['users___lists___list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsListResponse = operations['users/lists/list']['responses']['200']['content']['application/json'];
+type UsersListsListResponse = operations['users___lists___list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsPullRequest = operations['users/lists/pull']['requestBody']['content']['application/json'];
+type UsersListsPullRequest = operations['users___lists___pull']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsPushRequest = operations['users/lists/push']['requestBody']['content']['application/json'];
+type UsersListsPushRequest = operations['users___lists___push']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsShowRequest = operations['users/lists/show']['requestBody']['content']['application/json'];
+type UsersListsShowRequest = operations['users___lists___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsShowResponse = operations['users/lists/show']['responses']['200']['content']['application/json'];
+type UsersListsShowResponse = operations['users___lists___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsUnfavoriteRequest = operations['users/lists/unfavorite']['requestBody']['content']['application/json'];
+type UsersListsUnfavoriteRequest = operations['users___lists___unfavorite']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsUpdateMembershipRequest = operations['users/lists/update-membership']['requestBody']['content']['application/json'];
+type UsersListsUpdateMembershipRequest = operations['users___lists___update-membership']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsUpdateRequest = operations['users/lists/update']['requestBody']['content']['application/json'];
+type UsersListsUpdateRequest = operations['users___lists___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersListsUpdateResponse = operations['users/lists/update']['responses']['200']['content']['application/json'];
+type UsersListsUpdateResponse = operations['users___lists___update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersNotesRequest = operations['users/notes']['requestBody']['content']['application/json'];
+type UsersNotesRequest = operations['users___notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersNotesResponse = operations['users/notes']['responses']['200']['content']['application/json'];
+type UsersNotesResponse = operations['users___notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersPagesRequest = operations['users/pages']['requestBody']['content']['application/json'];
+type UsersPagesRequest = operations['users___pages']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersPagesResponse = operations['users/pages']['responses']['200']['content']['application/json'];
+type UsersPagesResponse = operations['users___pages']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersReactionsRequest = operations['users/reactions']['requestBody']['content']['application/json'];
+type UsersReactionsRequest = operations['users___reactions']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersReactionsResponse = operations['users/reactions']['responses']['200']['content']['application/json'];
+type UsersReactionsResponse = operations['users___reactions']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersRecommendationRequest = operations['users/recommendation']['requestBody']['content']['application/json'];
+type UsersRecommendationRequest = operations['users___recommendation']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersRecommendationResponse = operations['users/recommendation']['responses']['200']['content']['application/json'];
+type UsersRecommendationResponse = operations['users___recommendation']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersRelationRequest = operations['users/relation']['requestBody']['content']['application/json'];
+type UsersRelationRequest = operations['users___relation']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersRelationResponse = operations['users/relation']['responses']['200']['content']['application/json'];
+type UsersRelationResponse = operations['users___relation']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersReportAbuseRequest = operations['users/report-abuse']['requestBody']['content']['application/json'];
+type UsersReportAbuseRequest = operations['users___report-abuse']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type UsersRequest = operations['users']['requestBody']['content']['application/json'];
@@ -3084,25 +3084,25 @@ type UsersRequest = operations['users']['requestBody']['content']['application/j
 type UsersResponse = operations['users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersSearchByUsernameAndHostRequest = operations['users/search-by-username-and-host']['requestBody']['content']['application/json'];
+type UsersSearchByUsernameAndHostRequest = operations['users___search-by-username-and-host']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersSearchByUsernameAndHostResponse = operations['users/search-by-username-and-host']['responses']['200']['content']['application/json'];
+type UsersSearchByUsernameAndHostResponse = operations['users___search-by-username-and-host']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersSearchRequest = operations['users/search']['requestBody']['content']['application/json'];
+type UsersSearchRequest = operations['users___search']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersSearchResponse = operations['users/search']['responses']['200']['content']['application/json'];
+type UsersSearchResponse = operations['users___search']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersShowRequest = operations['users/show']['requestBody']['content']['application/json'];
+type UsersShowRequest = operations['users___show']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type UsersShowResponse = operations['users/show']['responses']['200']['content']['application/json'];
+type UsersShowResponse = operations['users___show']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type UsersUpdateMemoRequest = operations['users/update-memo']['requestBody']['content']['application/json'];
+type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['content']['application/json'];
 
 // Warnings were encountered during analysis:
 //
diff --git a/packages/misskey-js/generator/src/generator.ts b/packages/misskey-js/generator/src/generator.ts
index 49dcd4c1edab..78178d7c7ed4 100644
--- a/packages/misskey-js/generator/src/generator.ts
+++ b/packages/misskey-js/generator/src/generator.ts
@@ -60,13 +60,17 @@ async function generateEndpoints(
 	// misskey-jsはPOST固定で送っているので、こちらも決め打ちする。別メソッドに対応することがあればこちらも直す必要あり
 	const paths = openApiDocs.paths ?? {};
 	const postPathItems = Object.keys(paths)
-		.map(it => paths[it]?.post)
+		.map(it => ({
+			_path_: it.replace(/^\//, ''),
+			...paths[it]?.post,
+		}))
 		.filter(filterUndefined);
 
 	for (const operation of postPathItems) {
+		const path = operation._path_;
 		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
 		const operationId = operation.operationId!;
-		const endpoint = new Endpoint(operationId);
+		const endpoint = new Endpoint(path);
 		endpoints.push(endpoint);
 
 		if (isRequestBodyObject(operation.requestBody)) {
@@ -76,19 +80,21 @@ async function generateEndpoints(
 				// いまのところ複数のメディアタイプをとるエンドポイントは無いので決め打ちする
 				endpoint.request = new OperationTypeAlias(
 					operationId,
+					path,
 					supportMediaTypes[0],
 					OperationsAliasType.REQUEST,
 				);
 			}
 		}
 
-		if (isResponseObject(operation.responses['200']) && operation.responses['200'].content) {
+		if (operation.responses && isResponseObject(operation.responses['200']) && operation.responses['200'].content) {
 			const resContent = operation.responses['200'].content;
 			const supportMediaTypes = Object.keys(resContent);
 			if (supportMediaTypes.length > 0) {
 				// いまのところ複数のメディアタイプを返すエンドポイントは無いので決め打ちする
 				endpoint.response = new OperationTypeAlias(
 					operationId,
+					path,
 					supportMediaTypes[0],
 					OperationsAliasType.RESPONSE,
 				);
@@ -140,12 +146,19 @@ async function generateApiClientJSDoc(
 	endpointsFileName: string,
 	warningsOutputPath: string,
 ) {
-	const endpoints: { operationId: string; description: string; }[] = [];
+	const endpoints: {
+		operationId: string;
+		path: string;
+		description: string;
+	}[] = [];
 
 	// misskey-jsはPOST固定で送っているので、こちらも決め打ちする。別メソッドに対応することがあればこちらも直す必要あり
 	const paths = openApiDocs.paths ?? {};
 	const postPathItems = Object.keys(paths)
-		.map(it => paths[it]?.post)
+		.map(it => ({
+			_path_: it.replace(/^\//, ''),
+			...paths[it]?.post,
+		}))
 		.filter(filterUndefined);
 
 	for (const operation of postPathItems) {
@@ -155,6 +168,7 @@ async function generateApiClientJSDoc(
 		if (operation.description) {
 			endpoints.push({
 				operationId: operationId,
+				path: operation._path_,
 				description: operation.description,
 			});
 		}
@@ -175,7 +189,7 @@ async function generateApiClientJSDoc(
 			'    /**',
 			`     * ${endpoint.description.split('\n').join('\n     * ')}`,
 			'     */',
-			`    request<E extends '${endpoint.operationId}', P extends Endpoints[E][\'req\']>(`,
+			`    request<E extends '${endpoint.path}', P extends Endpoints[E][\'req\']>(`,
 			'      endpoint: E,',
 			'      params: P,',
 			'      credential?: string | null,',
@@ -234,21 +248,24 @@ interface IOperationTypeAlias {
 
 class OperationTypeAlias implements IOperationTypeAlias {
 	public readonly operationId: string;
+	public readonly path: string;
 	public readonly mediaType: string;
 	public readonly type: OperationsAliasType;
 
 	constructor(
 		operationId: string,
+		path: string,
 		mediaType: string,
 		type: OperationsAliasType,
 	) {
 		this.operationId = operationId;
+		this.path = path;
 		this.mediaType = mediaType;
 		this.type = type;
 	}
 
 	generateName(): string {
-		const nameBase = this.operationId.replace(/\//g, '-');
+		const nameBase = this.path.replace(/\//g, '-');
 		return toPascal(nameBase + this.type);
 	}
 
@@ -281,19 +298,19 @@ const emptyRequest = new EmptyTypeAlias(OperationsAliasType.REQUEST);
 const emptyResponse = new EmptyTypeAlias(OperationsAliasType.RESPONSE);
 
 class Endpoint {
-	public readonly operationId: string;
+	public readonly path: string;
 	public request?: IOperationTypeAlias;
 	public response?: IOperationTypeAlias;
 
-	constructor(operationId: string) {
-		this.operationId = operationId;
+	constructor(path: string) {
+		this.path = path;
 	}
 
 	toLine(): string {
 		const reqName = this.request?.generateName() ?? emptyRequest.generateName();
 		const resName = this.response?.generateName() ?? emptyResponse.generateName();
 
-		return `'${this.operationId}': { req: ${reqName}; res: ${resName} };`;
+		return `'${this.path}': { req: ${reqName}; res: ${resName} };`;
 	}
 }
 
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index a936931e99e8..60bf6659c0d9 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,555 +1,556 @@
+/* eslint @typescript-eslint/naming-convention: 0 */
 import { operations } from './types.js';
 
 export type EmptyRequest = Record<string, unknown> | undefined;
 export type EmptyResponse = Record<string, unknown> | undefined;
 
-export type AdminMetaResponse = operations['admin/meta']['responses']['200']['content']['application/json'];
-export type AdminAbuseUserReportsRequest = operations['admin/abuse-user-reports']['requestBody']['content']['application/json'];
-export type AdminAbuseUserReportsResponse = operations['admin/abuse-user-reports']['responses']['200']['content']['application/json'];
-export type AdminAccountsCreateRequest = operations['admin/accounts/create']['requestBody']['content']['application/json'];
-export type AdminAccountsCreateResponse = operations['admin/accounts/create']['responses']['200']['content']['application/json'];
-export type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBody']['content']['application/json'];
-export type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json'];
-export type AdminAccountsFindByEmailResponse = operations['admin/accounts/find-by-email']['responses']['200']['content']['application/json'];
-export type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json'];
-export type AdminAdCreateResponse = operations['admin/ad/create']['responses']['200']['content']['application/json'];
-export type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json'];
-export type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json'];
-export type AdminAdListResponse = operations['admin/ad/list']['responses']['200']['content']['application/json'];
-export type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json'];
-export type AdminAnnouncementsCreateRequest = operations['admin/announcements/create']['requestBody']['content']['application/json'];
-export type AdminAnnouncementsCreateResponse = operations['admin/announcements/create']['responses']['200']['content']['application/json'];
-export type AdminAnnouncementsDeleteRequest = operations['admin/announcements/delete']['requestBody']['content']['application/json'];
-export type AdminAnnouncementsListRequest = operations['admin/announcements/list']['requestBody']['content']['application/json'];
-export type AdminAnnouncementsListResponse = operations['admin/announcements/list']['responses']['200']['content']['application/json'];
-export type AdminAnnouncementsUpdateRequest = operations['admin/announcements/update']['requestBody']['content']['application/json'];
-export type AdminAvatarDecorationsCreateRequest = operations['admin/avatar-decorations/create']['requestBody']['content']['application/json'];
-export type AdminAvatarDecorationsDeleteRequest = operations['admin/avatar-decorations/delete']['requestBody']['content']['application/json'];
-export type AdminAvatarDecorationsListRequest = operations['admin/avatar-decorations/list']['requestBody']['content']['application/json'];
-export type AdminAvatarDecorationsListResponse = operations['admin/avatar-decorations/list']['responses']['200']['content']['application/json'];
-export type AdminAvatarDecorationsUpdateRequest = operations['admin/avatar-decorations/update']['requestBody']['content']['application/json'];
-export type AdminDeleteAllFilesOfAUserRequest = operations['admin/delete-all-files-of-a-user']['requestBody']['content']['application/json'];
-export type AdminUnsetUserAvatarRequest = operations['admin/unset-user-avatar']['requestBody']['content']['application/json'];
-export type AdminUnsetUserBannerRequest = operations['admin/unset-user-banner']['requestBody']['content']['application/json'];
-export type AdminDriveFilesRequest = operations['admin/drive/files']['requestBody']['content']['application/json'];
-export type AdminDriveFilesResponse = operations['admin/drive/files']['responses']['200']['content']['application/json'];
-export type AdminDriveShowFileRequest = operations['admin/drive/show-file']['requestBody']['content']['application/json'];
-export type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json'];
-export type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json'];
-export type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
-export type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json'];
-export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
-export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
-export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
-export type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
-export type AdminEmojiImportZipRequest = operations['admin/emoji/import-zip']['requestBody']['content']['application/json'];
-export type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
-export type AdminEmojiListRemoteResponse = operations['admin/emoji/list-remote']['responses']['200']['content']['application/json'];
-export type AdminEmojiListRequest = operations['admin/emoji/list']['requestBody']['content']['application/json'];
-export type AdminEmojiListResponse = operations['admin/emoji/list']['responses']['200']['content']['application/json'];
-export type AdminEmojiRemoveAliasesBulkRequest = operations['admin/emoji/remove-aliases-bulk']['requestBody']['content']['application/json'];
-export type AdminEmojiSetAliasesBulkRequest = operations['admin/emoji/set-aliases-bulk']['requestBody']['content']['application/json'];
-export type AdminEmojiSetCategoryBulkRequest = operations['admin/emoji/set-category-bulk']['requestBody']['content']['application/json'];
-export type AdminEmojiSetLicenseBulkRequest = operations['admin/emoji/set-license-bulk']['requestBody']['content']['application/json'];
-export type AdminEmojiUpdateRequest = operations['admin/emoji/update']['requestBody']['content']['application/json'];
-export type AdminFederationDeleteAllFilesRequest = operations['admin/federation/delete-all-files']['requestBody']['content']['application/json'];
-export type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
-export type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json'];
-export type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
-export type AdminGetIndexStatsResponse = operations['admin/get-index-stats']['responses']['200']['content']['application/json'];
-export type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
-export type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
-export type AdminGetUserIpsResponse = operations['admin/get-user-ips']['responses']['200']['content']['application/json'];
-export type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
-export type AdminInviteCreateResponse = operations['admin/invite/create']['responses']['200']['content']['application/json'];
-export type AdminInviteListRequest = operations['admin/invite/list']['requestBody']['content']['application/json'];
-export type AdminInviteListResponse = operations['admin/invite/list']['responses']['200']['content']['application/json'];
-export type AdminPromoCreateRequest = operations['admin/promo/create']['requestBody']['content']['application/json'];
-export type AdminQueueDeliverDelayedResponse = operations['admin/queue/deliver-delayed']['responses']['200']['content']['application/json'];
-export type AdminQueueInboxDelayedResponse = operations['admin/queue/inbox-delayed']['responses']['200']['content']['application/json'];
-export type AdminQueuePromoteRequest = operations['admin/queue/promote']['requestBody']['content']['application/json'];
-export type AdminQueueStatsResponse = operations['admin/queue/stats']['responses']['200']['content']['application/json'];
-export type AdminRelaysAddRequest = operations['admin/relays/add']['requestBody']['content']['application/json'];
-export type AdminRelaysAddResponse = operations['admin/relays/add']['responses']['200']['content']['application/json'];
-export type AdminRelaysListResponse = operations['admin/relays/list']['responses']['200']['content']['application/json'];
-export type AdminRelaysRemoveRequest = operations['admin/relays/remove']['requestBody']['content']['application/json'];
-export type AdminResetPasswordRequest = operations['admin/reset-password']['requestBody']['content']['application/json'];
-export type AdminResetPasswordResponse = operations['admin/reset-password']['responses']['200']['content']['application/json'];
-export type AdminResolveAbuseUserReportRequest = operations['admin/resolve-abuse-user-report']['requestBody']['content']['application/json'];
-export type AdminSendEmailRequest = operations['admin/send-email']['requestBody']['content']['application/json'];
-export type AdminServerInfoResponse = operations['admin/server-info']['responses']['200']['content']['application/json'];
-export type AdminShowModerationLogsRequest = operations['admin/show-moderation-logs']['requestBody']['content']['application/json'];
-export type AdminShowModerationLogsResponse = operations['admin/show-moderation-logs']['responses']['200']['content']['application/json'];
-export type AdminShowUserRequest = operations['admin/show-user']['requestBody']['content']['application/json'];
-export type AdminShowUserResponse = operations['admin/show-user']['responses']['200']['content']['application/json'];
-export type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['content']['application/json'];
-export type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json'];
-export type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json'];
-export type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json'];
-export type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['content']['application/json'];
-export type AdminDeleteAccountRequest = operations['admin/delete-account']['requestBody']['content']['application/json'];
-export type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json'];
-export type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json'];
-export type AdminRolesCreateResponse = operations['admin/roles/create']['responses']['200']['content']['application/json'];
-export type AdminRolesDeleteRequest = operations['admin/roles/delete']['requestBody']['content']['application/json'];
-export type AdminRolesListResponse = operations['admin/roles/list']['responses']['200']['content']['application/json'];
-export type AdminRolesShowRequest = operations['admin/roles/show']['requestBody']['content']['application/json'];
-export type AdminRolesShowResponse = operations['admin/roles/show']['responses']['200']['content']['application/json'];
-export type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody']['content']['application/json'];
-export type AdminRolesAssignRequest = operations['admin/roles/assign']['requestBody']['content']['application/json'];
-export type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
-export type AdminRolesUpdateDefaultPoliciesRequest = operations['admin/roles/update-default-policies']['requestBody']['content']['application/json'];
-export type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json'];
-export type AdminRolesUsersResponse = operations['admin/roles/users']['responses']['200']['content']['application/json'];
+export type AdminMetaResponse = operations['admin___meta']['responses']['200']['content']['application/json'];
+export type AdminAbuseUserReportsRequest = operations['admin___abuse-user-reports']['requestBody']['content']['application/json'];
+export type AdminAbuseUserReportsResponse = operations['admin___abuse-user-reports']['responses']['200']['content']['application/json'];
+export type AdminAccountsCreateRequest = operations['admin___accounts___create']['requestBody']['content']['application/json'];
+export type AdminAccountsCreateResponse = operations['admin___accounts___create']['responses']['200']['content']['application/json'];
+export type AdminAccountsDeleteRequest = operations['admin___accounts___delete']['requestBody']['content']['application/json'];
+export type AdminAccountsFindByEmailRequest = operations['admin___accounts___find-by-email']['requestBody']['content']['application/json'];
+export type AdminAccountsFindByEmailResponse = operations['admin___accounts___find-by-email']['responses']['200']['content']['application/json'];
+export type AdminAdCreateRequest = operations['admin___ad___create']['requestBody']['content']['application/json'];
+export type AdminAdCreateResponse = operations['admin___ad___create']['responses']['200']['content']['application/json'];
+export type AdminAdDeleteRequest = operations['admin___ad___delete']['requestBody']['content']['application/json'];
+export type AdminAdListRequest = operations['admin___ad___list']['requestBody']['content']['application/json'];
+export type AdminAdListResponse = operations['admin___ad___list']['responses']['200']['content']['application/json'];
+export type AdminAdUpdateRequest = operations['admin___ad___update']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsCreateRequest = operations['admin___announcements___create']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsCreateResponse = operations['admin___announcements___create']['responses']['200']['content']['application/json'];
+export type AdminAnnouncementsDeleteRequest = operations['admin___announcements___delete']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsListRequest = operations['admin___announcements___list']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsListResponse = operations['admin___announcements___list']['responses']['200']['content']['application/json'];
+export type AdminAnnouncementsUpdateRequest = operations['admin___announcements___update']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsCreateRequest = operations['admin___avatar-decorations___create']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsDeleteRequest = operations['admin___avatar-decorations___delete']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsListRequest = operations['admin___avatar-decorations___list']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations___list']['responses']['200']['content']['application/json'];
+export type AdminAvatarDecorationsUpdateRequest = operations['admin___avatar-decorations___update']['requestBody']['content']['application/json'];
+export type AdminDeleteAllFilesOfAUserRequest = operations['admin___delete-all-files-of-a-user']['requestBody']['content']['application/json'];
+export type AdminUnsetUserAvatarRequest = operations['admin___unset-user-avatar']['requestBody']['content']['application/json'];
+export type AdminUnsetUserBannerRequest = operations['admin___unset-user-banner']['requestBody']['content']['application/json'];
+export type AdminDriveFilesRequest = operations['admin___drive___files']['requestBody']['content']['application/json'];
+export type AdminDriveFilesResponse = operations['admin___drive___files']['responses']['200']['content']['application/json'];
+export type AdminDriveShowFileRequest = operations['admin___drive___show-file']['requestBody']['content']['application/json'];
+export type AdminDriveShowFileResponse = operations['admin___drive___show-file']['responses']['200']['content']['application/json'];
+export type AdminEmojiAddAliasesBulkRequest = operations['admin___emoji___add-aliases-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiAddRequest = operations['admin___emoji___add']['requestBody']['content']['application/json'];
+export type AdminEmojiAddResponse = operations['admin___emoji___add']['responses']['200']['content']['application/json'];
+export type AdminEmojiCopyRequest = operations['admin___emoji___copy']['requestBody']['content']['application/json'];
+export type AdminEmojiCopyResponse = operations['admin___emoji___copy']['responses']['200']['content']['application/json'];
+export type AdminEmojiDeleteBulkRequest = operations['admin___emoji___delete-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiDeleteRequest = operations['admin___emoji___delete']['requestBody']['content']['application/json'];
+export type AdminEmojiImportZipRequest = operations['admin___emoji___import-zip']['requestBody']['content']['application/json'];
+export type AdminEmojiListRemoteRequest = operations['admin___emoji___list-remote']['requestBody']['content']['application/json'];
+export type AdminEmojiListRemoteResponse = operations['admin___emoji___list-remote']['responses']['200']['content']['application/json'];
+export type AdminEmojiListRequest = operations['admin___emoji___list']['requestBody']['content']['application/json'];
+export type AdminEmojiListResponse = operations['admin___emoji___list']['responses']['200']['content']['application/json'];
+export type AdminEmojiRemoveAliasesBulkRequest = operations['admin___emoji___remove-aliases-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiSetAliasesBulkRequest = operations['admin___emoji___set-aliases-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiSetCategoryBulkRequest = operations['admin___emoji___set-category-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiSetLicenseBulkRequest = operations['admin___emoji___set-license-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiUpdateRequest = operations['admin___emoji___update']['requestBody']['content']['application/json'];
+export type AdminFederationDeleteAllFilesRequest = operations['admin___federation___delete-all-files']['requestBody']['content']['application/json'];
+export type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin___federation___refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
+export type AdminFederationRemoveAllFollowingRequest = operations['admin___federation___remove-all-following']['requestBody']['content']['application/json'];
+export type AdminFederationUpdateInstanceRequest = operations['admin___federation___update-instance']['requestBody']['content']['application/json'];
+export type AdminGetIndexStatsResponse = operations['admin___get-index-stats']['responses']['200']['content']['application/json'];
+export type AdminGetTableStatsResponse = operations['admin___get-table-stats']['responses']['200']['content']['application/json'];
+export type AdminGetUserIpsRequest = operations['admin___get-user-ips']['requestBody']['content']['application/json'];
+export type AdminGetUserIpsResponse = operations['admin___get-user-ips']['responses']['200']['content']['application/json'];
+export type AdminInviteCreateRequest = operations['admin___invite___create']['requestBody']['content']['application/json'];
+export type AdminInviteCreateResponse = operations['admin___invite___create']['responses']['200']['content']['application/json'];
+export type AdminInviteListRequest = operations['admin___invite___list']['requestBody']['content']['application/json'];
+export type AdminInviteListResponse = operations['admin___invite___list']['responses']['200']['content']['application/json'];
+export type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json'];
+export type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json'];
+export type AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json'];
+export type AdminQueuePromoteRequest = operations['admin___queue___promote']['requestBody']['content']['application/json'];
+export type AdminQueueStatsResponse = operations['admin___queue___stats']['responses']['200']['content']['application/json'];
+export type AdminRelaysAddRequest = operations['admin___relays___add']['requestBody']['content']['application/json'];
+export type AdminRelaysAddResponse = operations['admin___relays___add']['responses']['200']['content']['application/json'];
+export type AdminRelaysListResponse = operations['admin___relays___list']['responses']['200']['content']['application/json'];
+export type AdminRelaysRemoveRequest = operations['admin___relays___remove']['requestBody']['content']['application/json'];
+export type AdminResetPasswordRequest = operations['admin___reset-password']['requestBody']['content']['application/json'];
+export type AdminResetPasswordResponse = operations['admin___reset-password']['responses']['200']['content']['application/json'];
+export type AdminResolveAbuseUserReportRequest = operations['admin___resolve-abuse-user-report']['requestBody']['content']['application/json'];
+export type AdminSendEmailRequest = operations['admin___send-email']['requestBody']['content']['application/json'];
+export type AdminServerInfoResponse = operations['admin___server-info']['responses']['200']['content']['application/json'];
+export type AdminShowModerationLogsRequest = operations['admin___show-moderation-logs']['requestBody']['content']['application/json'];
+export type AdminShowModerationLogsResponse = operations['admin___show-moderation-logs']['responses']['200']['content']['application/json'];
+export type AdminShowUserRequest = operations['admin___show-user']['requestBody']['content']['application/json'];
+export type AdminShowUserResponse = operations['admin___show-user']['responses']['200']['content']['application/json'];
+export type AdminShowUsersRequest = operations['admin___show-users']['requestBody']['content']['application/json'];
+export type AdminShowUsersResponse = operations['admin___show-users']['responses']['200']['content']['application/json'];
+export type AdminSuspendUserRequest = operations['admin___suspend-user']['requestBody']['content']['application/json'];
+export type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json'];
+export type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json'];
+export type AdminDeleteAccountRequest = operations['admin___delete-account']['requestBody']['content']['application/json'];
+export type AdminUpdateUserNoteRequest = operations['admin___update-user-note']['requestBody']['content']['application/json'];
+export type AdminRolesCreateRequest = operations['admin___roles___create']['requestBody']['content']['application/json'];
+export type AdminRolesCreateResponse = operations['admin___roles___create']['responses']['200']['content']['application/json'];
+export type AdminRolesDeleteRequest = operations['admin___roles___delete']['requestBody']['content']['application/json'];
+export type AdminRolesListResponse = operations['admin___roles___list']['responses']['200']['content']['application/json'];
+export type AdminRolesShowRequest = operations['admin___roles___show']['requestBody']['content']['application/json'];
+export type AdminRolesShowResponse = operations['admin___roles___show']['responses']['200']['content']['application/json'];
+export type AdminRolesUpdateRequest = operations['admin___roles___update']['requestBody']['content']['application/json'];
+export type AdminRolesAssignRequest = operations['admin___roles___assign']['requestBody']['content']['application/json'];
+export type AdminRolesUnassignRequest = operations['admin___roles___unassign']['requestBody']['content']['application/json'];
+export type AdminRolesUpdateDefaultPoliciesRequest = operations['admin___roles___update-default-policies']['requestBody']['content']['application/json'];
+export type AdminRolesUsersRequest = operations['admin___roles___users']['requestBody']['content']['application/json'];
+export type AdminRolesUsersResponse = operations['admin___roles___users']['responses']['200']['content']['application/json'];
 export type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
 export type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
-export type AntennasCreateRequest = operations['antennas/create']['requestBody']['content']['application/json'];
-export type AntennasCreateResponse = operations['antennas/create']['responses']['200']['content']['application/json'];
-export type AntennasDeleteRequest = operations['antennas/delete']['requestBody']['content']['application/json'];
-export type AntennasListResponse = operations['antennas/list']['responses']['200']['content']['application/json'];
-export type AntennasNotesRequest = operations['antennas/notes']['requestBody']['content']['application/json'];
-export type AntennasNotesResponse = operations['antennas/notes']['responses']['200']['content']['application/json'];
-export type AntennasShowRequest = operations['antennas/show']['requestBody']['content']['application/json'];
-export type AntennasShowResponse = operations['antennas/show']['responses']['200']['content']['application/json'];
-export type AntennasUpdateRequest = operations['antennas/update']['requestBody']['content']['application/json'];
-export type AntennasUpdateResponse = operations['antennas/update']['responses']['200']['content']['application/json'];
-export type ApGetRequest = operations['ap/get']['requestBody']['content']['application/json'];
-export type ApGetResponse = operations['ap/get']['responses']['200']['content']['application/json'];
-export type ApShowRequest = operations['ap/show']['requestBody']['content']['application/json'];
-export type ApShowResponse = operations['ap/show']['responses']['200']['content']['application/json'];
-export type AppCreateRequest = operations['app/create']['requestBody']['content']['application/json'];
-export type AppCreateResponse = operations['app/create']['responses']['200']['content']['application/json'];
-export type AppShowRequest = operations['app/show']['requestBody']['content']['application/json'];
-export type AppShowResponse = operations['app/show']['responses']['200']['content']['application/json'];
-export type AuthAcceptRequest = operations['auth/accept']['requestBody']['content']['application/json'];
-export type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
-export type AuthSessionGenerateResponse = operations['auth/session/generate']['responses']['200']['content']['application/json'];
-export type AuthSessionShowRequest = operations['auth/session/show']['requestBody']['content']['application/json'];
-export type AuthSessionShowResponse = operations['auth/session/show']['responses']['200']['content']['application/json'];
-export type AuthSessionUserkeyRequest = operations['auth/session/userkey']['requestBody']['content']['application/json'];
-export type AuthSessionUserkeyResponse = operations['auth/session/userkey']['responses']['200']['content']['application/json'];
-export type BlockingCreateRequest = operations['blocking/create']['requestBody']['content']['application/json'];
-export type BlockingCreateResponse = operations['blocking/create']['responses']['200']['content']['application/json'];
-export type BlockingDeleteRequest = operations['blocking/delete']['requestBody']['content']['application/json'];
-export type BlockingDeleteResponse = operations['blocking/delete']['responses']['200']['content']['application/json'];
-export type BlockingListRequest = operations['blocking/list']['requestBody']['content']['application/json'];
-export type BlockingListResponse = operations['blocking/list']['responses']['200']['content']['application/json'];
-export type ChannelsCreateRequest = operations['channels/create']['requestBody']['content']['application/json'];
-export type ChannelsCreateResponse = operations['channels/create']['responses']['200']['content']['application/json'];
-export type ChannelsFeaturedResponse = operations['channels/featured']['responses']['200']['content']['application/json'];
-export type ChannelsFollowRequest = operations['channels/follow']['requestBody']['content']['application/json'];
-export type ChannelsFollowedRequest = operations['channels/followed']['requestBody']['content']['application/json'];
-export type ChannelsFollowedResponse = operations['channels/followed']['responses']['200']['content']['application/json'];
-export type ChannelsOwnedRequest = operations['channels/owned']['requestBody']['content']['application/json'];
-export type ChannelsOwnedResponse = operations['channels/owned']['responses']['200']['content']['application/json'];
-export type ChannelsShowRequest = operations['channels/show']['requestBody']['content']['application/json'];
-export type ChannelsShowResponse = operations['channels/show']['responses']['200']['content']['application/json'];
-export type ChannelsTimelineRequest = operations['channels/timeline']['requestBody']['content']['application/json'];
-export type ChannelsTimelineResponse = operations['channels/timeline']['responses']['200']['content']['application/json'];
-export type ChannelsUnfollowRequest = operations['channels/unfollow']['requestBody']['content']['application/json'];
-export type ChannelsUpdateRequest = operations['channels/update']['requestBody']['content']['application/json'];
-export type ChannelsUpdateResponse = operations['channels/update']['responses']['200']['content']['application/json'];
-export type ChannelsFavoriteRequest = operations['channels/favorite']['requestBody']['content']['application/json'];
-export type ChannelsUnfavoriteRequest = operations['channels/unfavorite']['requestBody']['content']['application/json'];
-export type ChannelsMyFavoritesResponse = operations['channels/my-favorites']['responses']['200']['content']['application/json'];
-export type ChannelsSearchRequest = operations['channels/search']['requestBody']['content']['application/json'];
-export type ChannelsSearchResponse = operations['channels/search']['responses']['200']['content']['application/json'];
-export type ChartsActiveUsersRequest = operations['charts/active-users']['requestBody']['content']['application/json'];
-export type ChartsActiveUsersResponse = operations['charts/active-users']['responses']['200']['content']['application/json'];
-export type ChartsApRequestRequest = operations['charts/ap-request']['requestBody']['content']['application/json'];
-export type ChartsApRequestResponse = operations['charts/ap-request']['responses']['200']['content']['application/json'];
-export type ChartsDriveRequest = operations['charts/drive']['requestBody']['content']['application/json'];
-export type ChartsDriveResponse = operations['charts/drive']['responses']['200']['content']['application/json'];
-export type ChartsFederationRequest = operations['charts/federation']['requestBody']['content']['application/json'];
-export type ChartsFederationResponse = operations['charts/federation']['responses']['200']['content']['application/json'];
-export type ChartsInstanceRequest = operations['charts/instance']['requestBody']['content']['application/json'];
-export type ChartsInstanceResponse = operations['charts/instance']['responses']['200']['content']['application/json'];
-export type ChartsNotesRequest = operations['charts/notes']['requestBody']['content']['application/json'];
-export type ChartsNotesResponse = operations['charts/notes']['responses']['200']['content']['application/json'];
-export type ChartsUserDriveRequest = operations['charts/user/drive']['requestBody']['content']['application/json'];
-export type ChartsUserDriveResponse = operations['charts/user/drive']['responses']['200']['content']['application/json'];
-export type ChartsUserFollowingRequest = operations['charts/user/following']['requestBody']['content']['application/json'];
-export type ChartsUserFollowingResponse = operations['charts/user/following']['responses']['200']['content']['application/json'];
-export type ChartsUserNotesRequest = operations['charts/user/notes']['requestBody']['content']['application/json'];
-export type ChartsUserNotesResponse = operations['charts/user/notes']['responses']['200']['content']['application/json'];
-export type ChartsUserPvRequest = operations['charts/user/pv']['requestBody']['content']['application/json'];
-export type ChartsUserPvResponse = operations['charts/user/pv']['responses']['200']['content']['application/json'];
-export type ChartsUserReactionsRequest = operations['charts/user/reactions']['requestBody']['content']['application/json'];
-export type ChartsUserReactionsResponse = operations['charts/user/reactions']['responses']['200']['content']['application/json'];
-export type ChartsUsersRequest = operations['charts/users']['requestBody']['content']['application/json'];
-export type ChartsUsersResponse = operations['charts/users']['responses']['200']['content']['application/json'];
-export type ClipsAddNoteRequest = operations['clips/add-note']['requestBody']['content']['application/json'];
-export type ClipsRemoveNoteRequest = operations['clips/remove-note']['requestBody']['content']['application/json'];
-export type ClipsCreateRequest = operations['clips/create']['requestBody']['content']['application/json'];
-export type ClipsCreateResponse = operations['clips/create']['responses']['200']['content']['application/json'];
-export type ClipsDeleteRequest = operations['clips/delete']['requestBody']['content']['application/json'];
-export type ClipsListResponse = operations['clips/list']['responses']['200']['content']['application/json'];
-export type ClipsNotesRequest = operations['clips/notes']['requestBody']['content']['application/json'];
-export type ClipsNotesResponse = operations['clips/notes']['responses']['200']['content']['application/json'];
-export type ClipsShowRequest = operations['clips/show']['requestBody']['content']['application/json'];
-export type ClipsShowResponse = operations['clips/show']['responses']['200']['content']['application/json'];
-export type ClipsUpdateRequest = operations['clips/update']['requestBody']['content']['application/json'];
-export type ClipsUpdateResponse = operations['clips/update']['responses']['200']['content']['application/json'];
-export type ClipsFavoriteRequest = operations['clips/favorite']['requestBody']['content']['application/json'];
-export type ClipsUnfavoriteRequest = operations['clips/unfavorite']['requestBody']['content']['application/json'];
-export type ClipsMyFavoritesResponse = operations['clips/my-favorites']['responses']['200']['content']['application/json'];
+export type AntennasCreateRequest = operations['antennas___create']['requestBody']['content']['application/json'];
+export type AntennasCreateResponse = operations['antennas___create']['responses']['200']['content']['application/json'];
+export type AntennasDeleteRequest = operations['antennas___delete']['requestBody']['content']['application/json'];
+export type AntennasListResponse = operations['antennas___list']['responses']['200']['content']['application/json'];
+export type AntennasNotesRequest = operations['antennas___notes']['requestBody']['content']['application/json'];
+export type AntennasNotesResponse = operations['antennas___notes']['responses']['200']['content']['application/json'];
+export type AntennasShowRequest = operations['antennas___show']['requestBody']['content']['application/json'];
+export type AntennasShowResponse = operations['antennas___show']['responses']['200']['content']['application/json'];
+export type AntennasUpdateRequest = operations['antennas___update']['requestBody']['content']['application/json'];
+export type AntennasUpdateResponse = operations['antennas___update']['responses']['200']['content']['application/json'];
+export type ApGetRequest = operations['ap___get']['requestBody']['content']['application/json'];
+export type ApGetResponse = operations['ap___get']['responses']['200']['content']['application/json'];
+export type ApShowRequest = operations['ap___show']['requestBody']['content']['application/json'];
+export type ApShowResponse = operations['ap___show']['responses']['200']['content']['application/json'];
+export type AppCreateRequest = operations['app___create']['requestBody']['content']['application/json'];
+export type AppCreateResponse = operations['app___create']['responses']['200']['content']['application/json'];
+export type AppShowRequest = operations['app___show']['requestBody']['content']['application/json'];
+export type AppShowResponse = operations['app___show']['responses']['200']['content']['application/json'];
+export type AuthAcceptRequest = operations['auth___accept']['requestBody']['content']['application/json'];
+export type AuthSessionGenerateRequest = operations['auth___session___generate']['requestBody']['content']['application/json'];
+export type AuthSessionGenerateResponse = operations['auth___session___generate']['responses']['200']['content']['application/json'];
+export type AuthSessionShowRequest = operations['auth___session___show']['requestBody']['content']['application/json'];
+export type AuthSessionShowResponse = operations['auth___session___show']['responses']['200']['content']['application/json'];
+export type AuthSessionUserkeyRequest = operations['auth___session___userkey']['requestBody']['content']['application/json'];
+export type AuthSessionUserkeyResponse = operations['auth___session___userkey']['responses']['200']['content']['application/json'];
+export type BlockingCreateRequest = operations['blocking___create']['requestBody']['content']['application/json'];
+export type BlockingCreateResponse = operations['blocking___create']['responses']['200']['content']['application/json'];
+export type BlockingDeleteRequest = operations['blocking___delete']['requestBody']['content']['application/json'];
+export type BlockingDeleteResponse = operations['blocking___delete']['responses']['200']['content']['application/json'];
+export type BlockingListRequest = operations['blocking___list']['requestBody']['content']['application/json'];
+export type BlockingListResponse = operations['blocking___list']['responses']['200']['content']['application/json'];
+export type ChannelsCreateRequest = operations['channels___create']['requestBody']['content']['application/json'];
+export type ChannelsCreateResponse = operations['channels___create']['responses']['200']['content']['application/json'];
+export type ChannelsFeaturedResponse = operations['channels___featured']['responses']['200']['content']['application/json'];
+export type ChannelsFollowRequest = operations['channels___follow']['requestBody']['content']['application/json'];
+export type ChannelsFollowedRequest = operations['channels___followed']['requestBody']['content']['application/json'];
+export type ChannelsFollowedResponse = operations['channels___followed']['responses']['200']['content']['application/json'];
+export type ChannelsOwnedRequest = operations['channels___owned']['requestBody']['content']['application/json'];
+export type ChannelsOwnedResponse = operations['channels___owned']['responses']['200']['content']['application/json'];
+export type ChannelsShowRequest = operations['channels___show']['requestBody']['content']['application/json'];
+export type ChannelsShowResponse = operations['channels___show']['responses']['200']['content']['application/json'];
+export type ChannelsTimelineRequest = operations['channels___timeline']['requestBody']['content']['application/json'];
+export type ChannelsTimelineResponse = operations['channels___timeline']['responses']['200']['content']['application/json'];
+export type ChannelsUnfollowRequest = operations['channels___unfollow']['requestBody']['content']['application/json'];
+export type ChannelsUpdateRequest = operations['channels___update']['requestBody']['content']['application/json'];
+export type ChannelsUpdateResponse = operations['channels___update']['responses']['200']['content']['application/json'];
+export type ChannelsFavoriteRequest = operations['channels___favorite']['requestBody']['content']['application/json'];
+export type ChannelsUnfavoriteRequest = operations['channels___unfavorite']['requestBody']['content']['application/json'];
+export type ChannelsMyFavoritesResponse = operations['channels___my-favorites']['responses']['200']['content']['application/json'];
+export type ChannelsSearchRequest = operations['channels___search']['requestBody']['content']['application/json'];
+export type ChannelsSearchResponse = operations['channels___search']['responses']['200']['content']['application/json'];
+export type ChartsActiveUsersRequest = operations['charts___active-users']['requestBody']['content']['application/json'];
+export type ChartsActiveUsersResponse = operations['charts___active-users']['responses']['200']['content']['application/json'];
+export type ChartsApRequestRequest = operations['charts___ap-request']['requestBody']['content']['application/json'];
+export type ChartsApRequestResponse = operations['charts___ap-request']['responses']['200']['content']['application/json'];
+export type ChartsDriveRequest = operations['charts___drive']['requestBody']['content']['application/json'];
+export type ChartsDriveResponse = operations['charts___drive']['responses']['200']['content']['application/json'];
+export type ChartsFederationRequest = operations['charts___federation']['requestBody']['content']['application/json'];
+export type ChartsFederationResponse = operations['charts___federation']['responses']['200']['content']['application/json'];
+export type ChartsInstanceRequest = operations['charts___instance']['requestBody']['content']['application/json'];
+export type ChartsInstanceResponse = operations['charts___instance']['responses']['200']['content']['application/json'];
+export type ChartsNotesRequest = operations['charts___notes']['requestBody']['content']['application/json'];
+export type ChartsNotesResponse = operations['charts___notes']['responses']['200']['content']['application/json'];
+export type ChartsUserDriveRequest = operations['charts___user___drive']['requestBody']['content']['application/json'];
+export type ChartsUserDriveResponse = operations['charts___user___drive']['responses']['200']['content']['application/json'];
+export type ChartsUserFollowingRequest = operations['charts___user___following']['requestBody']['content']['application/json'];
+export type ChartsUserFollowingResponse = operations['charts___user___following']['responses']['200']['content']['application/json'];
+export type ChartsUserNotesRequest = operations['charts___user___notes']['requestBody']['content']['application/json'];
+export type ChartsUserNotesResponse = operations['charts___user___notes']['responses']['200']['content']['application/json'];
+export type ChartsUserPvRequest = operations['charts___user___pv']['requestBody']['content']['application/json'];
+export type ChartsUserPvResponse = operations['charts___user___pv']['responses']['200']['content']['application/json'];
+export type ChartsUserReactionsRequest = operations['charts___user___reactions']['requestBody']['content']['application/json'];
+export type ChartsUserReactionsResponse = operations['charts___user___reactions']['responses']['200']['content']['application/json'];
+export type ChartsUsersRequest = operations['charts___users']['requestBody']['content']['application/json'];
+export type ChartsUsersResponse = operations['charts___users']['responses']['200']['content']['application/json'];
+export type ClipsAddNoteRequest = operations['clips___add-note']['requestBody']['content']['application/json'];
+export type ClipsRemoveNoteRequest = operations['clips___remove-note']['requestBody']['content']['application/json'];
+export type ClipsCreateRequest = operations['clips___create']['requestBody']['content']['application/json'];
+export type ClipsCreateResponse = operations['clips___create']['responses']['200']['content']['application/json'];
+export type ClipsDeleteRequest = operations['clips___delete']['requestBody']['content']['application/json'];
+export type ClipsListResponse = operations['clips___list']['responses']['200']['content']['application/json'];
+export type ClipsNotesRequest = operations['clips___notes']['requestBody']['content']['application/json'];
+export type ClipsNotesResponse = operations['clips___notes']['responses']['200']['content']['application/json'];
+export type ClipsShowRequest = operations['clips___show']['requestBody']['content']['application/json'];
+export type ClipsShowResponse = operations['clips___show']['responses']['200']['content']['application/json'];
+export type ClipsUpdateRequest = operations['clips___update']['requestBody']['content']['application/json'];
+export type ClipsUpdateResponse = operations['clips___update']['responses']['200']['content']['application/json'];
+export type ClipsFavoriteRequest = operations['clips___favorite']['requestBody']['content']['application/json'];
+export type ClipsUnfavoriteRequest = operations['clips___unfavorite']['requestBody']['content']['application/json'];
+export type ClipsMyFavoritesResponse = operations['clips___my-favorites']['responses']['200']['content']['application/json'];
 export type DriveResponse = operations['drive']['responses']['200']['content']['application/json'];
-export type DriveFilesRequest = operations['drive/files']['requestBody']['content']['application/json'];
-export type DriveFilesResponse = operations['drive/files']['responses']['200']['content']['application/json'];
-export type DriveFilesAttachedNotesRequest = operations['drive/files/attached-notes']['requestBody']['content']['application/json'];
-export type DriveFilesAttachedNotesResponse = operations['drive/files/attached-notes']['responses']['200']['content']['application/json'];
-export type DriveFilesCheckExistenceRequest = operations['drive/files/check-existence']['requestBody']['content']['application/json'];
-export type DriveFilesCheckExistenceResponse = operations['drive/files/check-existence']['responses']['200']['content']['application/json'];
-export type DriveFilesCreateRequest = operations['drive/files/create']['requestBody']['content']['multipart/form-data'];
-export type DriveFilesCreateResponse = operations['drive/files/create']['responses']['200']['content']['application/json'];
-export type DriveFilesDeleteRequest = operations['drive/files/delete']['requestBody']['content']['application/json'];
-export type DriveFilesFindByHashRequest = operations['drive/files/find-by-hash']['requestBody']['content']['application/json'];
-export type DriveFilesFindByHashResponse = operations['drive/files/find-by-hash']['responses']['200']['content']['application/json'];
-export type DriveFilesFindRequest = operations['drive/files/find']['requestBody']['content']['application/json'];
-export type DriveFilesFindResponse = operations['drive/files/find']['responses']['200']['content']['application/json'];
-export type DriveFilesShowRequest = operations['drive/files/show']['requestBody']['content']['application/json'];
-export type DriveFilesShowResponse = operations['drive/files/show']['responses']['200']['content']['application/json'];
-export type DriveFilesUpdateRequest = operations['drive/files/update']['requestBody']['content']['application/json'];
-export type DriveFilesUpdateResponse = operations['drive/files/update']['responses']['200']['content']['application/json'];
-export type DriveFilesUploadFromUrlRequest = operations['drive/files/upload-from-url']['requestBody']['content']['application/json'];
-export type DriveFoldersRequest = operations['drive/folders']['requestBody']['content']['application/json'];
-export type DriveFoldersResponse = operations['drive/folders']['responses']['200']['content']['application/json'];
-export type DriveFoldersCreateRequest = operations['drive/folders/create']['requestBody']['content']['application/json'];
-export type DriveFoldersCreateResponse = operations['drive/folders/create']['responses']['200']['content']['application/json'];
-export type DriveFoldersDeleteRequest = operations['drive/folders/delete']['requestBody']['content']['application/json'];
-export type DriveFoldersFindRequest = operations['drive/folders/find']['requestBody']['content']['application/json'];
-export type DriveFoldersFindResponse = operations['drive/folders/find']['responses']['200']['content']['application/json'];
-export type DriveFoldersShowRequest = operations['drive/folders/show']['requestBody']['content']['application/json'];
-export type DriveFoldersShowResponse = operations['drive/folders/show']['responses']['200']['content']['application/json'];
-export type DriveFoldersUpdateRequest = operations['drive/folders/update']['requestBody']['content']['application/json'];
-export type DriveFoldersUpdateResponse = operations['drive/folders/update']['responses']['200']['content']['application/json'];
-export type DriveStreamRequest = operations['drive/stream']['requestBody']['content']['application/json'];
-export type DriveStreamResponse = operations['drive/stream']['responses']['200']['content']['application/json'];
-export type EmailAddressAvailableRequest = operations['email-address/available']['requestBody']['content']['application/json'];
-export type EmailAddressAvailableResponse = operations['email-address/available']['responses']['200']['content']['application/json'];
+export type DriveFilesRequest = operations['drive___files']['requestBody']['content']['application/json'];
+export type DriveFilesResponse = operations['drive___files']['responses']['200']['content']['application/json'];
+export type DriveFilesAttachedNotesRequest = operations['drive___files___attached-notes']['requestBody']['content']['application/json'];
+export type DriveFilesAttachedNotesResponse = operations['drive___files___attached-notes']['responses']['200']['content']['application/json'];
+export type DriveFilesCheckExistenceRequest = operations['drive___files___check-existence']['requestBody']['content']['application/json'];
+export type DriveFilesCheckExistenceResponse = operations['drive___files___check-existence']['responses']['200']['content']['application/json'];
+export type DriveFilesCreateRequest = operations['drive___files___create']['requestBody']['content']['multipart/form-data'];
+export type DriveFilesCreateResponse = operations['drive___files___create']['responses']['200']['content']['application/json'];
+export type DriveFilesDeleteRequest = operations['drive___files___delete']['requestBody']['content']['application/json'];
+export type DriveFilesFindByHashRequest = operations['drive___files___find-by-hash']['requestBody']['content']['application/json'];
+export type DriveFilesFindByHashResponse = operations['drive___files___find-by-hash']['responses']['200']['content']['application/json'];
+export type DriveFilesFindRequest = operations['drive___files___find']['requestBody']['content']['application/json'];
+export type DriveFilesFindResponse = operations['drive___files___find']['responses']['200']['content']['application/json'];
+export type DriveFilesShowRequest = operations['drive___files___show']['requestBody']['content']['application/json'];
+export type DriveFilesShowResponse = operations['drive___files___show']['responses']['200']['content']['application/json'];
+export type DriveFilesUpdateRequest = operations['drive___files___update']['requestBody']['content']['application/json'];
+export type DriveFilesUpdateResponse = operations['drive___files___update']['responses']['200']['content']['application/json'];
+export type DriveFilesUploadFromUrlRequest = operations['drive___files___upload-from-url']['requestBody']['content']['application/json'];
+export type DriveFoldersRequest = operations['drive___folders']['requestBody']['content']['application/json'];
+export type DriveFoldersResponse = operations['drive___folders']['responses']['200']['content']['application/json'];
+export type DriveFoldersCreateRequest = operations['drive___folders___create']['requestBody']['content']['application/json'];
+export type DriveFoldersCreateResponse = operations['drive___folders___create']['responses']['200']['content']['application/json'];
+export type DriveFoldersDeleteRequest = operations['drive___folders___delete']['requestBody']['content']['application/json'];
+export type DriveFoldersFindRequest = operations['drive___folders___find']['requestBody']['content']['application/json'];
+export type DriveFoldersFindResponse = operations['drive___folders___find']['responses']['200']['content']['application/json'];
+export type DriveFoldersShowRequest = operations['drive___folders___show']['requestBody']['content']['application/json'];
+export type DriveFoldersShowResponse = operations['drive___folders___show']['responses']['200']['content']['application/json'];
+export type DriveFoldersUpdateRequest = operations['drive___folders___update']['requestBody']['content']['application/json'];
+export type DriveFoldersUpdateResponse = operations['drive___folders___update']['responses']['200']['content']['application/json'];
+export type DriveStreamRequest = operations['drive___stream']['requestBody']['content']['application/json'];
+export type DriveStreamResponse = operations['drive___stream']['responses']['200']['content']['application/json'];
+export type EmailAddressAvailableRequest = operations['email-address___available']['requestBody']['content']['application/json'];
+export type EmailAddressAvailableResponse = operations['email-address___available']['responses']['200']['content']['application/json'];
 export type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json'];
 export type EndpointResponse = operations['endpoint']['responses']['200']['content']['application/json'];
 export type EndpointsResponse = operations['endpoints']['responses']['200']['content']['application/json'];
-export type FederationFollowersRequest = operations['federation/followers']['requestBody']['content']['application/json'];
-export type FederationFollowersResponse = operations['federation/followers']['responses']['200']['content']['application/json'];
-export type FederationFollowingRequest = operations['federation/following']['requestBody']['content']['application/json'];
-export type FederationFollowingResponse = operations['federation/following']['responses']['200']['content']['application/json'];
-export type FederationInstancesRequest = operations['federation/instances']['requestBody']['content']['application/json'];
-export type FederationInstancesResponse = operations['federation/instances']['responses']['200']['content']['application/json'];
-export type FederationShowInstanceRequest = operations['federation/show-instance']['requestBody']['content']['application/json'];
-export type FederationShowInstanceResponse = operations['federation/show-instance']['responses']['200']['content']['application/json'];
-export type FederationUpdateRemoteUserRequest = operations['federation/update-remote-user']['requestBody']['content']['application/json'];
-export type FederationUsersRequest = operations['federation/users']['requestBody']['content']['application/json'];
-export type FederationUsersResponse = operations['federation/users']['responses']['200']['content']['application/json'];
-export type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json'];
-export type FederationStatsResponse = operations['federation/stats']['responses']['200']['content']['application/json'];
-export type FollowingCreateRequest = operations['following/create']['requestBody']['content']['application/json'];
-export type FollowingCreateResponse = operations['following/create']['responses']['200']['content']['application/json'];
-export type FollowingDeleteRequest = operations['following/delete']['requestBody']['content']['application/json'];
-export type FollowingDeleteResponse = operations['following/delete']['responses']['200']['content']['application/json'];
-export type FollowingUpdateRequest = operations['following/update']['requestBody']['content']['application/json'];
-export type FollowingUpdateResponse = operations['following/update']['responses']['200']['content']['application/json'];
-export type FollowingUpdateAllRequest = operations['following/update-all']['requestBody']['content']['application/json'];
-export type FollowingInvalidateRequest = operations['following/invalidate']['requestBody']['content']['application/json'];
-export type FollowingInvalidateResponse = operations['following/invalidate']['responses']['200']['content']['application/json'];
-export type FollowingRequestsAcceptRequest = operations['following/requests/accept']['requestBody']['content']['application/json'];
-export type FollowingRequestsCancelRequest = operations['following/requests/cancel']['requestBody']['content']['application/json'];
-export type FollowingRequestsCancelResponse = operations['following/requests/cancel']['responses']['200']['content']['application/json'];
-export type FollowingRequestsListRequest = operations['following/requests/list']['requestBody']['content']['application/json'];
-export type FollowingRequestsListResponse = operations['following/requests/list']['responses']['200']['content']['application/json'];
-export type FollowingRequestsRejectRequest = operations['following/requests/reject']['requestBody']['content']['application/json'];
-export type GalleryFeaturedRequest = operations['gallery/featured']['requestBody']['content']['application/json'];
-export type GalleryFeaturedResponse = operations['gallery/featured']['responses']['200']['content']['application/json'];
-export type GalleryPopularResponse = operations['gallery/popular']['responses']['200']['content']['application/json'];
-export type GalleryPostsRequest = operations['gallery/posts']['requestBody']['content']['application/json'];
-export type GalleryPostsResponse = operations['gallery/posts']['responses']['200']['content']['application/json'];
-export type GalleryPostsCreateRequest = operations['gallery/posts/create']['requestBody']['content']['application/json'];
-export type GalleryPostsCreateResponse = operations['gallery/posts/create']['responses']['200']['content']['application/json'];
-export type GalleryPostsDeleteRequest = operations['gallery/posts/delete']['requestBody']['content']['application/json'];
-export type GalleryPostsLikeRequest = operations['gallery/posts/like']['requestBody']['content']['application/json'];
-export type GalleryPostsShowRequest = operations['gallery/posts/show']['requestBody']['content']['application/json'];
-export type GalleryPostsShowResponse = operations['gallery/posts/show']['responses']['200']['content']['application/json'];
-export type GalleryPostsUnlikeRequest = operations['gallery/posts/unlike']['requestBody']['content']['application/json'];
-export type GalleryPostsUpdateRequest = operations['gallery/posts/update']['requestBody']['content']['application/json'];
-export type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses']['200']['content']['application/json'];
+export type FederationFollowersRequest = operations['federation___followers']['requestBody']['content']['application/json'];
+export type FederationFollowersResponse = operations['federation___followers']['responses']['200']['content']['application/json'];
+export type FederationFollowingRequest = operations['federation___following']['requestBody']['content']['application/json'];
+export type FederationFollowingResponse = operations['federation___following']['responses']['200']['content']['application/json'];
+export type FederationInstancesRequest = operations['federation___instances']['requestBody']['content']['application/json'];
+export type FederationInstancesResponse = operations['federation___instances']['responses']['200']['content']['application/json'];
+export type FederationShowInstanceRequest = operations['federation___show-instance']['requestBody']['content']['application/json'];
+export type FederationShowInstanceResponse = operations['federation___show-instance']['responses']['200']['content']['application/json'];
+export type FederationUpdateRemoteUserRequest = operations['federation___update-remote-user']['requestBody']['content']['application/json'];
+export type FederationUsersRequest = operations['federation___users']['requestBody']['content']['application/json'];
+export type FederationUsersResponse = operations['federation___users']['responses']['200']['content']['application/json'];
+export type FederationStatsRequest = operations['federation___stats']['requestBody']['content']['application/json'];
+export type FederationStatsResponse = operations['federation___stats']['responses']['200']['content']['application/json'];
+export type FollowingCreateRequest = operations['following___create']['requestBody']['content']['application/json'];
+export type FollowingCreateResponse = operations['following___create']['responses']['200']['content']['application/json'];
+export type FollowingDeleteRequest = operations['following___delete']['requestBody']['content']['application/json'];
+export type FollowingDeleteResponse = operations['following___delete']['responses']['200']['content']['application/json'];
+export type FollowingUpdateRequest = operations['following___update']['requestBody']['content']['application/json'];
+export type FollowingUpdateResponse = operations['following___update']['responses']['200']['content']['application/json'];
+export type FollowingUpdateAllRequest = operations['following___update-all']['requestBody']['content']['application/json'];
+export type FollowingInvalidateRequest = operations['following___invalidate']['requestBody']['content']['application/json'];
+export type FollowingInvalidateResponse = operations['following___invalidate']['responses']['200']['content']['application/json'];
+export type FollowingRequestsAcceptRequest = operations['following___requests___accept']['requestBody']['content']['application/json'];
+export type FollowingRequestsCancelRequest = operations['following___requests___cancel']['requestBody']['content']['application/json'];
+export type FollowingRequestsCancelResponse = operations['following___requests___cancel']['responses']['200']['content']['application/json'];
+export type FollowingRequestsListRequest = operations['following___requests___list']['requestBody']['content']['application/json'];
+export type FollowingRequestsListResponse = operations['following___requests___list']['responses']['200']['content']['application/json'];
+export type FollowingRequestsRejectRequest = operations['following___requests___reject']['requestBody']['content']['application/json'];
+export type GalleryFeaturedRequest = operations['gallery___featured']['requestBody']['content']['application/json'];
+export type GalleryFeaturedResponse = operations['gallery___featured']['responses']['200']['content']['application/json'];
+export type GalleryPopularResponse = operations['gallery___popular']['responses']['200']['content']['application/json'];
+export type GalleryPostsRequest = operations['gallery___posts']['requestBody']['content']['application/json'];
+export type GalleryPostsResponse = operations['gallery___posts']['responses']['200']['content']['application/json'];
+export type GalleryPostsCreateRequest = operations['gallery___posts___create']['requestBody']['content']['application/json'];
+export type GalleryPostsCreateResponse = operations['gallery___posts___create']['responses']['200']['content']['application/json'];
+export type GalleryPostsDeleteRequest = operations['gallery___posts___delete']['requestBody']['content']['application/json'];
+export type GalleryPostsLikeRequest = operations['gallery___posts___like']['requestBody']['content']['application/json'];
+export type GalleryPostsShowRequest = operations['gallery___posts___show']['requestBody']['content']['application/json'];
+export type GalleryPostsShowResponse = operations['gallery___posts___show']['responses']['200']['content']['application/json'];
+export type GalleryPostsUnlikeRequest = operations['gallery___posts___unlike']['requestBody']['content']['application/json'];
+export type GalleryPostsUpdateRequest = operations['gallery___posts___update']['requestBody']['content']['application/json'];
+export type GalleryPostsUpdateResponse = operations['gallery___posts___update']['responses']['200']['content']['application/json'];
 export type GetOnlineUsersCountResponse = operations['get-online-users-count']['responses']['200']['content']['application/json'];
 export type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json'];
-export type HashtagsListRequest = operations['hashtags/list']['requestBody']['content']['application/json'];
-export type HashtagsListResponse = operations['hashtags/list']['responses']['200']['content']['application/json'];
-export type HashtagsSearchRequest = operations['hashtags/search']['requestBody']['content']['application/json'];
-export type HashtagsSearchResponse = operations['hashtags/search']['responses']['200']['content']['application/json'];
-export type HashtagsShowRequest = operations['hashtags/show']['requestBody']['content']['application/json'];
-export type HashtagsShowResponse = operations['hashtags/show']['responses']['200']['content']['application/json'];
-export type HashtagsTrendResponse = operations['hashtags/trend']['responses']['200']['content']['application/json'];
-export type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content']['application/json'];
-export type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
+export type HashtagsListRequest = operations['hashtags___list']['requestBody']['content']['application/json'];
+export type HashtagsListResponse = operations['hashtags___list']['responses']['200']['content']['application/json'];
+export type HashtagsSearchRequest = operations['hashtags___search']['requestBody']['content']['application/json'];
+export type HashtagsSearchResponse = operations['hashtags___search']['responses']['200']['content']['application/json'];
+export type HashtagsShowRequest = operations['hashtags___show']['requestBody']['content']['application/json'];
+export type HashtagsShowResponse = operations['hashtags___show']['responses']['200']['content']['application/json'];
+export type HashtagsTrendResponse = operations['hashtags___trend']['responses']['200']['content']['application/json'];
+export type HashtagsUsersRequest = operations['hashtags___users']['requestBody']['content']['application/json'];
+export type HashtagsUsersResponse = operations['hashtags___users']['responses']['200']['content']['application/json'];
 export type IResponse = operations['i']['responses']['200']['content']['application/json'];
-export type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json'];
-export type I2faDoneResponse = operations['i/2fa/done']['responses']['200']['content']['application/json'];
-export type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
-export type I2faKeyDoneResponse = operations['i/2fa/key-done']['responses']['200']['content']['application/json'];
-export type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
-export type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
-export type I2faRegisterKeyResponse = operations['i/2fa/register-key']['responses']['200']['content']['application/json'];
-export type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
-export type I2faRegisterResponse = operations['i/2fa/register']['responses']['200']['content']['application/json'];
-export type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['content']['application/json'];
-export type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
-export type I2faUnregisterRequest = operations['i/2fa/unregister']['requestBody']['content']['application/json'];
-export type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
-export type IAppsResponse = operations['i/apps']['responses']['200']['content']['application/json'];
-export type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
-export type IAuthorizedAppsResponse = operations['i/authorized-apps']['responses']['200']['content']['application/json'];
-export type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
-export type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
-export type IDeleteAccountRequest = operations['i/delete-account']['requestBody']['content']['application/json'];
-export type IExportFollowingRequest = operations['i/export-following']['requestBody']['content']['application/json'];
-export type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
-export type IFavoritesResponse = operations['i/favorites']['responses']['200']['content']['application/json'];
-export type IGalleryLikesRequest = operations['i/gallery/likes']['requestBody']['content']['application/json'];
-export type IGalleryLikesResponse = operations['i/gallery/likes']['responses']['200']['content']['application/json'];
-export type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['content']['application/json'];
-export type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
-export type IImportBlockingRequest = operations['i/import-blocking']['requestBody']['content']['application/json'];
-export type IImportFollowingRequest = operations['i/import-following']['requestBody']['content']['application/json'];
-export type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json'];
-export type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json'];
-export type IImportAntennasRequest = operations['i/import-antennas']['requestBody']['content']['application/json'];
-export type INotificationsRequest = operations['i/notifications']['requestBody']['content']['application/json'];
-export type INotificationsResponse = operations['i/notifications']['responses']['200']['content']['application/json'];
-export type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
-export type INotificationsGroupedResponse = operations['i/notifications-grouped']['responses']['200']['content']['application/json'];
-export type IPageLikesRequest = operations['i/page-likes']['requestBody']['content']['application/json'];
-export type IPageLikesResponse = operations['i/page-likes']['responses']['200']['content']['application/json'];
-export type IPagesRequest = operations['i/pages']['requestBody']['content']['application/json'];
-export type IPagesResponse = operations['i/pages']['responses']['200']['content']['application/json'];
-export type IPinRequest = operations['i/pin']['requestBody']['content']['application/json'];
-export type IPinResponse = operations['i/pin']['responses']['200']['content']['application/json'];
-export type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
-export type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json'];
-export type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
-export type IRegistryGetAllResponse = operations['i/registry/get-all']['responses']['200']['content']['application/json'];
-export type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
-export type IRegistryGetDetailResponse = operations['i/registry/get-detail']['responses']['200']['content']['application/json'];
-export type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
-export type IRegistryGetResponse = operations['i/registry/get']['responses']['200']['content']['application/json'];
-export type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json'];
-export type IRegistryKeysWithTypeResponse = operations['i/registry/keys-with-type']['responses']['200']['content']['application/json'];
-export type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
-export type IRegistryKeysResponse = operations['i/registry/keys']['responses']['200']['content']['application/json'];
-export type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
-export type IRegistryScopesWithDomainResponse = operations['i/registry/scopes-with-domain']['responses']['200']['content']['application/json'];
-export type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
-export type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json'];
-export type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json'];
-export type ISigninHistoryResponse = operations['i/signin-history']['responses']['200']['content']['application/json'];
-export type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
-export type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
-export type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
-export type IUpdateEmailResponse = operations['i/update-email']['responses']['200']['content']['application/json'];
-export type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
-export type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
-export type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
-export type IMoveResponse = operations['i/move']['responses']['200']['content']['application/json'];
-export type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
-export type IWebhooksCreateResponse = operations['i/webhooks/create']['responses']['200']['content']['application/json'];
-export type IWebhooksListResponse = operations['i/webhooks/list']['responses']['200']['content']['application/json'];
-export type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
-export type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200']['content']['application/json'];
-export type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
-export type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json'];
-export type InviteCreateResponse = operations['invite/create']['responses']['200']['content']['application/json'];
-export type InviteDeleteRequest = operations['invite/delete']['requestBody']['content']['application/json'];
-export type InviteListRequest = operations['invite/list']['requestBody']['content']['application/json'];
-export type InviteListResponse = operations['invite/list']['responses']['200']['content']['application/json'];
-export type InviteLimitResponse = operations['invite/limit']['responses']['200']['content']['application/json'];
+export type I2faDoneRequest = operations['i___2fa___done']['requestBody']['content']['application/json'];
+export type I2faDoneResponse = operations['i___2fa___done']['responses']['200']['content']['application/json'];
+export type I2faKeyDoneRequest = operations['i___2fa___key-done']['requestBody']['content']['application/json'];
+export type I2faKeyDoneResponse = operations['i___2fa___key-done']['responses']['200']['content']['application/json'];
+export type I2faPasswordLessRequest = operations['i___2fa___password-less']['requestBody']['content']['application/json'];
+export type I2faRegisterKeyRequest = operations['i___2fa___register-key']['requestBody']['content']['application/json'];
+export type I2faRegisterKeyResponse = operations['i___2fa___register-key']['responses']['200']['content']['application/json'];
+export type I2faRegisterRequest = operations['i___2fa___register']['requestBody']['content']['application/json'];
+export type I2faRegisterResponse = operations['i___2fa___register']['responses']['200']['content']['application/json'];
+export type I2faUpdateKeyRequest = operations['i___2fa___update-key']['requestBody']['content']['application/json'];
+export type I2faRemoveKeyRequest = operations['i___2fa___remove-key']['requestBody']['content']['application/json'];
+export type I2faUnregisterRequest = operations['i___2fa___unregister']['requestBody']['content']['application/json'];
+export type IAppsRequest = operations['i___apps']['requestBody']['content']['application/json'];
+export type IAppsResponse = operations['i___apps']['responses']['200']['content']['application/json'];
+export type IAuthorizedAppsRequest = operations['i___authorized-apps']['requestBody']['content']['application/json'];
+export type IAuthorizedAppsResponse = operations['i___authorized-apps']['responses']['200']['content']['application/json'];
+export type IClaimAchievementRequest = operations['i___claim-achievement']['requestBody']['content']['application/json'];
+export type IChangePasswordRequest = operations['i___change-password']['requestBody']['content']['application/json'];
+export type IDeleteAccountRequest = operations['i___delete-account']['requestBody']['content']['application/json'];
+export type IExportFollowingRequest = operations['i___export-following']['requestBody']['content']['application/json'];
+export type IFavoritesRequest = operations['i___favorites']['requestBody']['content']['application/json'];
+export type IFavoritesResponse = operations['i___favorites']['responses']['200']['content']['application/json'];
+export type IGalleryLikesRequest = operations['i___gallery___likes']['requestBody']['content']['application/json'];
+export type IGalleryLikesResponse = operations['i___gallery___likes']['responses']['200']['content']['application/json'];
+export type IGalleryPostsRequest = operations['i___gallery___posts']['requestBody']['content']['application/json'];
+export type IGalleryPostsResponse = operations['i___gallery___posts']['responses']['200']['content']['application/json'];
+export type IImportBlockingRequest = operations['i___import-blocking']['requestBody']['content']['application/json'];
+export type IImportFollowingRequest = operations['i___import-following']['requestBody']['content']['application/json'];
+export type IImportMutingRequest = operations['i___import-muting']['requestBody']['content']['application/json'];
+export type IImportUserListsRequest = operations['i___import-user-lists']['requestBody']['content']['application/json'];
+export type IImportAntennasRequest = operations['i___import-antennas']['requestBody']['content']['application/json'];
+export type INotificationsRequest = operations['i___notifications']['requestBody']['content']['application/json'];
+export type INotificationsResponse = operations['i___notifications']['responses']['200']['content']['application/json'];
+export type INotificationsGroupedRequest = operations['i___notifications-grouped']['requestBody']['content']['application/json'];
+export type INotificationsGroupedResponse = operations['i___notifications-grouped']['responses']['200']['content']['application/json'];
+export type IPageLikesRequest = operations['i___page-likes']['requestBody']['content']['application/json'];
+export type IPageLikesResponse = operations['i___page-likes']['responses']['200']['content']['application/json'];
+export type IPagesRequest = operations['i___pages']['requestBody']['content']['application/json'];
+export type IPagesResponse = operations['i___pages']['responses']['200']['content']['application/json'];
+export type IPinRequest = operations['i___pin']['requestBody']['content']['application/json'];
+export type IPinResponse = operations['i___pin']['responses']['200']['content']['application/json'];
+export type IReadAnnouncementRequest = operations['i___read-announcement']['requestBody']['content']['application/json'];
+export type IRegenerateTokenRequest = operations['i___regenerate-token']['requestBody']['content']['application/json'];
+export type IRegistryGetAllRequest = operations['i___registry___get-all']['requestBody']['content']['application/json'];
+export type IRegistryGetAllResponse = operations['i___registry___get-all']['responses']['200']['content']['application/json'];
+export type IRegistryGetDetailRequest = operations['i___registry___get-detail']['requestBody']['content']['application/json'];
+export type IRegistryGetDetailResponse = operations['i___registry___get-detail']['responses']['200']['content']['application/json'];
+export type IRegistryGetRequest = operations['i___registry___get']['requestBody']['content']['application/json'];
+export type IRegistryGetResponse = operations['i___registry___get']['responses']['200']['content']['application/json'];
+export type IRegistryKeysWithTypeRequest = operations['i___registry___keys-with-type']['requestBody']['content']['application/json'];
+export type IRegistryKeysWithTypeResponse = operations['i___registry___keys-with-type']['responses']['200']['content']['application/json'];
+export type IRegistryKeysRequest = operations['i___registry___keys']['requestBody']['content']['application/json'];
+export type IRegistryKeysResponse = operations['i___registry___keys']['responses']['200']['content']['application/json'];
+export type IRegistryRemoveRequest = operations['i___registry___remove']['requestBody']['content']['application/json'];
+export type IRegistryScopesWithDomainResponse = operations['i___registry___scopes-with-domain']['responses']['200']['content']['application/json'];
+export type IRegistrySetRequest = operations['i___registry___set']['requestBody']['content']['application/json'];
+export type IRevokeTokenRequest = operations['i___revoke-token']['requestBody']['content']['application/json'];
+export type ISigninHistoryRequest = operations['i___signin-history']['requestBody']['content']['application/json'];
+export type ISigninHistoryResponse = operations['i___signin-history']['responses']['200']['content']['application/json'];
+export type IUnpinRequest = operations['i___unpin']['requestBody']['content']['application/json'];
+export type IUnpinResponse = operations['i___unpin']['responses']['200']['content']['application/json'];
+export type IUpdateEmailRequest = operations['i___update-email']['requestBody']['content']['application/json'];
+export type IUpdateEmailResponse = operations['i___update-email']['responses']['200']['content']['application/json'];
+export type IUpdateRequest = operations['i___update']['requestBody']['content']['application/json'];
+export type IUpdateResponse = operations['i___update']['responses']['200']['content']['application/json'];
+export type IMoveRequest = operations['i___move']['requestBody']['content']['application/json'];
+export type IMoveResponse = operations['i___move']['responses']['200']['content']['application/json'];
+export type IWebhooksCreateRequest = operations['i___webhooks___create']['requestBody']['content']['application/json'];
+export type IWebhooksCreateResponse = operations['i___webhooks___create']['responses']['200']['content']['application/json'];
+export type IWebhooksListResponse = operations['i___webhooks___list']['responses']['200']['content']['application/json'];
+export type IWebhooksShowRequest = operations['i___webhooks___show']['requestBody']['content']['application/json'];
+export type IWebhooksShowResponse = operations['i___webhooks___show']['responses']['200']['content']['application/json'];
+export type IWebhooksUpdateRequest = operations['i___webhooks___update']['requestBody']['content']['application/json'];
+export type IWebhooksDeleteRequest = operations['i___webhooks___delete']['requestBody']['content']['application/json'];
+export type InviteCreateResponse = operations['invite___create']['responses']['200']['content']['application/json'];
+export type InviteDeleteRequest = operations['invite___delete']['requestBody']['content']['application/json'];
+export type InviteListRequest = operations['invite___list']['requestBody']['content']['application/json'];
+export type InviteListResponse = operations['invite___list']['responses']['200']['content']['application/json'];
+export type InviteLimitResponse = operations['invite___limit']['responses']['200']['content']['application/json'];
 export type MetaRequest = operations['meta']['requestBody']['content']['application/json'];
 export type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
 export type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
 export type EmojiRequest = operations['emoji']['requestBody']['content']['application/json'];
 export type EmojiResponse = operations['emoji']['responses']['200']['content']['application/json'];
-export type MiauthGenTokenRequest = operations['miauth/gen-token']['requestBody']['content']['application/json'];
-export type MiauthGenTokenResponse = operations['miauth/gen-token']['responses']['200']['content']['application/json'];
-export type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
-export type MuteDeleteRequest = operations['mute/delete']['requestBody']['content']['application/json'];
-export type MuteListRequest = operations['mute/list']['requestBody']['content']['application/json'];
-export type MuteListResponse = operations['mute/list']['responses']['200']['content']['application/json'];
-export type RenoteMuteCreateRequest = operations['renote-mute/create']['requestBody']['content']['application/json'];
-export type RenoteMuteDeleteRequest = operations['renote-mute/delete']['requestBody']['content']['application/json'];
-export type RenoteMuteListRequest = operations['renote-mute/list']['requestBody']['content']['application/json'];
-export type RenoteMuteListResponse = operations['renote-mute/list']['responses']['200']['content']['application/json'];
-export type MyAppsRequest = operations['my/apps']['requestBody']['content']['application/json'];
-export type MyAppsResponse = operations['my/apps']['responses']['200']['content']['application/json'];
+export type MiauthGenTokenRequest = operations['miauth___gen-token']['requestBody']['content']['application/json'];
+export type MiauthGenTokenResponse = operations['miauth___gen-token']['responses']['200']['content']['application/json'];
+export type MuteCreateRequest = operations['mute___create']['requestBody']['content']['application/json'];
+export type MuteDeleteRequest = operations['mute___delete']['requestBody']['content']['application/json'];
+export type MuteListRequest = operations['mute___list']['requestBody']['content']['application/json'];
+export type MuteListResponse = operations['mute___list']['responses']['200']['content']['application/json'];
+export type RenoteMuteCreateRequest = operations['renote-mute___create']['requestBody']['content']['application/json'];
+export type RenoteMuteDeleteRequest = operations['renote-mute___delete']['requestBody']['content']['application/json'];
+export type RenoteMuteListRequest = operations['renote-mute___list']['requestBody']['content']['application/json'];
+export type RenoteMuteListResponse = operations['renote-mute___list']['responses']['200']['content']['application/json'];
+export type MyAppsRequest = operations['my___apps']['requestBody']['content']['application/json'];
+export type MyAppsResponse = operations['my___apps']['responses']['200']['content']['application/json'];
 export type NotesRequest = operations['notes']['requestBody']['content']['application/json'];
 export type NotesResponse = operations['notes']['responses']['200']['content']['application/json'];
-export type NotesChildrenRequest = operations['notes/children']['requestBody']['content']['application/json'];
-export type NotesChildrenResponse = operations['notes/children']['responses']['200']['content']['application/json'];
-export type NotesClipsRequest = operations['notes/clips']['requestBody']['content']['application/json'];
-export type NotesClipsResponse = operations['notes/clips']['responses']['200']['content']['application/json'];
-export type NotesConversationRequest = operations['notes/conversation']['requestBody']['content']['application/json'];
-export type NotesConversationResponse = operations['notes/conversation']['responses']['200']['content']['application/json'];
-export type NotesCreateRequest = operations['notes/create']['requestBody']['content']['application/json'];
-export type NotesCreateResponse = operations['notes/create']['responses']['200']['content']['application/json'];
-export type NotesDeleteRequest = operations['notes/delete']['requestBody']['content']['application/json'];
-export type NotesFavoritesCreateRequest = operations['notes/favorites/create']['requestBody']['content']['application/json'];
-export type NotesFavoritesDeleteRequest = operations['notes/favorites/delete']['requestBody']['content']['application/json'];
-export type NotesFeaturedRequest = operations['notes/featured']['requestBody']['content']['application/json'];
-export type NotesFeaturedResponse = operations['notes/featured']['responses']['200']['content']['application/json'];
-export type NotesGlobalTimelineRequest = operations['notes/global-timeline']['requestBody']['content']['application/json'];
-export type NotesGlobalTimelineResponse = operations['notes/global-timeline']['responses']['200']['content']['application/json'];
-export type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBody']['content']['application/json'];
-export type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json'];
-export type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json'];
-export type NotesLocalTimelineResponse = operations['notes/local-timeline']['responses']['200']['content']['application/json'];
-export type NotesMentionsRequest = operations['notes/mentions']['requestBody']['content']['application/json'];
-export type NotesMentionsResponse = operations['notes/mentions']['responses']['200']['content']['application/json'];
-export type NotesPollsRecommendationRequest = operations['notes/polls/recommendation']['requestBody']['content']['application/json'];
-export type NotesPollsRecommendationResponse = operations['notes/polls/recommendation']['responses']['200']['content']['application/json'];
-export type NotesPollsVoteRequest = operations['notes/polls/vote']['requestBody']['content']['application/json'];
-export type NotesReactionsRequest = operations['notes/reactions']['requestBody']['content']['application/json'];
-export type NotesReactionsResponse = operations['notes/reactions']['responses']['200']['content']['application/json'];
-export type NotesReactionsCreateRequest = operations['notes/reactions/create']['requestBody']['content']['application/json'];
-export type NotesReactionsDeleteRequest = operations['notes/reactions/delete']['requestBody']['content']['application/json'];
-export type NotesRenotesRequest = operations['notes/renotes']['requestBody']['content']['application/json'];
-export type NotesRenotesResponse = operations['notes/renotes']['responses']['200']['content']['application/json'];
-export type NotesRepliesRequest = operations['notes/replies']['requestBody']['content']['application/json'];
-export type NotesRepliesResponse = operations['notes/replies']['responses']['200']['content']['application/json'];
-export type NotesSearchByTagRequest = operations['notes/search-by-tag']['requestBody']['content']['application/json'];
-export type NotesSearchByTagResponse = operations['notes/search-by-tag']['responses']['200']['content']['application/json'];
-export type NotesSearchRequest = operations['notes/search']['requestBody']['content']['application/json'];
-export type NotesSearchResponse = operations['notes/search']['responses']['200']['content']['application/json'];
-export type NotesShowRequest = operations['notes/show']['requestBody']['content']['application/json'];
-export type NotesShowResponse = operations['notes/show']['responses']['200']['content']['application/json'];
-export type NotesStateRequest = operations['notes/state']['requestBody']['content']['application/json'];
-export type NotesStateResponse = operations['notes/state']['responses']['200']['content']['application/json'];
-export type NotesThreadMutingCreateRequest = operations['notes/thread-muting/create']['requestBody']['content']['application/json'];
-export type NotesThreadMutingDeleteRequest = operations['notes/thread-muting/delete']['requestBody']['content']['application/json'];
-export type NotesTimelineRequest = operations['notes/timeline']['requestBody']['content']['application/json'];
-export type NotesTimelineResponse = operations['notes/timeline']['responses']['200']['content']['application/json'];
-export type NotesTranslateRequest = operations['notes/translate']['requestBody']['content']['application/json'];
-export type NotesTranslateResponse = operations['notes/translate']['responses']['200']['content']['application/json'];
-export type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['content']['application/json'];
-export type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json'];
-export type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
-export type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
+export type NotesChildrenRequest = operations['notes___children']['requestBody']['content']['application/json'];
+export type NotesChildrenResponse = operations['notes___children']['responses']['200']['content']['application/json'];
+export type NotesClipsRequest = operations['notes___clips']['requestBody']['content']['application/json'];
+export type NotesClipsResponse = operations['notes___clips']['responses']['200']['content']['application/json'];
+export type NotesConversationRequest = operations['notes___conversation']['requestBody']['content']['application/json'];
+export type NotesConversationResponse = operations['notes___conversation']['responses']['200']['content']['application/json'];
+export type NotesCreateRequest = operations['notes___create']['requestBody']['content']['application/json'];
+export type NotesCreateResponse = operations['notes___create']['responses']['200']['content']['application/json'];
+export type NotesDeleteRequest = operations['notes___delete']['requestBody']['content']['application/json'];
+export type NotesFavoritesCreateRequest = operations['notes___favorites___create']['requestBody']['content']['application/json'];
+export type NotesFavoritesDeleteRequest = operations['notes___favorites___delete']['requestBody']['content']['application/json'];
+export type NotesFeaturedRequest = operations['notes___featured']['requestBody']['content']['application/json'];
+export type NotesFeaturedResponse = operations['notes___featured']['responses']['200']['content']['application/json'];
+export type NotesGlobalTimelineRequest = operations['notes___global-timeline']['requestBody']['content']['application/json'];
+export type NotesGlobalTimelineResponse = operations['notes___global-timeline']['responses']['200']['content']['application/json'];
+export type NotesHybridTimelineRequest = operations['notes___hybrid-timeline']['requestBody']['content']['application/json'];
+export type NotesHybridTimelineResponse = operations['notes___hybrid-timeline']['responses']['200']['content']['application/json'];
+export type NotesLocalTimelineRequest = operations['notes___local-timeline']['requestBody']['content']['application/json'];
+export type NotesLocalTimelineResponse = operations['notes___local-timeline']['responses']['200']['content']['application/json'];
+export type NotesMentionsRequest = operations['notes___mentions']['requestBody']['content']['application/json'];
+export type NotesMentionsResponse = operations['notes___mentions']['responses']['200']['content']['application/json'];
+export type NotesPollsRecommendationRequest = operations['notes___polls___recommendation']['requestBody']['content']['application/json'];
+export type NotesPollsRecommendationResponse = operations['notes___polls___recommendation']['responses']['200']['content']['application/json'];
+export type NotesPollsVoteRequest = operations['notes___polls___vote']['requestBody']['content']['application/json'];
+export type NotesReactionsRequest = operations['notes___reactions']['requestBody']['content']['application/json'];
+export type NotesReactionsResponse = operations['notes___reactions']['responses']['200']['content']['application/json'];
+export type NotesReactionsCreateRequest = operations['notes___reactions___create']['requestBody']['content']['application/json'];
+export type NotesReactionsDeleteRequest = operations['notes___reactions___delete']['requestBody']['content']['application/json'];
+export type NotesRenotesRequest = operations['notes___renotes']['requestBody']['content']['application/json'];
+export type NotesRenotesResponse = operations['notes___renotes']['responses']['200']['content']['application/json'];
+export type NotesRepliesRequest = operations['notes___replies']['requestBody']['content']['application/json'];
+export type NotesRepliesResponse = operations['notes___replies']['responses']['200']['content']['application/json'];
+export type NotesSearchByTagRequest = operations['notes___search-by-tag']['requestBody']['content']['application/json'];
+export type NotesSearchByTagResponse = operations['notes___search-by-tag']['responses']['200']['content']['application/json'];
+export type NotesSearchRequest = operations['notes___search']['requestBody']['content']['application/json'];
+export type NotesSearchResponse = operations['notes___search']['responses']['200']['content']['application/json'];
+export type NotesShowRequest = operations['notes___show']['requestBody']['content']['application/json'];
+export type NotesShowResponse = operations['notes___show']['responses']['200']['content']['application/json'];
+export type NotesStateRequest = operations['notes___state']['requestBody']['content']['application/json'];
+export type NotesStateResponse = operations['notes___state']['responses']['200']['content']['application/json'];
+export type NotesThreadMutingCreateRequest = operations['notes___thread-muting___create']['requestBody']['content']['application/json'];
+export type NotesThreadMutingDeleteRequest = operations['notes___thread-muting___delete']['requestBody']['content']['application/json'];
+export type NotesTimelineRequest = operations['notes___timeline']['requestBody']['content']['application/json'];
+export type NotesTimelineResponse = operations['notes___timeline']['responses']['200']['content']['application/json'];
+export type NotesTranslateRequest = operations['notes___translate']['requestBody']['content']['application/json'];
+export type NotesTranslateResponse = operations['notes___translate']['responses']['200']['content']['application/json'];
+export type NotesUnrenoteRequest = operations['notes___unrenote']['requestBody']['content']['application/json'];
+export type NotesUserListTimelineRequest = operations['notes___user-list-timeline']['requestBody']['content']['application/json'];
+export type NotesUserListTimelineResponse = operations['notes___user-list-timeline']['responses']['200']['content']['application/json'];
+export type NotificationsCreateRequest = operations['notifications___create']['requestBody']['content']['application/json'];
 export type PagePushRequest = operations['page-push']['requestBody']['content']['application/json'];
-export type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
-export type PagesCreateResponse = operations['pages/create']['responses']['200']['content']['application/json'];
-export type PagesDeleteRequest = operations['pages/delete']['requestBody']['content']['application/json'];
-export type PagesFeaturedResponse = operations['pages/featured']['responses']['200']['content']['application/json'];
-export type PagesLikeRequest = operations['pages/like']['requestBody']['content']['application/json'];
-export type PagesShowRequest = operations['pages/show']['requestBody']['content']['application/json'];
-export type PagesShowResponse = operations['pages/show']['responses']['200']['content']['application/json'];
-export type PagesUnlikeRequest = operations['pages/unlike']['requestBody']['content']['application/json'];
-export type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['application/json'];
-export type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
-export type FlashCreateResponse = operations['flash/create']['responses']['200']['content']['application/json'];
-export type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
-export type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
-export type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
-export type FlashShowRequest = operations['flash/show']['requestBody']['content']['application/json'];
-export type FlashShowResponse = operations['flash/show']['responses']['200']['content']['application/json'];
-export type FlashUnlikeRequest = operations['flash/unlike']['requestBody']['content']['application/json'];
-export type FlashUpdateRequest = operations['flash/update']['requestBody']['content']['application/json'];
-export type FlashMyRequest = operations['flash/my']['requestBody']['content']['application/json'];
-export type FlashMyResponse = operations['flash/my']['responses']['200']['content']['application/json'];
-export type FlashMyLikesRequest = operations['flash/my-likes']['requestBody']['content']['application/json'];
-export type FlashMyLikesResponse = operations['flash/my-likes']['responses']['200']['content']['application/json'];
+export type PagesCreateRequest = operations['pages___create']['requestBody']['content']['application/json'];
+export type PagesCreateResponse = operations['pages___create']['responses']['200']['content']['application/json'];
+export type PagesDeleteRequest = operations['pages___delete']['requestBody']['content']['application/json'];
+export type PagesFeaturedResponse = operations['pages___featured']['responses']['200']['content']['application/json'];
+export type PagesLikeRequest = operations['pages___like']['requestBody']['content']['application/json'];
+export type PagesShowRequest = operations['pages___show']['requestBody']['content']['application/json'];
+export type PagesShowResponse = operations['pages___show']['responses']['200']['content']['application/json'];
+export type PagesUnlikeRequest = operations['pages___unlike']['requestBody']['content']['application/json'];
+export type PagesUpdateRequest = operations['pages___update']['requestBody']['content']['application/json'];
+export type FlashCreateRequest = operations['flash___create']['requestBody']['content']['application/json'];
+export type FlashCreateResponse = operations['flash___create']['responses']['200']['content']['application/json'];
+export type FlashDeleteRequest = operations['flash___delete']['requestBody']['content']['application/json'];
+export type FlashFeaturedResponse = operations['flash___featured']['responses']['200']['content']['application/json'];
+export type FlashLikeRequest = operations['flash___like']['requestBody']['content']['application/json'];
+export type FlashShowRequest = operations['flash___show']['requestBody']['content']['application/json'];
+export type FlashShowResponse = operations['flash___show']['responses']['200']['content']['application/json'];
+export type FlashUnlikeRequest = operations['flash___unlike']['requestBody']['content']['application/json'];
+export type FlashUpdateRequest = operations['flash___update']['requestBody']['content']['application/json'];
+export type FlashMyRequest = operations['flash___my']['requestBody']['content']['application/json'];
+export type FlashMyResponse = operations['flash___my']['responses']['200']['content']['application/json'];
+export type FlashMyLikesRequest = operations['flash___my-likes']['requestBody']['content']['application/json'];
+export type FlashMyLikesResponse = operations['flash___my-likes']['responses']['200']['content']['application/json'];
 export type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
 export type PinnedUsersResponse = operations['pinned-users']['responses']['200']['content']['application/json'];
-export type PromoReadRequest = operations['promo/read']['requestBody']['content']['application/json'];
-export type RolesListResponse = operations['roles/list']['responses']['200']['content']['application/json'];
-export type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
-export type RolesShowResponse = operations['roles/show']['responses']['200']['content']['application/json'];
-export type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
-export type RolesUsersResponse = operations['roles/users']['responses']['200']['content']['application/json'];
-export type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
-export type RolesNotesResponse = operations['roles/notes']['responses']['200']['content']['application/json'];
+export type PromoReadRequest = operations['promo___read']['requestBody']['content']['application/json'];
+export type RolesListResponse = operations['roles___list']['responses']['200']['content']['application/json'];
+export type RolesShowRequest = operations['roles___show']['requestBody']['content']['application/json'];
+export type RolesShowResponse = operations['roles___show']['responses']['200']['content']['application/json'];
+export type RolesUsersRequest = operations['roles___users']['requestBody']['content']['application/json'];
+export type RolesUsersResponse = operations['roles___users']['responses']['200']['content']['application/json'];
+export type RolesNotesRequest = operations['roles___notes']['requestBody']['content']['application/json'];
+export type RolesNotesResponse = operations['roles___notes']['responses']['200']['content']['application/json'];
 export type RequestResetPasswordRequest = operations['request-reset-password']['requestBody']['content']['application/json'];
 export type ResetPasswordRequest = operations['reset-password']['requestBody']['content']['application/json'];
 export type ServerInfoResponse = operations['server-info']['responses']['200']['content']['application/json'];
 export type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
-export type SwShowRegistrationRequest = operations['sw/show-registration']['requestBody']['content']['application/json'];
-export type SwShowRegistrationResponse = operations['sw/show-registration']['responses']['200']['content']['application/json'];
-export type SwUpdateRegistrationRequest = operations['sw/update-registration']['requestBody']['content']['application/json'];
-export type SwUpdateRegistrationResponse = operations['sw/update-registration']['responses']['200']['content']['application/json'];
-export type SwRegisterRequest = operations['sw/register']['requestBody']['content']['application/json'];
-export type SwRegisterResponse = operations['sw/register']['responses']['200']['content']['application/json'];
-export type SwUnregisterRequest = operations['sw/unregister']['requestBody']['content']['application/json'];
+export type SwShowRegistrationRequest = operations['sw___show-registration']['requestBody']['content']['application/json'];
+export type SwShowRegistrationResponse = operations['sw___show-registration']['responses']['200']['content']['application/json'];
+export type SwUpdateRegistrationRequest = operations['sw___update-registration']['requestBody']['content']['application/json'];
+export type SwUpdateRegistrationResponse = operations['sw___update-registration']['responses']['200']['content']['application/json'];
+export type SwRegisterRequest = operations['sw___register']['requestBody']['content']['application/json'];
+export type SwRegisterResponse = operations['sw___register']['responses']['200']['content']['application/json'];
+export type SwUnregisterRequest = operations['sw___unregister']['requestBody']['content']['application/json'];
 export type TestRequest = operations['test']['requestBody']['content']['application/json'];
 export type TestResponse = operations['test']['responses']['200']['content']['application/json'];
-export type UsernameAvailableRequest = operations['username/available']['requestBody']['content']['application/json'];
-export type UsernameAvailableResponse = operations['username/available']['responses']['200']['content']['application/json'];
+export type UsernameAvailableRequest = operations['username___available']['requestBody']['content']['application/json'];
+export type UsernameAvailableResponse = operations['username___available']['responses']['200']['content']['application/json'];
 export type UsersRequest = operations['users']['requestBody']['content']['application/json'];
 export type UsersResponse = operations['users']['responses']['200']['content']['application/json'];
-export type UsersClipsRequest = operations['users/clips']['requestBody']['content']['application/json'];
-export type UsersClipsResponse = operations['users/clips']['responses']['200']['content']['application/json'];
-export type UsersFollowersRequest = operations['users/followers']['requestBody']['content']['application/json'];
-export type UsersFollowersResponse = operations['users/followers']['responses']['200']['content']['application/json'];
-export type UsersFollowingRequest = operations['users/following']['requestBody']['content']['application/json'];
-export type UsersFollowingResponse = operations['users/following']['responses']['200']['content']['application/json'];
-export type UsersGalleryPostsRequest = operations['users/gallery/posts']['requestBody']['content']['application/json'];
-export type UsersGalleryPostsResponse = operations['users/gallery/posts']['responses']['200']['content']['application/json'];
-export type UsersGetFrequentlyRepliedUsersRequest = operations['users/get-frequently-replied-users']['requestBody']['content']['application/json'];
-export type UsersGetFrequentlyRepliedUsersResponse = operations['users/get-frequently-replied-users']['responses']['200']['content']['application/json'];
-export type UsersFeaturedNotesRequest = operations['users/featured-notes']['requestBody']['content']['application/json'];
-export type UsersFeaturedNotesResponse = operations['users/featured-notes']['responses']['200']['content']['application/json'];
-export type UsersListsCreateRequest = operations['users/lists/create']['requestBody']['content']['application/json'];
-export type UsersListsCreateResponse = operations['users/lists/create']['responses']['200']['content']['application/json'];
-export type UsersListsDeleteRequest = operations['users/lists/delete']['requestBody']['content']['application/json'];
-export type UsersListsListRequest = operations['users/lists/list']['requestBody']['content']['application/json'];
-export type UsersListsListResponse = operations['users/lists/list']['responses']['200']['content']['application/json'];
-export type UsersListsPullRequest = operations['users/lists/pull']['requestBody']['content']['application/json'];
-export type UsersListsPushRequest = operations['users/lists/push']['requestBody']['content']['application/json'];
-export type UsersListsShowRequest = operations['users/lists/show']['requestBody']['content']['application/json'];
-export type UsersListsShowResponse = operations['users/lists/show']['responses']['200']['content']['application/json'];
-export type UsersListsFavoriteRequest = operations['users/lists/favorite']['requestBody']['content']['application/json'];
-export type UsersListsUnfavoriteRequest = operations['users/lists/unfavorite']['requestBody']['content']['application/json'];
-export type UsersListsUpdateRequest = operations['users/lists/update']['requestBody']['content']['application/json'];
-export type UsersListsUpdateResponse = operations['users/lists/update']['responses']['200']['content']['application/json'];
-export type UsersListsCreateFromPublicRequest = operations['users/lists/create-from-public']['requestBody']['content']['application/json'];
-export type UsersListsCreateFromPublicResponse = operations['users/lists/create-from-public']['responses']['200']['content']['application/json'];
-export type UsersListsUpdateMembershipRequest = operations['users/lists/update-membership']['requestBody']['content']['application/json'];
-export type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json'];
-export type UsersListsGetMembershipsResponse = operations['users/lists/get-memberships']['responses']['200']['content']['application/json'];
-export type UsersNotesRequest = operations['users/notes']['requestBody']['content']['application/json'];
-export type UsersNotesResponse = operations['users/notes']['responses']['200']['content']['application/json'];
-export type UsersPagesRequest = operations['users/pages']['requestBody']['content']['application/json'];
-export type UsersPagesResponse = operations['users/pages']['responses']['200']['content']['application/json'];
-export type UsersFlashsRequest = operations['users/flashs']['requestBody']['content']['application/json'];
-export type UsersFlashsResponse = operations['users/flashs']['responses']['200']['content']['application/json'];
-export type UsersReactionsRequest = operations['users/reactions']['requestBody']['content']['application/json'];
-export type UsersReactionsResponse = operations['users/reactions']['responses']['200']['content']['application/json'];
-export type UsersRecommendationRequest = operations['users/recommendation']['requestBody']['content']['application/json'];
-export type UsersRecommendationResponse = operations['users/recommendation']['responses']['200']['content']['application/json'];
-export type UsersRelationRequest = operations['users/relation']['requestBody']['content']['application/json'];
-export type UsersRelationResponse = operations['users/relation']['responses']['200']['content']['application/json'];
-export type UsersReportAbuseRequest = operations['users/report-abuse']['requestBody']['content']['application/json'];
-export type UsersSearchByUsernameAndHostRequest = operations['users/search-by-username-and-host']['requestBody']['content']['application/json'];
-export type UsersSearchByUsernameAndHostResponse = operations['users/search-by-username-and-host']['responses']['200']['content']['application/json'];
-export type UsersSearchRequest = operations['users/search']['requestBody']['content']['application/json'];
-export type UsersSearchResponse = operations['users/search']['responses']['200']['content']['application/json'];
-export type UsersShowRequest = operations['users/show']['requestBody']['content']['application/json'];
-export type UsersShowResponse = operations['users/show']['responses']['200']['content']['application/json'];
-export type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json'];
-export type UsersAchievementsResponse = operations['users/achievements']['responses']['200']['content']['application/json'];
-export type UsersUpdateMemoRequest = operations['users/update-memo']['requestBody']['content']['application/json'];
+export type UsersClipsRequest = operations['users___clips']['requestBody']['content']['application/json'];
+export type UsersClipsResponse = operations['users___clips']['responses']['200']['content']['application/json'];
+export type UsersFollowersRequest = operations['users___followers']['requestBody']['content']['application/json'];
+export type UsersFollowersResponse = operations['users___followers']['responses']['200']['content']['application/json'];
+export type UsersFollowingRequest = operations['users___following']['requestBody']['content']['application/json'];
+export type UsersFollowingResponse = operations['users___following']['responses']['200']['content']['application/json'];
+export type UsersGalleryPostsRequest = operations['users___gallery___posts']['requestBody']['content']['application/json'];
+export type UsersGalleryPostsResponse = operations['users___gallery___posts']['responses']['200']['content']['application/json'];
+export type UsersGetFrequentlyRepliedUsersRequest = operations['users___get-frequently-replied-users']['requestBody']['content']['application/json'];
+export type UsersGetFrequentlyRepliedUsersResponse = operations['users___get-frequently-replied-users']['responses']['200']['content']['application/json'];
+export type UsersFeaturedNotesRequest = operations['users___featured-notes']['requestBody']['content']['application/json'];
+export type UsersFeaturedNotesResponse = operations['users___featured-notes']['responses']['200']['content']['application/json'];
+export type UsersListsCreateRequest = operations['users___lists___create']['requestBody']['content']['application/json'];
+export type UsersListsCreateResponse = operations['users___lists___create']['responses']['200']['content']['application/json'];
+export type UsersListsDeleteRequest = operations['users___lists___delete']['requestBody']['content']['application/json'];
+export type UsersListsListRequest = operations['users___lists___list']['requestBody']['content']['application/json'];
+export type UsersListsListResponse = operations['users___lists___list']['responses']['200']['content']['application/json'];
+export type UsersListsPullRequest = operations['users___lists___pull']['requestBody']['content']['application/json'];
+export type UsersListsPushRequest = operations['users___lists___push']['requestBody']['content']['application/json'];
+export type UsersListsShowRequest = operations['users___lists___show']['requestBody']['content']['application/json'];
+export type UsersListsShowResponse = operations['users___lists___show']['responses']['200']['content']['application/json'];
+export type UsersListsFavoriteRequest = operations['users___lists___favorite']['requestBody']['content']['application/json'];
+export type UsersListsUnfavoriteRequest = operations['users___lists___unfavorite']['requestBody']['content']['application/json'];
+export type UsersListsUpdateRequest = operations['users___lists___update']['requestBody']['content']['application/json'];
+export type UsersListsUpdateResponse = operations['users___lists___update']['responses']['200']['content']['application/json'];
+export type UsersListsCreateFromPublicRequest = operations['users___lists___create-from-public']['requestBody']['content']['application/json'];
+export type UsersListsCreateFromPublicResponse = operations['users___lists___create-from-public']['responses']['200']['content']['application/json'];
+export type UsersListsUpdateMembershipRequest = operations['users___lists___update-membership']['requestBody']['content']['application/json'];
+export type UsersListsGetMembershipsRequest = operations['users___lists___get-memberships']['requestBody']['content']['application/json'];
+export type UsersListsGetMembershipsResponse = operations['users___lists___get-memberships']['responses']['200']['content']['application/json'];
+export type UsersNotesRequest = operations['users___notes']['requestBody']['content']['application/json'];
+export type UsersNotesResponse = operations['users___notes']['responses']['200']['content']['application/json'];
+export type UsersPagesRequest = operations['users___pages']['requestBody']['content']['application/json'];
+export type UsersPagesResponse = operations['users___pages']['responses']['200']['content']['application/json'];
+export type UsersFlashsRequest = operations['users___flashs']['requestBody']['content']['application/json'];
+export type UsersFlashsResponse = operations['users___flashs']['responses']['200']['content']['application/json'];
+export type UsersReactionsRequest = operations['users___reactions']['requestBody']['content']['application/json'];
+export type UsersReactionsResponse = operations['users___reactions']['responses']['200']['content']['application/json'];
+export type UsersRecommendationRequest = operations['users___recommendation']['requestBody']['content']['application/json'];
+export type UsersRecommendationResponse = operations['users___recommendation']['responses']['200']['content']['application/json'];
+export type UsersRelationRequest = operations['users___relation']['requestBody']['content']['application/json'];
+export type UsersRelationResponse = operations['users___relation']['responses']['200']['content']['application/json'];
+export type UsersReportAbuseRequest = operations['users___report-abuse']['requestBody']['content']['application/json'];
+export type UsersSearchByUsernameAndHostRequest = operations['users___search-by-username-and-host']['requestBody']['content']['application/json'];
+export type UsersSearchByUsernameAndHostResponse = operations['users___search-by-username-and-host']['responses']['200']['content']['application/json'];
+export type UsersSearchRequest = operations['users___search']['requestBody']['content']['application/json'];
+export type UsersSearchResponse = operations['users___search']['responses']['200']['content']['application/json'];
+export type UsersShowRequest = operations['users___show']['requestBody']['content']['application/json'];
+export type UsersShowResponse = operations['users___show']['responses']['200']['content']['application/json'];
+export type UsersAchievementsRequest = operations['users___achievements']['requestBody']['content']['application/json'];
+export type UsersAchievementsResponse = operations['users___achievements']['responses']['200']['content']['application/json'];
+export type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['content']['application/json'];
 export type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json'];
 export type FetchRssResponse = operations['fetch-rss']['responses']['200']['content']['application/json'];
 export type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json'];
 export type FetchExternalResourcesResponse = operations['fetch-external-resources']['responses']['200']['content']['application/json'];
 export type RetentionResponse = operations['retention']['responses']['200']['content']['application/json'];
-export type BubbleGameRegisterRequest = operations['bubble-game/register']['requestBody']['content']['application/json'];
-export type BubbleGameRankingRequest = operations['bubble-game/ranking']['requestBody']['content']['application/json'];
-export type BubbleGameRankingResponse = operations['bubble-game/ranking']['responses']['200']['content']['application/json'];
-export type ReversiCancelMatchRequest = operations['reversi/cancel-match']['requestBody']['content']['application/json'];
-export type ReversiGamesRequest = operations['reversi/games']['requestBody']['content']['application/json'];
-export type ReversiGamesResponse = operations['reversi/games']['responses']['200']['content']['application/json'];
-export type ReversiMatchRequest = operations['reversi/match']['requestBody']['content']['application/json'];
-export type ReversiMatchResponse = operations['reversi/match']['responses']['200']['content']['application/json'];
-export type ReversiInvitationsResponse = operations['reversi/invitations']['responses']['200']['content']['application/json'];
-export type ReversiShowGameRequest = operations['reversi/show-game']['requestBody']['content']['application/json'];
-export type ReversiShowGameResponse = operations['reversi/show-game']['responses']['200']['content']['application/json'];
-export type ReversiSurrenderRequest = operations['reversi/surrender']['requestBody']['content']['application/json'];
-export type ReversiVerifyRequest = operations['reversi/verify']['requestBody']['content']['application/json'];
-export type ReversiVerifyResponse = operations['reversi/verify']['responses']['200']['content']['application/json'];
+export type BubbleGameRegisterRequest = operations['bubble-game___register']['requestBody']['content']['application/json'];
+export type BubbleGameRankingRequest = operations['bubble-game___ranking']['requestBody']['content']['application/json'];
+export type BubbleGameRankingResponse = operations['bubble-game___ranking']['responses']['200']['content']['application/json'];
+export type ReversiCancelMatchRequest = operations['reversi___cancel-match']['requestBody']['content']['application/json'];
+export type ReversiGamesRequest = operations['reversi___games']['requestBody']['content']['application/json'];
+export type ReversiGamesResponse = operations['reversi___games']['responses']['200']['content']['application/json'];
+export type ReversiMatchRequest = operations['reversi___match']['requestBody']['content']['application/json'];
+export type ReversiMatchResponse = operations['reversi___match']['responses']['200']['content']['application/json'];
+export type ReversiInvitationsResponse = operations['reversi___invitations']['responses']['200']['content']['application/json'];
+export type ReversiShowGameRequest = operations['reversi___show-game']['requestBody']['content']['application/json'];
+export type ReversiShowGameResponse = operations['reversi___show-game']['responses']['200']['content']['application/json'];
+export type ReversiSurrenderRequest = operations['reversi___surrender']['requestBody']['content']['application/json'];
+export type ReversiVerifyRequest = operations['reversi___verify']['requestBody']['content']['application/json'];
+export type ReversiVerifyResponse = operations['reversi___verify']['responses']['200']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index c6e36d80aaed..4b2ad16b0fcb 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -19,7 +19,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:meta*
      */
-    post: operations['admin/meta'];
+    post: operations['admin___meta'];
   };
   '/admin/abuse-user-reports': {
     /**
@@ -28,7 +28,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-user-reports*
      */
-    post: operations['admin/abuse-user-reports'];
+    post: operations['admin___abuse-user-reports'];
   };
   '/admin/accounts/create': {
     /**
@@ -37,7 +37,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['admin/accounts/create'];
+    post: operations['admin___accounts___create'];
   };
   '/admin/accounts/delete': {
     /**
@@ -46,7 +46,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:account*
      */
-    post: operations['admin/accounts/delete'];
+    post: operations['admin___accounts___delete'];
   };
   '/admin/accounts/find-by-email': {
     /**
@@ -55,7 +55,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:account*
      */
-    post: operations['admin/accounts/find-by-email'];
+    post: operations['admin___accounts___find-by-email'];
   };
   '/admin/ad/create': {
     /**
@@ -64,7 +64,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
-    post: operations['admin/ad/create'];
+    post: operations['admin___ad___create'];
   };
   '/admin/ad/delete': {
     /**
@@ -73,7 +73,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
-    post: operations['admin/ad/delete'];
+    post: operations['admin___ad___delete'];
   };
   '/admin/ad/list': {
     /**
@@ -82,7 +82,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:ad*
      */
-    post: operations['admin/ad/list'];
+    post: operations['admin___ad___list'];
   };
   '/admin/ad/update': {
     /**
@@ -91,7 +91,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
-    post: operations['admin/ad/update'];
+    post: operations['admin___ad___update'];
   };
   '/admin/announcements/create': {
     /**
@@ -100,7 +100,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
-    post: operations['admin/announcements/create'];
+    post: operations['admin___announcements___create'];
   };
   '/admin/announcements/delete': {
     /**
@@ -109,7 +109,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
-    post: operations['admin/announcements/delete'];
+    post: operations['admin___announcements___delete'];
   };
   '/admin/announcements/list': {
     /**
@@ -118,7 +118,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:announcements*
      */
-    post: operations['admin/announcements/list'];
+    post: operations['admin___announcements___list'];
   };
   '/admin/announcements/update': {
     /**
@@ -127,7 +127,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
-    post: operations['admin/announcements/update'];
+    post: operations['admin___announcements___update'];
   };
   '/admin/avatar-decorations/create': {
     /**
@@ -136,7 +136,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
-    post: operations['admin/avatar-decorations/create'];
+    post: operations['admin___avatar-decorations___create'];
   };
   '/admin/avatar-decorations/delete': {
     /**
@@ -145,7 +145,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
-    post: operations['admin/avatar-decorations/delete'];
+    post: operations['admin___avatar-decorations___delete'];
   };
   '/admin/avatar-decorations/list': {
     /**
@@ -154,7 +154,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:avatar-decorations*
      */
-    post: operations['admin/avatar-decorations/list'];
+    post: operations['admin___avatar-decorations___list'];
   };
   '/admin/avatar-decorations/update': {
     /**
@@ -163,7 +163,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
-    post: operations['admin/avatar-decorations/update'];
+    post: operations['admin___avatar-decorations___update'];
   };
   '/admin/delete-all-files-of-a-user': {
     /**
@@ -172,7 +172,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:delete-all-files-of-a-user*
      */
-    post: operations['admin/delete-all-files-of-a-user'];
+    post: operations['admin___delete-all-files-of-a-user'];
   };
   '/admin/unset-user-avatar': {
     /**
@@ -181,7 +181,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-avatar*
      */
-    post: operations['admin/unset-user-avatar'];
+    post: operations['admin___unset-user-avatar'];
   };
   '/admin/unset-user-banner': {
     /**
@@ -190,7 +190,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-banner*
      */
-    post: operations['admin/unset-user-banner'];
+    post: operations['admin___unset-user-banner'];
   };
   '/admin/drive/clean-remote-files': {
     /**
@@ -199,7 +199,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
-    post: operations['admin/drive/clean-remote-files'];
+    post: operations['admin___drive___clean-remote-files'];
   };
   '/admin/drive/cleanup': {
     /**
@@ -208,7 +208,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
-    post: operations['admin/drive/cleanup'];
+    post: operations['admin___drive___cleanup'];
   };
   '/admin/drive/files': {
     /**
@@ -217,7 +217,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
-    post: operations['admin/drive/files'];
+    post: operations['admin___drive___files'];
   };
   '/admin/drive/show-file': {
     /**
@@ -226,7 +226,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
-    post: operations['admin/drive/show-file'];
+    post: operations['admin___drive___show-file'];
   };
   '/admin/emoji/add-aliases-bulk': {
     /**
@@ -235,7 +235,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/add-aliases-bulk'];
+    post: operations['admin___emoji___add-aliases-bulk'];
   };
   '/admin/emoji/add': {
     /**
@@ -244,7 +244,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/add'];
+    post: operations['admin___emoji___add'];
   };
   '/admin/emoji/copy': {
     /**
@@ -253,7 +253,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/copy'];
+    post: operations['admin___emoji___copy'];
   };
   '/admin/emoji/delete-bulk': {
     /**
@@ -262,7 +262,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/delete-bulk'];
+    post: operations['admin___emoji___delete-bulk'];
   };
   '/admin/emoji/delete': {
     /**
@@ -271,7 +271,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/delete'];
+    post: operations['admin___emoji___delete'];
   };
   '/admin/emoji/import-zip': {
     /**
@@ -281,7 +281,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['admin/emoji/import-zip'];
+    post: operations['admin___emoji___import-zip'];
   };
   '/admin/emoji/list-remote': {
     /**
@@ -290,7 +290,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
-    post: operations['admin/emoji/list-remote'];
+    post: operations['admin___emoji___list-remote'];
   };
   '/admin/emoji/list': {
     /**
@@ -299,7 +299,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
-    post: operations['admin/emoji/list'];
+    post: operations['admin___emoji___list'];
   };
   '/admin/emoji/remove-aliases-bulk': {
     /**
@@ -308,7 +308,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/remove-aliases-bulk'];
+    post: operations['admin___emoji___remove-aliases-bulk'];
   };
   '/admin/emoji/set-aliases-bulk': {
     /**
@@ -317,7 +317,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/set-aliases-bulk'];
+    post: operations['admin___emoji___set-aliases-bulk'];
   };
   '/admin/emoji/set-category-bulk': {
     /**
@@ -326,7 +326,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/set-category-bulk'];
+    post: operations['admin___emoji___set-category-bulk'];
   };
   '/admin/emoji/set-license-bulk': {
     /**
@@ -335,7 +335,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/set-license-bulk'];
+    post: operations['admin___emoji___set-license-bulk'];
   };
   '/admin/emoji/update': {
     /**
@@ -344,7 +344,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
-    post: operations['admin/emoji/update'];
+    post: operations['admin___emoji___update'];
   };
   '/admin/federation/delete-all-files': {
     /**
@@ -353,7 +353,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
-    post: operations['admin/federation/delete-all-files'];
+    post: operations['admin___federation___delete-all-files'];
   };
   '/admin/federation/refresh-remote-instance-metadata': {
     /**
@@ -362,7 +362,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
-    post: operations['admin/federation/refresh-remote-instance-metadata'];
+    post: operations['admin___federation___refresh-remote-instance-metadata'];
   };
   '/admin/federation/remove-all-following': {
     /**
@@ -371,7 +371,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
-    post: operations['admin/federation/remove-all-following'];
+    post: operations['admin___federation___remove-all-following'];
   };
   '/admin/federation/update-instance': {
     /**
@@ -380,7 +380,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
-    post: operations['admin/federation/update-instance'];
+    post: operations['admin___federation___update-instance'];
   };
   '/admin/get-index-stats': {
     /**
@@ -389,7 +389,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:index-stats*
      */
-    post: operations['admin/get-index-stats'];
+    post: operations['admin___get-index-stats'];
   };
   '/admin/get-table-stats': {
     /**
@@ -398,7 +398,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:table-stats*
      */
-    post: operations['admin/get-table-stats'];
+    post: operations['admin___get-table-stats'];
   };
   '/admin/get-user-ips': {
     /**
@@ -407,7 +407,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:user-ips*
      */
-    post: operations['admin/get-user-ips'];
+    post: operations['admin___get-user-ips'];
   };
   '/admin/invite/create': {
     /**
@@ -416,7 +416,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:invite-codes*
      */
-    post: operations['admin/invite/create'];
+    post: operations['admin___invite___create'];
   };
   '/admin/invite/list': {
     /**
@@ -425,7 +425,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:invite-codes*
      */
-    post: operations['admin/invite/list'];
+    post: operations['admin___invite___list'];
   };
   '/admin/promo/create': {
     /**
@@ -434,7 +434,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:promo*
      */
-    post: operations['admin/promo/create'];
+    post: operations['admin___promo___create'];
   };
   '/admin/queue/clear': {
     /**
@@ -443,7 +443,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
-    post: operations['admin/queue/clear'];
+    post: operations['admin___queue___clear'];
   };
   '/admin/queue/deliver-delayed': {
     /**
@@ -452,7 +452,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
-    post: operations['admin/queue/deliver-delayed'];
+    post: operations['admin___queue___deliver-delayed'];
   };
   '/admin/queue/inbox-delayed': {
     /**
@@ -461,7 +461,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
-    post: operations['admin/queue/inbox-delayed'];
+    post: operations['admin___queue___inbox-delayed'];
   };
   '/admin/queue/promote': {
     /**
@@ -470,7 +470,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
-    post: operations['admin/queue/promote'];
+    post: operations['admin___queue___promote'];
   };
   '/admin/queue/stats': {
     /**
@@ -479,7 +479,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
-    post: operations['admin/queue/stats'];
+    post: operations['admin___queue___stats'];
   };
   '/admin/relays/add': {
     /**
@@ -488,7 +488,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
-    post: operations['admin/relays/add'];
+    post: operations['admin___relays___add'];
   };
   '/admin/relays/list': {
     /**
@@ -497,7 +497,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:relays*
      */
-    post: operations['admin/relays/list'];
+    post: operations['admin___relays___list'];
   };
   '/admin/relays/remove': {
     /**
@@ -506,7 +506,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
-    post: operations['admin/relays/remove'];
+    post: operations['admin___relays___remove'];
   };
   '/admin/reset-password': {
     /**
@@ -515,7 +515,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:reset-password*
      */
-    post: operations['admin/reset-password'];
+    post: operations['admin___reset-password'];
   };
   '/admin/resolve-abuse-user-report': {
     /**
@@ -524,7 +524,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
      */
-    post: operations['admin/resolve-abuse-user-report'];
+    post: operations['admin___resolve-abuse-user-report'];
   };
   '/admin/send-email': {
     /**
@@ -533,7 +533,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:send-email*
      */
-    post: operations['admin/send-email'];
+    post: operations['admin___send-email'];
   };
   '/admin/server-info': {
     /**
@@ -542,7 +542,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:server-info*
      */
-    post: operations['admin/server-info'];
+    post: operations['admin___server-info'];
   };
   '/admin/show-moderation-logs': {
     /**
@@ -551,7 +551,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:show-moderation-log*
      */
-    post: operations['admin/show-moderation-logs'];
+    post: operations['admin___show-moderation-logs'];
   };
   '/admin/show-user': {
     /**
@@ -560,7 +560,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
      */
-    post: operations['admin/show-user'];
+    post: operations['admin___show-user'];
   };
   '/admin/show-users': {
     /**
@@ -569,7 +569,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
      */
-    post: operations['admin/show-users'];
+    post: operations['admin___show-users'];
   };
   '/admin/suspend-user': {
     /**
@@ -578,7 +578,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user*
      */
-    post: operations['admin/suspend-user'];
+    post: operations['admin___suspend-user'];
   };
   '/admin/unsuspend-user': {
     /**
@@ -587,7 +587,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user*
      */
-    post: operations['admin/unsuspend-user'];
+    post: operations['admin___unsuspend-user'];
   };
   '/admin/update-meta': {
     /**
@@ -596,7 +596,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:meta*
      */
-    post: operations['admin/update-meta'];
+    post: operations['admin___update-meta'];
   };
   '/admin/delete-account': {
     /**
@@ -605,7 +605,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account*
      */
-    post: operations['admin/delete-account'];
+    post: operations['admin___delete-account'];
   };
   '/admin/update-user-note': {
     /**
@@ -614,7 +614,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:user-note*
      */
-    post: operations['admin/update-user-note'];
+    post: operations['admin___update-user-note'];
   };
   '/admin/roles/create': {
     /**
@@ -623,7 +623,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
-    post: operations['admin/roles/create'];
+    post: operations['admin___roles___create'];
   };
   '/admin/roles/delete': {
     /**
@@ -632,7 +632,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
-    post: operations['admin/roles/delete'];
+    post: operations['admin___roles___delete'];
   };
   '/admin/roles/list': {
     /**
@@ -641,7 +641,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
-    post: operations['admin/roles/list'];
+    post: operations['admin___roles___list'];
   };
   '/admin/roles/show': {
     /**
@@ -650,7 +650,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
-    post: operations['admin/roles/show'];
+    post: operations['admin___roles___show'];
   };
   '/admin/roles/update': {
     /**
@@ -659,7 +659,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
-    post: operations['admin/roles/update'];
+    post: operations['admin___roles___update'];
   };
   '/admin/roles/assign': {
     /**
@@ -668,7 +668,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
-    post: operations['admin/roles/assign'];
+    post: operations['admin___roles___assign'];
   };
   '/admin/roles/unassign': {
     /**
@@ -677,7 +677,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
-    post: operations['admin/roles/unassign'];
+    post: operations['admin___roles___unassign'];
   };
   '/admin/roles/update-default-policies': {
     /**
@@ -686,7 +686,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
-    post: operations['admin/roles/update-default-policies'];
+    post: operations['admin___roles___update-default-policies'];
   };
   '/admin/roles/users': {
     /**
@@ -695,7 +695,7 @@ export type paths = {
      *
      * **Credential required**: *No* / **Permission**: *read:admin:roles*
      */
-    post: operations['admin/roles/users'];
+    post: operations['admin___roles___users'];
   };
   '/announcements': {
     /**
@@ -713,7 +713,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['antennas/create'];
+    post: operations['antennas___create'];
   };
   '/antennas/delete': {
     /**
@@ -722,7 +722,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['antennas/delete'];
+    post: operations['antennas___delete'];
   };
   '/antennas/list': {
     /**
@@ -731,7 +731,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['antennas/list'];
+    post: operations['antennas___list'];
   };
   '/antennas/notes': {
     /**
@@ -740,7 +740,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['antennas/notes'];
+    post: operations['antennas___notes'];
   };
   '/antennas/show': {
     /**
@@ -749,7 +749,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['antennas/show'];
+    post: operations['antennas___show'];
   };
   '/antennas/update': {
     /**
@@ -758,7 +758,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['antennas/update'];
+    post: operations['antennas___update'];
   };
   '/ap/get': {
     /**
@@ -767,7 +767,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:federation*
      */
-    post: operations['ap/get'];
+    post: operations['ap___get'];
   };
   '/ap/show': {
     /**
@@ -776,7 +776,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['ap/show'];
+    post: operations['ap___show'];
   };
   '/app/create': {
     /**
@@ -785,7 +785,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['app/create'];
+    post: operations['app___create'];
   };
   '/app/show': {
     /**
@@ -794,7 +794,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['app/show'];
+    post: operations['app___show'];
   };
   '/auth/accept': {
     /**
@@ -804,7 +804,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['auth/accept'];
+    post: operations['auth___accept'];
   };
   '/auth/session/generate': {
     /**
@@ -813,7 +813,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['auth/session/generate'];
+    post: operations['auth___session___generate'];
   };
   '/auth/session/show': {
     /**
@@ -822,7 +822,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['auth/session/show'];
+    post: operations['auth___session___show'];
   };
   '/auth/session/userkey': {
     /**
@@ -831,7 +831,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['auth/session/userkey'];
+    post: operations['auth___session___userkey'];
   };
   '/blocking/create': {
     /**
@@ -840,7 +840,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:blocks*
      */
-    post: operations['blocking/create'];
+    post: operations['blocking___create'];
   };
   '/blocking/delete': {
     /**
@@ -849,7 +849,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:blocks*
      */
-    post: operations['blocking/delete'];
+    post: operations['blocking___delete'];
   };
   '/blocking/list': {
     /**
@@ -858,7 +858,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:blocks*
      */
-    post: operations['blocking/list'];
+    post: operations['blocking___list'];
   };
   '/channels/create': {
     /**
@@ -867,7 +867,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
-    post: operations['channels/create'];
+    post: operations['channels___create'];
   };
   '/channels/featured': {
     /**
@@ -876,7 +876,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['channels/featured'];
+    post: operations['channels___featured'];
   };
   '/channels/follow': {
     /**
@@ -885,7 +885,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
-    post: operations['channels/follow'];
+    post: operations['channels___follow'];
   };
   '/channels/followed': {
     /**
@@ -894,7 +894,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:channels*
      */
-    post: operations['channels/followed'];
+    post: operations['channels___followed'];
   };
   '/channels/owned': {
     /**
@@ -903,7 +903,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:channels*
      */
-    post: operations['channels/owned'];
+    post: operations['channels___owned'];
   };
   '/channels/show': {
     /**
@@ -912,7 +912,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['channels/show'];
+    post: operations['channels___show'];
   };
   '/channels/timeline': {
     /**
@@ -921,7 +921,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['channels/timeline'];
+    post: operations['channels___timeline'];
   };
   '/channels/unfollow': {
     /**
@@ -930,7 +930,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
-    post: operations['channels/unfollow'];
+    post: operations['channels___unfollow'];
   };
   '/channels/update': {
     /**
@@ -939,7 +939,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
-    post: operations['channels/update'];
+    post: operations['channels___update'];
   };
   '/channels/favorite': {
     /**
@@ -948,7 +948,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
-    post: operations['channels/favorite'];
+    post: operations['channels___favorite'];
   };
   '/channels/unfavorite': {
     /**
@@ -957,7 +957,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
-    post: operations['channels/unfavorite'];
+    post: operations['channels___unfavorite'];
   };
   '/channels/my-favorites': {
     /**
@@ -966,7 +966,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:channels*
      */
-    post: operations['channels/my-favorites'];
+    post: operations['channels___my-favorites'];
   };
   '/channels/search': {
     /**
@@ -975,7 +975,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['channels/search'];
+    post: operations['channels___search'];
   };
   '/charts/active-users': {
     /**
@@ -984,14 +984,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/active-users'];
+    get: operations['charts___active-users'];
     /**
      * charts/active-users
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/active-users'];
+    post: operations['charts___active-users'];
   };
   '/charts/ap-request': {
     /**
@@ -1000,14 +1000,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/ap-request'];
+    get: operations['charts___ap-request'];
     /**
      * charts/ap-request
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/ap-request'];
+    post: operations['charts___ap-request'];
   };
   '/charts/drive': {
     /**
@@ -1016,14 +1016,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/drive'];
+    get: operations['charts___drive'];
     /**
      * charts/drive
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/drive'];
+    post: operations['charts___drive'];
   };
   '/charts/federation': {
     /**
@@ -1032,14 +1032,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/federation'];
+    get: operations['charts___federation'];
     /**
      * charts/federation
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/federation'];
+    post: operations['charts___federation'];
   };
   '/charts/instance': {
     /**
@@ -1048,14 +1048,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/instance'];
+    get: operations['charts___instance'];
     /**
      * charts/instance
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/instance'];
+    post: operations['charts___instance'];
   };
   '/charts/notes': {
     /**
@@ -1064,14 +1064,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/notes'];
+    get: operations['charts___notes'];
     /**
      * charts/notes
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/notes'];
+    post: operations['charts___notes'];
   };
   '/charts/user/drive': {
     /**
@@ -1080,14 +1080,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/user/drive'];
+    get: operations['charts___user___drive'];
     /**
      * charts/user/drive
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/user/drive'];
+    post: operations['charts___user___drive'];
   };
   '/charts/user/following': {
     /**
@@ -1096,14 +1096,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/user/following'];
+    get: operations['charts___user___following'];
     /**
      * charts/user/following
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/user/following'];
+    post: operations['charts___user___following'];
   };
   '/charts/user/notes': {
     /**
@@ -1112,14 +1112,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/user/notes'];
+    get: operations['charts___user___notes'];
     /**
      * charts/user/notes
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/user/notes'];
+    post: operations['charts___user___notes'];
   };
   '/charts/user/pv': {
     /**
@@ -1128,14 +1128,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/user/pv'];
+    get: operations['charts___user___pv'];
     /**
      * charts/user/pv
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/user/pv'];
+    post: operations['charts___user___pv'];
   };
   '/charts/user/reactions': {
     /**
@@ -1144,14 +1144,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/user/reactions'];
+    get: operations['charts___user___reactions'];
     /**
      * charts/user/reactions
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/user/reactions'];
+    post: operations['charts___user___reactions'];
   };
   '/charts/users': {
     /**
@@ -1160,14 +1160,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['charts/users'];
+    get: operations['charts___users'];
     /**
      * charts/users
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['charts/users'];
+    post: operations['charts___users'];
   };
   '/clips/add-note': {
     /**
@@ -1176,7 +1176,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['clips/add-note'];
+    post: operations['clips___add-note'];
   };
   '/clips/remove-note': {
     /**
@@ -1185,7 +1185,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['clips/remove-note'];
+    post: operations['clips___remove-note'];
   };
   '/clips/create': {
     /**
@@ -1194,7 +1194,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['clips/create'];
+    post: operations['clips___create'];
   };
   '/clips/delete': {
     /**
@@ -1203,7 +1203,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['clips/delete'];
+    post: operations['clips___delete'];
   };
   '/clips/list': {
     /**
@@ -1212,7 +1212,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['clips/list'];
+    post: operations['clips___list'];
   };
   '/clips/notes': {
     /**
@@ -1221,7 +1221,7 @@ export type paths = {
      *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
-    post: operations['clips/notes'];
+    post: operations['clips___notes'];
   };
   '/clips/show': {
     /**
@@ -1230,7 +1230,7 @@ export type paths = {
      *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
-    post: operations['clips/show'];
+    post: operations['clips___show'];
   };
   '/clips/update': {
     /**
@@ -1239,7 +1239,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['clips/update'];
+    post: operations['clips___update'];
   };
   '/clips/favorite': {
     /**
@@ -1248,7 +1248,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
      */
-    post: operations['clips/favorite'];
+    post: operations['clips___favorite'];
   };
   '/clips/unfavorite': {
     /**
@@ -1257,7 +1257,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
      */
-    post: operations['clips/unfavorite'];
+    post: operations['clips___unfavorite'];
   };
   '/clips/my-favorites': {
     /**
@@ -1266,7 +1266,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:clip-favorite*
      */
-    post: operations['clips/my-favorites'];
+    post: operations['clips___my-favorites'];
   };
   '/drive': {
     /**
@@ -1284,7 +1284,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/files'];
+    post: operations['drive___files'];
   };
   '/drive/files/attached-notes': {
     /**
@@ -1293,7 +1293,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/files/attached-notes'];
+    post: operations['drive___files___attached-notes'];
   };
   '/drive/files/check-existence': {
     /**
@@ -1302,7 +1302,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/files/check-existence'];
+    post: operations['drive___files___check-existence'];
   };
   '/drive/files/create': {
     /**
@@ -1311,7 +1311,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
-    post: operations['drive/files/create'];
+    post: operations['drive___files___create'];
   };
   '/drive/files/delete': {
     /**
@@ -1320,7 +1320,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
-    post: operations['drive/files/delete'];
+    post: operations['drive___files___delete'];
   };
   '/drive/files/find-by-hash': {
     /**
@@ -1329,7 +1329,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/files/find-by-hash'];
+    post: operations['drive___files___find-by-hash'];
   };
   '/drive/files/find': {
     /**
@@ -1338,7 +1338,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/files/find'];
+    post: operations['drive___files___find'];
   };
   '/drive/files/show': {
     /**
@@ -1347,7 +1347,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/files/show'];
+    post: operations['drive___files___show'];
   };
   '/drive/files/update': {
     /**
@@ -1356,7 +1356,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
-    post: operations['drive/files/update'];
+    post: operations['drive___files___update'];
   };
   '/drive/files/upload-from-url': {
     /**
@@ -1365,7 +1365,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
-    post: operations['drive/files/upload-from-url'];
+    post: operations['drive___files___upload-from-url'];
   };
   '/drive/folders': {
     /**
@@ -1374,7 +1374,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/folders'];
+    post: operations['drive___folders'];
   };
   '/drive/folders/create': {
     /**
@@ -1383,7 +1383,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
-    post: operations['drive/folders/create'];
+    post: operations['drive___folders___create'];
   };
   '/drive/folders/delete': {
     /**
@@ -1392,7 +1392,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
-    post: operations['drive/folders/delete'];
+    post: operations['drive___folders___delete'];
   };
   '/drive/folders/find': {
     /**
@@ -1401,7 +1401,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/folders/find'];
+    post: operations['drive___folders___find'];
   };
   '/drive/folders/show': {
     /**
@@ -1410,7 +1410,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/folders/show'];
+    post: operations['drive___folders___show'];
   };
   '/drive/folders/update': {
     /**
@@ -1419,7 +1419,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
-    post: operations['drive/folders/update'];
+    post: operations['drive___folders___update'];
   };
   '/drive/stream': {
     /**
@@ -1428,7 +1428,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
-    post: operations['drive/stream'];
+    post: operations['drive___stream'];
   };
   '/email-address/available': {
     /**
@@ -1437,7 +1437,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['email-address/available'];
+    post: operations['email-address___available'];
   };
   '/endpoint': {
     /**
@@ -1474,7 +1474,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['federation/followers'];
+    post: operations['federation___followers'];
   };
   '/federation/following': {
     /**
@@ -1483,7 +1483,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['federation/following'];
+    post: operations['federation___following'];
   };
   '/federation/instances': {
     /**
@@ -1492,14 +1492,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['federation/instances'];
+    get: operations['federation___instances'];
     /**
      * federation/instances
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['federation/instances'];
+    post: operations['federation___instances'];
   };
   '/federation/show-instance': {
     /**
@@ -1508,7 +1508,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['federation/show-instance'];
+    post: operations['federation___show-instance'];
   };
   '/federation/update-remote-user': {
     /**
@@ -1517,7 +1517,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['federation/update-remote-user'];
+    post: operations['federation___update-remote-user'];
   };
   '/federation/users': {
     /**
@@ -1526,7 +1526,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['federation/users'];
+    post: operations['federation___users'];
   };
   '/federation/stats': {
     /**
@@ -1535,14 +1535,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['federation/stats'];
+    get: operations['federation___stats'];
     /**
      * federation/stats
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['federation/stats'];
+    post: operations['federation___stats'];
   };
   '/following/create': {
     /**
@@ -1551,7 +1551,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/create'];
+    post: operations['following___create'];
   };
   '/following/delete': {
     /**
@@ -1560,7 +1560,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/delete'];
+    post: operations['following___delete'];
   };
   '/following/update': {
     /**
@@ -1569,7 +1569,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/update'];
+    post: operations['following___update'];
   };
   '/following/update-all': {
     /**
@@ -1578,7 +1578,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/update-all'];
+    post: operations['following___update-all'];
   };
   '/following/invalidate': {
     /**
@@ -1587,7 +1587,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/invalidate'];
+    post: operations['following___invalidate'];
   };
   '/following/requests/accept': {
     /**
@@ -1596,7 +1596,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/requests/accept'];
+    post: operations['following___requests___accept'];
   };
   '/following/requests/cancel': {
     /**
@@ -1605,7 +1605,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/requests/cancel'];
+    post: operations['following___requests___cancel'];
   };
   '/following/requests/list': {
     /**
@@ -1614,7 +1614,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:following*
      */
-    post: operations['following/requests/list'];
+    post: operations['following___requests___list'];
   };
   '/following/requests/reject': {
     /**
@@ -1623,7 +1623,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
-    post: operations['following/requests/reject'];
+    post: operations['following___requests___reject'];
   };
   '/gallery/featured': {
     /**
@@ -1632,7 +1632,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['gallery/featured'];
+    post: operations['gallery___featured'];
   };
   '/gallery/popular': {
     /**
@@ -1641,7 +1641,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['gallery/popular'];
+    post: operations['gallery___popular'];
   };
   '/gallery/posts': {
     /**
@@ -1650,7 +1650,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['gallery/posts'];
+    post: operations['gallery___posts'];
   };
   '/gallery/posts/create': {
     /**
@@ -1659,7 +1659,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:gallery*
      */
-    post: operations['gallery/posts/create'];
+    post: operations['gallery___posts___create'];
   };
   '/gallery/posts/delete': {
     /**
@@ -1668,7 +1668,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:gallery*
      */
-    post: operations['gallery/posts/delete'];
+    post: operations['gallery___posts___delete'];
   };
   '/gallery/posts/like': {
     /**
@@ -1677,7 +1677,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
      */
-    post: operations['gallery/posts/like'];
+    post: operations['gallery___posts___like'];
   };
   '/gallery/posts/show': {
     /**
@@ -1686,7 +1686,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['gallery/posts/show'];
+    post: operations['gallery___posts___show'];
   };
   '/gallery/posts/unlike': {
     /**
@@ -1695,7 +1695,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
      */
-    post: operations['gallery/posts/unlike'];
+    post: operations['gallery___posts___unlike'];
   };
   '/gallery/posts/update': {
     /**
@@ -1704,7 +1704,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:gallery*
      */
-    post: operations['gallery/posts/update'];
+    post: operations['gallery___posts___update'];
   };
   '/get-online-users-count': {
     /**
@@ -1738,7 +1738,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['hashtags/list'];
+    post: operations['hashtags___list'];
   };
   '/hashtags/search': {
     /**
@@ -1747,7 +1747,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['hashtags/search'];
+    post: operations['hashtags___search'];
   };
   '/hashtags/show': {
     /**
@@ -1756,7 +1756,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['hashtags/show'];
+    post: operations['hashtags___show'];
   };
   '/hashtags/trend': {
     /**
@@ -1765,14 +1765,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['hashtags/trend'];
+    get: operations['hashtags___trend'];
     /**
      * hashtags/trend
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['hashtags/trend'];
+    post: operations['hashtags___trend'];
   };
   '/hashtags/users': {
     /**
@@ -1781,7 +1781,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['hashtags/users'];
+    post: operations['hashtags___users'];
   };
   '/i': {
     /**
@@ -1800,7 +1800,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/done'];
+    post: operations['i___2fa___done'];
   };
   '/i/2fa/key-done': {
     /**
@@ -1810,7 +1810,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/key-done'];
+    post: operations['i___2fa___key-done'];
   };
   '/i/2fa/password-less': {
     /**
@@ -1820,7 +1820,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/password-less'];
+    post: operations['i___2fa___password-less'];
   };
   '/i/2fa/register-key': {
     /**
@@ -1830,7 +1830,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/register-key'];
+    post: operations['i___2fa___register-key'];
   };
   '/i/2fa/register': {
     /**
@@ -1840,7 +1840,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/register'];
+    post: operations['i___2fa___register'];
   };
   '/i/2fa/update-key': {
     /**
@@ -1850,7 +1850,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/update-key'];
+    post: operations['i___2fa___update-key'];
   };
   '/i/2fa/remove-key': {
     /**
@@ -1860,7 +1860,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/remove-key'];
+    post: operations['i___2fa___remove-key'];
   };
   '/i/2fa/unregister': {
     /**
@@ -1870,7 +1870,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/2fa/unregister'];
+    post: operations['i___2fa___unregister'];
   };
   '/i/apps': {
     /**
@@ -1880,7 +1880,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/apps'];
+    post: operations['i___apps'];
   };
   '/i/authorized-apps': {
     /**
@@ -1890,7 +1890,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/authorized-apps'];
+    post: operations['i___authorized-apps'];
   };
   '/i/claim-achievement': {
     /**
@@ -1899,7 +1899,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/claim-achievement'];
+    post: operations['i___claim-achievement'];
   };
   '/i/change-password': {
     /**
@@ -1909,7 +1909,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/change-password'];
+    post: operations['i___change-password'];
   };
   '/i/delete-account': {
     /**
@@ -1919,7 +1919,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/delete-account'];
+    post: operations['i___delete-account'];
   };
   '/i/export-blocking': {
     /**
@@ -1929,7 +1929,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-blocking'];
+    post: operations['i___export-blocking'];
   };
   '/i/export-following': {
     /**
@@ -1939,7 +1939,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-following'];
+    post: operations['i___export-following'];
   };
   '/i/export-mute': {
     /**
@@ -1949,7 +1949,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-mute'];
+    post: operations['i___export-mute'];
   };
   '/i/export-notes': {
     /**
@@ -1959,7 +1959,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-notes'];
+    post: operations['i___export-notes'];
   };
   '/i/export-clips': {
     /**
@@ -1969,7 +1969,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-clips'];
+    post: operations['i___export-clips'];
   };
   '/i/export-favorites': {
     /**
@@ -1979,7 +1979,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-favorites'];
+    post: operations['i___export-favorites'];
   };
   '/i/export-user-lists': {
     /**
@@ -1989,7 +1989,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-user-lists'];
+    post: operations['i___export-user-lists'];
   };
   '/i/export-antennas': {
     /**
@@ -1999,7 +1999,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/export-antennas'];
+    post: operations['i___export-antennas'];
   };
   '/i/favorites': {
     /**
@@ -2008,7 +2008,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:favorites*
      */
-    post: operations['i/favorites'];
+    post: operations['i___favorites'];
   };
   '/i/gallery/likes': {
     /**
@@ -2017,7 +2017,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:gallery-likes*
      */
-    post: operations['i/gallery/likes'];
+    post: operations['i___gallery___likes'];
   };
   '/i/gallery/posts': {
     /**
@@ -2026,7 +2026,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:gallery*
      */
-    post: operations['i/gallery/posts'];
+    post: operations['i___gallery___posts'];
   };
   '/i/import-blocking': {
     /**
@@ -2036,7 +2036,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/import-blocking'];
+    post: operations['i___import-blocking'];
   };
   '/i/import-following': {
     /**
@@ -2046,7 +2046,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/import-following'];
+    post: operations['i___import-following'];
   };
   '/i/import-muting': {
     /**
@@ -2056,7 +2056,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/import-muting'];
+    post: operations['i___import-muting'];
   };
   '/i/import-user-lists': {
     /**
@@ -2066,7 +2066,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/import-user-lists'];
+    post: operations['i___import-user-lists'];
   };
   '/i/import-antennas': {
     /**
@@ -2076,7 +2076,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/import-antennas'];
+    post: operations['i___import-antennas'];
   };
   '/i/notifications': {
     /**
@@ -2085,7 +2085,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:notifications*
      */
-    post: operations['i/notifications'];
+    post: operations['i___notifications'];
   };
   '/i/notifications-grouped': {
     /**
@@ -2094,7 +2094,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:notifications*
      */
-    post: operations['i/notifications-grouped'];
+    post: operations['i___notifications-grouped'];
   };
   '/i/page-likes': {
     /**
@@ -2103,7 +2103,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:page-likes*
      */
-    post: operations['i/page-likes'];
+    post: operations['i___page-likes'];
   };
   '/i/pages': {
     /**
@@ -2112,7 +2112,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:pages*
      */
-    post: operations['i/pages'];
+    post: operations['i___pages'];
   };
   '/i/pin': {
     /**
@@ -2121,7 +2121,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/pin'];
+    post: operations['i___pin'];
   };
   '/i/read-all-unread-notes': {
     /**
@@ -2130,7 +2130,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/read-all-unread-notes'];
+    post: operations['i___read-all-unread-notes'];
   };
   '/i/read-announcement': {
     /**
@@ -2139,7 +2139,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/read-announcement'];
+    post: operations['i___read-announcement'];
   };
   '/i/regenerate-token': {
     /**
@@ -2149,7 +2149,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/regenerate-token'];
+    post: operations['i___regenerate-token'];
   };
   '/i/registry/get-all': {
     /**
@@ -2158,7 +2158,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['i/registry/get-all'];
+    post: operations['i___registry___get-all'];
   };
   '/i/registry/get-detail': {
     /**
@@ -2167,7 +2167,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['i/registry/get-detail'];
+    post: operations['i___registry___get-detail'];
   };
   '/i/registry/get': {
     /**
@@ -2176,7 +2176,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['i/registry/get'];
+    post: operations['i___registry___get'];
   };
   '/i/registry/keys-with-type': {
     /**
@@ -2185,7 +2185,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['i/registry/keys-with-type'];
+    post: operations['i___registry___keys-with-type'];
   };
   '/i/registry/keys': {
     /**
@@ -2194,7 +2194,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['i/registry/keys'];
+    post: operations['i___registry___keys'];
   };
   '/i/registry/remove': {
     /**
@@ -2203,7 +2203,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/registry/remove'];
+    post: operations['i___registry___remove'];
   };
   '/i/registry/scopes-with-domain': {
     /**
@@ -2213,7 +2213,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/registry/scopes-with-domain'];
+    post: operations['i___registry___scopes-with-domain'];
   };
   '/i/registry/set': {
     /**
@@ -2222,7 +2222,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/registry/set'];
+    post: operations['i___registry___set'];
   };
   '/i/revoke-token': {
     /**
@@ -2232,7 +2232,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/revoke-token'];
+    post: operations['i___revoke-token'];
   };
   '/i/signin-history': {
     /**
@@ -2242,7 +2242,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/signin-history'];
+    post: operations['i___signin-history'];
   };
   '/i/unpin': {
     /**
@@ -2251,7 +2251,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/unpin'];
+    post: operations['i___unpin'];
   };
   '/i/update-email': {
     /**
@@ -2261,7 +2261,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/update-email'];
+    post: operations['i___update-email'];
   };
   '/i/update': {
     /**
@@ -2270,7 +2270,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/update'];
+    post: operations['i___update'];
   };
   '/i/move': {
     /**
@@ -2280,7 +2280,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['i/move'];
+    post: operations['i___move'];
   };
   '/i/webhooks/create': {
     /**
@@ -2289,7 +2289,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/webhooks/create'];
+    post: operations['i___webhooks___create'];
   };
   '/i/webhooks/list': {
     /**
@@ -2298,7 +2298,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['i/webhooks/list'];
+    post: operations['i___webhooks___list'];
   };
   '/i/webhooks/show': {
     /**
@@ -2307,7 +2307,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['i/webhooks/show'];
+    post: operations['i___webhooks___show'];
   };
   '/i/webhooks/update': {
     /**
@@ -2316,7 +2316,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/webhooks/update'];
+    post: operations['i___webhooks___update'];
   };
   '/i/webhooks/delete': {
     /**
@@ -2325,7 +2325,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['i/webhooks/delete'];
+    post: operations['i___webhooks___delete'];
   };
   '/invite/create': {
     /**
@@ -2334,7 +2334,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
-    post: operations['invite/create'];
+    post: operations['invite___create'];
   };
   '/invite/delete': {
     /**
@@ -2343,7 +2343,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
-    post: operations['invite/delete'];
+    post: operations['invite___delete'];
   };
   '/invite/list': {
     /**
@@ -2352,7 +2352,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
-    post: operations['invite/list'];
+    post: operations['invite___list'];
   };
   '/invite/limit': {
     /**
@@ -2361,7 +2361,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
-    post: operations['invite/limit'];
+    post: operations['invite___limit'];
   };
   '/meta': {
     /**
@@ -2412,7 +2412,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['miauth/gen-token'];
+    post: operations['miauth___gen-token'];
   };
   '/mute/create': {
     /**
@@ -2421,7 +2421,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
-    post: operations['mute/create'];
+    post: operations['mute___create'];
   };
   '/mute/delete': {
     /**
@@ -2430,7 +2430,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
-    post: operations['mute/delete'];
+    post: operations['mute___delete'];
   };
   '/mute/list': {
     /**
@@ -2439,7 +2439,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:mutes*
      */
-    post: operations['mute/list'];
+    post: operations['mute___list'];
   };
   '/renote-mute/create': {
     /**
@@ -2448,7 +2448,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
-    post: operations['renote-mute/create'];
+    post: operations['renote-mute___create'];
   };
   '/renote-mute/delete': {
     /**
@@ -2457,7 +2457,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
-    post: operations['renote-mute/delete'];
+    post: operations['renote-mute___delete'];
   };
   '/renote-mute/list': {
     /**
@@ -2466,7 +2466,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:mutes*
      */
-    post: operations['renote-mute/list'];
+    post: operations['renote-mute___list'];
   };
   '/my/apps': {
     /**
@@ -2475,7 +2475,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['my/apps'];
+    post: operations['my___apps'];
   };
   '/notes': {
     /**
@@ -2493,7 +2493,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/children'];
+    post: operations['notes___children'];
   };
   '/notes/clips': {
     /**
@@ -2502,7 +2502,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/clips'];
+    post: operations['notes___clips'];
   };
   '/notes/conversation': {
     /**
@@ -2511,7 +2511,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/conversation'];
+    post: operations['notes___conversation'];
   };
   '/notes/create': {
     /**
@@ -2520,7 +2520,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:notes*
      */
-    post: operations['notes/create'];
+    post: operations['notes___create'];
   };
   '/notes/delete': {
     /**
@@ -2529,7 +2529,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:notes*
      */
-    post: operations['notes/delete'];
+    post: operations['notes___delete'];
   };
   '/notes/favorites/create': {
     /**
@@ -2538,7 +2538,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:favorites*
      */
-    post: operations['notes/favorites/create'];
+    post: operations['notes___favorites___create'];
   };
   '/notes/favorites/delete': {
     /**
@@ -2547,7 +2547,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:favorites*
      */
-    post: operations['notes/favorites/delete'];
+    post: operations['notes___favorites___delete'];
   };
   '/notes/featured': {
     /**
@@ -2556,14 +2556,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['notes/featured'];
+    get: operations['notes___featured'];
     /**
      * notes/featured
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/featured'];
+    post: operations['notes___featured'];
   };
   '/notes/global-timeline': {
     /**
@@ -2572,7 +2572,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/global-timeline'];
+    post: operations['notes___global-timeline'];
   };
   '/notes/hybrid-timeline': {
     /**
@@ -2581,7 +2581,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['notes/hybrid-timeline'];
+    post: operations['notes___hybrid-timeline'];
   };
   '/notes/local-timeline': {
     /**
@@ -2590,7 +2590,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/local-timeline'];
+    post: operations['notes___local-timeline'];
   };
   '/notes/mentions': {
     /**
@@ -2599,7 +2599,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['notes/mentions'];
+    post: operations['notes___mentions'];
   };
   '/notes/polls/recommendation': {
     /**
@@ -2608,7 +2608,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['notes/polls/recommendation'];
+    post: operations['notes___polls___recommendation'];
   };
   '/notes/polls/vote': {
     /**
@@ -2617,7 +2617,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:votes*
      */
-    post: operations['notes/polls/vote'];
+    post: operations['notes___polls___vote'];
   };
   '/notes/reactions': {
     /**
@@ -2626,14 +2626,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['notes/reactions'];
+    get: operations['notes___reactions'];
     /**
      * notes/reactions
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/reactions'];
+    post: operations['notes___reactions'];
   };
   '/notes/reactions/create': {
     /**
@@ -2642,7 +2642,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:reactions*
      */
-    post: operations['notes/reactions/create'];
+    post: operations['notes___reactions___create'];
   };
   '/notes/reactions/delete': {
     /**
@@ -2651,7 +2651,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:reactions*
      */
-    post: operations['notes/reactions/delete'];
+    post: operations['notes___reactions___delete'];
   };
   '/notes/renotes': {
     /**
@@ -2660,7 +2660,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/renotes'];
+    post: operations['notes___renotes'];
   };
   '/notes/replies': {
     /**
@@ -2669,7 +2669,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/replies'];
+    post: operations['notes___replies'];
   };
   '/notes/search-by-tag': {
     /**
@@ -2678,7 +2678,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/search-by-tag'];
+    post: operations['notes___search-by-tag'];
   };
   '/notes/search': {
     /**
@@ -2687,7 +2687,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/search'];
+    post: operations['notes___search'];
   };
   '/notes/show': {
     /**
@@ -2696,7 +2696,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['notes/show'];
+    post: operations['notes___show'];
   };
   '/notes/state': {
     /**
@@ -2705,7 +2705,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['notes/state'];
+    post: operations['notes___state'];
   };
   '/notes/thread-muting/create': {
     /**
@@ -2714,7 +2714,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['notes/thread-muting/create'];
+    post: operations['notes___thread-muting___create'];
   };
   '/notes/thread-muting/delete': {
     /**
@@ -2723,7 +2723,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['notes/thread-muting/delete'];
+    post: operations['notes___thread-muting___delete'];
   };
   '/notes/timeline': {
     /**
@@ -2732,7 +2732,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['notes/timeline'];
+    post: operations['notes___timeline'];
   };
   '/notes/translate': {
     /**
@@ -2741,7 +2741,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['notes/translate'];
+    post: operations['notes___translate'];
   };
   '/notes/unrenote': {
     /**
@@ -2750,7 +2750,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:notes*
      */
-    post: operations['notes/unrenote'];
+    post: operations['notes___unrenote'];
   };
   '/notes/user-list-timeline': {
     /**
@@ -2759,7 +2759,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['notes/user-list-timeline'];
+    post: operations['notes___user-list-timeline'];
   };
   '/notifications/create': {
     /**
@@ -2768,7 +2768,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:notifications*
      */
-    post: operations['notifications/create'];
+    post: operations['notifications___create'];
   };
   '/notifications/flush': {
     /**
@@ -2777,7 +2777,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:notifications*
      */
-    post: operations['notifications/flush'];
+    post: operations['notifications___flush'];
   };
   '/notifications/mark-all-as-read': {
     /**
@@ -2786,7 +2786,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:notifications*
      */
-    post: operations['notifications/mark-all-as-read'];
+    post: operations['notifications___mark-all-as-read'];
   };
   '/notifications/test-notification': {
     /**
@@ -2795,7 +2795,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:notifications*
      */
-    post: operations['notifications/test-notification'];
+    post: operations['notifications___test-notification'];
   };
   '/page-push': {
     /**
@@ -2814,7 +2814,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:pages*
      */
-    post: operations['pages/create'];
+    post: operations['pages___create'];
   };
   '/pages/delete': {
     /**
@@ -2823,7 +2823,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:pages*
      */
-    post: operations['pages/delete'];
+    post: operations['pages___delete'];
   };
   '/pages/featured': {
     /**
@@ -2832,7 +2832,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['pages/featured'];
+    post: operations['pages___featured'];
   };
   '/pages/like': {
     /**
@@ -2841,7 +2841,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:page-likes*
      */
-    post: operations['pages/like'];
+    post: operations['pages___like'];
   };
   '/pages/show': {
     /**
@@ -2850,7 +2850,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['pages/show'];
+    post: operations['pages___show'];
   };
   '/pages/unlike': {
     /**
@@ -2859,7 +2859,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:page-likes*
      */
-    post: operations['pages/unlike'];
+    post: operations['pages___unlike'];
   };
   '/pages/update': {
     /**
@@ -2868,7 +2868,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:pages*
      */
-    post: operations['pages/update'];
+    post: operations['pages___update'];
   };
   '/flash/create': {
     /**
@@ -2877,7 +2877,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:flash*
      */
-    post: operations['flash/create'];
+    post: operations['flash___create'];
   };
   '/flash/delete': {
     /**
@@ -2886,7 +2886,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:flash*
      */
-    post: operations['flash/delete'];
+    post: operations['flash___delete'];
   };
   '/flash/featured': {
     /**
@@ -2895,7 +2895,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['flash/featured'];
+    post: operations['flash___featured'];
   };
   '/flash/like': {
     /**
@@ -2904,7 +2904,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
      */
-    post: operations['flash/like'];
+    post: operations['flash___like'];
   };
   '/flash/show': {
     /**
@@ -2913,7 +2913,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['flash/show'];
+    post: operations['flash___show'];
   };
   '/flash/unlike': {
     /**
@@ -2922,7 +2922,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
      */
-    post: operations['flash/unlike'];
+    post: operations['flash___unlike'];
   };
   '/flash/update': {
     /**
@@ -2931,7 +2931,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:flash*
      */
-    post: operations['flash/update'];
+    post: operations['flash___update'];
   };
   '/flash/my': {
     /**
@@ -2940,7 +2940,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:flash*
      */
-    post: operations['flash/my'];
+    post: operations['flash___my'];
   };
   '/flash/my-likes': {
     /**
@@ -2949,7 +2949,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:flash-likes*
      */
-    post: operations['flash/my-likes'];
+    post: operations['flash___my-likes'];
   };
   '/ping': {
     /**
@@ -2976,7 +2976,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['promo/read'];
+    post: operations['promo___read'];
   };
   '/roles/list': {
     /**
@@ -2985,7 +2985,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['roles/list'];
+    post: operations['roles___list'];
   };
   '/roles/show': {
     /**
@@ -2994,7 +2994,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['roles/show'];
+    post: operations['roles___show'];
   };
   '/roles/users': {
     /**
@@ -3003,7 +3003,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['roles/users'];
+    post: operations['roles___users'];
   };
   '/roles/notes': {
     /**
@@ -3012,7 +3012,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['roles/notes'];
+    post: operations['roles___notes'];
   };
   '/request-reset-password': {
     /**
@@ -3074,7 +3074,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['sw/show-registration'];
+    post: operations['sw___show-registration'];
   };
   '/sw/update-registration': {
     /**
@@ -3084,7 +3084,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['sw/update-registration'];
+    post: operations['sw___update-registration'];
   };
   '/sw/register': {
     /**
@@ -3094,7 +3094,7 @@ export type paths = {
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
-    post: operations['sw/register'];
+    post: operations['sw___register'];
   };
   '/sw/unregister': {
     /**
@@ -3103,7 +3103,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['sw/unregister'];
+    post: operations['sw___unregister'];
   };
   '/test': {
     /**
@@ -3121,7 +3121,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['username/available'];
+    post: operations['username___available'];
   };
   '/users': {
     /**
@@ -3139,7 +3139,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/clips'];
+    post: operations['users___clips'];
   };
   '/users/followers': {
     /**
@@ -3148,7 +3148,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/followers'];
+    post: operations['users___followers'];
   };
   '/users/following': {
     /**
@@ -3157,7 +3157,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/following'];
+    post: operations['users___following'];
   };
   '/users/gallery/posts': {
     /**
@@ -3166,7 +3166,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/gallery/posts'];
+    post: operations['users___gallery___posts'];
   };
   '/users/get-frequently-replied-users': {
     /**
@@ -3175,7 +3175,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/get-frequently-replied-users'];
+    post: operations['users___get-frequently-replied-users'];
   };
   '/users/featured-notes': {
     /**
@@ -3184,14 +3184,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['users/featured-notes'];
+    get: operations['users___featured-notes'];
     /**
      * users/featured-notes
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['users/featured-notes'];
+    post: operations['users___featured-notes'];
   };
   '/users/lists/create': {
     /**
@@ -3200,7 +3200,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/create'];
+    post: operations['users___lists___create'];
   };
   '/users/lists/delete': {
     /**
@@ -3209,7 +3209,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/delete'];
+    post: operations['users___lists___delete'];
   };
   '/users/lists/list': {
     /**
@@ -3218,7 +3218,7 @@ export type paths = {
      *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
-    post: operations['users/lists/list'];
+    post: operations['users___lists___list'];
   };
   '/users/lists/pull': {
     /**
@@ -3227,7 +3227,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/pull'];
+    post: operations['users___lists___pull'];
   };
   '/users/lists/push': {
     /**
@@ -3236,7 +3236,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/push'];
+    post: operations['users___lists___push'];
   };
   '/users/lists/show': {
     /**
@@ -3245,7 +3245,7 @@ export type paths = {
      *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
-    post: operations['users/lists/show'];
+    post: operations['users___lists___show'];
   };
   '/users/lists/favorite': {
     /**
@@ -3254,7 +3254,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/favorite'];
+    post: operations['users___lists___favorite'];
   };
   '/users/lists/unfavorite': {
     /**
@@ -3263,7 +3263,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/unfavorite'];
+    post: operations['users___lists___unfavorite'];
   };
   '/users/lists/update': {
     /**
@@ -3272,7 +3272,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/update'];
+    post: operations['users___lists___update'];
   };
   '/users/lists/create-from-public': {
     /**
@@ -3281,7 +3281,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/create-from-public'];
+    post: operations['users___lists___create-from-public'];
   };
   '/users/lists/update-membership': {
     /**
@@ -3290,7 +3290,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/lists/update-membership'];
+    post: operations['users___lists___update-membership'];
   };
   '/users/lists/get-memberships': {
     /**
@@ -3299,7 +3299,7 @@ export type paths = {
      *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
-    post: operations['users/lists/get-memberships'];
+    post: operations['users___lists___get-memberships'];
   };
   '/users/notes': {
     /**
@@ -3308,7 +3308,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/notes'];
+    post: operations['users___notes'];
   };
   '/users/pages': {
     /**
@@ -3317,7 +3317,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/pages'];
+    post: operations['users___pages'];
   };
   '/users/flashs': {
     /**
@@ -3326,7 +3326,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/flashs'];
+    post: operations['users___flashs'];
   };
   '/users/reactions': {
     /**
@@ -3335,7 +3335,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/reactions'];
+    post: operations['users___reactions'];
   };
   '/users/recommendation': {
     /**
@@ -3344,7 +3344,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['users/recommendation'];
+    post: operations['users___recommendation'];
   };
   '/users/relation': {
     /**
@@ -3353,7 +3353,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['users/relation'];
+    post: operations['users___relation'];
   };
   '/users/report-abuse': {
     /**
@@ -3362,7 +3362,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:report-abuse*
      */
-    post: operations['users/report-abuse'];
+    post: operations['users___report-abuse'];
   };
   '/users/search-by-username-and-host': {
     /**
@@ -3371,7 +3371,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/search-by-username-and-host'];
+    post: operations['users___search-by-username-and-host'];
   };
   '/users/search': {
     /**
@@ -3380,7 +3380,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/search'];
+    post: operations['users___search'];
   };
   '/users/show': {
     /**
@@ -3389,7 +3389,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/show'];
+    post: operations['users___show'];
   };
   '/users/achievements': {
     /**
@@ -3398,7 +3398,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['users/achievements'];
+    post: operations['users___achievements'];
   };
   '/users/update-memo': {
     /**
@@ -3407,7 +3407,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['users/update-memo'];
+    post: operations['users___update-memo'];
   };
   '/fetch-rss': {
     /**
@@ -3458,7 +3458,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['bubble-game/register'];
+    post: operations['bubble-game___register'];
   };
   '/bubble-game/ranking': {
     /**
@@ -3467,14 +3467,14 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    get: operations['bubble-game/ranking'];
+    get: operations['bubble-game___ranking'];
     /**
      * bubble-game/ranking
      * @description No description provided.
      *
      * **Credential required**: *No*
      */
-    post: operations['bubble-game/ranking'];
+    post: operations['bubble-game___ranking'];
   };
   '/reversi/cancel-match': {
     /**
@@ -3483,7 +3483,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['reversi/cancel-match'];
+    post: operations['reversi___cancel-match'];
   };
   '/reversi/games': {
     /**
@@ -3492,7 +3492,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['reversi/games'];
+    post: operations['reversi___games'];
   };
   '/reversi/match': {
     /**
@@ -3501,7 +3501,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['reversi/match'];
+    post: operations['reversi___match'];
   };
   '/reversi/invitations': {
     /**
@@ -3510,7 +3510,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
-    post: operations['reversi/invitations'];
+    post: operations['reversi___invitations'];
   };
   '/reversi/show-game': {
     /**
@@ -3519,7 +3519,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['reversi/show-game'];
+    post: operations['reversi___show-game'];
   };
   '/reversi/surrender': {
     /**
@@ -3528,7 +3528,7 @@ export type paths = {
      *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
-    post: operations['reversi/surrender'];
+    post: operations['reversi___surrender'];
   };
   '/reversi/verify': {
     /**
@@ -3537,7 +3537,7 @@ export type paths = {
      *
      * **Credential required**: *No*
      */
-    post: operations['reversi/verify'];
+    post: operations['reversi___verify'];
   };
 };
 
@@ -4860,7 +4860,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:meta*
    */
-  'admin/meta': {
+  admin___meta: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -5019,7 +5019,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-user-reports*
    */
-  'admin/abuse-user-reports': {
+  'admin___abuse-user-reports': {
     requestBody: {
       content: {
         'application/json': {
@@ -5111,7 +5111,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'admin/accounts/create': {
+  admin___accounts___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -5165,7 +5165,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:account*
    */
-  'admin/accounts/delete': {
+  admin___accounts___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -5217,7 +5217,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:account*
    */
-  'admin/accounts/find-by-email': {
+  'admin___accounts___find-by-email': {
     requestBody: {
       content: {
         'application/json': {
@@ -5270,7 +5270,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
    */
-  'admin/ad/create': {
+  admin___ad___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -5331,7 +5331,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
    */
-  'admin/ad/delete': {
+  admin___ad___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -5383,7 +5383,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:ad*
    */
-  'admin/ad/list': {
+  admin___ad___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -5443,7 +5443,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
    */
-  'admin/ad/update': {
+  admin___ad___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -5504,7 +5504,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
    */
-  'admin/announcements/create': {
+  admin___announcements___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -5593,7 +5593,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
    */
-  'admin/announcements/delete': {
+  admin___announcements___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -5645,7 +5645,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:announcements*
    */
-  'admin/announcements/list': {
+  admin___announcements___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -5719,7 +5719,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
    */
-  'admin/announcements/update': {
+  admin___announcements___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -5782,7 +5782,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
    */
-  'admin/avatar-decorations/create': {
+  'admin___avatar-decorations___create': {
     requestBody: {
       content: {
         'application/json': {
@@ -5836,7 +5836,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
    */
-  'admin/avatar-decorations/delete': {
+  'admin___avatar-decorations___delete': {
     requestBody: {
       content: {
         'application/json': {
@@ -5888,7 +5888,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:avatar-decorations*
    */
-  'admin/avatar-decorations/list': {
+  'admin___avatar-decorations___list': {
     requestBody: {
       content: {
         'application/json': {
@@ -5962,7 +5962,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
    */
-  'admin/avatar-decorations/update': {
+  'admin___avatar-decorations___update': {
     requestBody: {
       content: {
         'application/json': {
@@ -6018,7 +6018,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:delete-all-files-of-a-user*
    */
-  'admin/delete-all-files-of-a-user': {
+  'admin___delete-all-files-of-a-user': {
     requestBody: {
       content: {
         'application/json': {
@@ -6070,7 +6070,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-avatar*
    */
-  'admin/unset-user-avatar': {
+  'admin___unset-user-avatar': {
     requestBody: {
       content: {
         'application/json': {
@@ -6122,7 +6122,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-banner*
    */
-  'admin/unset-user-banner': {
+  'admin___unset-user-banner': {
     requestBody: {
       content: {
         'application/json': {
@@ -6174,7 +6174,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
    */
-  'admin/drive/clean-remote-files': {
+  'admin___drive___clean-remote-files': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -6218,7 +6218,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
    */
-  'admin/drive/cleanup': {
+  admin___drive___cleanup: {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -6262,7 +6262,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
    */
-  'admin/drive/files': {
+  admin___drive___files: {
     requestBody: {
       content: {
         'application/json': {
@@ -6333,7 +6333,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
    */
-  'admin/drive/show-file': {
+  'admin___drive___show-file': {
     requestBody: {
       content: {
         'application/json': {
@@ -6442,7 +6442,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/add-aliases-bulk': {
+  'admin___emoji___add-aliases-bulk': {
     requestBody: {
       content: {
         'application/json': {
@@ -6494,7 +6494,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/add': {
+  admin___emoji___add: {
     requestBody: {
       content: {
         'application/json': {
@@ -6556,7 +6556,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/copy': {
+  admin___emoji___copy: {
     requestBody: {
       content: {
         'application/json': {
@@ -6613,7 +6613,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/delete-bulk': {
+  'admin___emoji___delete-bulk': {
     requestBody: {
       content: {
         'application/json': {
@@ -6664,7 +6664,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/delete': {
+  admin___emoji___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -6717,7 +6717,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'admin/emoji/import-zip': {
+  'admin___emoji___import-zip': {
     requestBody: {
       content: {
         'application/json': {
@@ -6769,7 +6769,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
    */
-  'admin/emoji/list-remote': {
+  'admin___emoji___list-remote': {
     requestBody: {
       content: {
         'application/json': {
@@ -6843,7 +6843,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
    */
-  'admin/emoji/list': {
+  admin___emoji___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -6912,7 +6912,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/remove-aliases-bulk': {
+  'admin___emoji___remove-aliases-bulk': {
     requestBody: {
       content: {
         'application/json': {
@@ -6964,7 +6964,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/set-aliases-bulk': {
+  'admin___emoji___set-aliases-bulk': {
     requestBody: {
       content: {
         'application/json': {
@@ -7016,7 +7016,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/set-category-bulk': {
+  'admin___emoji___set-category-bulk': {
     requestBody: {
       content: {
         'application/json': {
@@ -7069,7 +7069,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/set-license-bulk': {
+  'admin___emoji___set-license-bulk': {
     requestBody: {
       content: {
         'application/json': {
@@ -7122,7 +7122,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
-  'admin/emoji/update': {
+  admin___emoji___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -7184,7 +7184,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
-  'admin/federation/delete-all-files': {
+  'admin___federation___delete-all-files': {
     requestBody: {
       content: {
         'application/json': {
@@ -7235,7 +7235,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
-  'admin/federation/refresh-remote-instance-metadata': {
+  'admin___federation___refresh-remote-instance-metadata': {
     requestBody: {
       content: {
         'application/json': {
@@ -7286,7 +7286,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
-  'admin/federation/remove-all-following': {
+  'admin___federation___remove-all-following': {
     requestBody: {
       content: {
         'application/json': {
@@ -7337,7 +7337,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
-  'admin/federation/update-instance': {
+  'admin___federation___update-instance': {
     requestBody: {
       content: {
         'application/json': {
@@ -7390,7 +7390,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:index-stats*
    */
-  'admin/get-index-stats': {
+  'admin___get-index-stats': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -7439,7 +7439,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:table-stats*
    */
-  'admin/get-table-stats': {
+  'admin___get-table-stats': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -7490,7 +7490,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:user-ips*
    */
-  'admin/get-user-ips': {
+  'admin___get-user-ips': {
     requestBody: {
       content: {
         'application/json': {
@@ -7548,7 +7548,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:invite-codes*
    */
-  'admin/invite/create': {
+  admin___invite___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -7603,7 +7603,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:invite-codes*
    */
-  'admin/invite/list': {
+  admin___invite___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -7666,7 +7666,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:promo*
    */
-  'admin/promo/create': {
+  admin___promo___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -7719,7 +7719,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
    */
-  'admin/queue/clear': {
+  admin___queue___clear: {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -7763,7 +7763,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
    */
-  'admin/queue/deliver-delayed': {
+  'admin___queue___deliver-delayed': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -7809,7 +7809,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
    */
-  'admin/queue/inbox-delayed': {
+  'admin___queue___inbox-delayed': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -7855,7 +7855,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
    */
-  'admin/queue/promote': {
+  admin___queue___promote: {
     requestBody: {
       content: {
         'application/json': {
@@ -7907,7 +7907,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
    */
-  'admin/queue/stats': {
+  admin___queue___stats: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -7958,7 +7958,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
    */
-  'admin/relays/add': {
+  admin___relays___add: {
     requestBody: {
       content: {
         'application/json': {
@@ -8021,7 +8021,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:relays*
    */
-  'admin/relays/list': {
+  admin___relays___list: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -8077,7 +8077,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
    */
-  'admin/relays/remove': {
+  admin___relays___remove: {
     requestBody: {
       content: {
         'application/json': {
@@ -8128,7 +8128,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:reset-password*
    */
-  'admin/reset-password': {
+  'admin___reset-password': {
     requestBody: {
       content: {
         'application/json': {
@@ -8184,7 +8184,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
    */
-  'admin/resolve-abuse-user-report': {
+  'admin___resolve-abuse-user-report': {
     requestBody: {
       content: {
         'application/json': {
@@ -8238,7 +8238,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:send-email*
    */
-  'admin/send-email': {
+  'admin___send-email': {
     requestBody: {
       content: {
         'application/json': {
@@ -8291,7 +8291,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:server-info*
    */
-  'admin/server-info': {
+  'admin___server-info': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -8361,7 +8361,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:show-moderation-log*
    */
-  'admin/show-moderation-logs': {
+  'admin___show-moderation-logs': {
     requestBody: {
       content: {
         'application/json': {
@@ -8432,7 +8432,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
    */
-  'admin/show-user': {
+  'admin___show-user': {
     requestBody: {
       content: {
         'application/json': {
@@ -8641,7 +8641,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
    */
-  'admin/show-users': {
+  'admin___show-users': {
     requestBody: {
       content: {
         'application/json': {
@@ -8716,7 +8716,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user*
    */
-  'admin/suspend-user': {
+  'admin___suspend-user': {
     requestBody: {
       content: {
         'application/json': {
@@ -8768,7 +8768,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user*
    */
-  'admin/unsuspend-user': {
+  'admin___unsuspend-user': {
     requestBody: {
       content: {
         'application/json': {
@@ -8820,7 +8820,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:meta*
    */
-  'admin/update-meta': {
+  'admin___update-meta': {
     requestBody: {
       content: {
         'application/json': {
@@ -8980,7 +8980,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account*
    */
-  'admin/delete-account': {
+  'admin___delete-account': {
     requestBody: {
       content: {
         'application/json': {
@@ -9032,7 +9032,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:user-note*
    */
-  'admin/update-user-note': {
+  'admin___update-user-note': {
     requestBody: {
       content: {
         'application/json': {
@@ -9085,7 +9085,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
-  'admin/roles/create': {
+  admin___roles___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -9153,7 +9153,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
-  'admin/roles/delete': {
+  admin___roles___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -9205,7 +9205,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
    */
-  'admin/roles/list': {
+  admin___roles___list: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -9251,7 +9251,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
    */
-  'admin/roles/show': {
+  admin___roles___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -9305,7 +9305,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
-  'admin/roles/update': {
+  admin___roles___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -9372,7 +9372,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
-  'admin/roles/assign': {
+  admin___roles___assign: {
     requestBody: {
       content: {
         'application/json': {
@@ -9427,7 +9427,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
-  'admin/roles/unassign': {
+  admin___roles___unassign: {
     requestBody: {
       content: {
         'application/json': {
@@ -9481,7 +9481,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
-  'admin/roles/update-default-policies': {
+  'admin___roles___update-default-policies': {
     requestBody: {
       content: {
         'application/json': {
@@ -9532,7 +9532,7 @@ export type operations = {
    *
    * **Credential required**: *No* / **Permission**: *read:admin:roles*
    */
-  'admin/roles/users': {
+  admin___roles___users: {
     requestBody: {
       content: {
         'application/json': {
@@ -9660,7 +9660,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'antennas/create': {
+  antennas___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -9726,7 +9726,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'antennas/delete': {
+  antennas___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -9778,7 +9778,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'antennas/list': {
+  antennas___list: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -9824,7 +9824,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'antennas/notes': {
+  antennas___notes: {
     requestBody: {
       content: {
         'application/json': {
@@ -9886,7 +9886,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'antennas/show': {
+  antennas___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -9940,7 +9940,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'antennas/update': {
+  antennas___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -10008,7 +10008,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:federation*
    */
-  'ap/get': {
+  ap___get: {
     requestBody: {
       content: {
         'application/json': {
@@ -10067,7 +10067,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'ap/show': {
+  ap___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -10134,7 +10134,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'app/create': {
+  app___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -10190,7 +10190,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'app/show': {
+  app___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -10245,7 +10245,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'auth/accept': {
+  auth___accept: {
     requestBody: {
       content: {
         'application/json': {
@@ -10296,7 +10296,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'auth/session/generate': {
+  auth___session___generate: {
     requestBody: {
       content: {
         'application/json': {
@@ -10353,7 +10353,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'auth/session/show': {
+  auth___session___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -10411,7 +10411,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'auth/session/userkey': {
+  auth___session___userkey: {
     requestBody: {
       content: {
         'application/json': {
@@ -10468,7 +10468,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:blocks*
    */
-  'blocking/create': {
+  blocking___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -10528,7 +10528,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:blocks*
    */
-  'blocking/delete': {
+  blocking___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -10588,7 +10588,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:blocks*
    */
-  'blocking/list': {
+  blocking___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -10646,7 +10646,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:channels*
    */
-  'channels/create': {
+  channels___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -10711,7 +10711,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'channels/featured': {
+  channels___featured: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -10757,7 +10757,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:channels*
    */
-  'channels/follow': {
+  channels___follow: {
     requestBody: {
       content: {
         'application/json': {
@@ -10809,7 +10809,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:channels*
    */
-  'channels/followed': {
+  channels___followed: {
     requestBody: {
       content: {
         'application/json': {
@@ -10867,7 +10867,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:channels*
    */
-  'channels/owned': {
+  channels___owned: {
     requestBody: {
       content: {
         'application/json': {
@@ -10925,7 +10925,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'channels/show': {
+  channels___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -10979,7 +10979,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'channels/timeline': {
+  channels___timeline: {
     requestBody: {
       content: {
         'application/json': {
@@ -11043,7 +11043,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:channels*
    */
-  'channels/unfollow': {
+  channels___unfollow: {
     requestBody: {
       content: {
         'application/json': {
@@ -11095,7 +11095,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:channels*
    */
-  'channels/update': {
+  channels___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -11158,7 +11158,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:channels*
    */
-  'channels/favorite': {
+  channels___favorite: {
     requestBody: {
       content: {
         'application/json': {
@@ -11210,7 +11210,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:channels*
    */
-  'channels/unfavorite': {
+  channels___unfavorite: {
     requestBody: {
       content: {
         'application/json': {
@@ -11262,7 +11262,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:channels*
    */
-  'channels/my-favorites': {
+  'channels___my-favorites': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -11308,7 +11308,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'channels/search': {
+  channels___search: {
     requestBody: {
       content: {
         'application/json': {
@@ -11372,7 +11372,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/active-users': {
+  'charts___active-users': {
     requestBody: {
       content: {
         'application/json': {
@@ -11440,7 +11440,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/ap-request': {
+  'charts___ap-request': {
     requestBody: {
       content: {
         'application/json': {
@@ -11502,7 +11502,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/drive': {
+  charts___drive: {
     requestBody: {
       content: {
         'application/json': {
@@ -11573,7 +11573,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/federation': {
+  charts___federation: {
     requestBody: {
       content: {
         'application/json': {
@@ -11640,7 +11640,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/instance': {
+  charts___instance: {
     requestBody: {
       content: {
         'application/json': {
@@ -11738,7 +11738,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/notes': {
+  charts___notes: {
     requestBody: {
       content: {
         'application/json': {
@@ -11819,7 +11819,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/user/drive': {
+  charts___user___drive: {
     requestBody: {
       content: {
         'application/json': {
@@ -11886,7 +11886,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/user/following': {
+  charts___user___following: {
     requestBody: {
       content: {
         'application/json': {
@@ -11971,7 +11971,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/user/notes': {
+  charts___user___notes: {
     requestBody: {
       content: {
         'application/json': {
@@ -12041,7 +12041,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/user/pv': {
+  charts___user___pv: {
     requestBody: {
       content: {
         'application/json': {
@@ -12110,7 +12110,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/user/reactions': {
+  charts___user___reactions: {
     requestBody: {
       content: {
         'application/json': {
@@ -12177,7 +12177,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'charts/users': {
+  charts___users: {
     requestBody: {
       content: {
         'application/json': {
@@ -12246,7 +12246,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'clips/add-note': {
+  'clips___add-note': {
     requestBody: {
       content: {
         'application/json': {
@@ -12306,7 +12306,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'clips/remove-note': {
+  'clips___remove-note': {
     requestBody: {
       content: {
         'application/json': {
@@ -12360,7 +12360,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'clips/create': {
+  clips___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -12416,7 +12416,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'clips/delete': {
+  clips___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -12468,7 +12468,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'clips/list': {
+  clips___list: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -12514,7 +12514,7 @@ export type operations = {
    *
    * **Credential required**: *No* / **Permission**: *read:account*
    */
-  'clips/notes': {
+  clips___notes: {
     requestBody: {
       content: {
         'application/json': {
@@ -12574,7 +12574,7 @@ export type operations = {
    *
    * **Credential required**: *No* / **Permission**: *read:account*
    */
-  'clips/show': {
+  clips___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -12628,7 +12628,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'clips/update': {
+  clips___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -12685,7 +12685,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
    */
-  'clips/favorite': {
+  clips___favorite: {
     requestBody: {
       content: {
         'application/json': {
@@ -12737,7 +12737,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
    */
-  'clips/unfavorite': {
+  clips___unfavorite: {
     requestBody: {
       content: {
         'application/json': {
@@ -12789,7 +12789,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:clip-favorite*
    */
-  'clips/my-favorites': {
+  'clips___my-favorites': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -12884,7 +12884,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/files': {
+  drive___files: {
     requestBody: {
       content: {
         'application/json': {
@@ -12950,7 +12950,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/files/attached-notes': {
+  'drive___files___attached-notes': {
     requestBody: {
       content: {
         'application/json': {
@@ -13010,7 +13010,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/files/check-existence': {
+  'drive___files___check-existence': {
     requestBody: {
       content: {
         'application/json': {
@@ -13063,7 +13063,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:drive*
    */
-  'drive/files/create': {
+  drive___files___create: {
     requestBody: {
       content: {
         'multipart/form-data': {
@@ -13139,7 +13139,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:drive*
    */
-  'drive/files/delete': {
+  drive___files___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -13191,7 +13191,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/files/find-by-hash': {
+  'drive___files___find-by-hash': {
     requestBody: {
       content: {
         'application/json': {
@@ -13244,7 +13244,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/files/find': {
+  drive___files___find: {
     requestBody: {
       content: {
         'application/json': {
@@ -13302,7 +13302,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/files/show': {
+  drive___files___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -13357,7 +13357,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:drive*
    */
-  'drive/files/update': {
+  drive___files___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -13416,7 +13416,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:drive*
    */
-  'drive/files/upload-from-url': {
+  'drive___files___upload-from-url': {
     requestBody: {
       content: {
         'application/json': {
@@ -13486,7 +13486,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/folders': {
+  drive___folders: {
     requestBody: {
       content: {
         'application/json': {
@@ -13549,7 +13549,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:drive*
    */
-  'drive/folders/create': {
+  drive___folders___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -13611,7 +13611,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:drive*
    */
-  'drive/folders/delete': {
+  drive___folders___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -13663,7 +13663,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/folders/find': {
+  drive___folders___find: {
     requestBody: {
       content: {
         'application/json': {
@@ -13721,7 +13721,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/folders/show': {
+  drive___folders___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -13775,7 +13775,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:drive*
    */
-  'drive/folders/update': {
+  drive___folders___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -13832,7 +13832,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:drive*
    */
-  'drive/stream': {
+  drive___stream: {
     requestBody: {
       content: {
         'application/json': {
@@ -13891,7 +13891,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'email-address/available': {
+  'email-address___available': {
     requestBody: {
       content: {
         'application/json': {
@@ -14106,7 +14106,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'federation/followers': {
+  federation___followers: {
     requestBody: {
       content: {
         'application/json': {
@@ -14165,7 +14165,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'federation/following': {
+  federation___following: {
     requestBody: {
       content: {
         'application/json': {
@@ -14224,7 +14224,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'federation/instances': {
+  federation___instances: {
     requestBody: {
       content: {
         'application/json': {
@@ -14291,7 +14291,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'federation/show-instance': {
+  'federation___show-instance': {
     requestBody: {
       content: {
         'application/json': {
@@ -14348,7 +14348,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'federation/update-remote-user': {
+  'federation___update-remote-user': {
     requestBody: {
       content: {
         'application/json': {
@@ -14400,7 +14400,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'federation/users': {
+  federation___users: {
     requestBody: {
       content: {
         'application/json': {
@@ -14459,7 +14459,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'federation/stats': {
+  federation___stats: {
     requestBody: {
       content: {
         'application/json': {
@@ -14518,7 +14518,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/create': {
+  following___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -14579,7 +14579,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/delete': {
+  following___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -14639,7 +14639,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/update': {
+  following___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -14702,7 +14702,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/update-all': {
+  'following___update-all': {
     requestBody: {
       content: {
         'application/json': {
@@ -14761,7 +14761,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/invalidate': {
+  following___invalidate: {
     requestBody: {
       content: {
         'application/json': {
@@ -14821,7 +14821,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/requests/accept': {
+  following___requests___accept: {
     requestBody: {
       content: {
         'application/json': {
@@ -14873,7 +14873,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/requests/cancel': {
+  following___requests___cancel: {
     requestBody: {
       content: {
         'application/json': {
@@ -14927,7 +14927,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:following*
    */
-  'following/requests/list': {
+  following___requests___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -14990,7 +14990,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:following*
    */
-  'following/requests/reject': {
+  following___requests___reject: {
     requestBody: {
       content: {
         'application/json': {
@@ -15042,7 +15042,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'gallery/featured': {
+  gallery___featured: {
     requestBody: {
       content: {
         'application/json': {
@@ -15098,7 +15098,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'gallery/popular': {
+  gallery___popular: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -15144,7 +15144,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'gallery/posts': {
+  gallery___posts: {
     requestBody: {
       content: {
         'application/json': {
@@ -15202,7 +15202,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:gallery*
    */
-  'gallery/posts/create': {
+  gallery___posts___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -15265,7 +15265,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:gallery*
    */
-  'gallery/posts/delete': {
+  gallery___posts___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -15317,7 +15317,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
    */
-  'gallery/posts/like': {
+  gallery___posts___like: {
     requestBody: {
       content: {
         'application/json': {
@@ -15369,7 +15369,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'gallery/posts/show': {
+  gallery___posts___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -15423,7 +15423,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
    */
-  'gallery/posts/unlike': {
+  gallery___posts___unlike: {
     requestBody: {
       content: {
         'application/json': {
@@ -15475,7 +15475,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:gallery*
    */
-  'gallery/posts/update': {
+  gallery___posts___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -15644,7 +15644,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'hashtags/list': {
+  hashtags___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -15706,7 +15706,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'hashtags/search': {
+  hashtags___search: {
     requestBody: {
       content: {
         'application/json': {
@@ -15763,7 +15763,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'hashtags/show': {
+  hashtags___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -15816,7 +15816,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'hashtags/trend': {
+  hashtags___trend: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -15866,7 +15866,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'hashtags/users': {
+  hashtags___users: {
     requestBody: {
       content: {
         'application/json': {
@@ -15980,7 +15980,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/done': {
+  i___2fa___done: {
     requestBody: {
       content: {
         'application/json': {
@@ -16036,7 +16036,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/key-done': {
+  'i___2fa___key-done': {
     requestBody: {
       content: {
         'application/json': {
@@ -16096,7 +16096,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/password-less': {
+  'i___2fa___password-less': {
     requestBody: {
       content: {
         'application/json': {
@@ -16148,7 +16148,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/register-key': {
+  'i___2fa___register-key': {
     requestBody: {
       content: {
         'application/json': {
@@ -16237,7 +16237,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/register': {
+  i___2fa___register: {
     requestBody: {
       content: {
         'application/json': {
@@ -16298,7 +16298,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/update-key': {
+  'i___2fa___update-key': {
     requestBody: {
       content: {
         'application/json': {
@@ -16351,7 +16351,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/remove-key': {
+  'i___2fa___remove-key': {
     requestBody: {
       content: {
         'application/json': {
@@ -16405,7 +16405,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/2fa/unregister': {
+  i___2fa___unregister: {
     requestBody: {
       content: {
         'application/json': {
@@ -16458,7 +16458,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/apps': {
+  i___apps: {
     requestBody: {
       content: {
         'application/json': {
@@ -16522,7 +16522,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/authorized-apps': {
+  'i___authorized-apps': {
     requestBody: {
       content: {
         'application/json': {
@@ -16590,7 +16590,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/claim-achievement': {
+  'i___claim-achievement': {
     requestBody: {
       content: {
         'application/json': {
@@ -16643,7 +16643,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/change-password': {
+  'i___change-password': {
     requestBody: {
       content: {
         'application/json': {
@@ -16697,7 +16697,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/delete-account': {
+  'i___delete-account': {
     requestBody: {
       content: {
         'application/json': {
@@ -16750,7 +16750,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-blocking': {
+  'i___export-blocking': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -16801,7 +16801,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-following': {
+  'i___export-following': {
     requestBody: {
       content: {
         'application/json': {
@@ -16862,7 +16862,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-mute': {
+  'i___export-mute': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -16913,7 +16913,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-notes': {
+  'i___export-notes': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -16964,7 +16964,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-clips': {
+  'i___export-clips': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -17015,7 +17015,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-favorites': {
+  'i___export-favorites': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -17066,7 +17066,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-user-lists': {
+  'i___export-user-lists': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -17117,7 +17117,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/export-antennas': {
+  'i___export-antennas': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -17167,7 +17167,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:favorites*
    */
-  'i/favorites': {
+  i___favorites: {
     requestBody: {
       content: {
         'application/json': {
@@ -17225,7 +17225,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:gallery-likes*
    */
-  'i/gallery/likes': {
+  i___gallery___likes: {
     requestBody: {
       content: {
         'application/json': {
@@ -17287,7 +17287,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:gallery*
    */
-  'i/gallery/posts': {
+  i___gallery___posts: {
     requestBody: {
       content: {
         'application/json': {
@@ -17346,7 +17346,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/import-blocking': {
+  'i___import-blocking': {
     requestBody: {
       content: {
         'application/json': {
@@ -17405,7 +17405,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/import-following': {
+  'i___import-following': {
     requestBody: {
       content: {
         'application/json': {
@@ -17465,7 +17465,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/import-muting': {
+  'i___import-muting': {
     requestBody: {
       content: {
         'application/json': {
@@ -17524,7 +17524,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/import-user-lists': {
+  'i___import-user-lists': {
     requestBody: {
       content: {
         'application/json': {
@@ -17583,7 +17583,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/import-antennas': {
+  'i___import-antennas': {
     requestBody: {
       content: {
         'application/json': {
@@ -17641,7 +17641,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:notifications*
    */
-  'i/notifications': {
+  i___notifications: {
     requestBody: {
       content: {
         'application/json': {
@@ -17709,7 +17709,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:notifications*
    */
-  'i/notifications-grouped': {
+  'i___notifications-grouped': {
     requestBody: {
       content: {
         'application/json': {
@@ -17777,7 +17777,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:page-likes*
    */
-  'i/page-likes': {
+  'i___page-likes': {
     requestBody: {
       content: {
         'application/json': {
@@ -17839,7 +17839,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:pages*
    */
-  'i/pages': {
+  i___pages: {
     requestBody: {
       content: {
         'application/json': {
@@ -17897,7 +17897,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/pin': {
+  i___pin: {
     requestBody: {
       content: {
         'application/json': {
@@ -17951,7 +17951,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/read-all-unread-notes': {
+  'i___read-all-unread-notes': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -17995,7 +17995,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/read-announcement': {
+  'i___read-announcement': {
     requestBody: {
       content: {
         'application/json': {
@@ -18048,7 +18048,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/regenerate-token': {
+  'i___regenerate-token': {
     requestBody: {
       content: {
         'application/json': {
@@ -18099,7 +18099,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'i/registry/get-all': {
+  'i___registry___get-all': {
     requestBody: {
       content: {
         'application/json': {
@@ -18154,7 +18154,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'i/registry/get-detail': {
+  'i___registry___get-detail': {
     requestBody: {
       content: {
         'application/json': {
@@ -18213,7 +18213,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'i/registry/get': {
+  i___registry___get: {
     requestBody: {
       content: {
         'application/json': {
@@ -18269,7 +18269,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'i/registry/keys-with-type': {
+  'i___registry___keys-with-type': {
     requestBody: {
       content: {
         'application/json': {
@@ -18326,7 +18326,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'i/registry/keys': {
+  i___registry___keys: {
     requestBody: {
       content: {
         'application/json': {
@@ -18381,7 +18381,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/registry/remove': {
+  i___registry___remove: {
     requestBody: {
       content: {
         'application/json': {
@@ -18436,7 +18436,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/registry/scopes-with-domain': {
+  'i___registry___scopes-with-domain': {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -18485,7 +18485,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/registry/set': {
+  i___registry___set: {
     requestBody: {
       content: {
         'application/json': {
@@ -18541,7 +18541,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/revoke-token': {
+  'i___revoke-token': {
     requestBody: {
       content: {
         'application/json': {
@@ -18595,7 +18595,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/signin-history': {
+  'i___signin-history': {
     requestBody: {
       content: {
         'application/json': {
@@ -18653,7 +18653,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/unpin': {
+  i___unpin: {
     requestBody: {
       content: {
         'application/json': {
@@ -18708,7 +18708,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/update-email': {
+  'i___update-email': {
     requestBody: {
       content: {
         'application/json': {
@@ -18769,7 +18769,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/update': {
+  i___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -19003,7 +19003,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'i/move': {
+  i___move: {
     requestBody: {
       content: {
         'application/json': {
@@ -19062,7 +19062,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/webhooks/create': {
+  i___webhooks___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -19132,7 +19132,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'i/webhooks/list': {
+  i___webhooks___list: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -19191,7 +19191,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'i/webhooks/show': {
+  i___webhooks___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -19258,7 +19258,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/webhooks/update': {
+  i___webhooks___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -19316,7 +19316,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'i/webhooks/delete': {
+  i___webhooks___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -19368,7 +19368,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
    */
-  'invite/create': {
+  invite___create: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -19414,7 +19414,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
    */
-  'invite/delete': {
+  invite___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -19466,7 +19466,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
    */
-  'invite/list': {
+  invite___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -19524,7 +19524,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
    */
-  'invite/limit': {
+  invite___limit: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -19728,7 +19728,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'miauth/gen-token': {
+  'miauth___gen-token': {
     requestBody: {
       content: {
         'application/json': {
@@ -19787,7 +19787,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:mutes*
    */
-  'mute/create': {
+  mute___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -19847,7 +19847,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:mutes*
    */
-  'mute/delete': {
+  mute___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -19899,7 +19899,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:mutes*
    */
-  'mute/list': {
+  mute___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -19957,7 +19957,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:mutes*
    */
-  'renote-mute/create': {
+  'renote-mute___create': {
     requestBody: {
       content: {
         'application/json': {
@@ -20015,7 +20015,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:mutes*
    */
-  'renote-mute/delete': {
+  'renote-mute___delete': {
     requestBody: {
       content: {
         'application/json': {
@@ -20067,7 +20067,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:mutes*
    */
-  'renote-mute/list': {
+  'renote-mute___list': {
     requestBody: {
       content: {
         'application/json': {
@@ -20125,7 +20125,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'my/apps': {
+  my___apps: {
     requestBody: {
       content: {
         'application/json': {
@@ -20245,7 +20245,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/children': {
+  notes___children: {
     requestBody: {
       content: {
         'application/json': {
@@ -20305,7 +20305,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/clips': {
+  notes___clips: {
     requestBody: {
       content: {
         'application/json': {
@@ -20359,7 +20359,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/conversation': {
+  notes___conversation: {
     requestBody: {
       content: {
         'application/json': {
@@ -20417,7 +20417,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:notes*
    */
-  'notes/create': {
+  notes___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -20512,7 +20512,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:notes*
    */
-  'notes/delete': {
+  notes___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -20570,7 +20570,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:favorites*
    */
-  'notes/favorites/create': {
+  notes___favorites___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -20628,7 +20628,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:favorites*
    */
-  'notes/favorites/delete': {
+  notes___favorites___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -20680,7 +20680,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/featured': {
+  notes___featured: {
     requestBody: {
       content: {
         'application/json': {
@@ -20738,7 +20738,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/global-timeline': {
+  'notes___global-timeline': {
     requestBody: {
       content: {
         'application/json': {
@@ -20802,7 +20802,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'notes/hybrid-timeline': {
+  'notes___hybrid-timeline': {
     requestBody: {
       content: {
         'application/json': {
@@ -20876,7 +20876,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/local-timeline': {
+  'notes___local-timeline': {
     requestBody: {
       content: {
         'application/json': {
@@ -20944,7 +20944,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'notes/mentions': {
+  notes___mentions: {
     requestBody: {
       content: {
         'application/json': {
@@ -21005,7 +21005,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'notes/polls/recommendation': {
+  notes___polls___recommendation: {
     requestBody: {
       content: {
         'application/json': {
@@ -21061,7 +21061,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:votes*
    */
-  'notes/polls/vote': {
+  notes___polls___vote: {
     requestBody: {
       content: {
         'application/json': {
@@ -21114,7 +21114,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/reactions': {
+  notes___reactions: {
     requestBody: {
       content: {
         'application/json': {
@@ -21175,7 +21175,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:reactions*
    */
-  'notes/reactions/create': {
+  notes___reactions___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -21228,7 +21228,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:reactions*
    */
-  'notes/reactions/delete': {
+  notes___reactions___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -21286,7 +21286,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/renotes': {
+  notes___renotes: {
     requestBody: {
       content: {
         'application/json': {
@@ -21346,7 +21346,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/replies': {
+  notes___replies: {
     requestBody: {
       content: {
         'application/json': {
@@ -21406,7 +21406,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/search-by-tag': {
+  'notes___search-by-tag': {
     requestBody: {
       content: {
         'application/json': {
@@ -21478,7 +21478,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/search': {
+  notes___search: {
     requestBody: {
       content: {
         'application/json': {
@@ -21551,7 +21551,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'notes/show': {
+  notes___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -21605,7 +21605,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'notes/state': {
+  notes___state: {
     requestBody: {
       content: {
         'application/json': {
@@ -21662,7 +21662,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'notes/thread-muting/create': {
+  'notes___thread-muting___create': {
     requestBody: {
       content: {
         'application/json': {
@@ -21720,7 +21720,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'notes/thread-muting/delete': {
+  'notes___thread-muting___delete': {
     requestBody: {
       content: {
         'application/json': {
@@ -21772,7 +21772,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'notes/timeline': {
+  notes___timeline: {
     requestBody: {
       content: {
         'application/json': {
@@ -21844,7 +21844,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'notes/translate': {
+  notes___translate: {
     requestBody: {
       content: {
         'application/json': {
@@ -21902,7 +21902,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:notes*
    */
-  'notes/unrenote': {
+  notes___unrenote: {
     requestBody: {
       content: {
         'application/json': {
@@ -21960,7 +21960,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'notes/user-list-timeline': {
+  'notes___user-list-timeline': {
     requestBody: {
       content: {
         'application/json': {
@@ -22037,7 +22037,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:notifications*
    */
-  'notifications/create': {
+  notifications___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -22096,7 +22096,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:notifications*
    */
-  'notifications/flush': {
+  notifications___flush: {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -22140,7 +22140,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:notifications*
    */
-  'notifications/mark-all-as-read': {
+  'notifications___mark-all-as-read': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -22184,7 +22184,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:notifications*
    */
-  'notifications/test-notification': {
+  'notifications___test-notification': {
     responses: {
       /** @description OK (without any results) */
       204: {
@@ -22289,7 +22289,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:pages*
    */
-  'pages/create': {
+  pages___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -22368,7 +22368,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:pages*
    */
-  'pages/delete': {
+  pages___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -22420,7 +22420,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'pages/featured': {
+  pages___featured: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -22466,7 +22466,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:page-likes*
    */
-  'pages/like': {
+  pages___like: {
     requestBody: {
       content: {
         'application/json': {
@@ -22518,7 +22518,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'pages/show': {
+  pages___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -22574,7 +22574,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:page-likes*
    */
-  'pages/unlike': {
+  pages___unlike: {
     requestBody: {
       content: {
         'application/json': {
@@ -22626,7 +22626,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:pages*
    */
-  'pages/update': {
+  pages___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -22700,7 +22700,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:flash*
    */
-  'flash/create': {
+  flash___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -22767,7 +22767,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:flash*
    */
-  'flash/delete': {
+  flash___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -22819,7 +22819,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'flash/featured': {
+  flash___featured: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -22865,7 +22865,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
    */
-  'flash/like': {
+  flash___like: {
     requestBody: {
       content: {
         'application/json': {
@@ -22917,7 +22917,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'flash/show': {
+  flash___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -22971,7 +22971,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
    */
-  'flash/unlike': {
+  flash___unlike: {
     requestBody: {
       content: {
         'application/json': {
@@ -23023,7 +23023,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:flash*
    */
-  'flash/update': {
+  flash___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -23087,7 +23087,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:flash*
    */
-  'flash/my': {
+  flash___my: {
     requestBody: {
       content: {
         'application/json': {
@@ -23145,7 +23145,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:flash-likes*
    */
-  'flash/my-likes': {
+  'flash___my-likes': {
     requestBody: {
       content: {
         'application/json': {
@@ -23301,7 +23301,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'promo/read': {
+  promo___read: {
     requestBody: {
       content: {
         'application/json': {
@@ -23353,7 +23353,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'roles/list': {
+  roles___list: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -23399,7 +23399,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'roles/show': {
+  roles___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -23453,7 +23453,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'roles/users': {
+  roles___users: {
     requestBody: {
       content: {
         'application/json': {
@@ -23517,7 +23517,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'roles/notes': {
+  roles___notes: {
     requestBody: {
       content: {
         'application/json': {
@@ -23847,7 +23847,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'sw/show-registration': {
+  'sw___show-registration': {
     requestBody: {
       content: {
         'application/json': {
@@ -23909,7 +23909,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'sw/update-registration': {
+  'sw___update-registration': {
     requestBody: {
       content: {
         'application/json': {
@@ -23968,7 +23968,7 @@ export type operations = {
    * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
-  'sw/register': {
+  sw___register: {
     requestBody: {
       content: {
         'application/json': {
@@ -24032,7 +24032,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'sw/unregister': {
+  sw___unregister: {
     requestBody: {
       content: {
         'application/json': {
@@ -24151,7 +24151,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'username/available': {
+  username___available: {
     requestBody: {
       content: {
         'application/json': {
@@ -24279,7 +24279,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/clips': {
+  users___clips: {
     requestBody: {
       content: {
         'application/json': {
@@ -24339,7 +24339,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/followers': {
+  users___followers: {
     requestBody: {
       content: {
         'application/json': {
@@ -24402,7 +24402,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/following': {
+  users___following: {
     requestBody: {
       content: {
         'application/json': {
@@ -24466,7 +24466,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/gallery/posts': {
+  users___gallery___posts: {
     requestBody: {
       content: {
         'application/json': {
@@ -24526,7 +24526,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/get-frequently-replied-users': {
+  'users___get-frequently-replied-users': {
     requestBody: {
       content: {
         'application/json': {
@@ -24585,7 +24585,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/featured-notes': {
+  'users___featured-notes': {
     requestBody: {
       content: {
         'application/json': {
@@ -24643,7 +24643,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/create': {
+  users___lists___create: {
     requestBody: {
       content: {
         'application/json': {
@@ -24696,7 +24696,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/delete': {
+  users___lists___delete: {
     requestBody: {
       content: {
         'application/json': {
@@ -24748,7 +24748,7 @@ export type operations = {
    *
    * **Credential required**: *No* / **Permission**: *read:account*
    */
-  'users/lists/list': {
+  users___lists___list: {
     requestBody: {
       content: {
         'application/json': {
@@ -24802,7 +24802,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/pull': {
+  users___lists___pull: {
     requestBody: {
       content: {
         'application/json': {
@@ -24856,7 +24856,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/push': {
+  users___lists___push: {
     requestBody: {
       content: {
         'application/json': {
@@ -24916,7 +24916,7 @@ export type operations = {
    *
    * **Credential required**: *No* / **Permission**: *read:account*
    */
-  'users/lists/show': {
+  users___lists___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -24972,7 +24972,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/favorite': {
+  users___lists___favorite: {
     requestBody: {
       content: {
         'application/json': {
@@ -25024,7 +25024,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/unfavorite': {
+  users___lists___unfavorite: {
     requestBody: {
       content: {
         'application/json': {
@@ -25076,7 +25076,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/update': {
+  users___lists___update: {
     requestBody: {
       content: {
         'application/json': {
@@ -25132,7 +25132,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/create-from-public': {
+  'users___lists___create-from-public': {
     requestBody: {
       content: {
         'application/json': {
@@ -25187,7 +25187,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/lists/update-membership': {
+  'users___lists___update-membership': {
     requestBody: {
       content: {
         'application/json': {
@@ -25242,7 +25242,7 @@ export type operations = {
    *
    * **Credential required**: *No* / **Permission**: *read:account*
    */
-  'users/lists/get-memberships': {
+  'users___lists___get-memberships': {
     requestBody: {
       content: {
         'application/json': {
@@ -25313,7 +25313,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/notes': {
+  users___notes: {
     requestBody: {
       content: {
         'application/json': {
@@ -25385,7 +25385,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/pages': {
+  users___pages: {
     requestBody: {
       content: {
         'application/json': {
@@ -25445,7 +25445,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/flashs': {
+  users___flashs: {
     requestBody: {
       content: {
         'application/json': {
@@ -25505,7 +25505,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/reactions': {
+  users___reactions: {
     requestBody: {
       content: {
         'application/json': {
@@ -25567,7 +25567,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'users/recommendation': {
+  users___recommendation: {
     requestBody: {
       content: {
         'application/json': {
@@ -25623,7 +25623,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'users/relation': {
+  users___relation: {
     requestBody: {
       content: {
         'application/json': {
@@ -25698,7 +25698,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:report-abuse*
    */
-  'users/report-abuse': {
+  'users___report-abuse': {
     requestBody: {
       content: {
         'application/json': {
@@ -25751,7 +25751,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/search-by-username-and-host': {
+  'users___search-by-username-and-host': {
     requestBody: {
       content: {
         'application/json': {
@@ -25809,7 +25809,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/search': {
+  users___search: {
     requestBody: {
       content: {
         'application/json': {
@@ -25873,7 +25873,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/show': {
+  users___show: {
     requestBody: {
       content: {
         'application/json': {
@@ -25931,7 +25931,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'users/achievements': {
+  users___achievements: {
     requestBody: {
       content: {
         'application/json': {
@@ -25988,7 +25988,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'users/update-memo': {
+  'users___update-memo': {
     requestBody: {
       content: {
         'application/json': {
@@ -26214,7 +26214,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'bubble-game/register': {
+  'bubble-game___register': {
     requestBody: {
       content: {
         'application/json': {
@@ -26275,7 +26275,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'bubble-game/ranking': {
+  'bubble-game___ranking': {
     requestBody: {
       content: {
         'application/json': {
@@ -26333,7 +26333,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'reversi/cancel-match': {
+  'reversi___cancel-match': {
     requestBody: {
       content: {
         'application/json': {
@@ -26385,7 +26385,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'reversi/games': {
+  reversi___games: {
     requestBody: {
       content: {
         'application/json': {
@@ -26445,7 +26445,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'reversi/match': {
+  reversi___match: {
     requestBody: {
       content: {
         'application/json': {
@@ -26507,7 +26507,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *read:account*
    */
-  'reversi/invitations': {
+  reversi___invitations: {
     responses: {
       /** @description OK (with results) */
       200: {
@@ -26553,7 +26553,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'reversi/show-game': {
+  'reversi___show-game': {
     requestBody: {
       content: {
         'application/json': {
@@ -26607,7 +26607,7 @@ export type operations = {
    *
    * **Credential required**: *Yes* / **Permission**: *write:account*
    */
-  'reversi/surrender': {
+  reversi___surrender: {
     requestBody: {
       content: {
         'application/json': {
@@ -26659,7 +26659,7 @@ export type operations = {
    *
    * **Credential required**: *No*
    */
-  'reversi/verify': {
+  reversi___verify: {
     requestBody: {
       content: {
         'application/json': {

From f90be427f51392ef3ed5a7eb7f35059274bb47fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 25 Mar 2024 18:31:30 +0900
Subject: [PATCH 128/266] =?UTF-8?q?fix(frontend):=20=E3=80=8C=E4=BB=8A?=
 =?UTF-8?q?=E6=97=A5=E8=AA=95=E7=94=9F=E6=97=A5=E3=81=AE=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=AD=E3=83=BC=E4=B8=AD=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?=
 =?UTF-8?q?=E3=80=8D=E3=82=A6=E3=82=A3=E3=82=B8=E3=82=A7=E3=83=83=E3=83=88?=
 =?UTF-8?q?=E3=81=8C=E6=AD=A3=E3=81=97=E3=81=8F=E5=8B=95=E4=BD=9C=E3=81=97?=
 =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12835)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (fix) タイムゾーンによっては誕生日のフォロー中ユーザーが正しく読み込まれない

* 文言をわかりやすく

* Update Changelog

* (add) reload button

* Update CHANGELOG.md

* run misskey-js

* fix

* Revert "文言をわかりやすく"

This reverts commit c5ab6419563cc70ec8ba758e800c74d3469131e3.

* Update packages/frontend/src/widgets/WidgetBirthdayFollowings.vue

* Update packages/frontend/src/widgets/WidgetBirthdayFollowings.vue

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  2 ++
 .../server/api/endpoints/users/following.ts   |  7 ++--
 .../src/widgets/WidgetBirthdayFollowings.vue  | 35 ++++++++++++++-----
 3 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d90b1425c13d..f41ff2171f1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@
   - 実装の都合により、プラグインは1つエラーを起こした時に即時停止するようになりました
 - Enhance: ページのデザインを変更
 - Enhance: 2要素認証(ワンタイムパスワード)の入力欄を改善
+- Enhance: 「今日誕生日のフォロー中ユーザー」ウィジェットを手動でリロードできるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
@@ -27,6 +28,7 @@
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/528)
 - Fix: コードブロックのシンタックスハイライトで使用される定義ファイルをCDNから取得するように #13177
   - CDNから取得せずMisskey本体にバンドルする場合は`pacakges/frontend/vite.config.ts`を修正してください。
+- Fix: タイムゾーンによっては、「今日誕生日のフォロー中ユーザー」ウィジェットが正しく動作しない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index 5d52ebba7660..6b3389f0b242 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -6,6 +6,7 @@
 import { IsNull } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
 import type { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/_.js';
+import { birthdaySchema } from '@/models/User.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { QueryService } from '@/core/QueryService.js';
 import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js';
@@ -66,7 +67,7 @@ export const paramDef = {
 			description: 'The local host is represented with `null`.',
 		},
 
-		birthday: { type: 'string', nullable: true },
+		birthday: { ...birthdaySchema, nullable: true },
 	},
 	anyOf: [
 		{ required: ['userId'] },
@@ -127,9 +128,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			if (ps.birthday) {
 				try {
-					const d = new Date(ps.birthday);
-					d.setHours(0, 0, 0, 0);
-					const birthday = `${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
+					const birthday = ps.birthday.substring(5, 10);
 					const birthdayUserQuery = this.userProfilesRepository.createQueryBuilder('user_profile');
 					birthdayUserQuery.select('user_profile.userId')
 						.where(`SUBSTR(user_profile.birthday, 6, 5) = '${birthday}'`);
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
index 5b448e2c3bed..49fd103d37eb 100644
--- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
@@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkContainer :showHeader="widgetProps.showHeader" class="mkw-bdayfollowings">
 	<template #icon><i class="ti ti-cake"></i></template>
 	<template #header>{{ i18n.ts._widgets.birthdayFollowings }}</template>
+	<template #func="{ buttonStyleClass }"><button class="_button" :class="buttonStyleClass" @click="actualFetch()"><i class="ti ti-refresh"></i></button></template>
 
 	<div :class="$style.bdayFRoot">
 		<MkLoading v-if="fetching"/>
@@ -53,7 +54,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-const users = ref<Misskey.entities.FollowingFolloweePopulated[]>([]);
+const users = ref<Misskey.Endpoints['users/following']['res']>([]);
 const fetching = ref(true);
 let lastFetchedAt = '1970-01-01';
 
@@ -70,19 +71,35 @@ const fetch = () => {
 	now.setHours(0, 0, 0, 0);
 
 	if (now > lfAtD) {
-		misskeyApi('users/following', {
-			limit: 18,
-			birthday: now.toISOString(),
-			userId: $i.id,
-		}).then(res => {
-			users.value = res;
-			fetching.value = false;
-		});
+		actualFetch();
 
 		lastFetchedAt = now.toISOString();
 	}
 };
 
+function actualFetch() {
+	if ($i == null) {
+		users.value = [];
+		fetching.value = false;
+		return;
+	}
+
+	const now = new Date();
+	now.setHours(0, 0, 0, 0);
+	fetching.value = true;
+	misskeyApi('users/following', {
+		limit: 18,
+		birthday: `${now.getFullYear().toString().padStart(4, '0')}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`,
+		userId: $i.id,
+	}).then(res => {
+		users.value = res;
+		window.setTimeout(() => {
+			// 早すぎるとチカチカする
+			fetching.value = false;
+		}, 100);
+	});
+}
+
 useInterval(fetch, 1000 * 60, {
 	immediate: true,
 	afterMounted: true,

From f3500ffda96913e41708a6ca04ef9bbf07af74e4 Mon Sep 17 00:00:00 2001
From: Nila <43315617+nilathedragon@users.noreply.github.com>
Date: Sat, 30 Mar 2024 02:28:47 +0100
Subject: [PATCH 129/266] fix: report progress out of 100% in
 CleanRemoteFilesProcessorService (#13633)

* Report progress out of 100% in CleanRemoteFilesProcessorService

* Add changelog entry
---
 CHANGELOG.md                                                    | 1 +
 .../src/queue/processors/CleanRemoteFilesProcessorService.ts    | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f41ff2171f1f..3cfbb5f9c8a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 - Enhance: misskey-dev/summaly@5.1.0の取り込み(プレビュー生成処理の効率化)
 - Fix: フォローリクエストを作成する際に既存のものは削除するように  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
+- Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts
index 917de8b72c13..728fc9e72b77 100644
--- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts
+++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts
@@ -63,7 +63,7 @@ export class CleanRemoteFilesProcessorService {
 				isLink: false,
 			});
 
-			job.updateProgress(deletedCount / total);
+			job.updateProgress(100 / total * deletedCount);
 		}
 
 		this.logger.succ('All cached remote files has been deleted.');

From b35ae97ba7b57ae2b04eb0cc25dd3360e321e537 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 30 Mar 2024 13:51:53 +0900
Subject: [PATCH 130/266] fix(backend): better `notes/translate` error response
 (#13631)

* fix(backend): better `notes/translate` error response

* Update CHANGELOG.md

* test(backend): perform administrative operations as `root`

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 .../server/api/endpoints/notes/translate.ts   |  13 ++-
 packages/backend/test/e2e/note.ts             | 107 ++++++++++++++----
 packages/misskey-js/src/autogen/types.ts      |   4 +
 4 files changed, 97 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cfbb5f9c8a3..5963549cc654 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 - Enhance: misskey-dev/summaly@5.1.0の取り込み(プレビュー生成処理の効率化)
 - Fix: フォローリクエストを作成する際に既存のものは削除するように  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
+- Fix: エンドポイント`notes/translate`のエラーを改善
 - Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
 
 ## 2024.3.1
diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts
index 78812351f498..38a9660aa239 100644
--- a/packages/backend/src/server/api/endpoints/notes/translate.ts
+++ b/packages/backend/src/server/api/endpoints/notes/translate.ts
@@ -21,7 +21,7 @@ export const meta = {
 
 	res: {
 		type: 'object',
-		optional: false, nullable: false,
+		optional: true, nullable: false,
 		properties: {
 			sourceLang: { type: 'string' },
 			text: { type: 'string' },
@@ -39,6 +39,11 @@ export const meta = {
 			code: 'NO_SUCH_NOTE',
 			id: 'bea9b03f-36e0-49c5-a4db-627a029f8971',
 		},
+		cannotTranslateInvisibleNote: {
+			message: 'Cannot translate invisible note.',
+			code: 'CANNOT_TRANSLATE_INVISIBLE_NOTE',
+			id: 'ea29f2ca-c368-43b3-aaf1-5ac3e74bbe5d',
+		},
 	},
 } as const;
 
@@ -72,17 +77,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			});
 
 			if (!(await this.noteEntityService.isVisibleForMe(note, me.id))) {
-				return 204; // TODO: 良い感じのエラー返す
+				throw new ApiError(meta.errors.cannotTranslateInvisibleNote);
 			}
 
 			if (note.text == null) {
-				return 204;
+				return;
 			}
 
 			const instance = await this.metaService.fetch();
 
 			if (instance.deeplAuthKey == null) {
-				return 204; // TODO: 良い感じのエラー返す
+				throw new ApiError(meta.errors.unavailable);
 			}
 
 			let targetLang = ps.targetLang;
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 11016f58aef3..bda31d964074 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -8,12 +8,13 @@ process.env.NODE_ENV = 'test';
 import * as assert from 'assert';
 import { MiNote } from '@/models/Note.js';
 import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
-import { api, initTestDb, post, signup, uploadFile, uploadUrl } from '../utils.js';
+import { api, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Note', () => {
 	let Notes: any;
 
+	let root: misskey.entities.SignupResponse;
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let tom: misskey.entities.SignupResponse;
@@ -21,6 +22,7 @@ describe('Note', () => {
 	beforeAll(async () => {
 		const connection = await initTestDb(true);
 		Notes = connection.getRepository(MiNote);
+		root = await signup({ username: 'root' });
 		alice = await signup({ username: 'alice' });
 		bob = await signup({ username: 'bob' });
 		tom = await signup({ username: 'tom', host: 'example.com' });
@@ -473,14 +475,14 @@ describe('Note', () => {
 						value: true,
 					},
 				} as any,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(res.status, 200);
 
 			const assign = await api('admin/roles/assign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(assign.status, 204);
 			assert.strictEqual(file.body!.isSensitive, false);
@@ -508,11 +510,11 @@ describe('Note', () => {
 			await api('admin/roles/unassign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			});
+			}, root);
 
 			await api('admin/roles/delete', {
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 		});
 	});
 
@@ -644,7 +646,7 @@ describe('Note', () => {
 				sensitiveWords: [
 					'test',
 				],
-			}, alice);
+			}, root);
 
 			assert.strictEqual(sensitive.status, 204);
 
@@ -663,7 +665,7 @@ describe('Note', () => {
 				sensitiveWords: [
 					'/Test/i',
 				],
-			}, alice);
+			}, root);
 
 			assert.strictEqual(sensitive.status, 204);
 
@@ -680,7 +682,7 @@ describe('Note', () => {
 				sensitiveWords: [
 					'Test hoge',
 				],
-			}, alice);
+			}, root);
 
 			assert.strictEqual(sensitive.status, 204);
 
@@ -697,7 +699,7 @@ describe('Note', () => {
 				prohibitedWords: [
 					'test',
 				],
-			}, alice);
+			}, root);
 
 			assert.strictEqual(prohibited.status, 204);
 
@@ -716,7 +718,7 @@ describe('Note', () => {
 				prohibitedWords: [
 					'/Test/i',
 				],
-			}, alice);
+			}, root);
 
 			assert.strictEqual(prohibited.status, 204);
 
@@ -733,7 +735,7 @@ describe('Note', () => {
 				prohibitedWords: [
 					'Test hoge',
 				],
-			}, alice);
+			}, root);
 
 			assert.strictEqual(prohibited.status, 204);
 
@@ -750,7 +752,7 @@ describe('Note', () => {
 				prohibitedWords: [
 					'test',
 				],
-			}, alice);
+			}, root);
 
 			assert.strictEqual(prohibited.status, 204);
 
@@ -785,7 +787,7 @@ describe('Note', () => {
 						value: 0,
 					},
 				} as any,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(res.status, 200);
 
@@ -794,7 +796,7 @@ describe('Note', () => {
 			const assign = await api('admin/roles/assign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(assign.status, 204);
 
@@ -810,11 +812,11 @@ describe('Note', () => {
 			await api('admin/roles/unassign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			});
+			}, root);
 
 			await api('admin/roles/delete', {
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 		});
 
 		test('ダイレクト投稿もエラーになる', async () => {
@@ -839,7 +841,7 @@ describe('Note', () => {
 						value: 0,
 					},
 				} as any,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(res.status, 200);
 
@@ -848,7 +850,7 @@ describe('Note', () => {
 			const assign = await api('admin/roles/assign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(assign.status, 204);
 
@@ -866,11 +868,11 @@ describe('Note', () => {
 			await api('admin/roles/unassign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			});
+			}, root);
 
 			await api('admin/roles/delete', {
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 		});
 
 		test('ダイレクトの宛先とメンションが同じ場合は重複してカウントしない', async () => {
@@ -895,7 +897,7 @@ describe('Note', () => {
 						value: 1,
 					},
 				} as any,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(res.status, 200);
 
@@ -904,7 +906,7 @@ describe('Note', () => {
 			const assign = await api('admin/roles/assign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 
 			assert.strictEqual(assign.status, 204);
 
@@ -921,11 +923,11 @@ describe('Note', () => {
 			await api('admin/roles/unassign', {
 				userId: alice.id,
 				roleId: res.body.id,
-			});
+			}, root);
 
 			await api('admin/roles/delete', {
 				roleId: res.body.id,
-			}, alice);
+			}, root);
 		});
 	});
 
@@ -960,4 +962,61 @@ describe('Note', () => {
 			assert.strictEqual(mainNote.repliesCount, 0);
 		});
 	});
+
+	describe('notes/translate', () => {
+		describe('翻訳機能の利用が許可されていない場合', () => {
+			let cannotTranslateRole: misskey.entities.Role;
+
+			beforeAll(async () => {
+				cannotTranslateRole = await role(root, {}, { canUseTranslator: false });
+				await api('admin/roles/assign', { roleId: cannotTranslateRole.id, userId: alice.id }, root);
+			});
+
+			test('翻訳機能の利用が許可されていない場合翻訳できない', async () => {
+				const aliceNote = await post(alice, { text: 'Hello' });
+				const res = await api('notes/translate', {
+					noteId: aliceNote.id,
+					targetLang: 'ja',
+				}, alice);
+
+				assert.strictEqual(res.status, 400);
+				assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
+			});
+
+			afterAll(async () => {
+				await api('admin/roles/unassign', { roleId: cannotTranslateRole.id, userId: alice.id }, root);
+			});
+		});
+
+		test('存在しないノートは翻訳できない', async () => {
+			const res = await api('notes/translate', { noteId: 'foo', targetLang: 'ja' }, alice);
+
+			assert.strictEqual(res.status, 400);
+			assert.strictEqual(res.body.error.code, 'NO_SUCH_NOTE');
+		});
+
+		test('不可視なノートは翻訳できない', async () => {
+			const aliceNote = await post(alice, { visibility: 'followers', text: 'Hello' });
+			const bobTranslateAttempt = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, bob);
+
+			assert.strictEqual(bobTranslateAttempt.status, 400);
+			assert.strictEqual(bobTranslateAttempt.body.error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE');
+		});
+
+		test('text: null なノートを翻訳すると空のレスポンスが返ってくる', async () => {
+			const aliceNote = await post(alice, { text: null, poll: { choices: ['kinoko', 'takenoko'] } });
+			const res = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, alice);
+
+			assert.strictEqual(res.status, 204);
+		});
+
+		test('サーバーに DeepL 認証キーが登録されていない場合翻訳できない', async () => {
+			const aliceNote = await post(alice, { text: 'Hello' });
+			const res = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, alice);
+
+			// NOTE: デフォルトでは登録されていないので落ちる
+			assert.strictEqual(res.status, 400);
+			assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
+		});
+	});
 });
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 4b2ad16b0fcb..b6b26c000cdf 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -21864,6 +21864,10 @@ export type operations = {
           };
         };
       };
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
       /** @description Client error */
       400: {
         content: {

From 2a851437ffcd8778d157a6841875f03330c994b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 30 Mar 2024 15:28:19 +0900
Subject: [PATCH 131/266] =?UTF-8?q?fix:=20misskey-js=E3=80=81bubble-game?=
 =?UTF-8?q?=E3=80=81reversi=E3=81=AE=E3=83=93=E3=83=AB=E3=83=89=E3=82=92es?=
 =?UTF-8?q?build=E3=81=AB=E7=B5=B1=E5=90=88=E3=81=99=E3=82=8B=20(#13600)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: ビルドが遅いパッケージのビルド速度を改善

* dependenciesの整理

* fix ci

* ビルド開始時に古いファイルを消す

* fix ci

* fix ci
---
 .github/workflows/lint.yml                 |   2 +-
 package.json                               |   4 +-
 packages/backend/.swcrc                    |   3 +-
 packages/misskey-bubble-game/.eslintignore |   1 +
 packages/misskey-bubble-game/build.js      | 114 ++++++++++---
 packages/misskey-bubble-game/package.json  |  26 ++-
 packages/misskey-bubble-game/src/index.ts  |   6 +-
 packages/misskey-bubble-game/tsconfig.json |   2 +-
 packages/misskey-js/.eslintignore          |   1 +
 packages/misskey-js/api-extractor.json     |   2 +-
 packages/misskey-js/build.js               | 105 ++++++++++++
 packages/misskey-js/package.json           |  29 ++--
 packages/misskey-js/src/api.ts             |   2 +-
 packages/misskey-js/src/index.ts           |  15 +-
 packages/misskey-js/tsconfig.json          |   2 +-
 packages/misskey-reversi/.eslintignore     |   1 +
 packages/misskey-reversi/build.js          | 114 ++++++++++---
 packages/misskey-reversi/package.json      |  26 ++-
 packages/misskey-reversi/tsconfig.json     |   2 +-
 pnpm-lock.yaml                             | 188 ++++-----------------
 scripts/dev.mjs                            |  59 +++----
 21 files changed, 422 insertions(+), 282 deletions(-)
 create mode 100644 packages/misskey-js/build.js

diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 31e974edaa8c..9b3f85fe1db9 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -92,6 +92,6 @@ jobs:
     - run: pnpm i --frozen-lockfile
     - run: pnpm --filter misskey-js run build
       if: ${{ matrix.workspace == 'backend' }}
-    - run: pnpm --filter misskey-reversi run build:tsc
+    - run: pnpm --filter misskey-reversi run build
       if: ${{ matrix.workspace == 'backend' }}
     - run: pnpm --filter ${{ matrix.workspace }} run typecheck
diff --git a/package.json b/package.json
index 8f5ab0b12425..84d6db5124a3 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,9 @@
 		"postcss": "8.4.35",
 		"tar": "6.2.0",
 		"terser": "5.28.1",
-		"typescript": "5.3.3"
+		"typescript": "5.3.3",
+		"esbuild": "0.19.11",
+		"glob": "10.3.10"
 	},
 	"devDependencies": {
 		"@types/node": "^20.11.28",
diff --git a/packages/backend/.swcrc b/packages/backend/.swcrc
index 0504a2d38933..845190b5f419 100644
--- a/packages/backend/.swcrc
+++ b/packages/backend/.swcrc
@@ -19,5 +19,6 @@
 		},
 		"target": "es2022"
 	},
-	"minify": false
+	"minify": false,
+	"sourceMaps": "inline"
 }
diff --git a/packages/misskey-bubble-game/.eslintignore b/packages/misskey-bubble-game/.eslintignore
index f22128f047fd..52ea8b3362e9 100644
--- a/packages/misskey-bubble-game/.eslintignore
+++ b/packages/misskey-bubble-game/.eslintignore
@@ -5,3 +5,4 @@ node_modules
 /jest.config.ts
 /test
 /test-d
+build.js
diff --git a/packages/misskey-bubble-game/build.js b/packages/misskey-bubble-game/build.js
index 4744dfaf7b44..0b79f4b9159b 100644
--- a/packages/misskey-bubble-game/build.js
+++ b/packages/misskey-bubble-game/build.js
@@ -1,31 +1,105 @@
+import * as esbuild from "esbuild";
 import { build } from "esbuild";
 import { globSync } from "glob";
+import { execa } from "execa";
+import fs from "node:fs";
+import { fileURLToPath } from "node:url";
+import { dirname } from "node:path";
+
+const _filename = fileURLToPath(import.meta.url);
+const _dirname = dirname(_filename);
+const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
 
 const entryPoints = globSync("./src/**/**.{ts,tsx}");
 
 /** @type {import('esbuild').BuildOptions} */
 const options = {
-  entryPoints,
-  minify: true,
-  outdir: "./built/esm",
-  target: "es2022",
-  platform: "browser",
-  format: "esm",
+	entryPoints,
+	minify: process.env.NODE_ENV === 'production',
+	outdir: "./built",
+	target: "es2022",
+	platform: "browser",
+	format: "esm",
+	sourcemap: 'linked',
 };
 
-if (process.env.WATCH === "true") {
-  options.watch = {
-    onRebuild(error, result) {
-      if (error) {
-        console.error("watch build failed:", error);
-      } else {
-        console.log("watch build succeeded:", result);
-      }
-    },
-  };
+// built配下をすべて削除する
+fs.rmSync('./built', { recursive: true, force: true });
+
+if (process.argv.map(arg => arg.toLowerCase()).includes("--watch")) {
+	await watchSrc();
+} else {
+	await buildSrc();
+}
+
+async function buildSrc() {
+	console.log(`[${_package.name}] start building...`);
+
+	await build(options)
+		.then(it => {
+			console.log(`[${_package.name}] build succeeded.`);
+		})
+		.catch((err) => {
+			process.stderr.write(err.stderr);
+			process.exit(1);
+		});
+
+	if (process.env.NODE_ENV === 'production') {
+		console.log(`[${_package.name}] skip building d.ts because NODE_ENV is production.`);
+	} else {
+		await buildDts();
+	}
+
+	console.log(`[${_package.name}] finish building.`);
 }
 
-build(options).catch((err) => {
-  process.stderr.write(err.stderr);
-  process.exit(1);
-});
+function buildDts() {
+	return execa(
+		'tsc',
+		[
+			'--project', 'tsconfig.json',
+			'--outDir', 'built',
+			'--declaration', 'true',
+			'--emitDeclarationOnly', 'true',
+		],
+		{
+			stdout: process.stdout,
+			stderr: process.stderr,
+		}
+	);
+}
+
+async function watchSrc() {
+	const plugins = [{
+		name: 'gen-dts',
+		setup(build) {
+			build.onStart(() => {
+				console.log(`[${_package.name}] detect changed...`);
+			});
+			build.onEnd(async result => {
+				if (result.errors.length > 0) {
+					console.error(`[${_package.name}] watch build failed:`, result);
+					return;
+				}
+				await buildDts();
+			});
+		},
+	}];
+
+	console.log(`[${_package.name}] start watching...`)
+
+	const context = await esbuild.context({ ...options, plugins });
+	await context.watch();
+
+	await new Promise((resolve, reject) => {
+		process.on('SIGHUP', resolve);
+		process.on('SIGINT', resolve);
+		process.on('SIGTERM', resolve);
+		process.on('SIGKILL', resolve);
+		process.on('uncaughtException', reject);
+		process.on('exit', resolve);
+	}).finally(async () => {
+		await context.dispose();
+		console.log(`[${_package.name}] finish watching.`);
+	});
+}
diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json
index ddc4c2134bcf..a3aad147a984 100644
--- a/packages/misskey-bubble-game/package.json
+++ b/packages/misskey-bubble-game/package.json
@@ -2,24 +2,21 @@
 	"type": "module",
 	"name": "misskey-bubble-game",
 	"version": "0.0.1",
-	"types": "./built/dts/index.d.ts",
+	"main": "./built/index.js",
+	"types": "./built/index.d.ts",
 	"exports": {
 		".": {
-			"import": "./built/esm/index.js",
-			"types": "./built/dts/index.d.ts"
+			"import": "./built/index.js",
+			"types": "./built/index.d.ts"
 		},
 		"./*": {
-			"import": "./built/esm/*",
-			"types": "./built/dts/*"
+			"import": "./built/*",
+			"types": "./built/*"
 		}
 	},
 	"scripts": {
 		"build": "node ./build.js",
-		"build:tsc": "npm run tsc",
-		"tsc": "npm run tsc-esm && npm run tsc-dts",
-		"tsc-esm": "tsc --outDir built/esm",
-		"tsc-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true",
-		"watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build:tsc\"",
+		"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
 		"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
 		"typecheck": "tsc --noEmit",
 		"lint": "pnpm typecheck && pnpm eslint"
@@ -27,21 +24,22 @@
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@types/matter-js": "0.19.6",
-		"@types/node": "20.11.5",
 		"@types/seedrandom": "3.0.8",
+		"@types/node": "20.11.5",
 		"@typescript-eslint/eslint-plugin": "7.1.0",
 		"@typescript-eslint/parser": "7.1.0",
 		"eslint": "8.57.0",
 		"nodemon": "3.0.2",
-		"typescript": "5.3.3"
+		"execa": "8.0.1",
+		"typescript": "5.3.3",
+		"esbuild": "0.19.11",
+		"glob": "10.3.10"
 	},
 	"files": [
 		"built"
 	],
 	"dependencies": {
-		"esbuild": "0.19.11",
 		"eventemitter3": "5.0.1",
-		"glob": "^10.3.10",
 		"matter-js": "0.19.0",
 		"seedrandom": "3.0.5"
 	}
diff --git a/packages/misskey-bubble-game/src/index.ts b/packages/misskey-bubble-game/src/index.ts
index 004a7d008ef4..c5f1f6806257 100644
--- a/packages/misskey-bubble-game/src/index.ts
+++ b/packages/misskey-bubble-game/src/index.ts
@@ -6,5 +6,9 @@
 import { DropAndFusionGame, Mono } from './game.js';
 
 export {
-	DropAndFusionGame, Mono,
+	DropAndFusionGame,
+};
+
+export type {
+	Mono,
 };
diff --git a/packages/misskey-bubble-game/tsconfig.json b/packages/misskey-bubble-game/tsconfig.json
index f56b65e86802..6e34e332e000 100644
--- a/packages/misskey-bubble-game/tsconfig.json
+++ b/packages/misskey-bubble-game/tsconfig.json
@@ -6,7 +6,7 @@
 		"moduleResolution": "nodenext",
 		"declaration": true,
 		"declarationMap": true,
-		"sourceMap": true,
+		"sourceMap": false,
 		"outDir": "./built/",
 		"removeComments": true,
 		"strict": true,
diff --git a/packages/misskey-js/.eslintignore b/packages/misskey-js/.eslintignore
index f22128f047fd..52ea8b3362e9 100644
--- a/packages/misskey-js/.eslintignore
+++ b/packages/misskey-js/.eslintignore
@@ -5,3 +5,4 @@ node_modules
 /jest.config.ts
 /test
 /test-d
+build.js
diff --git a/packages/misskey-js/api-extractor.json b/packages/misskey-js/api-extractor.json
index f80d0f20a821..a95281a6d559 100644
--- a/packages/misskey-js/api-extractor.json
+++ b/packages/misskey-js/api-extractor.json
@@ -45,7 +45,7 @@
    *
    * SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
    */
-  "mainEntryPointFilePath": "<projectFolder>/built/dts/index.d.ts",
+  "mainEntryPointFilePath": "<projectFolder>/built/index.d.ts",
 
   /**
    * A list of NPM package names whose exports should be treated as part of this package.
diff --git a/packages/misskey-js/build.js b/packages/misskey-js/build.js
new file mode 100644
index 000000000000..0b79f4b9159b
--- /dev/null
+++ b/packages/misskey-js/build.js
@@ -0,0 +1,105 @@
+import * as esbuild from "esbuild";
+import { build } from "esbuild";
+import { globSync } from "glob";
+import { execa } from "execa";
+import fs from "node:fs";
+import { fileURLToPath } from "node:url";
+import { dirname } from "node:path";
+
+const _filename = fileURLToPath(import.meta.url);
+const _dirname = dirname(_filename);
+const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
+
+const entryPoints = globSync("./src/**/**.{ts,tsx}");
+
+/** @type {import('esbuild').BuildOptions} */
+const options = {
+	entryPoints,
+	minify: process.env.NODE_ENV === 'production',
+	outdir: "./built",
+	target: "es2022",
+	platform: "browser",
+	format: "esm",
+	sourcemap: 'linked',
+};
+
+// built配下をすべて削除する
+fs.rmSync('./built', { recursive: true, force: true });
+
+if (process.argv.map(arg => arg.toLowerCase()).includes("--watch")) {
+	await watchSrc();
+} else {
+	await buildSrc();
+}
+
+async function buildSrc() {
+	console.log(`[${_package.name}] start building...`);
+
+	await build(options)
+		.then(it => {
+			console.log(`[${_package.name}] build succeeded.`);
+		})
+		.catch((err) => {
+			process.stderr.write(err.stderr);
+			process.exit(1);
+		});
+
+	if (process.env.NODE_ENV === 'production') {
+		console.log(`[${_package.name}] skip building d.ts because NODE_ENV is production.`);
+	} else {
+		await buildDts();
+	}
+
+	console.log(`[${_package.name}] finish building.`);
+}
+
+function buildDts() {
+	return execa(
+		'tsc',
+		[
+			'--project', 'tsconfig.json',
+			'--outDir', 'built',
+			'--declaration', 'true',
+			'--emitDeclarationOnly', 'true',
+		],
+		{
+			stdout: process.stdout,
+			stderr: process.stderr,
+		}
+	);
+}
+
+async function watchSrc() {
+	const plugins = [{
+		name: 'gen-dts',
+		setup(build) {
+			build.onStart(() => {
+				console.log(`[${_package.name}] detect changed...`);
+			});
+			build.onEnd(async result => {
+				if (result.errors.length > 0) {
+					console.error(`[${_package.name}] watch build failed:`, result);
+					return;
+				}
+				await buildDts();
+			});
+		},
+	}];
+
+	console.log(`[${_package.name}] start watching...`)
+
+	const context = await esbuild.context({ ...options, plugins });
+	await context.watch();
+
+	await new Promise((resolve, reject) => {
+		process.on('SIGHUP', resolve);
+		process.on('SIGINT', resolve);
+		process.on('SIGTERM', resolve);
+		process.on('SIGKILL', resolve);
+		process.on('uncaughtException', reject);
+		process.on('exit', resolve);
+	}).finally(async () => {
+		await context.dispose();
+		console.log(`[${_package.name}] finish watching.`);
+	});
+}
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 772f001c0734..a9c75c95c28e 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -3,23 +3,21 @@
 	"name": "misskey-js",
 	"version": "2024.3.1",
 	"description": "Misskey SDK for JavaScript",
-	"types": "./built/dts/index.d.ts",
+	"main": "./built/index.js",
+	"types": "./built/index.d.ts",
 	"exports": {
 		".": {
-			"import": "./built/esm/index.js",
-			"types": "./built/dts/index.d.ts"
+			"import": "./built/index.js",
+			"types": "./built/index.d.ts"
 		},
 		"./*": {
-			"import": "./built/esm/*",
-			"types": "./built/dts/*"
+			"import": "./built/*",
+			"types": "./built/*"
 		}
 	},
 	"scripts": {
-		"build": "npm run ts",
-		"ts": "npm run ts-esm && npm run ts-dts",
-		"ts-esm": "tsc --outDir built/esm",
-		"ts-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true",
-		"watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run ts\"",
+		"build": "node ./build.js",
+		"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
 		"tsd": "tsd",
 		"api": "pnpm api-extractor run --local --verbose",
 		"api-prod": "pnpm api-extractor run --verbose",
@@ -49,17 +47,16 @@
 		"mock-socket": "9.3.1",
 		"ncp": "2.0.0",
 		"nodemon": "3.1.0",
+		"execa": "8.0.1",
 		"tsd": "0.30.7",
-		"typescript": "5.3.3"
+		"typescript": "5.3.3",
+		"esbuild": "0.19.11",
+		"glob": "10.3.10"
 	},
 	"files": [
-		"built",
-		"built/esm",
-		"built/dts"
+		"built"
 	],
 	"dependencies": {
-		"@swc/cli": "0.1.63",
-		"@swc/core": "1.3.105",
 		"eventemitter3": "5.0.1",
 		"reconnecting-websocket": "4.4.0"
 	}
diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts
index 134ead0d7959..959a634a743f 100644
--- a/packages/misskey-js/src/api.ts
+++ b/packages/misskey-js/src/api.ts
@@ -3,7 +3,7 @@ import './autogen/apiClientJSDoc.js';
 import { SwitchCaseResponseType } from './api.types.js';
 import type { Endpoints } from './api.types.js';
 
-export {
+export type {
 	SwitchCaseResponseType,
 } from './api.types.js';
 
diff --git a/packages/misskey-js/src/index.ts b/packages/misskey-js/src/index.ts
index 54cae8ec0332..28007a8ade3c 100644
--- a/packages/misskey-js/src/index.ts
+++ b/packages/misskey-js/src/index.ts
@@ -1,17 +1,20 @@
-import { Endpoints } from './api.types.js';
+import { type Endpoints } from './api.types.js';
 import Stream, { Connection } from './streaming.js';
-import { Channels } from './streaming.types.js';
-import { Acct } from './acct.js';
+import { type Channels } from './streaming.types.js';
+import { type Acct } from './acct.js';
 import * as consts from './consts.js';
 
-export {
+export type {
 	Endpoints,
-	Stream,
-	Connection as ChannelConnection,
 	Channels,
 	Acct,
 };
 
+export {
+	Stream,
+	Connection as ChannelConnection,
+};
+
 export const permissions = consts.permissions;
 export const notificationTypes = consts.notificationTypes;
 export const noteVisibilities = consts.noteVisibilities;
diff --git a/packages/misskey-js/tsconfig.json b/packages/misskey-js/tsconfig.json
index f56b65e86802..6e34e332e000 100644
--- a/packages/misskey-js/tsconfig.json
+++ b/packages/misskey-js/tsconfig.json
@@ -6,7 +6,7 @@
 		"moduleResolution": "nodenext",
 		"declaration": true,
 		"declarationMap": true,
-		"sourceMap": true,
+		"sourceMap": false,
 		"outDir": "./built/",
 		"removeComments": true,
 		"strict": true,
diff --git a/packages/misskey-reversi/.eslintignore b/packages/misskey-reversi/.eslintignore
index f22128f047fd..52ea8b3362e9 100644
--- a/packages/misskey-reversi/.eslintignore
+++ b/packages/misskey-reversi/.eslintignore
@@ -5,3 +5,4 @@ node_modules
 /jest.config.ts
 /test
 /test-d
+build.js
diff --git a/packages/misskey-reversi/build.js b/packages/misskey-reversi/build.js
index 4744dfaf7b44..0b79f4b9159b 100644
--- a/packages/misskey-reversi/build.js
+++ b/packages/misskey-reversi/build.js
@@ -1,31 +1,105 @@
+import * as esbuild from "esbuild";
 import { build } from "esbuild";
 import { globSync } from "glob";
+import { execa } from "execa";
+import fs from "node:fs";
+import { fileURLToPath } from "node:url";
+import { dirname } from "node:path";
+
+const _filename = fileURLToPath(import.meta.url);
+const _dirname = dirname(_filename);
+const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
 
 const entryPoints = globSync("./src/**/**.{ts,tsx}");
 
 /** @type {import('esbuild').BuildOptions} */
 const options = {
-  entryPoints,
-  minify: true,
-  outdir: "./built/esm",
-  target: "es2022",
-  platform: "browser",
-  format: "esm",
+	entryPoints,
+	minify: process.env.NODE_ENV === 'production',
+	outdir: "./built",
+	target: "es2022",
+	platform: "browser",
+	format: "esm",
+	sourcemap: 'linked',
 };
 
-if (process.env.WATCH === "true") {
-  options.watch = {
-    onRebuild(error, result) {
-      if (error) {
-        console.error("watch build failed:", error);
-      } else {
-        console.log("watch build succeeded:", result);
-      }
-    },
-  };
+// built配下をすべて削除する
+fs.rmSync('./built', { recursive: true, force: true });
+
+if (process.argv.map(arg => arg.toLowerCase()).includes("--watch")) {
+	await watchSrc();
+} else {
+	await buildSrc();
+}
+
+async function buildSrc() {
+	console.log(`[${_package.name}] start building...`);
+
+	await build(options)
+		.then(it => {
+			console.log(`[${_package.name}] build succeeded.`);
+		})
+		.catch((err) => {
+			process.stderr.write(err.stderr);
+			process.exit(1);
+		});
+
+	if (process.env.NODE_ENV === 'production') {
+		console.log(`[${_package.name}] skip building d.ts because NODE_ENV is production.`);
+	} else {
+		await buildDts();
+	}
+
+	console.log(`[${_package.name}] finish building.`);
 }
 
-build(options).catch((err) => {
-  process.stderr.write(err.stderr);
-  process.exit(1);
-});
+function buildDts() {
+	return execa(
+		'tsc',
+		[
+			'--project', 'tsconfig.json',
+			'--outDir', 'built',
+			'--declaration', 'true',
+			'--emitDeclarationOnly', 'true',
+		],
+		{
+			stdout: process.stdout,
+			stderr: process.stderr,
+		}
+	);
+}
+
+async function watchSrc() {
+	const plugins = [{
+		name: 'gen-dts',
+		setup(build) {
+			build.onStart(() => {
+				console.log(`[${_package.name}] detect changed...`);
+			});
+			build.onEnd(async result => {
+				if (result.errors.length > 0) {
+					console.error(`[${_package.name}] watch build failed:`, result);
+					return;
+				}
+				await buildDts();
+			});
+		},
+	}];
+
+	console.log(`[${_package.name}] start watching...`)
+
+	const context = await esbuild.context({ ...options, plugins });
+	await context.watch();
+
+	await new Promise((resolve, reject) => {
+		process.on('SIGHUP', resolve);
+		process.on('SIGINT', resolve);
+		process.on('SIGTERM', resolve);
+		process.on('SIGKILL', resolve);
+		process.on('uncaughtException', reject);
+		process.on('exit', resolve);
+	}).finally(async () => {
+		await context.dispose();
+		console.log(`[${_package.name}] finish watching.`);
+	});
+}
diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json
index 7bfc890fef85..45a61208610d 100644
--- a/packages/misskey-reversi/package.json
+++ b/packages/misskey-reversi/package.json
@@ -2,24 +2,21 @@
 	"type": "module",
 	"name": "misskey-reversi",
 	"version": "0.0.1",
-	"types": "./built/dts/index.d.ts",
+	"main": "./built/index.js",
+	"types": "./built/index.d.ts",
 	"exports": {
 		".": {
-			"import": "./built/esm/index.js",
-			"types": "./built/dts/index.d.ts"
+			"import": "./built/index.js",
+			"types": "./built/index.d.ts"
 		},
 		"./*": {
-			"import": "./built/esm/*",
-			"types": "./built/dts/*"
+			"import": "./built/*",
+			"types": "./built/*"
 		}
 	},
 	"scripts": {
 		"build": "node ./build.js",
-		"build:tsc": "npm run tsc",
-		"tsc": "npm run tsc-esm && npm run tsc-dts",
-		"tsc-esm": "tsc --outDir built/esm",
-		"tsc-dts": "tsc --outDir built/dts --declaration true --emitDeclarationOnly true --declarationMap true",
-		"watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build:tsc\"",
+		"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
 		"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
 		"typecheck": "tsc --noEmit",
 		"lint": "pnpm typecheck && pnpm eslint"
@@ -30,15 +27,16 @@
 		"@typescript-eslint/eslint-plugin": "7.1.0",
 		"@typescript-eslint/parser": "7.1.0",
 		"eslint": "8.57.0",
+		"execa": "8.0.1",
 		"nodemon": "3.0.2",
-		"typescript": "5.3.3"
+		"typescript": "5.3.3",
+		"esbuild": "0.19.11",
+		"glob": "10.3.10"
 	},
 	"files": [
 		"built"
 	],
 	"dependencies": {
-		"crc-32": "1.2.2",
-		"esbuild": "0.19.11",
-		"glob": "10.3.10"
+		"crc-32": "1.2.2"
 	}
 }
diff --git a/packages/misskey-reversi/tsconfig.json b/packages/misskey-reversi/tsconfig.json
index f56b65e86802..6e34e332e000 100644
--- a/packages/misskey-reversi/tsconfig.json
+++ b/packages/misskey-reversi/tsconfig.json
@@ -6,7 +6,7 @@
 		"moduleResolution": "nodenext",
 		"declaration": true,
 		"declarationMap": true,
-		"sourceMap": true,
+		"sourceMap": false,
 		"outDir": "./built/",
 		"removeComments": true,
 		"strict": true,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 383b31b1f361..46512784c3ab 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -15,12 +15,18 @@ importers:
       cssnano:
         specifier: 6.0.5
         version: 6.0.5(postcss@8.4.35)
+      esbuild:
+        specifier: 0.19.11
+        version: 0.19.11
       execa:
         specifier: 8.0.1
         version: 8.0.1
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
+      glob:
+        specifier: 10.3.10
+        version: 10.3.10
       ignore-walk:
         specifier: 6.0.4
         version: 6.0.4
@@ -1037,15 +1043,9 @@ importers:
 
   packages/misskey-bubble-game:
     dependencies:
-      esbuild:
-        specifier: 0.19.11
-        version: 0.19.11
       eventemitter3:
         specifier: 5.0.1
         version: 5.0.1
-      glob:
-        specifier: ^10.3.10
-        version: 10.3.10
       matter-js:
         specifier: 0.19.0
         version: 0.19.0
@@ -1071,9 +1071,18 @@ importers:
       '@typescript-eslint/parser':
         specifier: 7.1.0
         version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      esbuild:
+        specifier: 0.19.11
+        version: 0.19.11
       eslint:
         specifier: 8.57.0
         version: 8.57.0
+      execa:
+        specifier: 8.0.1
+        version: 8.0.1
+      glob:
+        specifier: 10.3.10
+        version: 10.3.10
       nodemon:
         specifier: 3.0.2
         version: 3.0.2
@@ -1083,12 +1092,6 @@ importers:
 
   packages/misskey-js:
     dependencies:
-      '@swc/cli':
-        specifier: 0.1.63
-        version: 0.1.63(@swc/core@1.3.105)
-      '@swc/core':
-        specifier: 1.3.105
-        version: 1.3.105
       eventemitter3:
         specifier: 5.0.1
         version: 5.0.1
@@ -1104,7 +1107,7 @@ importers:
         version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       '@swc/jest':
         specifier: 0.2.31
-        version: 0.2.31(@swc/core@1.3.105)
+        version: 0.2.31(@swc/core@1.3.107)
       '@types/jest':
         specifier: 29.5.12
         version: 29.5.12
@@ -1117,9 +1120,18 @@ importers:
       '@typescript-eslint/parser':
         specifier: 7.1.0
         version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      esbuild:
+        specifier: 0.19.11
+        version: 0.19.11
       eslint:
         specifier: 8.57.0
         version: 8.57.0
+      execa:
+        specifier: 8.0.1
+        version: 8.0.1
+      glob:
+        specifier: 10.3.10
+        version: 10.3.10
       jest:
         specifier: 29.7.0
         version: 29.7.0(@types/node@20.11.22)
@@ -1186,12 +1198,6 @@ importers:
       crc-32:
         specifier: 1.2.2
         version: 1.2.2
-      esbuild:
-        specifier: 0.19.11
-        version: 0.19.11
-      glob:
-        specifier: 10.3.10
-        version: 10.3.10
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
@@ -1205,9 +1211,18 @@ importers:
       '@typescript-eslint/parser':
         specifier: 7.1.0
         version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      esbuild:
+        specifier: 0.19.11
+        version: 0.19.11
       eslint:
         specifier: 8.57.0
         version: 8.57.0
+      execa:
+        specifier: 8.0.1
+        version: 8.0.1
+      glob:
+        specifier: 10.3.10
+        version: 10.3.10
       nodemon:
         specifier: 3.0.2
         version: 3.0.2
@@ -6669,26 +6684,6 @@ packages:
       - supports-color
     dev: true
 
-  /@swc/cli@0.1.63(@swc/core@1.3.105):
-    resolution: {integrity: sha512-EM9oxxHzmmsprYRbGqsS2M4M/Gr5Gkcl0ROYYIdlUyTkhOiX822EQiRCpPCwdutdnzH2GyaTN7wc6i0Y+CKd3A==}
-    engines: {node: '>= 12.13'}
-    hasBin: true
-    peerDependencies:
-      '@swc/core': ^1.2.66
-      chokidar: 3.5.3
-    peerDependenciesMeta:
-      chokidar:
-        optional: true
-    dependencies:
-      '@mole-inc/bin-wrapper': 8.0.1
-      '@swc/core': 1.3.105
-      commander: 7.2.0
-      fast-glob: 3.3.2
-      semver: 7.5.4
-      slash: 3.0.0
-      source-map: 0.7.4
-    dev: false
-
   /@swc/cli@0.1.63(@swc/core@1.3.107)(chokidar@3.5.3):
     resolution: {integrity: sha512-EM9oxxHzmmsprYRbGqsS2M4M/Gr5Gkcl0ROYYIdlUyTkhOiX822EQiRCpPCwdutdnzH2GyaTN7wc6i0Y+CKd3A==}
     engines: {node: '>= 12.13'}
@@ -6721,14 +6716,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-arm64@1.3.105:
-    resolution: {integrity: sha512-buWeweLVDXXmcnfIemH4PGnpjwsDTUGitnPchdftb0u1FU8zSSP/lw/pUCBDG/XvWAp7c/aFxgN4CyG0j7eayA==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-darwin-arm64@1.3.107:
     resolution: {integrity: sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==}
     engines: {node: '>=10'}
@@ -6746,14 +6733,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-x64@1.3.105:
-    resolution: {integrity: sha512-hFmXPApqjA/8sy/9NpljHVaKi1OvL9QkJ2MbbTCCbJERuHMpMUeMBUWipHRfepGHFhU+9B9zkEup/qJaJR4XIg==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-darwin-x64@1.3.107:
     resolution: {integrity: sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==}
     engines: {node: '>=10'}
@@ -6782,14 +6761,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm-gnueabihf@1.3.105:
-    resolution: {integrity: sha512-mwXyMC41oMKkKrPpL8uJpOxw7fyfQoVtIw3Y5p0Blabk+espNYqix0E8VymHdRKuLmM//z5wVmMsuHdGBHvZeg==}
-    engines: {node: '>=10'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-linux-arm-gnueabihf@1.3.107:
     resolution: {integrity: sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==}
     engines: {node: '>=10'}
@@ -6807,14 +6778,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-gnu@1.3.105:
-    resolution: {integrity: sha512-H7yEIVydnUtqBSUxwmO6vpIQn7j+Rr0DF6ZOORPyd/SFzQJK9cJRtmJQ3ZMzlJ1Bb+1gr3MvjgLEnmyCYEm2Hg==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-linux-arm64-gnu@1.3.107:
     resolution: {integrity: sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==}
     engines: {node: '>=10'}
@@ -6832,14 +6795,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-musl@1.3.105:
-    resolution: {integrity: sha512-Jg7RTFT3pGFdGt5elPV6oDkinRy7q9cXpenjXnJnM2uvx3jOwnsAhexPyCDHom8SHL0j+9kaLLC66T3Gz1E4UA==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-linux-arm64-musl@1.3.107:
     resolution: {integrity: sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==}
     engines: {node: '>=10'}
@@ -6857,14 +6812,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-gnu@1.3.105:
-    resolution: {integrity: sha512-DJghplpyusAmp1X5pW/y93MmS/u83Sx5GrpJxI6KLPa82+NItTgMcl8KBQmW5GYAJpVKZyaIvBanS5TdR8aN2w==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-linux-x64-gnu@1.3.107:
     resolution: {integrity: sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==}
     engines: {node: '>=10'}
@@ -6882,14 +6829,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-musl@1.3.105:
-    resolution: {integrity: sha512-wD5jL2dZH/5nPNssBo6jhOvkI0lmWnVR4vnOXWjuXgjq1S0AJpO5jdre/6pYLmf26hft3M42bteDnjR4AAZ38w==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-linux-x64-musl@1.3.107:
     resolution: {integrity: sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==}
     engines: {node: '>=10'}
@@ -6907,14 +6846,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-arm64-msvc@1.3.105:
-    resolution: {integrity: sha512-UqJtwILUHRw2+3UTPnRkZrzM/bGdQtbR4UFdp79mZQYfryeOUVNg7aJj/bWUTkKtLiZ3o+FBNrM/x2X1mJX5bA==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-win32-arm64-msvc@1.3.107:
     resolution: {integrity: sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==}
     engines: {node: '>=10'}
@@ -6932,14 +6863,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-ia32-msvc@1.3.105:
-    resolution: {integrity: sha512-Z95C6vZgBEJ1snidYyjVKnVWiy/ZpPiIFIXGWkDr4ZyBgL3eZX12M6LzZ+NApHKffrbO4enbFyFomueBQgS2oA==}
-    engines: {node: '>=10'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-win32-ia32-msvc@1.3.107:
     resolution: {integrity: sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==}
     engines: {node: '>=10'}
@@ -6957,14 +6880,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-x64-msvc@1.3.105:
-    resolution: {integrity: sha512-3J8fkyDPFsS3mszuYUY4Wfk7/B2oio9qXUwF3DzOs2MK+XgdyMLIptIxL7gdfitXJBH8k39uVjrIw1JGJDjyFA==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-win32-x64-msvc@1.3.107:
     resolution: {integrity: sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==}
     engines: {node: '>=10'}
@@ -6982,30 +6897,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core@1.3.105:
-    resolution: {integrity: sha512-me2VZyr3OjqRpFrYQJJYy7x/zbFSl9nt+MAGnIcBtjDsN00iTVqEaKxBjPBFQV9BDAgPz2SRWes/DhhVm5SmMw==}
-    engines: {node: '>=10'}
-    requiresBuild: true
-    peerDependencies:
-      '@swc/helpers': ^0.5.0
-    peerDependenciesMeta:
-      '@swc/helpers':
-        optional: true
-    dependencies:
-      '@swc/counter': 0.1.1
-      '@swc/types': 0.1.5
-    optionalDependencies:
-      '@swc/core-darwin-arm64': 1.3.105
-      '@swc/core-darwin-x64': 1.3.105
-      '@swc/core-linux-arm-gnueabihf': 1.3.105
-      '@swc/core-linux-arm64-gnu': 1.3.105
-      '@swc/core-linux-arm64-musl': 1.3.105
-      '@swc/core-linux-x64-gnu': 1.3.105
-      '@swc/core-linux-x64-musl': 1.3.105
-      '@swc/core-win32-arm64-msvc': 1.3.105
-      '@swc/core-win32-ia32-msvc': 1.3.105
-      '@swc/core-win32-x64-msvc': 1.3.105
-
   /@swc/core@1.3.107:
     resolution: {integrity: sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==}
     engines: {node: '>=10'}
@@ -7033,17 +6924,6 @@ packages:
   /@swc/counter@0.1.1:
     resolution: {integrity: sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==}
 
-  /@swc/jest@0.2.31(@swc/core@1.3.105):
-    resolution: {integrity: sha512-Gh0Ste380O8KUY1IqsKr+aOvqqs2Loa+WcWWVNwl+lhXqOWK1iTFAP1K0IDfLqAuFP68+D/PxcpBJn21e6Quvw==}
-    engines: {npm: '>= 7.0.0'}
-    peerDependencies:
-      '@swc/core': '*'
-    dependencies:
-      '@jest/create-cache-key-function': 29.7.0
-      '@swc/core': 1.3.105
-      jsonc-parser: 3.2.0
-    dev: true
-
   /@swc/jest@0.2.31(@swc/core@1.3.107):
     resolution: {integrity: sha512-Gh0Ste380O8KUY1IqsKr+aOvqqs2Loa+WcWWVNwl+lhXqOWK1iTFAP1K0IDfLqAuFP68+D/PxcpBJn21e6Quvw==}
     engines: {npm: '>= 7.0.0'}
diff --git a/scripts/dev.mjs b/scripts/dev.mjs
index 1ca2c6c2eae6..bbb2547758b1 100644
--- a/scripts/dev.mjs
+++ b/scripts/dev.mjs
@@ -16,35 +16,36 @@ await execa('pnpm', ['clean'], {
 	stderr: process.stderr,
 });
 
-await execa('pnpm', ['build-pre'], {
-	cwd: _dirname + '/../',
-	stdout: process.stdout,
-	stderr: process.stderr,
-});
-
-await execa('pnpm', ['build-assets'], {
-	cwd: _dirname + '/../',
-	stdout: process.stdout,
-	stderr: process.stderr,
-});
-
-await execa('pnpm', ['--filter', 'misskey-js', 'ts'], {
-	cwd: _dirname + '/../',
-	stdout: process.stdout,
-	stderr: process.stderr,
-});
-
-await execa('pnpm', ['--filter', 'misskey-reversi', 'build:tsc'], {
-	cwd: _dirname + '/../',
-	stdout: process.stdout,
-	stderr: process.stderr,
-});
-
-await execa('pnpm', ['--filter', 'misskey-bubble-game', 'build:tsc'], {
-	cwd: _dirname + '/../',
-	stdout: process.stdout,
-	stderr: process.stderr,
-});
+await Promise.all([
+	execa('pnpm', ['build-pre'], {
+		cwd: _dirname + '/../',
+		stdout: process.stdout,
+		stderr: process.stderr,
+	}),
+	execa('pnpm', ['build-assets'], {
+		cwd: _dirname + '/../',
+		stdout: process.stdout,
+		stderr: process.stderr,
+	}),
+	execa('pnpm', ['--filter', 'misskey-js', 'build'], {
+		cwd: _dirname + '/../',
+		stdout: process.stdout,
+		stderr: process.stderr,
+	}),
+]);
+
+await Promise.all([
+	execa('pnpm', ['--filter', 'misskey-reversi', 'build'], {
+		cwd: _dirname + '/../',
+		stdout: process.stdout,
+		stderr: process.stderr,
+	}),
+	execa('pnpm', ['--filter', 'misskey-bubble-game', 'build'], {
+		cwd: _dirname + '/../',
+		stdout: process.stdout,
+		stderr: process.stderr,
+	}),
+]);
 
 execa('pnpm', ['build-pre', '--watch'], {
 	cwd: _dirname + '/../',

From 50da7d2a2728745bcf29cf71fb230c85a1845060 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 30 Mar 2024 15:34:05 +0900
Subject: [PATCH 132/266] =?UTF-8?q?enhance(frontend):=202=E8=A6=81?=
 =?UTF-8?q?=E7=B4=A0=E8=AA=8D=E8=A8=BC=E3=82=BB=E3=83=83=E3=83=88=E3=82=A2?=
 =?UTF-8?q?=E3=83=83=E3=83=97=E3=82=A6=E3=82=A3=E3=82=B6=E3=83=BC=E3=83=89?=
 =?UTF-8?q?=E3=81=AB=E3=82=A2=E3=83=97=E3=83=AA=E3=82=92=E8=B5=B7=E5=8B=95?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92=E6=96=B0?=
 =?UTF-8?q?=E8=A8=AD=20(#13636)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 2要素認証セットアップウィザードにアプリを起動するボタンを新設

* add comment

* use css module
---
 locales/index.d.ts                               | 10 +++++-----
 locales/ja-JP.yml                                |  4 ++--
 packages/frontend/src/components/MkButton.vue    |  2 ++
 .../frontend/src/pages/settings/2fa.qrdialog.vue | 16 +++++++++++++---
 4 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index e1250946f35f..428cf9135cff 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4928,6 +4928,10 @@ export interface Locale extends ILocale {
      * バックアップコードを使う
      */
     "useBackupCode": string;
+    /**
+     * アプリを起動
+     */
+    "launchApp": string;
     "_bubbleGame": {
         /**
          * 遊び方
@@ -7542,13 +7546,9 @@ export interface Locale extends ILocale {
          */
         "step1": ParameterizedString<"a" | "b">;
         /**
-         * 次に、表示されているQRコードをアプリでスキャンします。
+         * 次に、表示されているQRコードをアプリでスキャンするか、ボタンをクリックして端末上でアプリを開きます。
          */
         "step2": string;
-        /**
-         * QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。
-         */
-        "step2Click": string;
         /**
          * デスクトップアプリを使用する場合は次のURIを入力します
          */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 1c4df27d92ad..9e76e420c38a 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1228,6 +1228,7 @@ gameRetry: "リトライ"
 notUsePleaseLeaveBlank: "使用しない場合は空欄にしてください"
 useTotp: "ワンタイムパスワードを使う"
 useBackupCode: "バックアップコードを使う"
+launchApp: "アプリを起動"
 
 _bubbleGame:
   howToPlay: "遊び方"
@@ -1983,8 +1984,7 @@ _2fa:
   alreadyRegistered: "既に設定は完了しています。"
   registerTOTP: "認証アプリの設定を開始"
   step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。"
-  step2: "次に、表示されているQRコードをアプリでスキャンします。"
-  step2Click: "QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。"
+  step2: "次に、表示されているQRコードをアプリでスキャンするか、ボタンをクリックして端末上でアプリを開きます。"
   step2Uri: "デスクトップアプリを使用する場合は次のURIを入力します"
   step3Title: "確認コードを入力"
   step3: "アプリに表示されている確認コード(トークン)を入力します。"
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index 817f1aadf334..3489255b9152 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	v-else class="_button"
 	:class="[$style.root, { [$style.inline]: inline, [$style.primary]: primary, [$style.gradate]: gradate, [$style.danger]: danger, [$style.rounded]: rounded, [$style.full]: full, [$style.small]: small, [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike }]"
 	:to="to ?? '#'"
+	:behavior="linkBehavior"
 	@mousedown="onMousedown"
 >
 	<div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div>
@@ -43,6 +44,7 @@ const props = defineProps<{
 	inline?: boolean;
 	link?: boolean;
 	to?: string;
+	linkBehavior?: null | 'window' | 'browser';
 	autofocus?: boolean;
 	wait?: boolean;
 	danger?: boolean;
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
index 2ef664b9a363..73253b1ef43f 100644
--- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -33,8 +33,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 									<a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a>
 								</template>
 							</I18n>
-							<div>{{ i18n.ts._2fa.step2 }}<br>{{ i18n.ts._2fa.step2Click }}</div>
-							<a :href="twoFactorData.url"><img :class="$style.qr" :src="twoFactorData.qr"></a>
+							<div>{{ i18n.ts._2fa.step2 }}</div>
+							<div>
+								<a :class="$style.qrRoot" :href="twoFactorData.url"><img :class="$style.qr" :src="twoFactorData.qr"></a>
+								<!-- QRコード側にマージンが入っているので直下でOK -->
+								<div><MkButton inline rounded link :to="twoFactorData.url" :linkBehavior="'browser'">{{ i18n.ts.launchApp }}</MkButton></div>
+							</div>
 							<MkKeyValue :copy="twoFactorData.url">
 								<template #key>{{ i18n.ts._2fa.step2Uri }}</template>
 								<template #value>{{ twoFactorData.url }}</template>
@@ -177,8 +181,14 @@ function allDone() {
 	transform: translateX(-50px);
 }
 
-.qr {
+.qrRoot {
+	display: block;
+	margin: 0 auto;
 	width: 200px;
 	max-width: 100%;
 }
+
+.qr {
+	width: 100%;
+}
 </style>

From b96d9c6973b1c861306fdb9f51256cee5325a2b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 30 Mar 2024 16:02:03 +0900
Subject: [PATCH 133/266] =?UTF-8?q?fix/enhance(frontend):=20=E6=98=A0?=
 =?UTF-8?q?=E5=83=8F=E3=83=BB=E9=9F=B3=E5=A3=B0=E5=91=A8=E3=82=8A=E3=81=AE?=
 =?UTF-8?q?=E6=94=B9=E4=BF=AE=20(#13206)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 映像・音声周りの改修

* fix

* fix design

* fix lint

* キーボードショートカットを整備

* Update Changelog

* fix

* feat: ループ再生

* ネイティブの動作と同期されるように

* Update Changelog

* key指定を消す
---
 CHANGELOG.md                                  |   3 +
 locales/index.d.ts                            |  18 +++
 locales/ja-JP.yml                             |   7 +
 .../frontend/src/components/MkMediaAudio.vue  | 112 ++++++++++++++-
 .../frontend/src/components/MkMediaVideo.vue  | 131 +++++++++++++++++-
 packages/frontend/src/components/MkMenu.vue   |  91 +++++++++++-
 .../src/components/MkSwitch.button.vue        |  13 +-
 .../frontend/src/pages/settings/general.vue   |   2 +
 packages/frontend/src/scripts/keycode.ts      |   1 +
 packages/frontend/src/store.ts                |   4 +
 packages/frontend/src/types/menu.ts           |  10 +-
 11 files changed, 373 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5963549cc654..ebf932012959 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,9 @@
 - Enhance: ページのデザインを変更
 - Enhance: 2要素認証(ワンタイムパスワード)の入力欄を改善
 - Enhance: 「今日誕生日のフォロー中ユーザー」ウィジェットを手動でリロードできるように
+- Enhance: 映像・音声の再生にブラウザのネイティブプレイヤーを使用できるように
+- Enhance: 映像・音声の再生メニューに「再生速度」「ループ再生」「ピクチャインピクチャ」を追加
+- Enhance: 映像・音声の再生にキーボードショートカットが使えるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 428cf9135cff..3dbe46c7b2f6 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4932,6 +4932,10 @@ export interface Locale extends ILocale {
      * アプリを起動
      */
     "launchApp": string;
+    /**
+     * 動画・音声の再生にブラウザのUIを使用する
+     */
+    "useNativeUIForVideoAudioPlayer": string;
     "_bubbleGame": {
         /**
          * 遊び方
@@ -9834,6 +9838,20 @@ export interface Locale extends ILocale {
          */
         "summaryProxyDescription2": string;
     };
+    "_mediaControls": {
+        /**
+         * ピクチャインピクチャ
+         */
+        "pip": string;
+        /**
+         * 再生速度
+         */
+        "playbackRate": string;
+        /**
+         * ループ再生
+         */
+        "loop": string;
+    };
 }
 declare const locales: {
     [lang: string]: Locale;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 9e76e420c38a..aa765d13108f 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1229,6 +1229,7 @@ notUsePleaseLeaveBlank: "使用しない場合は空欄にしてください"
 useTotp: "ワンタイムパスワードを使う"
 useBackupCode: "バックアップコードを使う"
 launchApp: "アプリを起動"
+useNativeUIForVideoAudioPlayer: "動画・音声の再生にブラウザのUIを使用する"
 
 _bubbleGame:
   howToPlay: "遊び方"
@@ -2619,3 +2620,9 @@ _urlPreviewSetting:
   summaryProxy: "プレビューを生成するプロキシのエンドポイント"
   summaryProxyDescription: "Misskey本体ではなく、サマリープロキシを使用してプレビューを生成します。"
   summaryProxyDescription2: "プロキシには下記パラメータがクエリ文字列として連携されます。プロキシ側がこれらをサポートしない場合、設定値は無視されます。"
+
+_mediaControls:
+  pip: "ピクチャインピクチャ"
+  playbackRate: "再生速度"
+  loop: "ループ再生"
+  
\ No newline at end of file
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index 96c9b9fd66c8..5d2edf467e54 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -5,11 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div
+	ref="playerEl"
+	v-hotkey="keymap"
+	tabindex="0"
 	:class="[
 		$style.audioContainer,
 		(audio.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitive,
 	]"
 	@contextmenu.stop
+	@keydown.stop
 >
 	<button v-if="hide" :class="$style.hidden" @click="hide = false">
 		<div :class="$style.hiddenTextWrapper">
@@ -18,6 +22,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<span style="display: block;">{{ i18n.ts.clickToShow }}</span>
 		</div>
 	</button>
+
+	<div v-else-if="defaultStore.reactiveState.useNativeUIForVideoAudioPlayer.value" :class="$style.nativeAudioContainer">
+		<audio
+			ref="audioEl"
+			preload="metadata"
+			controls
+			:class="$style.nativeAudio"
+			@keydown.prevent
+		>
+			<source :src="audio.url">
+		</audio>
+	</div>
+
 	<div v-else :class="$style.audioControls">
 		<audio
 			ref="audioEl"
@@ -72,6 +89,41 @@ const props = defineProps<{
 	audio: Misskey.entities.DriveFile;
 }>();
 
+const keymap = {
+	'up': () => {
+		if (hasFocus() && audioEl.value) {
+			volume.value = Math.min(volume.value + 0.1, 1);
+		}
+	},
+	'down': () => {
+		if (hasFocus() && audioEl.value) {
+			volume.value = Math.max(volume.value - 0.1, 0);
+		}
+	},
+	'left': () => {
+		if (hasFocus() && audioEl.value) {
+			audioEl.value.currentTime = Math.max(audioEl.value.currentTime - 5, 0);
+		}
+	},
+	'right': () => {
+		if (hasFocus() && audioEl.value) {
+			audioEl.value.currentTime = Math.min(audioEl.value.currentTime + 5, audioEl.value.duration);
+		}
+	},
+	'space': () => {
+		if (hasFocus()) {
+			togglePlayPause();
+		}
+	},
+};
+
+// PlayerElもしくはその子要素にフォーカスがあるかどうか
+function hasFocus() {
+	if (!playerEl.value) return false;
+	return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement);
+}
+
+const playerEl = shallowRef<HTMLDivElement>();
 const audioEl = shallowRef<HTMLAudioElement>();
 
 // eslint-disable-next-line vue/no-setup-props-destructure
@@ -85,6 +137,30 @@ function showMenu(ev: MouseEvent) {
 
 	menu = [
 		// TODO: 再生キューに追加
+		{
+			type: 'switch',
+			text: i18n.ts._mediaControls.loop,
+			icon: 'ti ti-repeat',
+			ref: loop,
+		},
+		{
+			type: 'radio',
+			text: i18n.ts._mediaControls.playbackRate,
+			icon: 'ti ti-clock-play',
+			ref: speed,
+			options: {
+				'0.25x': 0.25,
+				'0.5x': 0.5,
+				'0.75x': 0.75,
+				'1.0x': 1,
+				'1.25x': 1.25,
+				'1.5x': 1.5,
+				'2.0x': 2,
+			},
+		},
+		{
+			type: 'divider',
+		},
 		{
 			text: i18n.ts.hide,
 			icon: 'ti ti-eye-off',
@@ -147,6 +223,8 @@ const rangePercent = computed({
 	},
 });
 const volume = ref(.25);
+const speed = ref(1);
+const loop = ref(false); // TODO: ドライブファイルのフラグに置き換える
 const bufferedEnd = ref(0);
 const bufferedDataRatio = computed(() => {
 	if (!audioEl.value) return 0;
@@ -176,6 +254,7 @@ function toggleMute() {
 }
 
 let onceInit = false;
+let mediaTickFrameId: number | null = null;
 let stopAudioElWatch: () => void;
 
 function init() {
@@ -195,8 +274,12 @@ function init() {
 					}
 
 					elapsedTimeMs.value = audioEl.value.currentTime * 1000;
+
+					if (audioEl.value.loop !== loop.value) {
+						loop.value = audioEl.value.loop;
+					}
 				}
-				window.requestAnimationFrame(updateMediaTick);
+				mediaTickFrameId = window.requestAnimationFrame(updateMediaTick);
 			}
 
 			updateMediaTick();
@@ -234,6 +317,14 @@ watch(volume, (to) => {
 	if (audioEl.value) audioEl.value.volume = to;
 });
 
+watch(speed, (to) => {
+	if (audioEl.value) audioEl.value.playbackRate = to;
+});
+
+watch(loop, (to) => {
+	if (audioEl.value) audioEl.value.loop = to;
+});
+
 onMounted(() => {
 	init();
 });
@@ -252,6 +343,10 @@ onDeactivated(() => {
 	hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore');
 	stopAudioElWatch();
 	onceInit = false;
+	if (mediaTickFrameId) {
+		window.cancelAnimationFrame(mediaTickFrameId);
+		mediaTickFrameId = null;
+	}
 });
 </script>
 
@@ -262,6 +357,10 @@ onDeactivated(() => {
 	border: .5px solid var(--divider);
 	border-radius: var(--radius);
 	overflow: clip;
+
+	&:focus {
+		outline: none;
+	}
 }
 
 .sensitive {
@@ -367,4 +466,15 @@ onDeactivated(() => {
 		}
 	}
 }
+
+.nativeAudioContainer {
+	display: flex;
+	align-items: center;
+	padding: 6px;
+}
+
+.nativeAudio {
+	display: block;
+	width: 100%;
+}
 </style>
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 73c1b6ff9d55..1e3868bc3622 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div
 	ref="playerEl"
+	v-hotkey="keymap"
+	tabindex="0"
 	:class="[
 		$style.videoContainer,
 		controlsShowing && $style.active,
@@ -14,15 +16,37 @@ SPDX-License-Identifier: AGPL-3.0-only
 	@mouseover="onMouseOver"
 	@mouseleave="onMouseLeave"
 	@contextmenu.stop
+	@keydown.stop
 >
 	<button v-if="hide" :class="$style.hidden" @click="hide = false">
 		<div :class="$style.hiddenTextWrapper">
 			<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
-			<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
+			<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ defaultStore.state.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
 			<span style="display: block;">{{ i18n.ts.clickToShow }}</span>
 		</div>
 	</button>
-	<div v-else :class="$style.videoRoot" @click.self="togglePlayPause">
+
+	<div v-else-if="defaultStore.reactiveState.useNativeUIForVideoAudioPlayer.value" :class="$style.videoRoot">
+		<video
+			ref="videoEl"
+			:class="$style.video"
+			:poster="video.thumbnailUrl ?? undefined"
+			:title="video.comment ?? undefined"
+			:alt="video.comment"
+			preload="metadata"
+			controls
+			@keydown.prevent
+		>
+			<source :src="video.url">
+		</video>
+		<i class="ti ti-eye-off" :class="$style.hide" @click="hide = true"></i>
+		<div :class="$style.indicators">
+			<div v-if="video.comment" :class="$style.indicator">ALT</div>
+			<div v-if="video.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
+		</div>
+	</div>
+
+	<div v-else :class="$style.videoRoot">
 		<video
 			ref="videoEl"
 			:class="$style.video"
@@ -31,6 +55,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:alt="video.comment"
 			preload="metadata"
 			playsinline
+			@keydown.prevent
+			@click.self="togglePlayPause"
 		>
 			<source :src="video.url">
 		</video>
@@ -100,6 +126,40 @@ const props = defineProps<{
 	video: Misskey.entities.DriveFile;
 }>();
 
+const keymap = {
+	'up': () => {
+		if (hasFocus() && videoEl.value) {
+			volume.value = Math.min(volume.value + 0.1, 1);
+		}
+	},
+	'down': () => {
+		if (hasFocus() && videoEl.value) {
+			volume.value = Math.max(volume.value - 0.1, 0);
+		}
+	},
+	'left': () => {
+		if (hasFocus() && videoEl.value) {
+			videoEl.value.currentTime = Math.max(videoEl.value.currentTime - 5, 0);
+		}
+	},
+	'right': () => {
+		if (hasFocus() && videoEl.value) {
+			videoEl.value.currentTime = Math.min(videoEl.value.currentTime + 5, videoEl.value.duration);
+		}
+	},
+	'space': () => {
+		if (hasFocus()) {
+			togglePlayPause();
+		}
+	},
+};
+
+// PlayerElもしくはその子要素にフォーカスがあるかどうか
+function hasFocus() {
+	if (!playerEl.value) return false;
+	return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement);
+}
+
 // eslint-disable-next-line vue/no-setup-props-destructure
 const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
 
@@ -111,6 +171,35 @@ function showMenu(ev: MouseEvent) {
 
 	menu = [
 		// TODO: 再生キューに追加
+		{
+			type: 'switch',
+			text: i18n.ts._mediaControls.loop,
+			icon: 'ti ti-repeat',
+			ref: loop,
+		},
+		{
+			type: 'radio',
+			text: i18n.ts._mediaControls.playbackRate,
+			icon: 'ti ti-clock-play',
+			ref: speed,
+			options: {
+				'0.25x': 0.25,
+				'0.5x': 0.5,
+				'0.75x': 0.75,
+				'1.0x': 1,
+				'1.25x': 1.25,
+				'1.5x': 1.5,
+				'2.0x': 2,
+			},
+		},
+		...(document.pictureInPictureEnabled ? [{
+			text: i18n.ts._mediaControls.pip,
+			icon: 'ti ti-picture-in-picture',
+			action: togglePictureInPicture,
+		}] : []),
+		{
+			type: 'divider',
+		},
 		{
 			text: i18n.ts.hide,
 			icon: 'ti ti-eye-off',
@@ -186,6 +275,8 @@ const rangePercent = computed({
 	},
 });
 const volume = ref(.25);
+const speed = ref(1);
+const loop = ref(false); // TODO: ドライブファイルのフラグに置き換える
 const bufferedEnd = ref(0);
 const bufferedDataRatio = computed(() => {
 	if (!videoEl.value) return 0;
@@ -243,6 +334,16 @@ function toggleFullscreen() {
 	}
 }
 
+function togglePictureInPicture() {
+	if (videoEl.value) {
+		if (document.pictureInPictureElement) {
+			document.exitPictureInPicture();
+		} else {
+			videoEl.value.requestPictureInPicture();
+		}
+	}
+}
+
 function toggleMute() {
 	if (volume.value === 0) {
 		volume.value = .25;
@@ -252,6 +353,7 @@ function toggleMute() {
 }
 
 let onceInit = false;
+let mediaTickFrameId: number | null = null;
 let stopVideoElWatch: () => void;
 
 function init() {
@@ -271,8 +373,12 @@ function init() {
 					}
 
 					elapsedTimeMs.value = videoEl.value.currentTime * 1000;
+
+					if (videoEl.value.loop !== loop.value) {
+						loop.value = videoEl.value.loop;
+					}
 				}
-				window.requestAnimationFrame(updateMediaTick);
+				mediaTickFrameId = window.requestAnimationFrame(updateMediaTick);
 			}
 
 			updateMediaTick();
@@ -316,6 +422,14 @@ watch(volume, (to) => {
 	if (videoEl.value) videoEl.value.volume = to;
 });
 
+watch(speed, (to) => {
+	if (videoEl.value) videoEl.value.playbackRate = to;
+});
+
+watch(loop, (to) => {
+	if (videoEl.value) videoEl.value.loop = to;
+});
+
 watch(hide, (to) => {
 	if (to && isFullscreen.value) {
 		document.exitFullscreen();
@@ -341,6 +455,10 @@ onDeactivated(() => {
 	hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore');
 	stopVideoElWatch();
 	onceInit = false;
+	if (mediaTickFrameId) {
+		window.cancelAnimationFrame(mediaTickFrameId);
+		mediaTickFrameId = null;
+	}
 });
 </script>
 
@@ -349,6 +467,10 @@ onDeactivated(() => {
 	container-type: inline-size;
 	position: relative;
 	overflow: clip;
+
+	&:focus {
+		outline: none;
+	}
 }
 
 .sensitive {
@@ -412,7 +534,7 @@ onDeactivated(() => {
 	font: inherit;
 	color: inherit;
 	cursor: pointer;
-	padding: 120px 0;
+	padding: 60px 0;
 	display: flex;
 	align-items: center;
 	justify-content: center;
@@ -436,7 +558,6 @@ onDeactivated(() => {
 	display: block;
 	height: 100%;
 	width: 100%;
-	pointer-events: none;
 }
 
 .videoOverlayPlayButton {
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index faed6416d0d7..d91239b9e221 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -42,9 +42,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</button>
 			<button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } ]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
-				<MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
+				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
+				<MkSwitchButton v-else :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
+				<div :class="$style.item_content">
+					<span :class="[$style.item_content_text, { [$style.switchText]: !item.icon }]">{{ item.text }}</span>
+					<MkSwitchButton v-if="item.icon" :class="[$style.switchButton, $style.caret]" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
+				</div>
+			</button>
+			<button v-else-if="item.type === 'radio'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showRadioOptions(item, $event)" @click="!preferClick ? null : showRadioOptions(item, $event)">
+				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]" style="pointer-events: none;"></i>
 				<div :class="$style.item_content">
-					<span :class="[$style.item_content_text, $style.switchText]">{{ item.text }}</span>
+					<span :class="$style.item_content_text" style="pointer-events: none;">{{ item.text }}</span>
+					<span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span>
+				</div>
+			</button>
+			<button v-else-if="item.type === 'radioOption'" :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.radioActive]: item.active }]" @click="clicked(item.action, $event, false)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+				<div :class="$style.icon">
+					<span :class="[$style.radio, { [$style.radioChecked]: item.active }]"></span>
+				</div>
+				<div :class="$style.item_content">
+					<span :class="$style.item_content_text">{{ item.text }}</span>
 				</div>
 			</button>
 			<button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)">
@@ -77,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ComputedRef, computed, defineAsyncComponent, isRef, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
 import MkSwitchButton from '@/components/MkSwitch.button.vue';
-import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js';
+import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuRadio, MenuRadioOption, MenuParent } from '@/types/menu.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { isTouchUsing } from '@/scripts/touch.js';
@@ -168,6 +185,31 @@ function onItemMouseLeave(item) {
 	if (childCloseTimer) window.clearTimeout(childCloseTimer);
 }
 
+async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
+	const children: MenuItem[] = Object.keys(item.options).map<MenuRadioOption>(key => {
+		const value = item.options[key];
+		return {
+			type: 'radioOption',
+			text: key,
+			action: () => {
+				item.ref = value;
+			},
+			active: computed(() => item.ref === value),
+		};
+	});
+
+	if (props.asDrawer) {
+		os.popupMenu(children, ev.currentTarget ?? ev.target).finally(() => {
+			emit('close');
+		});
+		emit('hide');
+	} else {
+		childTarget.value = (ev.currentTarget ?? ev.target) as HTMLElement;
+		childMenu.value = children;
+		childShowingItem.value = item;
+	}
+}
+
 async function showChildren(item: MenuParent, ev: MouseEvent) {
 	const children: MenuItem[] = await (async () => {
 		if (childrenCache.has(item)) {
@@ -196,8 +238,10 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
 	}
 }
 
-function clicked(fn: MenuAction, ev: MouseEvent) {
+function clicked(fn: MenuAction, ev: MouseEvent, doClose = true) {
 	fn(ev);
+
+	if (!doClose) return;
 	close(true);
 }
 
@@ -350,6 +394,15 @@ onBeforeUnmount(() => {
 		}
 	}
 
+	&.radioActive {
+		color: var(--accent) !important;
+		opacity: 1;
+
+		&:before {
+			background-color: var(--accentedBg) !important;
+		}
+	}
+
 	&:not(:active):focus-visible {
 		box-shadow: 0 0 0 2px var(--focus) inset;
 	}
@@ -417,11 +470,11 @@ onBeforeUnmount(() => {
 
 .switchButton {
 	margin-left: -2px;
+	--height: 1.35em;
 }
 
 .switchText {
 	margin-left: 8px;
-	margin-top: 2px;
 	overflow: hidden;
 	text-overflow: ellipsis;
 }
@@ -461,4 +514,32 @@ onBeforeUnmount(() => {
 	margin: 8px 0;
 	border-top: solid 0.5px var(--divider);
 }
+
+.radio {
+	display: inline-block;
+	position: relative;
+	width: 1em;
+	height: 1em;
+	vertical-align: -.125em;
+	border-radius: 50%;
+	border: solid 2px var(--divider);
+	background-color: var(--panel);
+
+	&.radioChecked {
+		border-color: var(--accent);
+
+		&::after {
+			content: "";
+			display: block;
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			transform: translate(-50%, -50%);
+			width: 50%;
+			height: 50%;
+			border-radius: 50%;
+			background-color: var(--accent);
+		}
+	}
+}
 </style>
diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue
index c95c933663c4..226908e221e4 100644
--- a/packages/frontend/src/components/MkSwitch.button.vue
+++ b/packages/frontend/src/components/MkSwitch.button.vue
@@ -41,13 +41,15 @@ const toggle = () => {
 
 <style lang="scss" module>
 .button {
+	--height: 21px;
+
 	position: relative;
 	display: inline-flex;
 	flex-shrink: 0;
 	margin: 0;
 	box-sizing: border-box;
-	width: 32px;
-	height: 23px;
+	width: calc(var(--height) * 1.6);
+	height: calc(var(--height) + 2px); // 枠線
 	outline: none;
 	background: var(--switchOffBg);
 	background-clip: content-box;
@@ -69,9 +71,10 @@ const toggle = () => {
 
 .knob {
 	position: absolute;
+	box-sizing: border-box;
 	top: 3px;
-	width: 15px;
-	height: 15px;
+	width: calc(var(--height) - 6px);
+	height: calc(var(--height) - 6px);
 	border-radius: 999px;
 	transition: all 0.2s ease;
 
@@ -82,7 +85,7 @@ const toggle = () => {
 }
 
 .knobChecked {
-	left: 12px;
+	left: calc(calc(100% - var(--height)) + 3px);
 	background: var(--switchOnFg);
 }
 </style>
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 285f874dde1e..f2f82c480844 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -132,6 +132,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
 				<MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch>
 				<MkSwitch v-model="enableSeasonalScreenEffect">{{ i18n.ts.seasonalScreenEffect }}</MkSwitch>
+				<MkSwitch v-model="useNativeUIForVideoAudioPlayer">{{ i18n.ts.useNativeUIForVideoAudioPlayer }}</MkSwitch>
 			</div>
 			<div>
 				<MkRadios v-model="emojiStyle">
@@ -308,6 +309,7 @@ const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disable
 const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications'));
 const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect'));
 const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe'));
+const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
diff --git a/packages/frontend/src/scripts/keycode.ts b/packages/frontend/src/scripts/keycode.ts
index bc1f485f5ee2..7ffceafadae1 100644
--- a/packages/frontend/src/scripts/keycode.ts
+++ b/packages/frontend/src/scripts/keycode.ts
@@ -15,6 +15,7 @@ export default (input: string): string[] => {
 export const aliases = {
 	'esc': 'Escape',
 	'enter': ['Enter', 'NumpadEnter'],
+	'space': [' ', 'Spacebar'],
 	'up': 'ArrowUp',
 	'down': 'ArrowDown',
 	'left': 'ArrowLeft',
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 7742acc60d58..faefbd8ce430 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -442,6 +442,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: true,
 	},
+	useNativeUIForVideoAudioPlayer: {
+		where: 'device',
+		default: false,
+	},
 
 	sound_masterVolume: {
 		where: 'device',
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index 712f3464e5dc..138eb7dd6231 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -6,6 +6,8 @@
 import * as Misskey from 'misskey-js';
 import { ComputedRef, Ref } from 'vue';
 
+interface MenuRadioOptionsDef extends Record<string, any> { }
+
 export type MenuAction = (ev: MouseEvent) => void;
 
 export type MenuDivider = { type: 'divider' };
@@ -14,13 +16,15 @@ export type MenuLabel = { type: 'label', text: string };
 export type MenuLink = { type: 'link', to: string, text: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User };
 export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, icon?: string, indicate?: boolean };
 export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction };
-export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, disabled?: boolean | Ref<boolean> };
+export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, icon?: string, disabled?: boolean | Ref<boolean> };
 export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean | ComputedRef<boolean>, avatar?: Misskey.entities.User; action: MenuAction };
+export type MenuRadio = { type: 'radio', text: string, icon?: string, ref: Ref<MenuRadioOptionsDef[keyof MenuRadioOptionsDef]>, options: MenuRadioOptionsDef, disabled?: boolean | Ref<boolean> };
+export type MenuRadioOption = { type: 'radioOption', text: string, action: MenuAction; active?: boolean | ComputedRef<boolean> };
 export type MenuParent = { type: 'parent', text: string, icon?: string, children: MenuItem[] | (() => Promise<MenuItem[]> | MenuItem[]) };
 
 export type MenuPending = { type: 'pending' };
 
-type OuterMenuItem = MenuDivider | MenuNull | MenuLabel | MenuLink | MenuA | MenuUser | MenuSwitch | MenuButton | MenuParent;
+type OuterMenuItem = MenuDivider | MenuNull | MenuLabel | MenuLink | MenuA | MenuUser | MenuSwitch | MenuButton | MenuRadio | MenuRadioOption | MenuParent;
 type OuterPromiseMenuItem = Promise<MenuLabel | MenuLink | MenuA | MenuUser | MenuSwitch | MenuButton | MenuParent>;
 export type MenuItem = OuterMenuItem | OuterPromiseMenuItem;
-export type InnerMenuItem = MenuDivider | MenuPending | MenuLabel | MenuLink | MenuA | MenuUser | MenuSwitch | MenuButton | MenuParent;
+export type InnerMenuItem = MenuDivider | MenuPending | MenuLabel | MenuLink | MenuA | MenuUser | MenuSwitch | MenuButton | MenuRadio | MenuRadioOption | MenuParent;

From d4ca973e3408a7d28788efe57bcd882c0ce9eedc Mon Sep 17 00:00:00 2001
From: 1Step621 <86859447+1STEP621@users.noreply.github.com>
Date: Sat, 30 Mar 2024 20:30:22 +0900
Subject: [PATCH 134/266] =?UTF-8?q?Enhance(frontend):=20=E3=82=82=E3=81=A3?=
 =?UTF-8?q?=E3=81=A8=EF=BC=81=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=A7=E3=83=AA?=
 =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E4=B8=80=E8=A6=A7?=
 =?UTF-8?q?=E3=81=8C=E9=96=8B=E3=81=91=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#12935)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* もっと!ボタンでリアクション一覧が開けるように

* update CHANGELOG.md && デバッグ用に最大リアクション表示数を1にしてたのを一応戻した

* fix

* デザイン調整

* maxNumberもどす

* fix CHANGELOG

* fix

* move changelog

* :art:

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                           | 1 +
 packages/frontend/src/components/MkNote.vue            | 7 +++----
 packages/frontend/src/components/MkNoteDetailed.vue    | 9 ++++++---
 packages/frontend/src/components/MkReactionsViewer.vue | 3 +++
 packages/frontend/src/pages/note.vue                   | 3 ++-
 packages/frontend/src/router/definition.ts             | 2 +-
 6 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebf932012959..390639bd69ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@
 - Enhance: 映像・音声の再生にブラウザのネイティブプレイヤーを使用できるように
 - Enhance: 映像・音声の再生メニューに「再生速度」「ループ再生」「ピクチャインピクチャ」を追加
 - Enhance: 映像・音声の再生にキーボードショートカットが使えるように
+- Enhance: ノートについているリアクションの「もっと!」から、リアクションの一覧を表示できるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 4add3aa6dd14..f5536e79bfcb 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 			<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @mockUpdateMyReaction="emitUpdReaction">
 				<template #more>
-					<div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div>
+					<MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA>
 				</template>
 			</MkReactionsViewer>
 			<footer :class="$style.footer">
@@ -1020,9 +1020,8 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .reactionOmitted {
 	display: inline-block;
-	height: 32px;
-	margin: 2px;
-	padding: 0 6px;
+	margin-left: 8px;
 	opacity: .8;
+	font-size: 95%;
 }
 </style>
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index ebccd6098608..eec1aad53c49 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -234,9 +234,12 @@ import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkButton from '@/components/MkButton.vue';
 import { isEnabledUrlPreview } from '@/instance.js';
 
-const props = defineProps<{
+const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
-}>();
+	initialTab: string;
+}>(), {
+	initialTab: 'replies',
+});
 
 const inChannel = inject('inChannel', null);
 
@@ -304,7 +307,7 @@ provide('react', (reaction: string) => {
 	});
 });
 
-const tab = ref('replies');
+const tab = ref(props.initialTab);
 const reactionTabType = ref<string | null>(null);
 
 const renotesPagination = computed<Paging>(() => ({
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index 1bd37d842b55..63b202f9f3fb 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -100,6 +100,9 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe
 }
 
 .root {
+	display: flex;
+	flex-wrap: wrap;
+	align-items: center;
 	margin: 4px -2px 0 -2px;
 
 	&:empty {
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 4c985b96e6af..e14651742a94 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</div>
 						<div class="_margin _gaps_s">
 							<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
-							<MkNoteDetailed :key="note.id" v-model:note="note" :class="$style.note"/>
+							<MkNoteDetailed :key="note.id" v-model:note="note" :initialTab="initialTab" :class="$style.note"/>
 						</div>
 						<div v-if="clips && clips.length > 0" class="_margin">
 							<div style="font-weight: bold; padding: 12px;">{{ i18n.ts.clip }}</div>
@@ -66,6 +66,7 @@ import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	noteId: string;
+	initialTab?: string;
 }>();
 
 const note = ref<null | Misskey.entities.Note>();
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index eaeeafd49908..c9f03b738f4c 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -35,7 +35,7 @@ const routes: RouteDef[] = [{
 	component: page(() => import('@/pages/user/index.vue')),
 }, {
 	name: 'note',
-	path: '/notes/:noteId',
+	path: '/notes/:noteId/:initialTab?',
 	component: page(() => import('@/pages/note.vue')),
 }, {
 	name: 'list',

From 0f2e6513318e05b0a88526b52130b911b5631ec9 Mon Sep 17 00:00:00 2001
From: Zero King <l2dy@icloud.com>
Date: Sun, 31 Mar 2024 09:43:28 +0800
Subject: [PATCH 135/266] fix(frontend): remove duplicate CSS declaration
 (#13640)

---
 packages/frontend/src/components/MkCode.vue | 2 --
 1 file changed, 2 deletions(-)

diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index ede068b20d3f..a3c80e743b6a 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -80,11 +80,9 @@ function copy() {
 .codePlaceholderRoot {
 	display: block;
 	width: 100%;
-	background: none;
 	border: none;
 	outline: none;
   font: inherit;
-  color: inherit;
 	cursor: pointer;
 
 	box-sizing: border-box;

From efafa02f6820a31df5dded620cf0f6ef30454e0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 31 Mar 2024 12:43:39 +0900
Subject: [PATCH 136/266] =?UTF-8?q?enhance(backend):=20=E3=83=93=E3=83=87?=
 =?UTF-8?q?=E3=82=AA=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AB=E3=83=93?=
 =?UTF-8?q?=E3=83=87=E3=82=AA=E3=83=88=E3=83=A9=E3=83=83=E3=82=AF=E3=81=8C?=
 =?UTF-8?q?=E3=81=82=E3=82=8B=E3=81=8B=E3=82=92=E7=A2=BA=E8=AA=8D=E3=81=99?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13568)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(backend): ビデオファイルにビデオトラックがあるかを確認するように

(cherry picked from commit 23d38a2d6492a2b24e9b2c031d66c3e8a5d382ef)

* Update Changelog

* Update Changelog

* Revert "Update Changelog"

This reverts commit 93fd996932b87ef550c38b48bd0678060f3ed1af.

* fix(test) ffmpegをインストールするように

* 入れる方間違えた

* fix test

* 拡張子変わらなかったのでそのまま行く

* ログを出力するように

* msg

* remove unused import

* add log

* attempt to fix test error

* Revert "attempt to fix test error"

This reverts commit d9d6524cadd655e6d8e9398b26fdfef332f30f4d.

* Update FileInfoService.ts

* oggも検査の対象にする
---
 .github/workflows/test-backend.yml            |   2 +
 CHANGELOG.md                                  |   1 +
 packages/backend/src/core/FileInfoService.ts  |  49 +++++++++++++++++-
 .../backend/test/resources/kick_gaba7.m4a     | Bin 0 -> 9817 bytes
 packages/backend/test/unit/FileInfoService.ts |  27 ++++++++--
 5 files changed, 74 insertions(+), 5 deletions(-)
 create mode 100644 packages/backend/test/resources/kick_gaba7.m4a

diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 49a6a3980505..a803db4508a5 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -45,6 +45,8 @@ jobs:
       with:
         version: 8
         run_install: false
+    - name: Install FFmpeg
+      uses: FedericoCarboni/setup-ffmpeg@v3
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
       with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 390639bd69ca..f6b9cf093925 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
 - Fix: エンドポイント`notes/translate`のエラーを改善
 - Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
+- Fix: 一部の音声ファイルが映像ファイルとして扱われる問題を修正
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts
index b8babcb3a724..169285f0333d 100644
--- a/packages/backend/src/core/FileInfoService.ts
+++ b/packages/backend/src/core/FileInfoService.ts
@@ -14,11 +14,12 @@ import FFmpeg from 'fluent-ffmpeg';
 import isSvg from 'is-svg';
 import probeImageSize from 'probe-image-size';
 import { type predictionType } from 'nsfwjs';
-import sharp from 'sharp';
 import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
 import { encode } from 'blurhash';
 import { createTempDir } from '@/misc/create-temp.js';
 import { AiService } from '@/core/AiService.js';
+import { LoggerService } from '@/core/LoggerService.js';
+import type Logger from '@/logger.js';
 import { bindThis } from '@/decorators.js';
 
 export type FileInfo = {
@@ -49,9 +50,13 @@ const TYPE_SVG = {
 
 @Injectable()
 export class FileInfoService {
+	private logger: Logger;
+
 	constructor(
 		private aiService: AiService,
+		private loggerService: LoggerService,
 	) {
+		this.logger = this.loggerService.getLogger('file-info');
 	}
 
 	/**
@@ -317,6 +322,34 @@ export class FileInfoService {
 		return mime;
 	}
 
+	/**
+	 * ビデオファイルにビデオトラックがあるかどうかチェック
+	 * (ない場合:m4a, webmなど)
+	 *
+	 * @param path ファイルパス
+	 * @returns ビデオトラックがあるかどうか(エラー発生時は常に`true`を返す)
+	 */
+	@bindThis
+	private hasVideoTrackOnVideoFile(path: string): Promise<boolean> {
+		const sublogger = this.logger.createSubLogger('ffprobe');
+		sublogger.info(`Checking the video file. File path: ${path}`);
+		return new Promise((resolve) => {
+			try {
+				FFmpeg.ffprobe(path, (err, metadata) => {
+					if (err) {
+						sublogger.warn(`Could not check the video file. Returns true. File path: ${path}`, err);
+						resolve(true);
+						return;
+					}
+					resolve(metadata.streams.some((stream) => stream.codec_type === 'video'));
+				});
+			} catch (err) {
+				sublogger.warn(`Could not check the video file. Returns true. File path: ${path}`, err as Error);
+				resolve(true);
+			}
+		});
+	}
+
 	/**
 	 * Detect MIME Type and extension
 	 */
@@ -339,6 +372,20 @@ export class FileInfoService {
 				return TYPE_SVG;
 			}
 
+			if ((type.mime.startsWith('video') || type.mime === 'application/ogg') && !(await this.hasVideoTrackOnVideoFile(path))) {
+				const newMime = `audio/${type.mime.split('/')[1]}`;
+				if (newMime === 'audio/mp4') {
+					return {
+						mime: 'audio/mp4',
+						ext: 'm4a',
+					};
+				}
+				return {
+					mime: newMime,
+					ext: type.ext,
+				};
+			}
+
 			return {
 				mime: this.fixMime(type.mime),
 				ext: type.ext,
diff --git a/packages/backend/test/resources/kick_gaba7.m4a b/packages/backend/test/resources/kick_gaba7.m4a
new file mode 100644
index 0000000000000000000000000000000000000000..321df6349f016358b24b7bac2df8273ec5c448e5
GIT binary patch
literal 9817
zcmeHtWmFtZ^Y1Pm+?`}`C%6P#+}%A3i@SS(1%d<%0fGj1cPF?8cTJGsK?5WNxyvK>
z{onW8ulL)nGry_o?wYFVuAZ4beHs7&P+EETy4boqI|2X*fF}+Ix1$R$7XSc2vvRYv
z1ORB(9ib2pYCIVLp*m3Z@f_Xo|I@!CU~&K>Z1{gf9n>uGj{wx?$EQ9nIjK-y+*pKd
zY{U0)O676m&q&e1P4bF2Vw=CwtyyEz;*|Tkdu|`bwtiy+m&shW1n*x$jy%1be*aiF
zyvs!>2{i^wJPcL&4IQ8Xq){|cC6IDLLI;Rnc<w(~{K>Yzd9n7$IPc`xbMzwfYQkYh
zT#o_p=4;!Zc-G)FY!xMj#ET&LpuR_()Z=*lKM{N4zz4N0?=Tx-_gORm6kQXUY6-i$
zNo4zH_CwI-IbQy!?51<E)t*^|zBQkS5V>!%@AWh?3sbZPmMs%sTF1w$k#V0sYEI&{
zkMY)iOiro`J1*k{mG06c;-obVJ7=rN2`O}K;zeXvlZN^6oZX8rKPK?si9rw0-^J|Y
zds2T=`-KIp0N|bNVq#tbSkV%aC9+s*I)fH&&Ja6`BF9{!)L}47g%o5cgchL>V9!Wj
zGIWoS*oKfHI7<n6%wFC9$kox$814Q|+@M`}HTAl~^rijEb%!hsS6)<0%bRg(XpV2G
zBv2ZVybMjTPVEN+nATI|%K*W6DO&xq2CBgTFDtGWyFY!#!Jp4_?<xEqaBVj^UBrYQ
z`whCYeR?b<B&2mkg&lD8{S?0^T^-fD7XN*@Jrqe(Gpk;J;uBpz%01nL`AwYZjpme=
zAT#FX86~yy;OvEH(BtE9lDP;iN41u9z^og9C;@=M{PC8&JrP>K98Mcfy2xNq>wMxn
zV)afHZ;q7KfSF9oF&f+ZgzEMEz*O?9eopn8LM?FiNFUJ{DiSQ3C`}+(jes(=GNB^R
z+?O9iXF5$CiW`kf$ay>Xm?YTSk-?zmhP|3JgZDnZ^0LqeRy5SroLUORsa5MMB{E&7
zNo<k2H)15{d2@ena8#2;HQV}|=I}-I;jgb|{<`-K66O&#r6-NIrn*?+D=V_r$bMHJ
zz;{Kw)#pwL=g$21<*}3jAU2nCoYtiv&FwE<>-LMkKfk-=##xkGGI|kN*dzPv<?Rvu
zPvzc-+r|y1CZEUWZ&8iMiI>XK=y5(&gWH=o-$|v;>NYH794|y)y(`oGdJNXTpX}ec
zet@hUJgUV`2nC2z`i4o<43X24qp)gn+4MmReghqSfam~gh^NDQbcp;nx=%gihhC~D
zD>KOhXs(V8CX~mTr@W*Y;Wy1EP`20P6RD8Zm|ujI<qbp|sSVhn+n<DHP^cwNIMx@A
zt0KE${y7K|n2^cZhX=2sBnNM@XJdY=Y5_4>Ns3CMwOpd>cTq|mf~LWRiKG`zase=}
zjTu#t=;D{lS(2qD_Eyai=~wKqP~C+Ig{w+2Np>#%FKzUFl$_gJ#z#8_60T15?-8C+
z%d`s}MV;6Z(iv9kGPECDBpNa=eR+-Qv)}dW>X-kU$E$PVA3HCeS6l74Q2QmD)KLz~
zh`zO=IV1xm(^Ct$B-O+Veo6Wr)Lkm0&H77LR4%<#IDJ{`)3xW9gUv)^L~)Jv@`Y?#
zWsRx&wY8%^l^lENW{biPj*op<5Ydy#>!sZ#6$eoBOrdj535&S9qr-Z~9I~TVhWpDY
z>O>Z&{Dq1UNL{t+FU7K2+Go6WMIAjL>DvTmB&%(;lB$P`V%Vlr^df2%y9jf{QHDm9
zfP$1B-D-1{uAVCS09ZKzb1iqLE&@G6Q;(j=!crhZi~B1IvAD{h1`rv2Moyn=(~Dfm
zNd9Hb7_6#NW}+i+eSr#Gp6TK8sra<Z@#QGxV3*=u)eMGMAV0utKF?TPlEg7D@qnp<
zYvua$S+N-n>Vv*jtGBnGIvI6hBsEArk{e_pkb}!?7s&?fFuR|5^}gsg)5J0Ku<Otc
zQ3(TY^dbV;pDJZW;@$I@k>bVlY+C}@pIQ-AF&aH5N+*79#RMw`yDpTF*mdCNHxtYI
z8J{<O>}v@JqxyRGT<9}smaaFiw|5xB+}u&ySr3bdf_{kpgnlZRTn$oVgvA%F9!9>X
z@{dL+H)lvL8O)_YI`W`VYg>6H<TgSg{cEz5_k#HR6BP&`NKKy19-TfIT$^xA$Vr$Z
zkndLuL;_*%X%HkQ(Ci$gb`bNy9%iSFd;h4}EN#p1g*POXNh;Zi!7A20xv-ajMIQoN
zzAHOqY$lt1Zs)Kpj_cx&q4`Rd$<0R&>*qTLH?rEL+q5Os&(E-Y?WM$)FE5=H@v6A$
zpFIfg5yGzKZI}vot+;5kh3M&WuBqKflwDd*T|Fpc+_5WBUsjL$Wy5}kR$_-m;|j>#
zT3L4Ev=o=kJotkXf4T~y-k);pmMEtRQFAFDQn~Wf;|?%=Rm`H0QeMW*O-(%7Vwx^U
zLiO!ld#6LMn6OzJc#F?qB~BmVxxY-xeR8T^7Ro)1B#j&9GEArg0PC{>kkd=!+O99j
z*`?`2F6}m;1#F>#QLb^ZiUj<oYQjS0^9GZ=m_@X&R1vcnO-czzdJb6dFztM!SS)Qh
zilAi@X%|`@Wi2rG&S)ho06Q%wjg$Yrza8-+KlTmI4=A~8Vk1g>8}YO6kPHxl`YozJ
z?IoAyBQ%)kQ|vf*Q^TBR?WK?}Hwsbc{<zskYGC=9O7tJmj93z>vxiuwpr0$na@C^t
zwx%msNZr0$<@UKs+KGogh^X)kibf}mN#TB>dvB|u$d#)r*<+5dYz_t_w{YMj?<J-@
zxGkhQbO}(kUEgdBsBiKevwxXG53A>h<#t9&FS}UXO+{)`P+8$yFCKGLDP^wf9}7HT
zoX-Nn6ul?spRH=JM+%YFU(wDma`@ccf^J0_fp4qh>EdEy><gT0IrW1+X%>(j_%$-i
zM2}NEwqNx|;(YVPuP-S@D<5_yshA91U)Sl2PaFD>%2wgeSOVsyH6+q8`}4JYa0>mQ
zGtF61R;HXz%3X=hGdFRSOw<KT_?lOX3s)XDm>Z~>u2Ufmbf^UZ6xWEhI#|e5Ka*P@
z=heJlmDYD-llM>_(^0Uk)tq6*2eH#_>W`Duw<_rH&=MmkDbk6+Y*mN`f9ohqK0HWC
za+H6I{;h)m73C1oEG7uBWgW}iL6NH*^~!R#?@0;K#ONR!>pnD<_@M^o25|3S2$7W^
z)!lIZc;Dxjg!#$s17>Gd`h8%R#f10I3LUW(oP)uby}^85*eaRAn|b|#+XJmv>IvWA
z$uLRG9G1cDx1W#TM-SlvhXBwDqgyk7O(e{%VbQlE&vKfr=ia<}hAGR9Q@y;%Jq*;a
z$DMv3mhW!W!n!x)Le0oI)q6GRl*L42vN!)$gkO#4{z}QAYKHQ>&e}Q!d73TCE0ZK}
zcI8UVlFu>4O;t-zol^N@^B>37oP%EhmL2!TWgl@i@Sl_U?dcVZff=ovWBh#mkhYlI
zM_|PSYVj?Q*y2fXmLbs0d*iW#-)oL{gBUSfLg(q=_5kE!JAj7RdUDl|At%!@_K=SU
z37PfK!b?~Fdq2~e{p}evWpogQ2DuDws0SFC7z2PLGwQ{&N{b;Zshi(e(Ng6fPqAbw
zS!K1zbi%4=kt&Bwfj)}Ja^jmgm~4Q(-$awBZ{~MCAu0M^&iyQycS^w%{}ZX;x0?3f
z4lh;@;!LEr&H}{Hf(sfoRF7d?$^K>A&R~kI4GnX%0PMZ&Fbcz@UfT#a%E_PLca^72
zJ9xwL=bl%ipVxF4L+A4wCvBg9*IlZMhP6rUdCCjS74gO_(QoO(VvaYau;H1Zic6T1
z#>0KRfhmpPw8a9($YJ2bpH}Qqxi8lbQXg%8&=i#;wNUHa*t>3?YaBpMF4`sj`0J~<
zD2WouW18jO>}uTn>~+zo-;c9R&AD+~?=z|5?Ld?J#0m|2f-E@{q!?L7^RS8JMoo>(
z9h|^0*d>g;N=Bwqme+6`hbPt<EFDU*;4eNqiPlk_Lf0~dnK`;w5g*kiDV`iFpQ#D;
z3llRu=F7h?_*S=*U-K3_41v?&2#MPC_r=>E>=bWeN-1JH`C10NN?(+nn%^U90*C}#
zn)>n`^@p8NaW!M%f1d_rtfVL!P-HN{H=**<iKJ2kdk}K4@IlvC_Rn_c#54?*i>SoI
z7>;rbHXaM)_#&m^lY#S-LR%;oS}!GyC}U-aG@PNBtgg!epIz?#Po9f}{`4kr05bGc
zG+|N4tYmvG8*GYOAr~clRqdE}i5Xp7wA!OBC%;Q5Ti;jCQCYW{+oZdZHI7=dz_feQ
z_z7C9u#4iKr*-kEOF6EqQ%_Z5hN-4Iq9KoO_J^>EV;ub)W_gV$lK)5g2);8Hc!pG>
zQwL@7q)U`${jsk;YPTv$&I5=?sis6o5)&e0GI3Z)7CfG<PKYNgwnNgJ2Tk0&^b#gP
zN{Z=$E`#{|et4Swc-E-jjq6sWhKP1!Og1w*PB1heju<OJ#(2*bgCUx`pbf96imY2B
zt$^NuLx#a3QHIJ=aIFrfjs{~F+N{lmeB5v*IPOyfEAAK46?D8bk-%Z>Uv4X0VFLWu
zTxObwr}{+6*RO)U6>X&8+=epfX|AxcWf{3EH47Eg)y2Td^_jscgbKLYt%ehuO=Fe`
z(g<kSvm89xW@F;docCr<uRZrBxXHko_piEwUWo+qxqX$~^I<ee@++;4_S0QA`6#ci
zFZccEP42{>IwIT4(~cl<WZX8Oi4jSe{TYJCoI+a)w^{HAZ){5iChS~*b}PG2X{OSK
z&I}8(#9PYNxtL*2cvr3@JL2)0h4DJ6ynUnRkJ?#V0BOO?GGUf=;jet!@|btvr@^MG
zOijcMo<!s*80V1yLIAjgGlDlOktHgjA?y4kVHXYS)sV|?w3y1Y$SsGWTHxI%7S491
za}frf7CHP6y3suE!(wQ~2=;jY98HKI07lyAZf*=4x~^+Nk?_hVUvN`AaIBH(g?YBj
z7yfa?Qh%i$>cxV782W*9XI>+9e$YrE8LIi7&dcJ$O7dq};|9<n>_Hzy9$Zhb?VyD}
zP6+xTJYAPC=v!E1^XDgf&xLoi$bM1jb0B?!Nm9#mGUz7aQr#<oF}5q00gG64#}#x=
zLcZuI+NiKxgK?%0Y~eEq0JMAtQWI_iz^4$e=R#9bG!z{wG?2Jnny!tlC@oak9~YSy
z(-~zlS`8t*iR$k()w{O>O$&L}!n|J#bGj1md$!A4jg|VsK(R++zUxtmHF&%xzBMt|
zxWZkB-$kc>RDZnem@c>yg0yoy{%rH&ymdj#q5A%=<hj)bm!Ra9K~gReny{#%HOzRi
zc()*zI7It;aN4&~1)@*o3+#qxfW9blN;cd!ZU8T_cM24SqTQabIuc~{ef*uuGFz3k
z*nVme!(J8j2kgZk=K6+8P}V=;(0W=DM<Fx8A#=qwMxj63lPF8`pI2MohJzBx(xCN7
zebUOiEZvO*dioyelW&ylYAG5o!MZdUh$0p2#2?13@!E)qz}+DSUUQmeXi+FA7$b~`
z`&8e03S5e`OJ|S2X`?7NgEPMEPLy3uC9DNwZjWbt7JfY>dB!Jczh)S&WE(d*4*Ro_
z<C>|pPLROFsVgmEh~7f~u0fkj1(OloePhi{y67X`DlE<0yGBz=eaF?{0hIxBJgo#7
zhFpQTP6x2<HDY?MnCpSgq}^zTO-g)mlk9<7DS4*pCix=hYoCSs=v=QvUWEIsr~b3T
zUKCV{Bu&dye!B^B8>9!ViJ6?juyP@8^#Y&vPu{y!CS=QR6mO9=@UGrD)doDK94;>V
z(7d&LlsTH#6XfuYMi?j=U7Vp#VBed3G1aq*75cPX;wwWNrbmC9<EKN&f?o;o!LzLE
z2S%XAFVR(C(@(bGZu!WV9E}pIzJO|5SpU?h$Z4q-vAa@kwi#AOyskabO1YNm)R|a=
z7&s{Jzs`X9o2jDj2a~){o4G^=wC&fC{W@5kZXKCG@p)@O?L9KNSHIX-B_~y(V*==`
zRy#~)O7NapFG1xvWULLRw-h~K>vYvcnbEi&3+=Ee;4mf&ku)FRl9oyPC9&*}5Q8)=
zk2mHGqsUeM7FpNbV8jL{bOBSLW6QN^hl0_<smZ>Y9)z;k2I;1?czMnDcA<tJ3c(ta
z%=oZ?*J&~d4f4h1gDM}s2UsiJnU%=I@GkS|HzKZ!=^-E$rg2Hos_@S%Ifci0_Zp80
z0VMUK5g7v^X;o~ddc6`Y$Pp-e$PzXw>Od5PgGerGXvdtzB(eVVov~ytq{24fgoQ+T
ztd02S=v8?qy8G~Yv0yl`H#e7M3h=ZJqAx=kkqB0&Id{+zs4G`2%n1SJSSvIKmQ_Wv
zIj$R8qn%<#(OY5cRlK-mo1lMKROj~AzS=3$H?pTB-DLYkt!!1TvRqajK3As{|DHV>
zmM1~qsL~g`t%2I^*H^TB$W(3zRvGjg@MlPKRSaMcW%zzm`*qv+SETuk@5~R~)`hdc
zF<5<4E?`_3LVP}+uf*l)ly}oouSJEX=KmaY;bt@+-`<3)0Z3=2W_tZaUDqI;1{pSH
zX@iKXMwyH+RW3LVLUQhS+xk`cqIYgP?7<^nc|l<&{krTkWun(qWkAXTA`eJ_JH2?)
z8_e4EhVCr{B+uh<t2<+5938KBnuAvGu_rr-snU3^*CI!Y?VufRN+7c}ruA*UjAUt(
zdyZ-bmAy~8VtcF@+feLIJQhV7+b%>+8Od4INPr7f+NaW1SwIA<g6Pu$#OTy8-I+t&
z;QqVf(9ioW>Zgwfdl_M$Toghr7@@g@e7FXe44Ak+x$bs+!>{Sg!Be7b_Db`fwBX&T
z@l~l^P_!i)YkIMOoHtL!PyLm;JeW$yG7>DTq`>U0ACYd|2l=G9U8HVxl1!bdeBDI5
zsbY^T<p$TC^=z8bEsEpA;-pBjf*@T9%r$@l`=r{<VU=UX_Zr*T_#{fPx5xPSXH3?V
z!hYXAyhUVwqyHO2@<snP{R)I?|CAbwIk-Irr7~k-i(YE0;}}OsV?^6VrDcjfOIra-
z-ON+qq2c2_^bQS~=~;Rl?Xz`(779!5c_PbTPz@e_5fl2<fl4jg#rNfxlY{Qj%z2~i
zuje9Gr$dCSAUhtY?{0ivqU;X6=KrPEUtf?=QPD+WPz=h5-Ee>Db^7u3D2CltH)mUO
zwQY?3mgQvc<W6LUF<BtX+Rj{gt}PW_mW3O6mIJ+Nsehhon^6OokG?hjJ|3Db{-3c9
zBZ$uXLS?$(mX@bqB~e_;m?VKjnsHUKk|rAqHK1k~Sby1{6FrOS=&zc@zwQ^*G~f0$
z)tKbB(aBw?1klnem2SWwskb}=Ie@*ZwYmO*r#?UXwBUzUU)^CzC2%jwx2>H-X{VDR
zTNJ8WtF6`3(HuUb-?%7@CFlGN0XuXzd-o>%2J%tqoI2$tMmhpl)F5Nb1HRsIb+a9<
ziYdlL)iu^!$$j9f>eGF{&L-!pt9YDkna4dAY{e+)53DY&VH}c&Tt_CI@>HK}P`9Xo
z&jA$nzRnkm@-fp)0H$t-ehA^*+Z!vP5^A6e@O4;Kt}+2~xTBWXE0;%sG~QNGY7<6!
zE@S&ea@M=+-rV>)(`K)Ig_rYm9`Yx6qHZH@s-u^NgKB<4fp~?E+9Xa0K1e22BJXeV
zZk7r1vn)<Cno~kAi)Oe9D$ZM(JndWBmJY{a5PcfW4VUn%>ekvy3c1L;%VVMMt^z38
zpF0izwLY)RjHHc(9krEpmLlOwA_4rFg{Ks_=E&+?eNW1W|CF`a>-BkuS>kkD#US(s
zw1Hy8$ZSKtuP^4%wp$tZDrNKk<RJUyQbug?ha+4l_DB1E;z_7=-O3j%=qGzzn4S9j
zX`Sa~xUJ-o9^vQ1q(2?^RH%ibgOFTgh)ivbZ*gl!<YMv~n95ZL13VFlb{T$|Rm?>4
z1*Vjpc-LT5_hbe9I!EIzgAT`=<r$@qQcm)s8!cQS;vb*%dW6~)5vCWBAPr|8DQL|!
zN*FRjbBAHvpwu@+6TJDXE^KIQ08D+~73&@w=6CHybnX|unkJ6xrBOC9gm057Le!u!
zg-v&3?IyXCIIk3aIEySZE8RRN8lgO@uCPMV);0usS++#R6}^w`ri8mc`$kImhx8X+
zpt(0@L7tYdbwQ#(U^!H#YyUKYyjB9)TH-_PP`f46syr79R+pz4j1>ppBEUliRI7|g
zFr#5kA@nRDoz-#e{1!nX6mzcY{9f_L;sk>w8IhuGc2_-SRH#%FH2%8#LB_N)J;3oD
zQ(v8ZV|7=$iRppfqF1hBb-Lo1R?ec2ZnT1ATV-mB>f36g8oUj@?R-oD)?8zW1_S}|
zNIB&D$A*`oY=+l+JWL{nG~2Bo_C7RAf6^;|Ol0Bi2QE>3g{&dU+^ee>y38nUOpc8@
z$bKklmz8Z}%%iwvHkmUt#Htxn@wcxTQ)k6I<Cmn1Va8b#uCjZrIgAUa!H&2#ziOY0
z%HvrWiOo@ERN3DpI!rAn%SsM4p-D@oOl$k1@WlpF?DsX9TsZp&gs^t!Jr~os%dDXn
zukL=MAv&RwsdEo?>-yu%`~eZnc;_$Iqn@nxAWl&?!;32DRj=CBmvolE@5UY58<bfK
zxC4n(1xbBu-h|Yoi3BgDOT>K&CwSWa{ubA!hS5EH#~i1HUIhfOgHf?c_jPF&{&>!#
zs16lNQ;}sHa0^>bPrDqYGr4Yb=Z)A_eki?s!T8$w(!%A^-}JV<cIW~_YxDTXzThaQ
zQPgXBfukWAM@YwbB<rMo%f?XW$U|f_P-iXmu5;X4;LvPK#;BP_exrcab*PMl`TJ+7
z?GqZ&u4)^827fj-W6BN2bHxh#A8i7e<9yM#f>IcyOemITX0KV42c69Je=W5MR24c^
zMEVGSNs~Ti#AO16=^B!z(&@UDVh?lWfP-sRPe*I6T4{%~w|VEjCxx+-$SeEkVoGta
zsUk#2FKBvji1?bqw`f`ES@o`oSD$|{Y{WZU2#nGadDIkbF_e&KzP$4<3NG6i{m>Ep
za~8$d@eEy~{InWxiCP~yt((*(i?JWQl=t*il3pwjvPjG{a7{iBla=ApV37sO>`Y@6
z7JWQ_TB^y!rS?nXhgB8oK)=+ETP~;5;p&5((w|j6nhux=$l}+RuAcel?d5;<v090d
zsfZkdYJzgz1?39*pYOaU$dtidH$)q%5;o)8uG4^#6Q2FN`LJEE`a^4%$egH-A*qmv
zDN#P2s3z~DfHT*w0O{T4T`jU8nvHu%L0wXPG;ked#yyj%cj}o!FVnq)*Ick?V0MXE
zh`8|ZYs7q6ONer(PK0a3xL{A-DiDlloi|bu=Uq~Ef`(hyV@^Ce^NJQGNQOY%f{T#Y
zlxERXozk7%cwAH@=SR9*m0QT%R$qwuvsj|@&;vc{%|a#R_|HDyg{abOKFe?y^-%1W
zu=C!?y*>gT1XdQ%`%sinj*Zh#%xMpP&WSe}v~|yA(M)aO`}>dM%Fw|pBfiggyHjzQ
z34Y=<INY&gnrb(9^Lq!_Z!lCV?&2W@*wRJAXF|+Azm=g6Rt-nz!EH_e_%}D{M|9$Z
z<omE;q0ziF1?0$(*C=(KIr96@Y_nb`)*_x|&f8QQ==<olJ4ad`=xVQ-kZ@S)Q-#n_
zGL4I|wnrwvq-3Gi%qcgrnP^=vl`~R|1$~?n0Qb#TN$Y$n0JX-XqW>lz+Q#Ea{(-D;
z)Wuw3m+@uOfeBB{r|bF(kGczszm#fJVNSXKu-5+sfnVIM<IUqv{mJy3Yaa^h%jQE-
z#Hq@=_O#yG7*xHc?_mKx?7IH1-->*Usy;)W#O-U%3<FJtNxC47$pp&}<CA8g_a-j^
zk%NO5^b4Me;N2n^f`nt)MfyO&wux_r#d!Tp*{>ZPP={p1LCbBiYF`Al(>uc+n9dMp
zLJJS(Gq91xOpnmLard|;WrBod#J<!$?>Pz&I4~Dqd!c#cs(MYjYWZE;INkn(PQIO6
zN+{bVnvy3^La7nwEgPd4AF2I&aCq%POVjnKwsg{gsG?+6m2x8C(++J?V-eaHDcmU<
zmw4cFcJ*4^nhsohW}|Ppbx62<{a`okZ9HZ-caFL-gsnZ3khpe+@^dN$io2KOYG&zJ
zn4`9h{1ifA({)|>MMG`2EYbC_PDRheHP_zbGNGlTmdmPRNq!C&T^%wV=yyo>wU%qF
zi^l*4hzCa4wJn3yYN__d<Fkv5_bdA$WUQM>=7O6lDVYhF!ifQfyH}#Y4#d7HAGVlg
z&|SzGp@HvcpJZjsj6oyzZcHyM1zjypYfQ#Dxnio<-M&BNz&t8hK7<VknujUvfmee2
z)h!qUq>oaU+=c%lM*>ZxV)xp|Xz}-aeDCtB=Bw{vv!Xd4J&TQjU`7c9la?|4CS#^9
z#J(S|*jeR^pJ{PL0qEtpKSo~!7`DnpH1sTKAPQ8aPy+12d$b2m)WYi90*+XuF&tLh
zKijyRdi|MmIJF*KS9EusSZaFzs!AJA;#2+fn4Pv{99aTo-X-*DLhHoE#X{%^smHZp
zm5~^AQ_)HDSe7)U5y6i+5*k=H`10{QAM#qoF9eb<8?=M`UB7(p+`f7CtUkwZ#W4dW
z=>JRHe7{E^>YH%VA8{6Fb3?GVKWdgo&K*trv1(;fqWU~RP`z}`1@Giopmt~5NF%*K
zVNm*I<M7LQX#wqm&zlF}CIxLTDxLM90zK~C=BV@XeYhXK+nR+4&z=VDj03+Zf%E}s
z#%bLp+w%1T#9ST%!rH(ZJJJ%HEG%vZ^SnI;E#8ie3byasDOs;L&%o|{aZ7&3G`Sk=
zwTX3kmFond&p)O1BiW>fA<9HW4u%W>_yO-g#C*wU1<z?8m<Y9{a0iYsXkQheXH8bh
zRW(du*&6ONSTVdk7paFpPdFmAH$T5v?GW2ab$cEAdsA4+4HA4umit)nn*z+O3kB{j
zw(yimRqr~mE#pT3&{?!)Cyq+oB8Yq?Bz#|r;coV&B3bto&h^q!2gpN|2s>r>sL4Mh
z=i;jo(G@}~BGFj@On?Rbyk|%-MPx%jWHcUW#qW6RC|8g9!cA_g+!a$C0_1OzNhCMx
zQi{J-_GZ`p(C6P~&!D_>wFhC^NqR>V_bd7A4ujr!`JujICl_SFMbKMvt&i^^#g%T&
zjg%ZAHz#;To=h!JoF@3YB>BVVl%L59&g@a@h4QennPL)V2|t0s-ggxnx(zvA$OK^p
z-RSV6Bp`b>&7EKgdLVmD5j%r%NV*08ER9}5i)gb#gzA-}n%P^5g9Gg?i)*t+Ixb0G
zbNlh-GCV~urO}fgkSM3L8aneM6$dW<t>9<57DQXXbBszHszI72CplFHJ3Ih}B0;!p
zq|28+F1~ZRU-V*5g?{26Up4v`KfU|ifsU5uC671}@i`jWpS6HjmC4chcVf@Qda1hs
z07QL9XJ;>Xgbt2gHc&VRY!RbUKLOsh4?h(10ssU&UH_r~qX4h|uQK>Qn*TQm0d5oK
z;RdmXHyV4`|K$_$KR*Ap2KW0P0{-RVzx@2G`L7!IU*T^~X9@Lihl`Y!4(=ZRG{G$Z
z&*0~J{)riQ>j<@lz}rBM(Esjxg){(Q60Fnz*OTlvPzSevYyjMyJ)QoQpSTvh43&pC
zK^-igbby79woX=X0oUE}Z=+92aH50$T~7&hvwUiQqNgrldb&A){+5BZ?jGh2aM{}3
z!~L&h{9O;wgP%xx3iOGdp81;)0Ju-WQ>bv_v2=$%br^sc92`soS3`nxLNL)&6#~%e
z3GiQ2Faka<3Y-Dqc>sXiJ^mV_zf|Bi5&!_{Nrt-s2E#ceJPeDc3OGHveFEt}+yma`
z2bcdPcs&sA9(WA*#s=qOaP-0v3r9K}d2qlFSpi{ic*6l70^n;n;NAU8HimOkI1J!`
zz+nxC9USnv38aKOd72M|?j9D-|2TY#1?~qx?r!Y@eJZ#)JHTzhPjSH?czlH4Dc#Mj
zUEuuRmj4;OxhK>E?ge1zX!#d^QltK#S%%LHH;9YNlg0l{?6!YTCHhhi=ch2J;K#zA
Vj64|zC_}ug_yjq)IN=xP{{mAaTcH2|

literal 0
HcmV?d00001

diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts
index 2eec80d76346..40d187f5a882 100644
--- a/packages/backend/test/unit/FileInfoService.ts
+++ b/packages/backend/test/unit/FileInfoService.ts
@@ -15,6 +15,7 @@ import { GlobalModule } from '@/GlobalModule.js';
 import { FileInfoService } from '@/core/FileInfoService.js';
 //import { DI } from '@/di-symbols.js';
 import { AiService } from '@/core/AiService.js';
+import { LoggerService } from '@/core/LoggerService.js';
 import type { TestingModule } from '@nestjs/testing';
 import type { MockFunctionMetadata } from 'jest-mock';
 
@@ -35,6 +36,7 @@ describe('FileInfoService', () => {
 			],
 			providers: [
 				AiService,
+				LoggerService,
 				FileInfoService,
 			],
 		})
@@ -323,8 +325,26 @@ describe('FileInfoService', () => {
 			});
 		});
 
-		/*
-		 * video/webmとして検出されてしまう
+		test('MPEG-4 AUDIO (M4A)', async () => {
+			const path = `${resources}/kick_gaba7.m4a`;
+			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
+			delete info.warnings;
+			delete info.blurhash;
+			delete info.sensitive;
+			delete info.porn;
+			delete info.width;
+			delete info.height;
+			delete info.orientation;
+			assert.deepStrictEqual(info, {
+				size: 9817,
+				md5: '74c9279a4abe98789565f1dc1a541a42',
+				type: {
+					mime: 'audio/mp4',
+					ext: 'm4a',
+				},
+			});
+		});
+
 		test('WEBM AUDIO', async () => {
 			const path = `${resources}/kick_gaba7.webm`;
 			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
@@ -337,13 +357,12 @@ describe('FileInfoService', () => {
 			delete info.orientation;
 			assert.deepStrictEqual(info, {
 				size: 8879,
-				md5: '3350083dec312419cfdc06c16413aca7',
+				md5: '53bc1adcb6acbbda67ff9bd484896438',
 				type: {
 					mime: 'audio/webm',
 					ext: 'webm',
 				},
 			});
 		});
-		 */
 	});
 });

From 61978cb4ca481f099828ef1b0b95258029937008 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 31 Mar 2024 14:16:42 +0900
Subject: [PATCH 137/266] =?UTF-8?q?fix(frontend):=20=E3=83=9A=E3=83=BC?=
 =?UTF-8?q?=E3=82=B8=E3=83=87=E3=82=B6=E3=82=A4=E3=83=B3=E3=81=AE=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#13642)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 locales/index.d.ts                            |  8 ++++
 locales/ja-JP.yml                             |  3 +-
 .../src/components/page/page.block.vue        | 15 +++++++
 .../src/components/page/page.dynamic.vue      | 43 +++++++++++++++++++
 .../src/components/page/page.text.vue         |  2 +-
 5 files changed, 69 insertions(+), 2 deletions(-)
 create mode 100644 packages/frontend/src/components/page/page.dynamic.vue

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 3dbe46c7b2f6..01bec41d9e9f 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -8830,6 +8830,14 @@ export interface Locale extends ILocale {
              * ボタン
              */
             "button": string;
+            /**
+             * 動的ブロック
+             */
+            "dynamic": string;
+            /**
+             * このブロックは廃止されています。今後は{play}を利用してください。
+             */
+            "dynamicDescription": ParameterizedString<"play">;
             /**
              * ノート埋め込み
              */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index aa765d13108f..4ba9ea0221b1 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2331,6 +2331,8 @@ _pages:
     section: "セクション"
     image: "画像"
     button: "ボタン"
+    dynamic: "動的ブロック"
+    dynamicDescription: "このブロックは廃止されています。今後は{play}を利用してください。"
 
     note: "ノート埋め込み"
     _note:
@@ -2625,4 +2627,3 @@ _mediaControls:
   pip: "ピクチャインピクチャ"
   playbackRate: "再生速度"
   loop: "ループ再生"
-  
\ No newline at end of file
diff --git a/packages/frontend/src/components/page/page.block.vue b/packages/frontend/src/components/page/page.block.vue
index 164720ac6bd0..c7f72dce8cf4 100644
--- a/packages/frontend/src/components/page/page.block.vue
+++ b/packages/frontend/src/components/page/page.block.vue
@@ -14,6 +14,7 @@ import XText from './page.text.vue';
 import XSection from './page.section.vue';
 import XImage from './page.image.vue';
 import XNote from './page.note.vue';
+import XDynamic from './page.dynamic.vue';
 
 function getComponent(type: string) {
 	switch (type) {
@@ -21,6 +22,20 @@ function getComponent(type: string) {
 		case 'section': return XSection;
 		case 'image': return XImage;
 		case 'note': return XNote;
+
+		// 動的ページの代替用ブロック
+		case 'button':
+		case 'if':
+		case 'textarea':
+		case 'post':
+		case 'canvas':
+		case 'numberInput':
+		case 'textInput':
+		case 'switch':
+		case 'radioButton':
+		case 'counter':
+			return XDynamic;
+
 		default: return null;
 	}
 }
diff --git a/packages/frontend/src/components/page/page.dynamic.vue b/packages/frontend/src/components/page/page.dynamic.vue
new file mode 100644
index 000000000000..8c511a690da4
--- /dev/null
+++ b/packages/frontend/src/components/page/page.dynamic.vue
@@ -0,0 +1,43 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<!-- 動的ページのブロックの代替。利用できないということを表示する -->
+<template>
+<div :class="$style.root">
+	<div :class="$style.heading"><i class="ti ti-dice-5"></i> {{ i18n.ts._pages.blocks.dynamic }}</div>
+	<I18n :src="i18n.ts._pages.blocks.dynamicDescription" tag="div" :class="$style.text">
+		<template #play>
+			<MkA to="/play" class="_link">Play</MkA>
+		</template>
+	</I18n>
+</div>
+</template>
+
+<script lang="ts" setup>
+import * as Misskey from 'misskey-js';
+import { i18n } from '@/i18n.js';
+
+const props = defineProps<{
+	block: Misskey.entities.PageBlock,
+	page: Misskey.entities.Page,
+}>();
+</script>
+
+<style lang="scss" module>
+.root {
+	border: 1px solid var(--divider);
+	border-radius: var(--radius);
+	padding: var(--margin);
+	text-align: center;
+}
+
+.heading {
+	font-weight: 700;
+}
+
+.text {
+	font-size: 90%;
+}
+</style>
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index 4e501bd6999e..e0c7956f6e34 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div class="_gaps" :class="$style.textRoot">
 	<Mfm :text="block.text ?? ''" :isNote="false"/>
-	<div v-if="isEnabledUrlPreview">
+	<div v-if="isEnabledUrlPreview" class="_gaps_s">
 		<MkUrlPreview v-for="url in urls" :key="url" :url="url"/>
 	</div>
 </div>

From b4b47d85cf50486980cc3fa3575cf48c7fb0a2e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 1 Apr 2024 20:44:24 +0900
Subject: [PATCH 138/266] refactor(frontend): use `scrollX` or `scrollY`
 (#13645)

---
 .../frontend/src/components/MkContextMenu.vue |  8 ++--
 packages/frontend/src/components/MkModal.vue  | 24 ++++++------
 .../src/components/MkUrlPreviewPopup.vue      |  4 +-
 .../frontend/src/components/MkUserPopup.vue   |  4 +-
 .../frontend/src/scripts/popup-position.ts    | 38 +++++++++----------
 .../frontend/src/scripts/use-chart-tooltip.ts |  6 +--
 6 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index 5ca3c77fb23f..a807742bb9ba 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -47,12 +47,12 @@ onMounted(() => {
 	const width = rootEl.value!.offsetWidth;
 	const height = rootEl.value!.offsetHeight;
 
-	if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
-		left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
+	if (left + width - window.scrollX >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
+		left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.scrollX;
 	}
 
-	if (top + height - window.pageYOffset >= (window.innerHeight - SCROLLBAR_THICKNESS)) {
-		top = (window.innerHeight - SCROLLBAR_THICKNESS) - height + window.pageYOffset;
+	if (top + height - window.scrollY >= (window.innerHeight - SCROLLBAR_THICKNESS)) {
+		top = (window.innerHeight - SCROLLBAR_THICKNESS) - height + window.scrollY;
 	}
 
 	if (top < 0) {
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 40e67fb4e088..eb240da75978 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -175,8 +175,8 @@ const align = () => {
 	let left;
 	let top;
 
-	const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
-	const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
+	const x = srcRect.left + (fixed.value ? 0 : window.scrollX);
+	const y = srcRect.top + (fixed.value ? 0 : window.scrollY);
 
 	if (props.anchor.x === 'center') {
 		left = x + (props.src.offsetWidth / 2) - (width / 2);
@@ -220,24 +220,24 @@ const align = () => {
 		}
 	} else {
 		// 画面から横にはみ出る場合
-		if (left + width - window.pageXOffset > (window.innerWidth - SCROLLBAR_THICKNESS)) {
-			left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset - 1;
+		if (left + width - window.scrollX > (window.innerWidth - SCROLLBAR_THICKNESS)) {
+			left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.scrollX - 1;
 		}
 
-		const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.pageYOffset);
+		const underSpace = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - (top - window.scrollY);
 		const upperSpace = (srcRect.top - MARGIN);
 
 		// 画面から縦にはみ出る場合
-		if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
+		if (top + height - window.scrollY > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
 			if (props.noOverlap && props.anchor.x === 'center') {
 				if (underSpace >= (upperSpace / 3)) {
 					maxHeight.value = underSpace;
 				} else {
 					maxHeight.value = upperSpace;
-					top = window.pageYOffset + ((upperSpace + MARGIN) - height);
+					top = window.scrollY + ((upperSpace + MARGIN) - height);
 				}
 			} else {
-				top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
+				top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.scrollY - 1;
 			}
 		} else {
 			maxHeight.value = underSpace;
@@ -255,15 +255,15 @@ const align = () => {
 	let transformOriginX = 'center';
 	let transformOriginY = 'center';
 
-	if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.pageYOffset)) {
+	if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.scrollY)) {
 		transformOriginY = 'top';
-	} else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) {
+	} else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.scrollY)) {
 		transformOriginY = 'bottom';
 	}
 
-	if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) {
+	if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.scrollX)) {
 		transformOriginX = 'left';
-	} else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) {
+	} else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.scrollX)) {
 		transformOriginX = 'right';
 	}
 
diff --git a/packages/frontend/src/components/MkUrlPreviewPopup.vue b/packages/frontend/src/components/MkUrlPreviewPopup.vue
index cf75064be795..e972973dbaf6 100644
--- a/packages/frontend/src/components/MkUrlPreviewPopup.vue
+++ b/packages/frontend/src/components/MkUrlPreviewPopup.vue
@@ -33,8 +33,8 @@ const left = ref(0);
 
 onMounted(() => {
 	const rect = props.source.getBoundingClientRect();
-	const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
-	const y = rect.top + props.source.offsetHeight + window.pageYOffset;
+	const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.scrollX;
+	const y = rect.top + props.source.offsetHeight + window.scrollY;
 
 	top.value = y;
 	left.value = x;
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index fb1a8f4fdc97..41b27a1afb1d 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -106,8 +106,8 @@ onMounted(() => {
 	}
 
 	const rect = props.source.getBoundingClientRect();
-	const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
-	const y = rect.top + props.source.offsetHeight + window.pageYOffset;
+	const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.scrollX;
+	const y = rect.top + props.source.offsetHeight + window.scrollY;
 
 	top.value = y;
 	left.value = x;
diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts
index 8c9e3c02c36d..3dad41a8b31f 100644
--- a/packages/frontend/src/scripts/popup-position.ts
+++ b/packages/frontend/src/scripts/popup-position.ts
@@ -26,8 +26,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
 		let top: number;
 
 		if (props.anchorElement) {
-			left = rect.left + window.pageXOffset + (props.anchorElement.offsetWidth / 2);
-			top = (rect.top + window.pageYOffset - contentHeight) - props.innerMargin;
+			left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2);
+			top = (rect.top + window.scrollY - contentHeight) - props.innerMargin;
 		} else {
 			left = props.x;
 			top = (props.y - contentHeight) - props.innerMargin;
@@ -35,8 +35,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
 
 		left -= (el.offsetWidth / 2);
 
-		if (left + contentWidth - window.pageXOffset > window.innerWidth) {
-			left = window.innerWidth - contentWidth + window.pageXOffset - 1;
+		if (left + contentWidth - window.scrollX > window.innerWidth) {
+			left = window.innerWidth - contentWidth + window.scrollX - 1;
 		}
 
 		return [left, top];
@@ -47,8 +47,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
 		let top: number;
 
 		if (props.anchorElement) {
-			left = rect.left + window.pageXOffset + (props.anchorElement.offsetWidth / 2);
-			top = (rect.top + window.pageYOffset + props.anchorElement.offsetHeight) + props.innerMargin;
+			left = rect.left + window.scrollX + (props.anchorElement.offsetWidth / 2);
+			top = (rect.top + window.scrollY + props.anchorElement.offsetHeight) + props.innerMargin;
 		} else {
 			left = props.x;
 			top = (props.y) + props.innerMargin;
@@ -56,8 +56,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
 
 		left -= (el.offsetWidth / 2);
 
-		if (left + contentWidth - window.pageXOffset > window.innerWidth) {
-			left = window.innerWidth - contentWidth + window.pageXOffset - 1;
+		if (left + contentWidth - window.scrollX > window.innerWidth) {
+			left = window.innerWidth - contentWidth + window.scrollX - 1;
 		}
 
 		return [left, top];
@@ -68,8 +68,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
 		let top: number;
 
 		if (props.anchorElement) {
-			left = (rect.left + window.pageXOffset - contentWidth) - props.innerMargin;
-			top = rect.top + window.pageYOffset + (props.anchorElement.offsetHeight / 2);
+			left = (rect.left + window.scrollX - contentWidth) - props.innerMargin;
+			top = rect.top + window.scrollY + (props.anchorElement.offsetHeight / 2);
 		} else {
 			left = (props.x - contentWidth) - props.innerMargin;
 			top = props.y;
@@ -77,8 +77,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
 
 		top -= (el.offsetHeight / 2);
 
-		if (top + contentHeight - window.pageYOffset > window.innerHeight) {
-			top = window.innerHeight - contentHeight + window.pageYOffset - 1;
+		if (top + contentHeight - window.scrollY > window.innerHeight) {
+			top = window.innerHeight - contentHeight + window.scrollY - 1;
 		}
 
 		return [left, top];
@@ -89,15 +89,15 @@ export function calcPopupPosition(el: HTMLElement, props: {
 		let top: number;
 
 		if (props.anchorElement) {
-			left = (rect.left + props.anchorElement.offsetWidth + window.pageXOffset) + props.innerMargin;
+			left = (rect.left + props.anchorElement.offsetWidth + window.scrollX) + props.innerMargin;
 
 			if (props.align === 'top') {
-				top = rect.top + window.pageYOffset;
+				top = rect.top + window.scrollY;
 				if (props.alignOffset != null) top += props.alignOffset;
 			} else if (props.align === 'bottom') {
 				// TODO
 			} else { // center
-				top = rect.top + window.pageYOffset + (props.anchorElement.offsetHeight / 2);
+				top = rect.top + window.scrollY + (props.anchorElement.offsetHeight / 2);
 				top -= (el.offsetHeight / 2);
 			}
 		} else {
@@ -106,8 +106,8 @@ export function calcPopupPosition(el: HTMLElement, props: {
 			top -= (el.offsetHeight / 2);
 		}
 
-		if (top + contentHeight - window.pageYOffset > window.innerHeight) {
-			top = window.innerHeight - contentHeight + window.pageYOffset - 1;
+		if (top + contentHeight - window.scrollY > window.innerHeight) {
+			top = window.innerHeight - contentHeight + window.scrollY - 1;
 		}
 
 		return [left, top];
@@ -123,7 +123,7 @@ export function calcPopupPosition(el: HTMLElement, props: {
 				const [left, top] = calcPosWhenTop();
 
 				// ツールチップを上に向かって表示するスペースがなければ下に向かって出す
-				if (top - window.pageYOffset < 0) {
+				if (top - window.scrollY < 0) {
 					const [left, top] = calcPosWhenBottom();
 					return { left, top, transformOrigin: 'center top' };
 				}
@@ -141,7 +141,7 @@ export function calcPopupPosition(el: HTMLElement, props: {
 				const [left, top] = calcPosWhenLeft();
 
 				// ツールチップを左に向かって表示するスペースがなければ右に向かって出す
-				if (left - window.pageXOffset < 0) {
+				if (left - window.scrollX < 0) {
 					const [left, top] = calcPosWhenRight();
 					return { left, top, transformOrigin: 'left center' };
 				}
diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts
index 7e4bf5c9c665..bed221a62255 100644
--- a/packages/frontend/src/scripts/use-chart-tooltip.ts
+++ b/packages/frontend/src/scripts/use-chart-tooltip.ts
@@ -53,11 +53,11 @@ export function useChartTooltip(opts: { position: 'top' | 'middle' } = { positio
 		const rect = context.chart.canvas.getBoundingClientRect();
 
 		tooltipShowing.value = true;
-		tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
+		tooltipX.value = rect.left + window.scrollX + context.tooltip.caretX;
 		if (opts.position === 'top') {
-			tooltipY.value = rect.top + window.pageYOffset;
+			tooltipY.value = rect.top + window.scrollY;
 		} else if (opts.position === 'middle') {
-			tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
+			tooltipY.value = rect.top + window.scrollY + context.tooltip.caretY;
 		}
 	}
 

From c4fc582469a2596a4802496367699b9e04aed9f7 Mon Sep 17 00:00:00 2001
From: Jorge <46056498+jorgectf@users.noreply.github.com>
Date: Wed, 3 Apr 2024 06:02:36 +0200
Subject: [PATCH 139/266] Merge pull request from GHSA-fpvp-74wx-35p3

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 .github/workflows/storybook.yml | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml
index 87481b12cf19..ca82f4bcf3de 100644
--- a/.github/workflows/storybook.yml
+++ b/.github/workflows/storybook.yml
@@ -87,12 +87,13 @@ jobs:
         if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
           echo "skip=true" >> $GITHUB_OUTPUT
         fi
-        BRANCH="${{ github.event.pull_request.head.user.login }}:${{ github.event.pull_request.head.ref }}"
-        if [ "$BRANCH" = "misskey-dev:${{ github.event.pull_request.head.ref }}" ]; then
-          BRANCH="${{ github.event.pull_request.head.ref }}"
+        BRANCH="${{ github.event.pull_request.head.user.login }}:$HEAD_REF"
+        if [ "$BRANCH" = "misskey-dev:$HEAD_REF" ]; then
+          BRANCH="$HEAD_REF"
         fi
         pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name $BRANCH $(echo "$CHROMATIC_PARAMETER")
       env:
+        HEAD_REF: ${{ github.event.pull_request.head.ref }}
         CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
     - name: Notify that Chromatic detects changes
       uses: actions/github-script@v7.0.1

From efa42a1624b0727232263f4ec196e4908ef1e712 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 4 Apr 2024 22:25:28 +0900
Subject: [PATCH 140/266] =?UTF-8?q?fix(backend):=20=E3=83=90=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=83=B3=E3=83=89=E3=81=AEpnpm=20dev?=
 =?UTF-8?q?=E3=81=AB=E3=82=88=E3=82=8B=E3=83=93=E3=83=AB=E3=83=89=E5=BE=8C?=
 =?UTF-8?q?=E3=81=ABbuild-assets=E3=82=92=E8=A1=8C=E3=81=86=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#13659)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* moveto scripts

* add scripts/dev.mjs
---
 packages/backend/package.json                 |  8 +--
 .../backend/{ => scripts}/check_connect.js    |  2 +-
 packages/backend/scripts/dev.mjs              | 61 +++++++++++++++++++
 .../{ => scripts}/generate_api_json.js        |  4 +-
 packages/backend/{ => scripts}/watch.mjs      |  0
 pnpm-lock.yaml                                |  6 +-
 6 files changed, 71 insertions(+), 10 deletions(-)
 rename packages/backend/{ => scripts}/check_connect.js (85%)
 create mode 100644 packages/backend/scripts/dev.mjs
 rename packages/backend/{ => scripts}/generate_api_json.js (70%)
 rename packages/backend/{ => scripts}/watch.mjs (100%)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index d64fcc3d2a05..7f70ae0c975a 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -11,14 +11,14 @@
 		"start:test": "cross-env NODE_ENV=test node ./built/boot/entry.js",
 		"migrate": "pnpm typeorm migration:run -d ormconfig.js",
 		"revert": "pnpm typeorm migration:revert -d ormconfig.js",
-		"check:connect": "node ./check_connect.js",
+		"check:connect": "node ./scripts/check_connect.js",
 		"build": "swc src -d built -D",
 		"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc",
 		"watch:swc": "swc src -d built -D -w",
 		"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
-		"watch": "node watch.mjs",
+		"watch": "node ./scripts/watch.mjs",
 		"restart": "pnpm build && pnpm start",
-		"dev": "nodemon -w src -e ts,js,mjs,cjs,json --exec \"cross-env NODE_ENV=development pnpm run restart\"",
+		"dev": "node ./scripts/dev.mjs",
 		"typecheck": "tsc --noEmit && tsc -p test --noEmit",
 		"eslint": "eslint --quiet \"src/**/*.ts\"",
 		"lint": "pnpm typecheck && pnpm eslint",
@@ -31,7 +31,7 @@
 		"test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e",
 		"test-and-coverage": "pnpm jest-and-coverage",
 		"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
-		"generate-api-json": "pnpm build && node ./generate_api_json.js"
+		"generate-api-json": "pnpm build && node ./scripts/generate_api_json.js"
 	},
 	"optionalDependencies": {
 		"@swc/core-android-arm64": "1.3.11",
diff --git a/packages/backend/check_connect.js b/packages/backend/scripts/check_connect.js
similarity index 85%
rename from packages/backend/check_connect.js
rename to packages/backend/scripts/check_connect.js
index d88e649c099d..ba25fd416c78 100644
--- a/packages/backend/check_connect.js
+++ b/packages/backend/scripts/check_connect.js
@@ -4,7 +4,7 @@
  */
 
 import Redis from 'ioredis';
-import { loadConfig } from './built/config.js';
+import { loadConfig } from '../built/config.js';
 
 const config = loadConfig();
 const redis = new Redis(config.redis);
diff --git a/packages/backend/scripts/dev.mjs b/packages/backend/scripts/dev.mjs
new file mode 100644
index 000000000000..2d0de0f91666
--- /dev/null
+++ b/packages/backend/scripts/dev.mjs
@@ -0,0 +1,61 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { execa, execaNode } from 'execa';
+
+/** @type {import('execa').ExecaChildProcess | undefined} */
+let backendProcess;
+
+async function execBuildAssets() {
+	await execa('pnpm', ['run', 'build-assets'], {
+		cwd: '../../',
+		stdout: process.stdout,
+		stderr: process.stderr,
+	})
+}
+
+function execStart() {
+	// pnpm run start を呼び出したいが、windowsだとプロセスグループ単位でのkillが出来ずゾンビプロセス化するので
+	// 上記と同等の動きをするコマンドで子・孫プロセスを作らないようにしたい
+	backendProcess = execaNode('./built/boot/entry.js', [], {
+		stdout: process.stdout,
+		stderr: process.stderr,
+		env: {
+			'NODE_ENV': 'development',
+		},
+	});
+}
+
+async function killProc() {
+	if (backendProcess) {
+		backendProcess.kill();
+		await new Promise(resolve => backendProcess.on('exit', resolve));
+		backendProcess = undefined;
+	}
+}
+
+(async () => {
+	execaNode(
+		'./node_modules/nodemon/bin/nodemon.js',
+		[
+			'-w', 'src',
+			'-e', 'ts,js,mjs,cjs,json',
+			'--exec', 'pnpm', 'run', 'build',
+		],
+		{
+			stdio: [process.stdin, process.stdout, process.stderr, 'ipc'],
+		})
+		.on('message', async (message) => {
+			if (message.type === 'exit') {
+				// かならずbuild->build-assetsの順番で呼び出したいので、
+				// 少々トリッキーだがnodemonからのexitイベントを利用してbuild-assets->startを行う。
+				// pnpm restartをbuildが終わる前にbuild-assetsが動いてしまうので、バラバラに呼び出す必要がある
+
+				await killProc();
+				await execBuildAssets();
+				execStart();
+			}
+		})
+})();
diff --git a/packages/backend/generate_api_json.js b/packages/backend/scripts/generate_api_json.js
similarity index 70%
rename from packages/backend/generate_api_json.js
rename to packages/backend/scripts/generate_api_json.js
index 602ced1d7553..b4769ef8012e 100644
--- a/packages/backend/generate_api_json.js
+++ b/packages/backend/scripts/generate_api_json.js
@@ -3,8 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { loadConfig } from './built/config.js'
-import { genOpenapiSpec } from './built/server/api/openapi/gen-spec.js'
+import { loadConfig } from '../built/config.js'
+import { genOpenapiSpec } from '../built/server/api/openapi/gen-spec.js'
 import { writeFileSync } from "node:fs";
 
 const config = loadConfig();
diff --git a/packages/backend/watch.mjs b/packages/backend/scripts/watch.mjs
similarity index 100%
rename from packages/backend/watch.mjs
rename to packages/backend/scripts/watch.mjs
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 46512784c3ab..91c2a704e294 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6678,7 +6678,7 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.21(typescript@5.3.3)
-      vue-component-type-helpers: 2.0.6
+      vue-component-type-helpers: 2.0.7
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -19348,8 +19348,8 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-component-type-helpers@2.0.6:
-    resolution: {integrity: sha512-qdGXCtoBrwqk1BT6r2+1Wcvl583ZVkuSZ3or7Y1O2w5AvWtlvvxwjGhmz5DdPJS9xqRdDlgTJ/38ehWnEi0tFA==}
+  /vue-component-type-helpers@2.0.7:
+    resolution: {integrity: sha512-7e12Evdll7JcTIocojgnCgwocX4WzIYStGClBQ+QuWPinZo/vQolv2EMq4a3lg16TKfwWafLimG77bxb56UauA==}
     dev: true
 
   /vue-demi@0.14.7(vue@3.4.21):

From 2f8fb105a5b1d2f6e5cd70ff3246ead07e63144d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 5 Apr 2024 15:59:43 +0900
Subject: [PATCH 141/266] =?UTF-8?q?fix(deps):=20aiscript-vscode=E3=81=AE?=
 =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=B9=E3=83=88=E3=83=BC=E3=83=AB=E4=B8=AD?=
 =?UTF-8?q?=E3=81=ABWARN=E3=81=8C=E5=87=BA=E3=82=8B=E3=81=AE=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#13661)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/package.json |  2 +-
 pnpm-lock.yaml                 | 39 +++++++++++++++++-----------------
 2 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index db7f7b76f649..cbf4e595920f 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -29,7 +29,7 @@
 		"@twemoji/parser": "15.0.0",
 		"@vitejs/plugin-vue": "5.0.4",
 		"@vue/compiler-sfc": "3.4.21",
-		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
+		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.4",
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
 		"buraha": "0.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 91c2a704e294..1dbb172b5dd7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -722,8 +722,8 @@ importers:
         specifier: 3.4.21
         version: 3.4.21
       aiscript-vscode:
-        specifier: github:aiscript-dev/aiscript-vscode#v0.1.2
-        version: github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07
+        specifier: github:aiscript-dev/aiscript-vscode#v0.1.4
+        version: github.com/aiscript-dev/aiscript-vscode/3f79d6f0550369267220aa67702287948d885424
       astring:
         specifier: 1.8.6
         version: 1.8.6
@@ -5009,7 +5009,7 @@ packages:
     resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dependencies:
-      semver: 7.5.4
+      semver: 7.6.0
     dev: false
 
   /@nsfw-filter/gif-frames@1.0.2:
@@ -6678,7 +6678,7 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.21(typescript@5.3.3)
-      vue-component-type-helpers: 2.0.7
+      vue-component-type-helpers: 2.0.10
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -10595,7 +10595,7 @@ packages:
       '@one-ini/wasm': 0.1.1
       commander: 10.0.1
       minimatch: 9.0.1
-      semver: 7.5.4
+      semver: 7.6.0
     dev: true
 
   /ee-first@1.1.1:
@@ -13125,7 +13125,7 @@ packages:
       '@babel/parser': 7.23.9
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
-      semver: 7.5.4
+      semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -14121,7 +14121,7 @@ packages:
     resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==}
     engines: {node: 14 || >=16.14}
     dependencies:
-      semver: 7.5.4
+      semver: 7.6.0
 
   /lru-cache@4.1.5:
     resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
@@ -14192,7 +14192,7 @@ packages:
     resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
     engines: {node: '>=10'}
     dependencies:
-      semver: 7.5.4
+      semver: 7.6.0
     dev: true
 
   /make-fetch-happen@13.0.0:
@@ -15303,7 +15303,7 @@ packages:
     dependencies:
       hosted-git-info: 4.1.0
       is-core-module: 2.13.1
-      semver: 7.5.4
+      semver: 7.6.0
       validate-npm-package-license: 3.0.4
     dev: true
 
@@ -17446,7 +17446,6 @@ packages:
     hasBin: true
     dependencies:
       lru-cache: 6.0.0
-    dev: true
 
   /send@0.18.0:
     resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
@@ -19299,7 +19298,7 @@ packages:
     engines: {vscode: ^1.82.0}
     dependencies:
       minimatch: 5.1.2
-      semver: 7.5.4
+      semver: 7.6.0
       vscode-languageserver-protocol: 3.17.5
     dev: false
 
@@ -19348,8 +19347,8 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-component-type-helpers@2.0.7:
-    resolution: {integrity: sha512-7e12Evdll7JcTIocojgnCgwocX4WzIYStGClBQ+QuWPinZo/vQolv2EMq4a3lg16TKfwWafLimG77bxb56UauA==}
+  /vue-component-type-helpers@2.0.10:
+    resolution: {integrity: sha512-FC5fKJjDks3Ue/KRSYBdsiCaZa0kUPQfs8yQpb8W9mlO6BenV8G1z58xobeRMzevnmEcDa09LLwuXDwb4f6NMQ==}
     dev: true
 
   /vue-demi@0.14.7(vue@3.4.21):
@@ -19871,10 +19870,10 @@ packages:
     resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
     dev: true
 
-  '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz':
-    resolution: {tarball: https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz}
+  '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz':
+    resolution: {tarball: https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz}
     name: '@aiscript-dev/aiscript-languageserver'
-    version: 0.1.5
+    version: 0.1.6
     hasBin: true
     dependencies:
       seedrandom: 3.0.5
@@ -19884,13 +19883,13 @@ packages:
       vscode-languageserver-textdocument: 1.0.11
     dev: false
 
-  github.com/aiscript-dev/aiscript-vscode/793211d40243c8775f6b85f015c221c82cbffb07:
-    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/793211d40243c8775f6b85f015c221c82cbffb07}
+  github.com/aiscript-dev/aiscript-vscode/3f79d6f0550369267220aa67702287948d885424:
+    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424}
     name: aiscript-vscode
-    version: 0.1.2
+    version: 0.1.4
     engines: {vscode: ^1.83.0}
     dependencies:
-      '@aiscript-dev/aiscript-languageserver': '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.5/aiscript-dev-aiscript-languageserver-0.1.5.tgz'
+      '@aiscript-dev/aiscript-languageserver': '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz'
       vscode-languageclient: 9.0.1
     dev: false
 

From 959cc8ff37de620bf0082f48f59963c00d045fe9 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 7 Apr 2024 21:14:13 +0900
Subject: [PATCH 142/266] refactor(general): use `Date.now()` instead of
 creating a new `Date` instance (#13671)

---
 packages/backend/src/core/AccountMoveService.ts             | 4 ++--
 packages/backend/src/core/PushNotificationService.ts        | 2 +-
 .../backend/src/server/api/endpoints/i/import-blocking.ts   | 2 +-
 .../backend/src/server/api/endpoints/i/import-following.ts  | 2 +-
 .../backend/src/server/api/endpoints/i/import-muting.ts     | 2 +-
 .../backend/src/server/api/endpoints/i/import-user-lists.ts | 2 +-
 packages/frontend/src/components/global/MkTime.vue          | 4 ++--
 packages/frontend/src/widgets/WidgetUnixClock.vue           | 6 +++---
 packages/sw/src/sw.ts                                       | 2 +-
 9 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts
index 5bd885df406b..b6b591d24036 100644
--- a/packages/backend/src/core/AccountMoveService.ts
+++ b/packages/backend/src/core/AccountMoveService.ts
@@ -305,7 +305,7 @@ export class AccountMoveService {
 		let resultUser: MiLocalUser | MiRemoteUser | null = null;
 
 		if (this.userEntityService.isRemoteUser(dst)) {
-			if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
+			if (Date.now() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
 				await this.apPersonService.updatePerson(dst.uri);
 			}
 			dst = await this.apPersonService.fetchPerson(dst.uri) ?? dst;
@@ -321,7 +321,7 @@ export class AccountMoveService {
 				if (!src) continue; // oldAccountを探してもこのサーバーに存在しない場合はフォロー関係もないということなのでスルー
 
 				if (this.userEntityService.isRemoteUser(dst)) {
-					if ((new Date()).getTime() - (src.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
+					if (Date.now() - (src.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {
 						await this.apPersonService.updatePerson(srcUri);
 					}
 
diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts
index 3b706d985433..6a845b951de6 100644
--- a/packages/backend/src/core/PushNotificationService.ts
+++ b/packages/backend/src/core/PushNotificationService.ts
@@ -101,7 +101,7 @@ export class PushNotificationService implements OnApplicationShutdown {
 				type,
 				body: (type === 'notification' || type === 'unreadAntennaNote') ? truncateBody(type, body) : body,
 				userId,
-				dateTime: (new Date()).getTime(),
+				dateTime: Date.now(),
 			}), {
 				proxy: this.config.proxy,
 			}).catch((err: any) => {
diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts
index 8ddbe5663e83..260610853930 100644
--- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts
@@ -75,7 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
 				me,
-				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > Date.now(),
 				true,
 			);
 			if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts
index 390dd9cd715f..d5e824df2740 100644
--- a/packages/backend/src/server/api/endpoints/i/import-following.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-following.ts
@@ -75,7 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
 				me,
-				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > Date.now(),
 				true,
 			);
 			if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts
index 51a9cdf5a58a..0f5800404eaf 100644
--- a/packages/backend/src/server/api/endpoints/i/import-muting.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts
@@ -75,7 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
 				me,
-				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > Date.now(),
 				true,
 			);
 			if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts
index a3b67301a790..bacdd5c88f28 100644
--- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts
@@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const checkMoving = await this.accountMoveService.validateAlsoKnownAs(
 				me,
-				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > (new Date()).getTime(),
+				(old, src) => !!src.movedAt && src.movedAt.getTime() + 1000 * 60 * 60 * 2 > Date.now(),
 				true,
 			);
 			if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 67532268d3be..23fe99bd9c32 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -47,7 +47,7 @@ const invalid = Number.isNaN(_time);
 const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
 
 // eslint-disable-next-line vue/no-setup-props-destructure
-const now = ref((props.origin ?? new Date()).getTime());
+const now = ref(props.origin?.getTime() ?? Date.now());
 const ago = computed(() => (now.value - _time) / 1000/*ms*/);
 
 const relative = computed<string>(() => {
@@ -77,7 +77,7 @@ let tickId: number;
 let currentInterval: number;
 
 function tick() {
-	now.value = (new Date()).getTime();
+	now.value = Date.now();
 	const nextInterval = ago.value < 60 ? 10000 : ago.value < 3600 ? 60000 : 180000;
 
 	if (currentInterval !== nextInterval) {
diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue
index 2ac7d1c7810d..832cd575cc00 100644
--- a/packages/frontend/src/widgets/WidgetUnixClock.vue
+++ b/packages/frontend/src/widgets/WidgetUnixClock.vue
@@ -68,9 +68,9 @@ watch(showColon, (v) => {
 });
 
 const tick = () => {
-	const now = new Date();
-	ss.value = Math.floor(now.getTime() / 1000).toString();
-	ms.value = Math.floor(now.getTime() % 1000 / 10).toString().padStart(2, '0');
+	const now = Date.now();
+	ss.value = Math.floor(now / 1000).toString();
+	ms.value = Math.floor(now % 1000 / 10).toString().padStart(2, '0');
 	if (ss.value !== prevSec) showColon.value = true;
 	prevSec = ss.value;
 };
diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts
index 46fe9fc90f86..cc79d887133c 100644
--- a/packages/sw/src/sw.ts
+++ b/packages/sw/src/sw.ts
@@ -76,7 +76,7 @@ globalThis.addEventListener('push', ev => {
 			case 'notification':
 			case 'unreadAntennaNote':
 				// 1日以上経過している場合は無視
-				if ((new Date()).getTime() - data.dateTime > 1000 * 60 * 60 * 24) break;
+				if (Date.now() - data.dateTime > 1000 * 60 * 60 * 24) break;
 
 				return createNotification(data);
 			case 'readAllNotifications':

From 960c4df48e31483209ac0421a009686685acd82d Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 7 Apr 2024 21:16:37 +0900
Subject: [PATCH 143/266] enhance(frontend): better condition for posting and
 displaying Notes (#13670)

* enhance(frontend): better condition for posting and displaying Notes

* Update CHANGELOG.md
---
 CHANGELOG.md                                        | 4 ++++
 packages/frontend/src/components/MkNote.vue         | 1 +
 packages/frontend/src/components/MkNoteDetailed.vue | 4 +++-
 packages/frontend/src/components/MkPostForm.vue     | 8 +++++++-
 4 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f6b9cf093925..41cbdea0239d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,8 @@
 - Enhance: 映像・音声の再生メニューに「再生速度」「ループ再生」「ピクチャインピクチャ」を追加
 - Enhance: 映像・音声の再生にキーボードショートカットが使えるように
 - Enhance: ノートについているリアクションの「もっと!」から、リアクションの一覧を表示できるように
+- Enhance: リプライにて引用がある場合テキストが空でもノートできるように
+  - 引用したいノートのURLをコピーしリプライ投稿画面にペーストして添付することで達成できます
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
@@ -33,6 +35,8 @@
 - Fix: コードブロックのシンタックスハイライトで使用される定義ファイルをCDNから取得するように #13177
   - CDNから取得せずMisskey本体にバンドルする場合は`pacakges/frontend/vite.config.ts`を修正してください。
 - Fix: タイムゾーンによっては、「今日誕生日のフォロー中ユーザー」ウィジェットが正しく動作しない問題を修正
+- Fix: CWのみの引用リノートが詳細ページで純粋なリノートとして誤って扱われてしまう問題を修正
+- Fix: ノート詳細ページにおいてCW付き引用リノートのCWボタンのラベルに「引用」が含まれていない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index f5536e79bfcb..22b1691a8664 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -242,6 +242,7 @@ if (noteViewInterruptors.length > 0) {
 
 const isRenote = (
 	note.value.renote != null &&
+	note.value.reply == null &&
 	note.value.text == null &&
 	note.value.cw == null &&
 	note.value.fileIds && note.value.fileIds.length === 0 &&
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index eec1aad53c49..ed1c0a9e9693 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -68,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="$style.noteContent">
 			<p v-if="appearNote.cw != null" :class="$style.cw">
 				<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
-				<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/>
+				<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll"/>
 			</p>
 			<div v-show="appearNote.cw == null || showContent">
 				<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
@@ -266,7 +266,9 @@ if (noteViewInterruptors.length > 0) {
 
 const isRenote = (
 	note.value.renote != null &&
+	note.value.reply == null &&
 	note.value.text == null &&
+	note.value.cw == null &&
 	note.value.fileIds && note.value.fileIds.length === 0 &&
 	note.value.poll == null
 );
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index e03faeaf555f..014b866fbdbe 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -253,7 +253,13 @@ const maxTextLength = computed((): number => {
 
 const canPost = computed((): boolean => {
 	return !props.mock && !posting.value && !posted.value &&
-		(1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
+		(
+			1 <= textLength.value ||
+			1 <= files.value.length ||
+			poll.value != null ||
+			props.renote != null ||
+			(props.reply != null && quoteId.value != null)
+		) &&
 		(textLength.value <= maxTextLength.value) &&
 		(!poll.value || poll.value.choices.length >= 2);
 });

From b322f55c8791493da9788313fd3df9d52f1327ef Mon Sep 17 00:00:00 2001
From: Srgr0 <66754887+Srgr0@users.noreply.github.com>
Date: Mon, 8 Apr 2024 22:41:26 +0900
Subject: [PATCH 144/266] dev: fix misskey-tga (#13312)

* Update deploy-test-environment.yml

* Update deploy-test-environment.yml

* use github.repository

---------

Co-authored-by: anatawa12 <anatawa12@icloud.com>
---
 .github/workflows/deploy-test-environment.yml | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/deploy-test-environment.yml b/.github/workflows/deploy-test-environment.yml
index 77cdcfaf881e..66b15beb91ea 100644
--- a/.github/workflows/deploy-test-environment.yml
+++ b/.github/workflows/deploy-test-environment.yml
@@ -50,12 +50,9 @@ jobs:
 
       - name: Get PR ref
         id: get-ref
-        env:
-          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: |
-          PR_NUMBER=$(jq --raw-output .issue.number $GITHUB_EVENT_PATH)
-          PR_REF=$(gh pr view $PR_NUMBER --json headRefName -q '.headRefName')
-          echo "pr-ref=$PR_REF" > $GITHUB_OUTPUT
+          PR_REF="refs/pull/${{ github.event.issue.number }}/head"
+          echo "pr-ref=$PR_REF" >> $GITHUB_OUTPUT
 
       - name: Extract wait time
         id: get-wait-time

From 7586ef7ba86ae9516e4a9460c0845750dda22e77 Mon Sep 17 00:00:00 2001
From: 1Step621 <86859447+1STEP621@users.noreply.github.com>
Date: Tue, 9 Apr 2024 14:20:00 +0900
Subject: [PATCH 145/266] =?UTF-8?q?fix(frontend):=20MkDialog=E3=81=AEinput?=
 =?UTF-8?q?=E3=81=A7=E5=AD=97=E6=95=B0=E5=88=B6=E9=99=90=E3=81=AB=E9=81=95?=
 =?UTF-8?q?=E5=8F=8D=E3=81=97=E3=81=A6=E3=81=84=E3=81=A6=E3=82=82Enter?=
 =?UTF-8?q?=E3=82=AD=E3=83=BC=E3=81=8C=E6=8A=BC=E3=81=9B=E3=81=A6=E3=81=97?=
 =?UTF-8?q?=E3=81=BE=E3=81=86=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1367?=
 =?UTF-8?q?7)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* MkDialogのinputで字数制限に違反していてもEnterキーが押せてしまうのを修正

* update CHANGELOG.md
---
 CHANGELOG.md                                  | 1 +
 packages/frontend/src/components/MkDialog.vue | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41cbdea0239d..ab4ecb3ffe92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,7 @@
 - Fix: タイムゾーンによっては、「今日誕生日のフォロー中ユーザー」ウィジェットが正しく動作しない問題を修正
 - Fix: CWのみの引用リノートが詳細ページで純粋なリノートとして誤って扱われてしまう問題を修正
 - Fix: ノート詳細ページにおいてCW付き引用リノートのCWボタンのラベルに「引用」が含まれていない問題を修正
+- Fix: ダイアログの入力で字数制限に違反していてもEnterキーが押せてしまう問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 4577d37c0837..c52404a3190b 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -161,7 +161,7 @@ function onKeydown(evt: KeyboardEvent) {
 }
 
 function onInputKeydown(evt: KeyboardEvent) {
-	if (evt.key === 'Enter') {
+	if (evt.key === 'Enter' && okButtonDisabledReason.value === null) {
 		evt.preventDefault();
 		evt.stopPropagation();
 		ok();

From eb1ef1484afbdb09407a603ff69414e7f88bb9ff Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 9 Apr 2024 20:52:29 +0900
Subject: [PATCH 146/266] enhance(frontend): add link of 2fa guide

---
 locales/index.d.ts                                    | 4 ++++
 locales/ja-JP.yml                                     | 1 +
 packages/frontend/src/pages/settings/2fa.qrdialog.vue | 3 +++
 packages/frontend/src/pages/settings/2fa.vue          | 6 +++++-
 4 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 01bec41d9e9f..54f028572683 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -7645,6 +7645,10 @@ export interface Locale extends ILocale {
          * バックアップコードが全て使用されました。認証アプリを利用できない場合、これ以上アカウントにアクセスできなくなります。認証アプリを再登録してください。
          */
         "backupCodesExhaustedWarning": string;
+        /**
+         * 詳細なガイドはこちら
+         */
+        "moreDetailedGuideHere": string;
     };
     "_permissions": {
         /**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 4ba9ea0221b1..ac88420b9d72 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2009,6 +2009,7 @@ _2fa:
   backupCodesDescription: "認証アプリが使用できなくなった場合、以下のバックアップコードを使ってアカウントにアクセスできます。これらのコードは必ず安全な場所に保管してください。各コードは一回だけ使用できます。"
   backupCodeUsedWarning: "バックアップコードが使用されました。認証アプリが使えなくなっている場合、なるべく早く認証アプリを再設定してください。"
   backupCodesExhaustedWarning: "バックアップコードが全て使用されました。認証アプリを利用できない場合、これ以上アカウントにアクセスできなくなります。認証アプリを再登録してください。"
+  moreDetailedGuideHere: "詳細なガイドはこちら"
 
 _permissions:
   "read:account": "アカウントの情報を見る"
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
index 73253b1ef43f..2244047b3165 100644
--- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -25,6 +25,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<div style="height: 100cqh; overflow: auto; text-align: center;">
 					<MkSpacer :marginMin="20" :marginMax="28">
 						<div class="_gaps">
+							<MkInfo><MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank">{{ i18n.ts._2fa.moreDetailedGuideHere }}</MkLink></MkInfo>
+
 							<I18n :src="i18n.ts._2fa.step1" tag="div">
 								<template #a>
 									<a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
@@ -113,6 +115,7 @@ import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkInfo from '@/components/MkInfo.vue';
+import MkLink from '@/components/MkLink.vue';
 import { confetti } from '@/scripts/confetti.js';
 import { signinRequired } from '@/account.js';
 
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 975f23cdd1de..b7d648c1a4d3 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -30,7 +30,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
 			</div>
 
-			<MkButton v-else-if="!$i.twoFactorEnabled" primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
+			<div v-else-if="!$i.twoFactorEnabled" class="_gaps_s">
+				<MkButton primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
+				<MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank"><i class="ti ti-help-circle"></i> {{ i18n.ts.learnMore }}</MkLink>
+			</div>
 		</MkFolder>
 
 		<MkFolder>
@@ -79,6 +82,7 @@ import MkInfo from '@/components/MkInfo.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
+import MkLink from '@/components/MkLink.vue';
 import * as os from '@/os.js';
 import { signinRequired, updateAccount } from '@/account.js';
 import { i18n } from '@/i18n.js';

From f5100cc81f6ffdcfe2b9bf6041f97098a4e82d02 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 13 Apr 2024 12:51:37 +0900
Subject: [PATCH 147/266] =?UTF-8?q?feat(frontend):=20=E3=82=A2=E3=83=83?=
 =?UTF-8?q?=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89=E3=81=99=E3=82=8B=E3=83=95?=
 =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AE=E5=90=8D=E5=89=8D=E3=82=92?=
 =?UTF-8?q?=E3=83=A9=E3=83=B3=E3=83=80=E3=83=A0=E6=96=87=E5=AD=97=E5=88=97?=
 =?UTF-8?q?=E3=81=AB=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#13688)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(frontend): アップロードするファイルの名前をランダム文字列にできるように

* Update Changelog

* refactor

* 設定項目を移動

* fix

* 「オリジナルのファイル名を保持」に変更

* 拡張子を付加するように
---
 CHANGELOG.md                                   |  1 +
 locales/index.d.ts                             |  8 ++++++++
 locales/ja-JP.yml                              |  2 ++
 packages/frontend/src/pages/settings/drive.vue |  5 +++++
 packages/frontend/src/scripts/upload.ts        | 10 +++++++---
 packages/frontend/src/store.ts                 |  4 ++++
 6 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab4ecb3ffe92..1332da69f917 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 
 ### Client
+- Feat: アップロードするファイルの名前をランダム文字列にできるように
 - Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
 - Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
 - Enhance: リアクション・いいねの総数を表示するように
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 54f028572683..d6875c086807 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4936,6 +4936,14 @@ export interface Locale extends ILocale {
      * 動画・音声の再生にブラウザのUIを使用する
      */
     "useNativeUIForVideoAudioPlayer": string;
+    /**
+     * オリジナルのファイル名を保持
+     */
+    "keepOriginalFilename": string;
+    /**
+     * この設定をオフにすると、アップロード時にファイル名が自動でランダム文字列に置き換えられます。
+     */
+    "keepOriginalFilenameDescription": string;
     "_bubbleGame": {
         /**
          * 遊び方
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ac88420b9d72..0b581a01e328 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1230,6 +1230,8 @@ useTotp: "ワンタイムパスワードを使う"
 useBackupCode: "バックアップコードを使う"
 launchApp: "アプリを起動"
 useNativeUIForVideoAudioPlayer: "動画・音声の再生にブラウザのUIを使用する"
+keepOriginalFilename: "オリジナルのファイル名を保持"
+keepOriginalFilenameDescription: "この設定をオフにすると、アップロード時にファイル名が自動でランダム文字列に置き換えられます。"
 
 _bubbleGame:
   howToPlay: "遊び方"
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 1919f808640f..81a8d474d29b 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -44,6 +44,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.keepOriginalUploading }}</template>
 				<template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template>
 			</MkSwitch>
+			<MkSwitch v-model="keepOriginalFilename">
+				<template #label>{{ i18n.ts.keepOriginalFilename }}</template>
+				<template #caption>{{ i18n.ts.keepOriginalFilenameDescription }}</template>
+			</MkSwitch>
 			<MkSwitch v-model="alwaysMarkNsfw" @update:modelValue="saveProfile()">
 				<template #label>{{ i18n.ts.alwaysMarkSensitive }}</template>
 			</MkSwitch>
@@ -96,6 +100,7 @@ const meterStyle = computed(() => {
 });
 
 const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading'));
+const keepOriginalFilename = computed(defaultStore.makeGetterSetter('keepOriginalFilename'));
 
 misskeyApi('drive').then(info => {
 	capacity.value = info.capacity;
diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts
index 6c46b2bc1ba2..3e947183c9ea 100644
--- a/packages/frontend/src/scripts/upload.ts
+++ b/packages/frontend/src/scripts/upload.ts
@@ -5,6 +5,7 @@
 
 import { reactive, ref } from 'vue';
 import * as Misskey from 'misskey-js';
+import { v4 as uuid } from 'uuid';
 import { readAndCompressImage } from '@misskey-dev/browser-image-resizer';
 import { getCompressionConfig } from './upload/compress-config.js';
 import { defaultStore } from '@/store.js';
@@ -39,13 +40,16 @@ export function uploadFile(
 	if (folder && typeof folder === 'object') folder = folder.id;
 
 	return new Promise((resolve, reject) => {
-		const id = Math.random().toString();
+		const id = uuid();
 
 		const reader = new FileReader();
 		reader.onload = async (): Promise<void> => {
+			const filename = name ?? file.name ?? 'untitled';
+			const extension = filename.split('.').length > 1 ? '.' + filename.split('.').pop() : '';
+
 			const ctx = reactive<Uploading>({
-				id: id,
-				name: name ?? file.name ?? 'untitled',
+				id,
+				name: defaultStore.state.keepOriginalFilename ? filename : id + extension,
 				progressMax: undefined,
 				progressValue: undefined,
 				img: window.URL.createObjectURL(file),
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index faefbd8ce430..9b5011739a06 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -446,6 +446,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: false,
 	},
+	keepOriginalFilename: {
+		where: 'device',
+		default: true,
+	},
 
 	sound_masterVolume: {
 		where: 'device',

From 5c7c44c9ebd12e9ae0dd6d7fab8f6dd78ba54eb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 13 Apr 2024 20:38:25 +0900
Subject: [PATCH 148/266] =?UTF-8?q?fix(backend):=20=E7=99=BB=E9=8C=B2?=
 =?UTF-8?q?=E3=81=AB=E3=83=A1=E3=83=BC=E3=83=AB=E8=AA=8D=E8=A8=BC=E3=81=8C?=
 =?UTF-8?q?=E5=BF=85=E9=A0=88=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=84?=
 =?UTF-8?q?=E3=82=8B=E5=A0=B4=E5=90=88=E3=80=81=E7=99=BB=E9=8C=B2=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E3=83=A1=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9=E3=82=92=E5=89=8A=E9=99=A4?=
 =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#13703)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように (MisskeyIO#606)

(cherry picked from commit 6b7df2bd10dc28b84f525a621b66fc49bf59cac6)

* Update Changelog

---------

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 CHANGELOG.md                                           |  2 ++
 .../backend/src/server/api/endpoints/i/update-email.ts | 10 ++++++++++
 2 files changed, 12 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1332da69f917..d184a0b3987a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,8 @@
 - Fix: エンドポイント`notes/translate`のエラーを改善
 - Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
 - Fix: 一部の音声ファイルが映像ファイルとして扱われる問題を修正
+- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように  
+  (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts
index 386827869020..eea657ebbd07 100644
--- a/packages/backend/src/server/api/endpoints/i/update-email.ts
+++ b/packages/backend/src/server/api/endpoints/i/update-email.ts
@@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
 import { UserAuthService } from '@/core/UserAuthService.js';
+import { MetaService } from '@/core/MetaService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -39,6 +40,12 @@ export const meta = {
 			code: 'UNAVAILABLE',
 			id: 'a2defefb-f220-8849-0af6-17f816099323',
 		},
+
+		emailRequired: {
+			message: 'Email address is required.',
+			code: 'EMAIL_REQUIRED',
+			id: '324c7a88-59f2-492f-903f-89134f93e47e',
+		},
 	},
 
 	res: {
@@ -66,6 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.userProfilesRepository)
 		private userProfilesRepository: UserProfilesRepository,
 
+		private metaService: MetaService,
 		private userEntityService: UserEntityService,
 		private emailService: EmailService,
 		private userAuthService: UserAuthService,
@@ -97,6 +105,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				if (!res.available) {
 					throw new ApiError(meta.errors.unavailable);
 				}
+			} else if ((await this.metaService.fetch()).emailRequiredForSignup) {
+				throw new ApiError(meta.errors.emailRequired);
 			}
 
 			await this.userProfilesRepository.update(me.id, {

From 48a7679b8a8b3df80d7f90ac6f4a852f47a8df22 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sun, 14 Apr 2024 08:08:26 +0900
Subject: [PATCH 149/266] test: do not use indexedDB in cypress environment due
 to chrome bug (#13709)

---
 cypress/support/commands.ts                |  4 ++++
 packages/frontend/src/scripts/idb-proxy.ts | 10 ++++++++++
 2 files changed, 14 insertions(+)

diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index c2d92e1663e8..281f2e6ccdd8 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -30,9 +30,13 @@ Cypress.Commands.add('visitHome', () => {
 })
 
 Cypress.Commands.add('resetState', () => {
+	// iframe.contentWindow.indexedDB.deleteDatabase() がchromeのバグで使用できないため、indexedDBを無効化している。
+	// see https://github.com/misskey-dev/misskey/issues/13605#issuecomment-2053652123
+	/*
 	cy.window().then(win => {
 		win.indexedDB.deleteDatabase('keyval-store');
 	});
+	 */
 	cy.request('POST', '/api/reset-db', {}).as('reset');
 	cy.get('@reset').its('status').should('equal', 204);
 	cy.reload(true);
diff --git a/packages/frontend/src/scripts/idb-proxy.ts b/packages/frontend/src/scripts/idb-proxy.ts
index 1ca0990ba9e2..6b511f2a5fc7 100644
--- a/packages/frontend/src/scripts/idb-proxy.ts
+++ b/packages/frontend/src/scripts/idb-proxy.ts
@@ -15,6 +15,16 @@ const fallbackName = (key: string) => `idbfallback::${key}`;
 
 let idbAvailable = typeof window !== 'undefined' ? !!(window.indexedDB && window.indexedDB.open) : true;
 
+// iframe.contentWindow.indexedDB.deleteDatabase() がchromeのバグで使用できないため、indexedDBを無効化している。
+// バグが治って再度有効化するのであれば、cypressのコマンド内のコメントアウトを外すこと
+// see https://github.com/misskey-dev/misskey/issues/13605#issuecomment-2053652123
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-expect-error
+if (window.Cypress) {
+	idbAvailable = false;
+	console.log('Cypress detected. It will use localStorage.');
+}
+
 if (idbAvailable) {
 	await iset('idb-test', 'test')
 		.catch(err => {

From 7cf0c18f83f82416c9b1bb5bca5b669e77240527 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Apr 2024 10:22:03 +0900
Subject: [PATCH 150/266] =?UTF-8?q?fix(backend):=20FileServerService?=
 =?UTF-8?q?=E3=81=A7=E3=83=AC=E3=83=B3=E3=82=B8=E3=83=AA=E3=82=AF=E3=82=A8?=
 =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=AE=E5=A0=B4=E5=90=88=E3=81=AB=E9=81=A9?=
 =?UTF-8?q?=E5=88=87=E3=81=AA=E3=83=AC=E3=82=B9=E3=83=9D=E3=83=B3=E3=82=B9?=
 =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=81=8C=E8=BF=94=E3=82=89=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1370?=
 =?UTF-8?q?1)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* return 206 for every ranged response - fixes #494

(cherry picked from commit 92eec2178fd103e9ea2bcd646aacab1fb496a33b)

* detect size of remote files - fixes #494

without this, remote files are assumed to have size 0 (even if we just
downloaded them!) and the range-related code won't run

(cherry picked from commit 960f4fcff78a1f019c9a9377853fcd90dbfb7575)

---------

Co-authored-by: dakkar <dakkar@thenautilus.net>
---
 packages/backend/src/server/FileServerService.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index f51d7aebca68..ce7702143ed9 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -194,6 +194,7 @@ export class FileServerService {
 						reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
 						reply.header('Accept-Ranges', 'bytes');
 						reply.header('Content-Length', chunksize);
+						reply.code(206);
 					} else {
 						image = {
 							data: fs.createReadStream(file.path),
@@ -263,7 +264,6 @@ export class FileServerService {
 					const parts = range.replace(/bytes=/, '').split('-');
 					const start = parseInt(parts[0], 10);
 					let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
-					console.log(end);
 					if (end > file.file.size) {
 						end = file.file.size - 1;
 					}
@@ -433,6 +433,7 @@ export class FileServerService {
 					reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
 					reply.header('Accept-Ranges', 'bytes');
 					reply.header('Content-Length', chunksize);
+					reply.code(206);
 				} else {
 					image = {
 						data: fs.createReadStream(file.path),
@@ -529,6 +530,9 @@ export class FileServerService {
 		if (!file.storedInternal) {
 			if (!(file.isLink && file.uri)) return '204';
 			const result = await this.downloadAndDetectTypeFromUrl(file.uri);
+			if (!file.size) {
+				file.size = (await fs.promises.stat(result.path)).size;
+			}
 			return {
 				...result,
 				url: file.uri,

From 8c5d9a6295ab506b935bbd5856894239997a8158 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 14 Apr 2024 10:23:48 +0900
Subject: [PATCH 151/266] fix(backend): incorrect logic for determining whether
 Quote or not (#13700)

* fix(backend): incorrect logic for determining whether Quote or not

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 .../src/core/FanoutTimelineEndpointService.ts |   6 +-
 .../backend/src/core/NoteCreateService.ts     |  23 ++-
 .../backend/src/core/NoteDeleteService.ts     |   4 +-
 packages/backend/src/misc/is-pure-renote.ts   |  15 --
 packages/backend/src/misc/is-quote.ts         |  12 --
 packages/backend/src/misc/is-renote.ts        |  36 +++++
 .../src/server/ActivityPubServerService.ts    |   4 +-
 .../src/server/api/endpoints/notes/create.ts  |   6 +-
 .../backend/test/unit/NoteCreateService.ts    | 144 ++++++++++++++++++
 packages/backend/test/unit/misc/is-renote.ts  |  88 +++++++++++
 11 files changed, 296 insertions(+), 43 deletions(-)
 delete mode 100644 packages/backend/src/misc/is-pure-renote.ts
 delete mode 100644 packages/backend/src/misc/is-quote.ts
 create mode 100644 packages/backend/src/misc/is-renote.ts
 create mode 100644 packages/backend/test/unit/NoteCreateService.ts
 create mode 100644 packages/backend/test/unit/misc/is-renote.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d184a0b3987a..47e8e0cf19aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@
 - Fix: エンドポイント`notes/translate`のエラーを改善
 - Fix: CleanRemoteFilesProcessorService report progress from 100% (#13632)
 - Fix: 一部の音声ファイルが映像ファイルとして扱われる問題を修正
+- Fix: リプライのみの引用リノートと、CWのみの引用リノートが純粋なリノートとして誤って扱われてしまう問題を修正
 - Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
 
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 9c239b4dfc40..006433df7a22 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -13,7 +13,7 @@ import type { NotesRepository } from '@/models/_.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
-import { isPureRenote } from '@/misc/is-pure-renote.js';
+import { isQuote, isRenote } from '@/misc/is-renote.js';
 import { CacheService } from '@/core/CacheService.js';
 import { isReply } from '@/misc/is-reply.js';
 import { isInstanceMuted } from '@/misc/is-instance-muted.js';
@@ -95,7 +95,7 @@ export class FanoutTimelineEndpointService {
 
 			if (ps.excludePureRenotes) {
 				const parentFilter = filter;
-				filter = (note) => !isPureRenote(note) && parentFilter(note);
+				filter = (note) => (!isRenote(note) || isQuote(note)) && parentFilter(note);
 			}
 
 			if (ps.me) {
@@ -116,7 +116,7 @@ export class FanoutTimelineEndpointService {
 				filter = (note) => {
 					if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
 					if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
-					if (isPureRenote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
+					if (isRenote(note) && !isQuote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
 					if (isInstanceMuted(note, userMutedInstances)) return false;
 
 					return parentFilter(note);
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 81ae2908d3dc..32104fea9072 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -306,7 +306,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 
 		// Check blocking
-		if (data.renote && !this.isQuote(data)) {
+		if (this.isRenote(data) && !this.isQuote(data)) {
 			if (data.renote.userHost === null) {
 				if (data.renote.userId !== user.id) {
 					const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);
@@ -641,7 +641,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 			}
 
 			// If it is renote
-			if (data.renote) {
+			if (this.isRenote(data)) {
 				const type = this.isQuote(data) ? 'quote' : 'renote';
 
 				// Notify
@@ -725,9 +725,20 @@ export class NoteCreateService implements OnApplicationShutdown {
 	}
 
 	@bindThis
-	private isQuote(note: Option): note is Option & { renote: MiNote } {
-		// sync with misc/is-quote.ts
-		return !!note.renote && (!!note.text || !!note.cw || (!!note.files && !!note.files.length) || !!note.poll);
+	private isRenote(note: Option): note is Option & { renote: MiNote } {
+		return note.renote != null;
+	}
+
+	@bindThis
+	private isQuote(note: Option & { renote: MiNote }): note is Option & { renote: MiNote } & (
+		{ text: string } | { cw: string } | { reply: MiNote } | { poll: IPoll } | { files: MiDriveFile[] }
+	) {
+		// NOTE: SYNC WITH misc/is-quote.ts
+		return note.text != null ||
+			note.reply != null ||
+			note.cw != null ||
+			note.poll != null ||
+			(note.files != null && note.files.length > 0);
 	}
 
 	@bindThis
@@ -795,7 +806,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 	private async renderNoteOrRenoteActivity(data: Option, note: MiNote) {
 		if (data.localOnly) return null;
 
-		const content = data.renote && !this.isQuote(data)
+		const content = this.isRenote(data) && !this.isQuote(data)
 			? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note)
 			: this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note);
 
diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts
index fdf843c3e82c..801ed02e00b3 100644
--- a/packages/backend/src/core/NoteDeleteService.ts
+++ b/packages/backend/src/core/NoteDeleteService.ts
@@ -24,7 +24,7 @@ import { bindThis } from '@/decorators.js';
 import { MetaService } from '@/core/MetaService.js';
 import { SearchService } from '@/core/SearchService.js';
 import { ModerationLogService } from '@/core/ModerationLogService.js';
-import { isPureRenote } from '@/misc/is-pure-renote.js';
+import { isQuote, isRenote } from '@/misc/is-renote.js';
 
 @Injectable()
 export class NoteDeleteService {
@@ -79,7 +79,7 @@ export class NoteDeleteService {
 				let renote: MiNote | null = null;
 
 				// if deleted note is renote
-				if (isPureRenote(note)) {
+				if (isRenote(note) && !isQuote(note)) {
 					renote = await this.notesRepository.findOneBy({
 						id: note.renoteId,
 					});
diff --git a/packages/backend/src/misc/is-pure-renote.ts b/packages/backend/src/misc/is-pure-renote.ts
deleted file mode 100644
index f9c2243a06ff..000000000000
--- a/packages/backend/src/misc/is-pure-renote.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import type { MiNote } from '@/models/Note.js';
-
-export function isPureRenote(note: MiNote): note is MiNote & { renoteId: NonNullable<MiNote['renoteId']> } {
-	if (!note.renoteId) return false;
-
-	if (note.text) return false; // it's quoted with text
-	if (note.fileIds.length !== 0) return false; // it's quoted with files
-	if (note.hasPoll) return false; // it's quoted with poll
-	return true;
-}
diff --git a/packages/backend/src/misc/is-quote.ts b/packages/backend/src/misc/is-quote.ts
deleted file mode 100644
index 75b29f63f492..000000000000
--- a/packages/backend/src/misc/is-quote.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import type { MiNote } from '@/models/Note.js';
-
-// eslint-disable-next-line import/no-default-export
-export default function(note: MiNote): boolean {
-	// sync with NoteCreateService.isQuote
-	return note.renoteId != null && (note.text != null || note.hasPoll || (note.fileIds != null && note.fileIds.length > 0));
-}
diff --git a/packages/backend/src/misc/is-renote.ts b/packages/backend/src/misc/is-renote.ts
new file mode 100644
index 000000000000..5d48aba36092
--- /dev/null
+++ b/packages/backend/src/misc/is-renote.ts
@@ -0,0 +1,36 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import type { MiNote } from '@/models/Note.js';
+
+type Renote =
+	MiNote & {
+		renoteId: NonNullable<MiNote['renoteId']>
+	};
+
+type Quote =
+	Renote & ({
+		text: NonNullable<MiNote['text']>
+	} | {
+		cw: NonNullable<MiNote['cw']>
+	} | {
+		replyId: NonNullable<MiNote['replyId']>
+		reply: NonNullable<MiNote['reply']>
+	} | {
+		hasPoll: true
+	});
+
+export function isRenote(note: MiNote): note is Renote {
+	return note.renoteId != null;
+}
+
+export function isQuote(note: Renote): note is Quote {
+	// NOTE: SYNC WITH NoteCreateService.isQuote
+	return note.text != null ||
+		note.cw != null ||
+		note.replyId != null ||
+		note.hasPoll ||
+		note.fileIds.length > 0;
+}
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 60366dd5c231..3255d64621db 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -28,7 +28,7 @@ import { UtilityService } from '@/core/UtilityService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { IActivity } from '@/core/activitypub/type.js';
-import { isPureRenote } from '@/misc/is-pure-renote.js';
+import { isQuote, isRenote } from '@/misc/is-renote.js';
 import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
 import type { FindOptionsWhere } from 'typeorm';
 
@@ -91,7 +91,7 @@ export class ActivityPubServerService {
 	 */
 	@bindThis
 	private async packActivity(note: MiNote): Promise<any> {
-		if (isPureRenote(note)) {
+		if (isRenote(note) && !isQuote(note)) {
 			const renote = await this.notesRepository.findOneByOrFail({ id: note.renoteId });
 			return this.apRendererService.renderAnnounce(renote.uri ? renote.uri : `${this.config.url}/notes/${renote.id}`, note);
 		}
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index bfb92144393d..beb77ca7ab02 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -16,7 +16,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { NoteCreateService } from '@/core/NoteCreateService.js';
 import { DI } from '@/di-symbols.js';
-import { isPureRenote } from '@/misc/is-pure-renote.js';
+import { isQuote, isRenote } from '@/misc/is-renote.js';
 import { MetaService } from '@/core/MetaService.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { IdentifiableError } from '@/misc/identifiable-error.js';
@@ -275,7 +275,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 				if (renote == null) {
 					throw new ApiError(meta.errors.noSuchRenoteTarget);
-				} else if (isPureRenote(renote)) {
+				} else if (isRenote(renote) && !isQuote(renote)) {
 					throw new ApiError(meta.errors.cannotReRenote);
 				}
 
@@ -321,7 +321,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 				if (reply == null) {
 					throw new ApiError(meta.errors.noSuchReplyTarget);
-				} else if (isPureRenote(reply)) {
+				} else if (isRenote(reply) && !isQuote(reply)) {
 					throw new ApiError(meta.errors.cannotReplyToPureRenote);
 				} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) {
 					throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
diff --git a/packages/backend/test/unit/NoteCreateService.ts b/packages/backend/test/unit/NoteCreateService.ts
new file mode 100644
index 000000000000..f2d4c8ffbb77
--- /dev/null
+++ b/packages/backend/test/unit/NoteCreateService.ts
@@ -0,0 +1,144 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Test } from '@nestjs/testing';
+
+import { CoreModule } from '@/core/CoreModule.js';
+import { NoteCreateService } from '@/core/NoteCreateService.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { MiNote } from '@/models/Note.js';
+import { IPoll } from '@/models/Poll.js';
+import { MiDriveFile } from '@/models/DriveFile.js';
+
+describe('NoteCreateService', () => {
+	let noteCreateService: NoteCreateService;
+
+	beforeAll(async () => {
+		const app = await Test.createTestingModule({
+			imports: [GlobalModule, CoreModule],
+		}).compile();
+		noteCreateService = app.get<NoteCreateService>(NoteCreateService);
+	});
+
+	describe('is-renote', () => {
+		const base: MiNote = {
+			id: 'some-note-id',
+			replyId: null,
+			reply: null,
+			renoteId: null,
+			renote: null,
+			threadId: null,
+			text: null,
+			name: null,
+			cw: null,
+			userId: 'some-user-id',
+			user: null,
+			localOnly: false,
+			reactionAcceptance: null,
+			renoteCount: 0,
+			repliesCount: 0,
+			clippedCount: 0,
+			reactions: {},
+			visibility: 'public',
+			uri: null,
+			url: null,
+			fileIds: [],
+			attachedFileTypes: [],
+			visibleUserIds: [],
+			mentions: [],
+			mentionedRemoteUsers: '',
+			reactionAndUserPairCache: [],
+			emojis: [],
+			tags: [],
+			hasPoll: false,
+			channelId: null,
+			channel: null,
+			userHost: null,
+			replyUserId: null,
+			replyUserHost: null,
+			renoteUserId: null,
+			renoteUserHost: null,
+		};
+
+		const poll: IPoll = {
+			choices: ['kinoko', 'takenoko'],
+			multiple: false,
+			expiresAt: null,
+		};
+
+		const file: MiDriveFile = {
+			id: 'some-file-id',
+			userId: null,
+			user: null,
+			userHost: null,
+			md5: '',
+			name: '',
+			type: '',
+			size: 0,
+			comment: null,
+			blurhash: null,
+			properties: {},
+			storedInternal: false,
+			url: '',
+			thumbnailUrl: null,
+			webpublicUrl: null,
+			webpublicType: null,
+			accessKey: null,
+			thumbnailAccessKey: null,
+			webpublicAccessKey: null,
+			uri: null,
+			src: null,
+			folderId: null,
+			folder: null,
+			isSensitive: false,
+			maybeSensitive: false,
+			maybePorn: false,
+			isLink: false,
+			requestHeaders: null,
+			requestIp: null,
+		};
+
+		test('note without renote should not be Renote', () => {
+			const note = { renote: null };
+			expect(noteCreateService['isRenote'](note)).toBe(false);
+		});
+
+		test('note with renote should be Renote and not be Quote', () => {
+			const note = { renote: base };
+			expect(noteCreateService['isRenote'](note)).toBe(true);
+			expect(noteCreateService['isQuote'](note)).toBe(false);
+		});
+
+		test('note with renote and text should be Quote', () => {
+			const note = { renote: base, text: 'some-text' };
+			expect(noteCreateService['isRenote'](note)).toBe(true);
+			expect(noteCreateService['isQuote'](note)).toBe(true);
+		});
+
+		test('note with renote and cw should be Quote', () => {
+			const note = { renote: base, cw: 'some-cw' };
+			expect(noteCreateService['isRenote'](note)).toBe(true);
+			expect(noteCreateService['isQuote'](note)).toBe(true);
+		});
+
+		test('note with renote and reply should be Quote', () => {
+			const note = { renote: base, reply: { ...base, id: 'another-note-id' } };
+			expect(noteCreateService['isRenote'](note)).toBe(true);
+			expect(noteCreateService['isQuote'](note)).toBe(true);
+		});
+
+		test('note with renote and poll should be Quote', () => {
+			const note = { renote: base, poll };
+			expect(noteCreateService['isRenote'](note)).toBe(true);
+			expect(noteCreateService['isQuote'](note)).toBe(true);
+		});
+
+		test('note with renote and non-empty files should be Quote', () => {
+			const note = { renote: base, files: [file] };
+			expect(noteCreateService['isRenote'](note)).toBe(true);
+			expect(noteCreateService['isQuote'](note)).toBe(true);
+		});
+	});
+});
diff --git a/packages/backend/test/unit/misc/is-renote.ts b/packages/backend/test/unit/misc/is-renote.ts
new file mode 100644
index 000000000000..0b713e8bf6b4
--- /dev/null
+++ b/packages/backend/test/unit/misc/is-renote.ts
@@ -0,0 +1,88 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { isQuote, isRenote } from '@/misc/is-renote.js';
+import { MiNote } from '@/models/Note.js';
+
+const base: MiNote = {
+	id: 'some-note-id',
+	replyId: null,
+	reply: null,
+	renoteId: null,
+	renote: null,
+	threadId: null,
+	text: null,
+	name: null,
+	cw: null,
+	userId: 'some-user-id',
+	user: null,
+	localOnly: false,
+	reactionAcceptance: null,
+	renoteCount: 0,
+	repliesCount: 0,
+	clippedCount: 0,
+	reactions: {},
+	visibility: 'public',
+	uri: null,
+	url: null,
+	fileIds: [],
+	attachedFileTypes: [],
+	visibleUserIds: [],
+	mentions: [],
+	mentionedRemoteUsers: '',
+	reactionAndUserPairCache: [],
+	emojis: [],
+	tags: [],
+	hasPoll: false,
+	channelId: null,
+	channel: null,
+	userHost: null,
+	replyUserId: null,
+	replyUserHost: null,
+	renoteUserId: null,
+	renoteUserHost: null,
+};
+
+describe('misc:is-renote', () => {
+	test('note without renoteId should not be Renote', () => {
+		expect(isRenote(base)).toBe(false);
+	});
+
+	test('note with renoteId should be Renote and not be Quote', () => {
+		const note: MiNote = { ...base, renoteId: 'some-renote-id' };
+		expect(isRenote(note)).toBe(true);
+		expect(isQuote(note as any)).toBe(false);
+	});
+
+	test('note with renoteId and text should be Quote', () => {
+		const note: MiNote = { ...base, renoteId: 'some-renote-id', text: 'some-text' };
+		expect(isRenote(note)).toBe(true);
+		expect(isQuote(note as any)).toBe(true);
+	});
+
+	test('note with renoteId and cw should be Quote', () => {
+		const note: MiNote = { ...base, renoteId: 'some-renote-id', cw: 'some-cw' };
+		expect(isRenote(note)).toBe(true);
+		expect(isQuote(note as any)).toBe(true);
+	});
+
+	test('note with renoteId and replyId should be Quote', () => {
+		const note: MiNote = { ...base, renoteId: 'some-renote-id', replyId: 'some-reply-id' };
+		expect(isRenote(note)).toBe(true);
+		expect(isQuote(note as any)).toBe(true);
+	});
+
+	test('note with renoteId and poll should be Quote', () => {
+		const note: MiNote = { ...base, renoteId: 'some-renote-id', hasPoll: true };
+		expect(isRenote(note)).toBe(true);
+		expect(isQuote(note as any)).toBe(true);
+	});
+
+	test('note with renoteId and non-empty fileIds should be Quote', () => {
+		const note: MiNote = { ...base, renoteId: 'some-renote-id', fileIds: ['some-file-id'] };
+		expect(isRenote(note)).toBe(true);
+		expect(isQuote(note as any)).toBe(true);
+	});
+});

From bba3097765317cbf95d09627961b5b5dce16a972 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Apr 2024 21:30:24 +0900
Subject: [PATCH 152/266] =?UTF-8?q?enhance:=20=E3=82=AF=E3=83=AA=E3=83=83?=
 =?UTF-8?q?=E3=83=97=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E6=95=B0=E3=82=92?=
 =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#13686)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: クリップのノート数を表示できるように

* Update Changelog
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  4 ++
 locales/ja-JP.yml                             |  1 +
 .../src/core/entities/ClipEntityService.ts    |  6 ++-
 .../backend/src/models/json-schema/clip.ts    |  4 ++
 .../frontend/src/components/MkClipPreview.vue | 52 +++++++++++++------
 packages/frontend/src/pages/clip.vue          | 13 +++--
 .../frontend/src/pages/my-clips/index.vue     | 10 ++--
 packages/frontend/src/pages/note.vue          |  4 +-
 .../frontend/src/scripts/get-note-menu.ts     | 36 +++++++++++--
 packages/misskey-js/src/autogen/types.ts      |  1 +
 11 files changed, 99 insertions(+), 33 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47e8e0cf19aa..a238d99a062a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 - Enhance: URLプレビューの有効化・無効化を設定できるように #13569
 - Enhance: アンテナでBotによるノートを除外できるように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
+- Enhance: クリップのノート数を表示するように
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 
 ### Client
diff --git a/locales/index.d.ts b/locales/index.d.ts
index d6875c086807..cbea39f1cd18 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4944,6 +4944,10 @@ export interface Locale extends ILocale {
      * この設定をオフにすると、アップロード時にファイル名が自動でランダム文字列に置き換えられます。
      */
     "keepOriginalFilenameDescription": string;
+    /**
+     * 説明文はありません
+     */
+    "noDescription": string;
     "_bubbleGame": {
         /**
          * 遊び方
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0b581a01e328..4ab2f5adb02d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1232,6 +1232,7 @@ launchApp: "アプリを起動"
 useNativeUIForVideoAudioPlayer: "動画・音声の再生にブラウザのUIを使用する"
 keepOriginalFilename: "オリジナルのファイル名を保持"
 keepOriginalFilenameDescription: "この設定をオフにすると、アップロード時にファイル名が自動でランダム文字列に置き換えられます。"
+noDescription: "説明文はありません"
 
 _bubbleGame:
   howToPlay: "遊び方"
diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts
index 26fcd6714d16..ce49c3458c54 100644
--- a/packages/backend/src/core/entities/ClipEntityService.ts
+++ b/packages/backend/src/core/entities/ClipEntityService.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import type { ClipFavoritesRepository, ClipsRepository, MiUser } from '@/models/_.js';
+import type { ClipNotesRepository, ClipFavoritesRepository, ClipsRepository, MiUser } from '@/models/_.js';
 import { awaitAll } from '@/misc/prelude/await-all.js';
 import type { Packed } from '@/misc/json-schema.js';
 import type { } from '@/models/Blocking.js';
@@ -20,6 +20,9 @@ export class ClipEntityService {
 		@Inject(DI.clipsRepository)
 		private clipsRepository: ClipsRepository,
 
+		@Inject(DI.clipNotesRepository)
+		private clipNotesRepository: ClipNotesRepository,
+
 		@Inject(DI.clipFavoritesRepository)
 		private clipFavoritesRepository: ClipFavoritesRepository,
 
@@ -47,6 +50,7 @@ export class ClipEntityService {
 			isPublic: clip.isPublic,
 			favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }),
 			isFavorited: meId ? await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: meId } }) : undefined,
+			notesCount: meId ? await this.clipNotesRepository.countBy({ clipId: clip.id }) : undefined,
 		});
 	}
 
diff --git a/packages/backend/src/models/json-schema/clip.ts b/packages/backend/src/models/json-schema/clip.ts
index ca4886c97837..c4e7055cd88a 100644
--- a/packages/backend/src/models/json-schema/clip.ts
+++ b/packages/backend/src/models/json-schema/clip.ts
@@ -52,5 +52,9 @@ export const packedClipSchema = {
 			type: 'boolean',
 			optional: true, nullable: false,
 		},
+		notesCount: {
+			type: 'integer',
+			optional: true, nullable: false,
+		},
 	},
 } as const;
diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue
index c51ad4356da0..6299a28e9f68 100644
--- a/packages/frontend/src/components/MkClipPreview.vue
+++ b/packages/frontend/src/components/MkClipPreview.vue
@@ -4,37 +4,59 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="$style.root" class="_panel">
-	<b>{{ clip.name }}</b>
-	<div v-if="clip.description" :class="$style.description">{{ clip.description }}</div>
-	<div v-if="clip.lastClippedAt">{{ i18n.ts.updatedAt }}: <MkTime :time="clip.lastClippedAt" mode="detail"/></div>
-	<div :class="$style.user">
-		<MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
+<MkA :to="`/clips/${clip.id}`" :class="$style.link">
+	<div :class="$style.root" class="_panel _gaps_s">
+		<b>{{ clip.name }}</b>
+		<div :class="$style.description">
+			<div v-if="clip.description"><Mfm :text="clip.description" :plain="true" :nowrap="true"/></div>
+			<div v-if="clip.lastClippedAt">{{ i18n.ts.updatedAt }}: <MkTime :time="clip.lastClippedAt" mode="detail"/></div>
+			<div v-if="clip.notesCount != null">{{ i18n.ts.notesCount }}: {{ number(clip.notesCount) }} / {{ $i?.policies.noteEachClipsLimit }} ({{ i18n.tsx.remainingN({ n: remaining }) }})</div>
+		</div>
+		<div :class="$style.divider"></div>
+		<div>
+			<MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
+		</div>
 	</div>
-</div>
+</MkA>
 </template>
 
 <script lang="ts" setup>
+import * as Misskey from 'misskey-js';
+import { computed } from 'vue';
 import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import number from '@/filters/number.js';
 
-defineProps<{
-	clip: any;
+const props = defineProps<{
+	clip: Misskey.entities.Clip;
 }>();
+
+const remaining = computed(() => {
+	return ($i?.policies && props.clip.notesCount != null) ? ($i.policies.noteEachClipsLimit - props.clip.notesCount) : i18n.ts.unknown;
+});
 </script>
 
 <style lang="scss" module>
-.root {
+.link {
 	display: block;
+
+	&:hover {
+		text-decoration: none;
+		color: var(--accent);
+	}
+}
+
+.root {
 	padding: 16px;
 }
 
-.description {
-	padding: 8px 0;
+.divider {
+	height: 1px;
+	background: var(--divider);
 }
 
-.user {
-	padding-top: 16px;
-	border-top: solid 0.5px var(--divider);
+.description {
+	font-size: 90%;
 }
 
 .userAvatar {
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index c38cc117bcd4..fd64a55c651e 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -9,11 +9,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkSpacer :contentMax="800">
 		<div v-if="clip" class="_gaps">
 			<div class="_panel">
-				<div v-if="clip.description" :class="$style.description">
-					<Mfm :text="clip.description" :isNote="false"/>
+				<div class="_gaps_s" :class="$style.description">
+					<div v-if="clip.description">
+						<Mfm :text="clip.description" :isNote="false"/>
+					</div>
+					<div v-else>({{ i18n.ts.noDescription }})</div>
+					<div>
+						<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
+						<MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
+					</div>
 				</div>
-				<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
-				<MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
 				<div :class="$style.user">
 					<MkAvatar :user="clip.user" :class="$style.avatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
 				</div>
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 803b28899a85..1a0d7177fc86 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -11,16 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div v-if="tab === 'my'" key="my" class="_gaps">
 				<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
 
-				<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="_gaps">
-					<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`">
-						<MkClipPreview :clip="item"/>
-					</MkA>
+				<MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
+					<MkClipPreview v-for="item in items" :key="item.id" :clip="item"/>
 				</MkPagination>
 			</div>
 			<div v-else-if="tab === 'favorites'" key="favorites" class="_gaps">
-				<MkA v-for="item in favorites" :key="item.id" :to="`/clips/${item.id}`">
-					<MkClipPreview :clip="item"/>
-				</MkA>
+				<MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/>
 			</div>
 		</MkHorizontalSwipe>
 	</MkSpacer>
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index e14651742a94..97f32d35cd6f 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -26,9 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<div v-if="clips && clips.length > 0" class="_margin">
 							<div style="font-weight: bold; padding: 12px;">{{ i18n.ts.clip }}</div>
 							<div class="_gaps">
-								<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`">
-									<MkClipPreview :clip="item"/>
-								</MkA>
+								<MkClipPreview v-for="item in clips" :key="item.id" :clip="item"/>
 							</div>
 						</div>
 						<div v-if="!showPrev" class="_buttons" :class="$style.loadPrev">
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index b273bd36f398..87921bc67f5b 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -26,6 +26,14 @@ export async function getNoteClipMenu(props: {
 	isDeleted: Ref<boolean>;
 	currentClip?: Misskey.entities.Clip;
 }) {
+	function getClipName(clip: Misskey.entities.Clip) {
+		if ($i && clip.userId === $i.id && clip.notesCount != null) {
+			return `${clip.name} (${clip.notesCount}/${$i.policies.noteEachClipsLimit})`;
+		} else {
+			return clip.name;
+		}
+	}
+
 	const isRenote = (
 		props.note.renote != null &&
 		props.note.text == null &&
@@ -37,7 +45,7 @@ export async function getNoteClipMenu(props: {
 
 	const clips = await clipsCache.fetch();
 	const menu: MenuItem[] = [...clips.map(clip => ({
-		text: clip.name,
+		text: getClipName(clip),
 		action: () => {
 			claimAchievement('noteClipped1');
 			os.promiseDialog(
@@ -50,7 +58,18 @@ export async function getNoteClipMenu(props: {
 							text: i18n.tsx.confirmToUnclipAlreadyClippedNote({ name: clip.name }),
 						});
 						if (!confirm.canceled) {
-							os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
+							os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id }).then(() => {
+								clipsCache.set(clips.map(c => {
+									if (c.id === clip.id) {
+										return {
+											...c,
+											notesCount: Math.max(0, ((c.notesCount ?? 0) - 1)),
+										};
+									} else {
+										return c;
+									}
+								}));
+							});
 							if (props.currentClip?.id === clip.id) props.isDeleted.value = true;
 						}
 					} else {
@@ -60,7 +79,18 @@ export async function getNoteClipMenu(props: {
 						});
 					}
 				},
-			);
+			).then(() => {
+				clipsCache.set(clips.map(c => {
+					if (c.id === clip.id) {
+						return {
+							...c,
+							notesCount: (c.notesCount ?? 0) + 1,
+						};
+					} else {
+						return c;
+					}
+				}));
+			});
 		},
 	})), { type: 'divider' }, {
 		icon: 'ti ti-plus',
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index b6b26c000cdf..ae001cf874c6 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4460,6 +4460,7 @@ export type components = {
       isPublic: boolean;
       favoritedCount: number;
       isFavorited?: boolean;
+      notesCount?: number;
     };
     FederationInstance: {
       /** Format: id */

From b4faa7c4ec7f8557c4b29d4af7db5cdd92a5bb84 Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Mon, 15 Apr 2024 09:25:11 +0900
Subject: [PATCH 153/266] chore: Use integrity for Redoc script (#13716)

* Use integrity for redoc scripts

* official?
---
 packages/backend/assets/redoc.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/assets/redoc.html b/packages/backend/assets/redoc.html
index a9ebf662fc5a..2557b4532ec3 100644
--- a/packages/backend/assets/redoc.html
+++ b/packages/backend/assets/redoc.html
@@ -19,6 +19,6 @@
 	</head>
 	<body>
 		<redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc>
-		<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
+		<script src="https://cdn.redoc.ly/redoc/v2.1.3/bundles/redoc.standalone.js" integrity="sha256-u4DgqzYXoArvNF/Ymw3puKexfOC6lYfw0sfmeliBJ1I=" crossorigin="anonymous"></script>
 	</body>
 </html>

From c687b4eaa558aa3138d81f8fa4d9bbc376d0bd6c Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Mon, 15 Apr 2024 09:28:09 +0900
Subject: [PATCH 154/266] =?UTF-8?q?fix(backend):=20nginx=E7=B5=8C=E7=94=B1?=
 =?UTF-8?q?=E3=81=A7/files/=E3=81=ABRange=E3=83=AA=E3=82=AF=E3=82=A8?=
 =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=95=E3=82=8C=E3=81=9F=E5=A0=B4=E5=90=88?=
 =?UTF-8?q?=E3=81=AB=E6=AD=A3=E3=81=97=E3=81=8F=E5=BF=9C=E7=AD=94=E3=81=A7?=
 =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#13712)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fix files

* CHANGELOG
---
 CHANGELOG.md                                     | 1 +
 packages/backend/src/server/FileServerService.ts | 7 ++++---
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a238d99a062a..de18aded0c95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,7 @@
 - Fix: リプライのみの引用リノートと、CWのみの引用リノートが純粋なリノートとして誤って扱われてしまう問題を修正
 - Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
+- Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index ce7702143ed9..9db3aa1bfb37 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -214,6 +214,8 @@ export class FileServerService {
 				}
 
 				reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream');
+				reply.header('Content-Length', file.file.size);
+				reply.header('Cache-Control', 'max-age=31536000, immutable');
 				reply.header('Content-Disposition',
 					contentDisposition(
 						'inline',
@@ -256,6 +258,7 @@ export class FileServerService {
 				return fs.createReadStream(file.path);
 			} else {
 				reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.file.type) ? file.file.type : 'application/octet-stream');
+				reply.header('Content-Length', file.file.size);
 				reply.header('Cache-Control', 'max-age=31536000, immutable');
 				reply.header('Content-Disposition', contentDisposition('inline', file.filename));
 
@@ -530,9 +533,7 @@ export class FileServerService {
 		if (!file.storedInternal) {
 			if (!(file.isLink && file.uri)) return '204';
 			const result = await this.downloadAndDetectTypeFromUrl(file.uri);
-			if (!file.size) {
-				file.size = (await fs.promises.stat(result.path)).size;
-			}
+			file.size = (await fs.promises.stat(result.path)).size;	// DB file.sizeは正確とは限らないので
 			return {
 				...result,
 				url: file.uri,

From ca0d148a78bd1277479a38565f08c22cdfb4fcc2 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 15 Apr 2024 22:11:17 +0900
Subject: [PATCH 155/266] =?UTF-8?q?ci:=20Check=20Misskey=20JS=20autogen?=
 =?UTF-8?q?=E3=82=92=E6=A7=98=E3=80=85=E6=94=B9=E5=96=84=20(#13718)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../workflows/check-misskey-js-autogen.yml    | 143 +++++++++---------
 1 file changed, 72 insertions(+), 71 deletions(-)

diff --git a/.github/workflows/check-misskey-js-autogen.yml b/.github/workflows/check-misskey-js-autogen.yml
index 8fad129115ae..4aa0646b7b89 100644
--- a/.github/workflows/check-misskey-js-autogen.yml
+++ b/.github/workflows/check-misskey-js-autogen.yml
@@ -5,24 +5,23 @@ on:
     branches:
       - master
       - develop
+      - improve-misskey-js-autogen-check
     paths:
       - packages/backend/**
 
 jobs:
-  check-misskey-js-autogen:
+  # pull_request_target safety: permissions: read-all, and there are no secrets used in this job
+  generate-misskey-js:
     runs-on: ubuntu-latest
     permissions:
-      pull-requests: write
-
-    env:
-      api_json_name: "api-head.json"
-
+      contents: read
+    if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
     steps:
       - name: checkout
         uses: actions/checkout@v4.1.1
         with:
           submodules: true
-          ref: ${{ github.event.pull_request.head.sha }}
+          ref: refs/pull/${{ github.event.pull_request.number }}/merge
 
       - name: setup pnpm
         uses: pnpm/action-setup@v3
@@ -39,79 +38,81 @@ jobs:
       - name: install dependencies
         run: pnpm i --frozen-lockfile
 
-      - name: wait get-api-diff
-        uses: lewagon/wait-on-check-action@v1.3.3
+      # generate api.json
+      - name: Copy Config
+        run: cp .config/example.yml .config/default.yml
+      - name: Build
+        run: pnpm build
+      - name: Generate API JSON
+        run: pnpm --filter backend generate-api-json
+
+      # build misskey js
+      - name: Build misskey-js
+        run: |-
+          cp packages/backend/built/api.json packages/misskey-js/generator/api.json
+          pnpm run --filter misskey-js-type-generator generate
+
+      # packages/misskey-js/generator/built/autogen
+      - name: Upload Generated
+        uses: actions/upload-artifact@v4
         with:
-          ref: ${{ github.event.pull_request.head.sha }}
-          check-regexp: get-from-misskey .+
-          repo-token: ${{ secrets.GITHUB_TOKEN }}
-          wait-interval: 30
+          name: generated-misskey-js
+          path: packages/misskey-js/generator/built/autogen
 
-      - name: Download artifact
-        uses: actions/github-script@v7.0.1
+  # pull_request_target safety: permissions: read-all, and there are no secrets used in this job
+  get-actual-misskey-js:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+    if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
+    steps:
+      - name: checkout
+        uses: actions/checkout@v4.1.1
         with:
-          script: |
-            const fs = require('fs');
-
-            const workflows = await github.rest.actions.listWorkflowRunsForRepo({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              head_sha: `${{ github.event.pull_request.head.sha }}`
-            }).then(x => x.data.workflow_runs);
-
-            console.log(workflows.map(x => ({name: x.name, title: x.display_title})));
-
-            const run_id = workflows.find(x => x.name.includes("Get api.json from Misskey")).id;
-
-            let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
-               owner: context.repo.owner,
-               repo: context.repo.repo,
-               run_id: run_id,
-            });
-
-            let matchArtifacts = allArtifacts.data.artifacts.filter((artifact) => {
-              return artifact.name.startsWith("api-artifact-") || artifact.name == "api-artifact"
-            });
-
-            await Promise.all(matchArtifacts.map(async (artifact) => {
-              let download = await github.rest.actions.downloadArtifact({
-                owner: context.repo.owner,
-                repo: context.repo.repo,
-                artifact_id: artifact.id,
-                archive_format: 'zip',
-              });
-              await fs.promises.writeFile(`${process.env.GITHUB_WORKSPACE}/${artifact.name}.zip`, Buffer.from(download.data));
-            }));
-
-      - name: unzip artifacts
-        run: |-
-          find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec unzip {} -d . ';'
-          ls -la
+          submodules: true
+          ref: refs/pull/${{ github.event.pull_request.number }}/merge
 
-      - name: get head checksum
-        run: |-
-          checksum=$(realpath head_checksum)
+      - name: Upload From Merged
+        uses: actions/upload-artifact@v4
+        with:
+          name: actual-misskey-js
+          path: packages/misskey-js/src/autogen
 
-          cd packages/misskey-js/src
-          find autogen -type f -exec sh -c 'echo $(sed -E "s/^\s+\*\s+generatedAt:.+$//" {} | sha256sum | cut -d" " -f 1) {}' \; > $checksum
-          cd ../../..
+  # pull_request_target safety: nothing is cloned from repository
+  comment-misskey-js-autogen:
+    runs-on: ubuntu-latest
+    needs: [generate-misskey-js, get-actual-misskey-js]
+    permissions:
+      pull-requests: write
+    steps:
+      - name: download generated-misskey-js
+        uses: actions/download-artifact@v4
+        with:
+          name: generated-misskey-js
+          path: misskey-js-generated
 
-      - name: build autogen
-        run: |-
-            checksum=$(realpath ${api_json_name}_checksum)
-            mv $api_json_name packages/misskey-js/generator/api.json
+      - name: download actual-misskey-js
+        uses: actions/download-artifact@v4
+        with:
+          name: actual-misskey-js
+          path: misskey-js-actual
+
+      - name: check misskey-js changes
+        id: check-changes
+        run: |
+          diff -r -u --label=generated --label=on-tree ./misskey-js-generated ./misskey-js-actual > misskey-js.diff || true
 
-            cd packages/misskey-js/generator
-            pnpm run generate
-            cd built
-            find autogen -type f -exec sh -c 'echo $(sed -E "s/^\s+\*\s+generatedAt:.+$//" {} | sha256sum | cut -d" " -f 1) {}' \; > $checksum
-            cd ../../../..
+          if [ -s misskey-js.diff ]; then
+            echo "changes=true" >> $GITHUB_OUTPUT
+          else
+            echo "changes=false" >> $GITHUB_OUTPUT
+          fi
 
-      - name: check update for type definitions
-        run: diff head_checksum ${api_json_name}_checksum
+      - name: Print full diff
+        run: cat ./misskey-js.diff
 
       - name: send message
-        if: failure()
+        if: steps.check-changes.outputs.changes == 'true'
         uses: thollander/actions-comment-pull-request@v2
         with:
           comment_tag: check-misskey-js-autogen
@@ -125,7 +126,7 @@ jobs:
             ```
 
       - name: send message
-        if: success()
+        if: steps.check-changes.outputs.changes == 'false'
         uses: thollander/actions-comment-pull-request@v2
         with:
           comment_tag: check-misskey-js-autogen

From e9e877f64e83bf34f90373a366567b852d3cce18 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 16 Apr 2024 13:37:14 +0900
Subject: [PATCH 156/266] =?UTF-8?q?fix:=20=E3=83=80=E3=82=A4=E3=83=AC?=
 =?UTF-8?q?=E3=82=AF=E3=83=88=E6=8A=95=E7=A8=BF=E3=81=AE=E5=AE=9B=E5=85=88?=
 =?UTF-8?q?=E3=81=8C=E4=BF=9D=E5=AD=98=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=20(#13717)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: ダイレクト投稿の宛先が保存されない

* fix: 同じユーザーが複数回宛先に追加できる問題

* fix: 関係ないユーザーが宛先に追加される可能性がある
---
 CHANGELOG.md                                    |  1 +
 packages/frontend/src/components/MkPostForm.vue | 12 +++++++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index de18aded0c95..4aad65d83704 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,7 @@
 - Fix: CWのみの引用リノートが詳細ページで純粋なリノートとして誤って扱われてしまう問題を修正
 - Fix: ノート詳細ページにおいてCW付き引用リノートのCWボタンのラベルに「引用」が含まれていない問題を修正
 - Fix: ダイアログの入力で字数制限に違反していてもEnterキーが押せてしまう問題を修正
+- Fix: ダイレクト投稿の宛先が保存されない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 014b866fbdbe..d7efca9de9c9 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -388,7 +388,7 @@ function addMissingMention() {
 	for (const x of extractMentions(ast)) {
 		if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
 			misskeyApi('users/show', { username: x.username, host: x.host }).then(user => {
-				visibleUsers.value.push(user);
+				pushVisibleUser(user);
 			});
 		}
 	}
@@ -679,6 +679,7 @@ function saveDraft() {
 			localOnly: localOnly.value,
 			files: files.value,
 			poll: poll.value,
+			visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(x => x.id) : undefined,
 		},
 	};
 
@@ -960,6 +961,15 @@ onMounted(() => {
 				if (draft.data.poll) {
 					poll.value = draft.data.poll;
 				}
+				if (draft.data.visibleUserIds) {
+					misskeyApi('users/show', { userIds: draft.data.visibleUserIds }).then(users => {
+						for (let i = 0; i < users.length; i++) {
+							if (users[i].id === draft.data.visibleUserIds[i]) {
+								pushVisibleUser(users[i]);
+							}
+						}
+					});
+				}
 			}
 		}
 

From 6f489b58a18310fa9d8aef695d984f7ceb312102 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 17 Apr 2024 10:48:42 +0900
Subject: [PATCH 157/266] =?UTF-8?q?enhance(frontend):=20=E3=83=9A=E3=83=BC?=
 =?UTF-8?q?=E3=82=B8=E3=81=AE=E8=A1=A8=E7=A4=BA=E9=83=A8=E4=B8=8A=E9=83=A8?=
 =?UTF-8?q?=E3=81=AB=E7=B7=A8=E9=9B=86=E3=83=AA=E3=83=B3=E3=82=AF=E3=82=92?=
 =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20(#13724)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/pages/page.vue | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index ab44533b8103..893c2deebf5c 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:leaveActiveClass="defaultStore.state.animation ? $style.fadeLeaveActive : ''"
 			:enterFromClass="defaultStore.state.animation ? $style.fadeEnterFrom : ''"
 			:leaveToClass="defaultStore.state.animation ? $style.fadeLeaveTo : ''"
+			mode="out-in"
 		>
 			<div v-if="page" :key="page.id" class="_gaps">
 				<div :class="$style.pageMain">
@@ -41,8 +42,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</div>
 						<div :class="$style.pageBannerTitle" class="_gaps_s">
 							<h1>{{ page.title || page.name }}</h1>
-							<div v-if="page.user" :class="$style.pageBannerTitleUser">
-								<MkAvatar :user="page.user" :class="$style.avatar" indicator link preview/> <MkA :to="`/@${username}`"><MkUserName :user="page.user" :nowrap="false"/></MkA>
+							<div :class="$style.pageBannerTitleSub">
+								<div v-if="page.user" :class="$style.pageBannerTitleUser">
+									<MkAvatar :user="page.user" :class="$style.avatar" indicator link preview/> <MkA :to="`/@${username}`"><MkUserName :user="page.user" :nowrap="false"/></MkA>
+								</div>
+								<div :class="$style.pageBannerTitleSubActions">
+									<button v-tooltip="i18n.ts.share" class="_button" :class="$style.generalActionButton" @click="share"><i class="ti ti-share ti-fw"></i></button>
+									<MkA v-if="page.userId === $i?.id" v-tooltip="i18n.ts._pages.editThisPage" :to="`/pages/edit/${page.id}`" class="_button" :class="$style.generalActionButton"><i class="ti ti-pencil ti-fw"></i></MkA>
+								</div>
 							</div>
 						</div>
 					</div>
@@ -355,8 +362,15 @@ definePageMetadata(() => ({
 			margin: 0;
 		}
 
+		.pageBannerTitleSub {
+			display: flex;
+			align-items: center;
+			width: 100%;
+		}
+
 		.pageBannerTitleUser {
 			--height: 32px;
+			flex-shrink: 0;
 
 			.avatar {
 				height: var(--height);
@@ -365,6 +379,14 @@ definePageMetadata(() => ({
 
 			line-height: var(--height);
 		}
+
+		.pageBannerTitleSubActions {
+			flex-shrink: 0;
+			display: flex;
+			align-items: center;
+			gap: var(--marginHalf);
+			margin-left: auto;
+		}
 	}
 }
 

From 977e2d2c09c3fbc2fd2eaead8fc7314d8d6f9fc4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 17 Apr 2024 10:53:16 +0900
Subject: [PATCH 158/266] =?UTF-8?q?enhance(frontend):=20=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=81=99=E3=82=8B=E3=81=8B=E3=81=A9=E3=81=86?=
 =?UTF-8?q?=E3=81=8B=E3=81=AE=E7=A2=BA=E8=AA=8D=E3=83=80=E3=82=A4=E3=82=A2?=
 =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=92=E5=87=BA=E3=81=9B=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#13723)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(frontend): フォローするかどうかの確認ダイアログを出せるように

* Update Changelog
---
 CHANGELOG.md                                        |  1 +
 locales/index.d.ts                                  |  4 ++++
 locales/ja-JP.yml                                   |  1 +
 packages/frontend/src/components/MkFollowButton.vue | 12 ++++++++++++
 packages/frontend/src/pages/settings/general.vue    |  3 +++
 packages/frontend/src/store.ts                      |  4 ++++
 6 files changed, 25 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4aad65d83704..d27979d88ff9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@
 - Enhance: ノートについているリアクションの「もっと!」から、リアクションの一覧を表示できるように
 - Enhance: リプライにて引用がある場合テキストが空でもノートできるように
   - 引用したいノートのURLをコピーしリプライ投稿画面にペーストして添付することで達成できます
+- Enhance: フォローするかどうかの確認ダイアログを出せるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/locales/index.d.ts b/locales/index.d.ts
index cbea39f1cd18..8e31fc8d597c 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4948,6 +4948,10 @@ export interface Locale extends ILocale {
      * 説明文はありません
      */
     "noDescription": string;
+    /**
+     * フォローの際常に確認する
+     */
+    "alwaysConfirmFollow": string;
     "_bubbleGame": {
         /**
          * 遊び方
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 4ab2f5adb02d..f5984597929a 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1233,6 +1233,7 @@ useNativeUIForVideoAudioPlayer: "動画・音声の再生にブラウザのUIを
 keepOriginalFilename: "オリジナルのファイル名を保持"
 keepOriginalFilenameDescription: "この設定をオフにすると、アップロード時にファイル名が自動でランダム文字列に置き換えられます。"
 noDescription: "説明文はありません"
+alwaysConfirmFollow: "フォローの際常に確認する"
 
 _bubbleGame:
   howToPlay: "遊び方"
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index 28450e11fcc6..636e61db8f4b 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -93,6 +93,18 @@ async function onClick() {
 				userId: props.user.id,
 			});
 		} else {
+			if (defaultStore.state.alwaysConfirmFollow) {
+				const { canceled } = await os.confirm({
+					type: 'question',
+					text: i18n.tsx.followConfirm({ name: props.user.name || props.user.username }),
+				});
+
+				if (canceled) {
+					wait.value = false;
+					return;
+				}
+			}
+
 			if (hasPendingFollowRequestFromYou.value) {
 				await misskeyApi('following/requests/cancel', {
 					userId: props.user.id,
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index f2f82c480844..55d514ddf9d6 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -165,6 +165,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="keepScreenOn">{{ i18n.ts.keepScreenOn }}</MkSwitch>
 				<MkSwitch v-model="disableStreamingTimeline">{{ i18n.ts.disableStreamingTimeline }}</MkSwitch>
 				<MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch>
+				<MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch>
 			</div>
 			<MkSelect v-model="serverDisconnectedBehavior">
 				<template #label>{{ i18n.ts.whenServerDisconnected }}</template>
@@ -310,6 +311,7 @@ const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroup
 const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect'));
 const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe'));
 const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer'));
+const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
@@ -351,6 +353,7 @@ watch([
 	keepScreenOn,
 	disableStreamingTimeline,
 	enableSeasonalScreenEffect,
+	alwaysConfirmFollow,
 ], async () => {
 	await reloadAsk();
 });
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 9b5011739a06..e6a348b79fe9 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -450,6 +450,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: true,
 	},
+	alwaysConfirmFollow: {
+		where: 'device',
+		default: true,
+	},
 
 	sound_masterVolume: {
 		where: 'device',

From e423b8ce4b28ecbe4e300fc67389e4def3761eb6 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Wed, 17 Apr 2024 14:23:41 +0900
Subject: [PATCH 159/266] =?UTF-8?q?=E7=B4=B0=E3=81=8B=E3=81=84=E3=83=9F?=
 =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=88=E3=81=AE=E5=87=A6=E7=90=86=E3=81=AE?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#13695)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: some replies are removed from global timeline

* refactor: 各チャンネルのミュートとブロックの処理をまとめる

* fix: リノートをミュートでその人のノートのリノートをミュートしていたを修正

* refactor: isPureRenotePackedを他のところでも使う

* docs(changelog): CHANGELOGを更新

* test: withReplies = falseでフォローしてる人によるリプライが流れてくる

* test: ノートミュートしているユーザーの通常ノートのリノートが流れてくる/含まれる
---
 CHANGELOG.md                                  |  3 ++
 .../src/core/FanoutTimelineEndpointService.ts |  2 +-
 packages/backend/src/misc/is-renote.ts        | 31 +++++++++++++++++++
 .../backend/src/server/api/stream/channel.ts  | 22 +++++++++++++
 .../src/server/api/stream/channels/antenna.ts |  8 +----
 .../src/server/api/stream/channels/channel.ts | 11 ++-----
 .../api/stream/channels/global-timeline.ts    | 25 +++------------
 .../src/server/api/stream/channels/hashtag.ts | 11 ++-----
 .../api/stream/channels/home-timeline.ts      | 18 +++--------
 .../api/stream/channels/hybrid-timeline.ts    | 16 ++--------
 .../api/stream/channels/local-timeline.ts     | 14 +++------
 .../api/stream/channels/role-timeline.ts      |  9 +-----
 .../server/api/stream/channels/user-list.ts   | 17 +++-------
 packages/backend/test/e2e/renote-mute.ts      | 29 +++++++++++++++++
 packages/backend/test/e2e/streaming.ts        | 12 ++++++-
 15 files changed, 124 insertions(+), 104 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d27979d88ff9..36632e5024f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,9 @@
 - Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
 - Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
+- Fix: 一部のタイムラインのストリーミングでインスタンスミュートが効かない問題を修正
+- Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
+- Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 006433df7a22..884723ff819f 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -116,7 +116,7 @@ export class FanoutTimelineEndpointService {
 				filter = (note) => {
 					if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
 					if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
-					if (isRenote(note) && !isQuote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
+					if (!ps.ignoreAuthorFromMute && isRenote(note) && !isQuote(note) && userIdsWhoMeMutingRenotes.has(note.userId)) return false;
 					if (isInstanceMuted(note, userMutedInstances)) return false;
 
 					return parentFilter(note);
diff --git a/packages/backend/src/misc/is-renote.ts b/packages/backend/src/misc/is-renote.ts
index 5d48aba36092..48f821806c1a 100644
--- a/packages/backend/src/misc/is-renote.ts
+++ b/packages/backend/src/misc/is-renote.ts
@@ -4,6 +4,7 @@
  */
 
 import type { MiNote } from '@/models/Note.js';
+import type { Packed } from '@/misc/json-schema.js';
 
 type Renote =
 	MiNote & {
@@ -34,3 +35,33 @@ export function isQuote(note: Renote): note is Quote {
 		note.hasPoll ||
 		note.fileIds.length > 0;
 }
+
+type PackedRenote =
+	Packed<'Note'> & {
+		renoteId: NonNullable<Packed<'Note'>['renoteId']>
+	};
+
+type PackedQuote =
+	PackedRenote & ({
+		text: NonNullable<Packed<'Note'>['text']>
+	} | {
+		cw: NonNullable<Packed<'Note'>['cw']>
+	} | {
+		replyId: NonNullable<Packed<'Note'>['replyId']>
+	} | {
+		poll: NonNullable<Packed<'Note'>['poll']>
+	} | {
+		fileIds: NonNullable<Packed<'Note'>['fileIds']>
+	});
+
+export function isRenotePacked(note: Packed<'Note'>): note is PackedRenote {
+	return note.renoteId != null;
+}
+
+export function isQuotePacked(note: PackedRenote): note is PackedQuote {
+	return note.text != null ||
+		note.cw != null ||
+		note.replyId != null ||
+		note.poll != null ||
+		(note.fileIds != null && note.fileIds.length > 0);
+}
diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts
index 44a143538b4b..a267d27fba70 100644
--- a/packages/backend/src/server/api/stream/channel.ts
+++ b/packages/backend/src/server/api/stream/channel.ts
@@ -4,6 +4,10 @@
  */
 
 import { bindThis } from '@/decorators.js';
+import { isInstanceMuted } from '@/misc/is-instance-muted.js';
+import { isUserRelated } from '@/misc/is-user-related.js';
+import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
+import type { Packed } from '@/misc/json-schema.js';
 import type Connection from './Connection.js';
 
 /**
@@ -54,6 +58,24 @@ export default abstract class Channel {
 		return this.connection.subscriber;
 	}
 
+	/*
+	 * ミュートとブロックされてるを処理する
+	 */
+	protected isNoteMutedOrBlocked(note: Packed<'Note'>): boolean {
+		// 流れてきたNoteがインスタンスミュートしたインスタンスが関わる
+		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return true;
+
+		// 流れてきたNoteがミュートしているユーザーが関わる
+		if (isUserRelated(note, this.userIdsWhoMeMuting)) return true;
+		// 流れてきたNoteがブロックされているユーザーが関わる
+		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return true;
+
+		// 流れてきたNoteがリノートをミュートしてるユーザが行ったもの
+		if (isRenotePacked(note) && !isQuotePacked(note) && this.userIdsWhoMeMutingRenotes.has(note.user.id)) return true;
+
+		return false;
+	}
+
 	constructor(id: string, connection: Connection) {
 		this.id = id;
 		this.connection = connection;
diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts
index 135d162e6395..4a1d2dd109d2 100644
--- a/packages/backend/src/server/api/stream/channels/antenna.ts
+++ b/packages/backend/src/server/api/stream/channels/antenna.ts
@@ -4,7 +4,6 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
@@ -40,12 +39,7 @@ class AntennaChannel extends Channel {
 		if (data.type === 'note') {
 			const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true });
 
-			// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-			if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-			// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-			if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
-
-			if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
+			if (this.isNoteMutedOrBlocked(note)) return;
 
 			this.connection.cacheNote(note);
 
diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts
index 90ee1ecda5d3..140dd3dd9bcd 100644
--- a/packages/backend/src/server/api/stream/channels/channel.ts
+++ b/packages/backend/src/server/api/stream/channels/channel.ts
@@ -4,10 +4,10 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
+import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class ChannelChannel extends Channel {
@@ -38,14 +38,9 @@ class ChannelChannel extends Channel {
 	private async onNote(note: Packed<'Note'>) {
 		if (note.channelId !== this.channelId) return;
 
-		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
+		if (this.isNoteMutedOrBlocked(note)) return;
 
-		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
-
-		if (this.user && note.renoteId && !note.text) {
+		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
 				const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
 				note.renote.myReaction = myRenoteReaction;
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index 723b89c90877..17116258d89b 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -4,14 +4,12 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import { checkWordMute } from '@/misc/check-word-mute.js';
-import { isInstanceMuted } from '@/misc/is-instance-muted.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { MetaService } from '@/core/MetaService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
+import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class GlobalTimelineChannel extends Channel {
@@ -52,26 +50,11 @@ class GlobalTimelineChannel extends Channel {
 		if (note.visibility !== 'public') return;
 		if (note.channelId != null) return;
 
-		// 関係ない返信は除外
-		if (note.reply && !this.following[note.userId]?.withReplies) {
-			const reply = note.reply;
-			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
-			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
-		}
-
-		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
-
-		// Ignore notes from instances the user has muted
-		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
-
-		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
+		if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;
 
-		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
+		if (this.isNoteMutedOrBlocked(note)) return;
 
-		if (this.user && note.renoteId && !note.text) {
+		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
 				const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
 				note.renote.myReaction = myRenoteReaction;
diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts
index 377b1a016239..57bada5d9cfb 100644
--- a/packages/backend/src/server/api/stream/channels/hashtag.ts
+++ b/packages/backend/src/server/api/stream/channels/hashtag.ts
@@ -5,10 +5,10 @@
 
 import { Injectable } from '@nestjs/common';
 import { normalizeForSearch } from '@/misc/normalize-for-search.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
+import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class HashtagChannel extends Channel {
@@ -43,14 +43,9 @@ class HashtagChannel extends Channel {
 		const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
 		if (!matched) return;
 
-		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
+		if (this.isNoteMutedOrBlocked(note)) return;
 
-		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
-
-		if (this.user && note.renoteId && !note.text) {
+		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
 				const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
 				note.renote.myReaction = myRenoteReaction;
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index f45bf8622eb2..878a3180cb62 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -4,12 +4,10 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import { checkWordMute } from '@/misc/check-word-mute.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
-import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
+import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class HomeTimelineChannel extends Channel {
@@ -51,9 +49,6 @@ class HomeTimelineChannel extends Channel {
 			if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
 		}
 
-		// Ignore notes from instances the user has muted
-		if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return;
-
 		if (note.visibility === 'followers') {
 			if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
 		} else if (note.visibility === 'specified') {
@@ -72,7 +67,7 @@ class HomeTimelineChannel extends Channel {
 		}
 
 		// 純粋なリノート(引用リノートでないリノート)の場合
-		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && note.poll == null) {
+		if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
 			if (!this.withRenotes) return;
 			if (note.renote.reply) {
 				const reply = note.renote.reply;
@@ -81,14 +76,9 @@ class HomeTimelineChannel extends Channel {
 			}
 		}
 
-		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
-
-		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
+		if (this.isNoteMutedOrBlocked(note)) return;
 
-		if (this.user && note.renoteId && !note.text) {
+		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
 				const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
 				note.renote.myReaction = myRenoteReaction;
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index d67da6f56531..575d23d53c18 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -4,14 +4,12 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import { checkWordMute } from '@/misc/check-word-mute.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
-import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { MetaService } from '@/core/MetaService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
+import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class HybridTimelineChannel extends Channel {
@@ -71,8 +69,7 @@ class HybridTimelineChannel extends Channel {
 			if (!isMe && !note.visibleUserIds!.includes(this.user!.id)) return;
 		}
 
-		// Ignore notes from instances the user has muted
-		if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return;
+		if (this.isNoteMutedOrBlocked(note)) return;
 
 		if (note.reply) {
 			const reply = note.reply;
@@ -85,14 +82,7 @@ class HybridTimelineChannel extends Channel {
 			}
 		}
 
-		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
-
-		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
-
-		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
+		if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;
 
 		if (this.user && note.renoteId && !note.text) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts
index 43d26124ef2c..442d08ae51a5 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -4,13 +4,12 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import { checkWordMute } from '@/misc/check-word-mute.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { MetaService } from '@/core/MetaService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
+import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class LocalTimelineChannel extends Channel {
@@ -61,16 +60,11 @@ class LocalTimelineChannel extends Channel {
 			if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return;
 		}
 
-		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
+		if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;
 
-		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
+		if (this.isNoteMutedOrBlocked(note)) return;
 
-		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
-
-		if (this.user && note.renoteId && !note.text) {
+		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
 				const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
 				note.renote.myReaction = myRenoteReaction;
diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts
index 80aab4b35ec1..6a4ad224607b 100644
--- a/packages/backend/src/server/api/stream/channels/role-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts
@@ -4,8 +4,6 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import { isUserRelated } from '@/misc/is-user-related.js';
-import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
@@ -46,12 +44,7 @@ class RoleTimelineChannel extends Channel {
 			}
 			if (note.visibility !== 'public') return;
 
-			// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-			if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-			// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-			if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
-
-			if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
+			if (this.isNoteMutedOrBlocked(note)) return;
 
 			this.send('note', note);
 		} else {
diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts
index f7bb106c03eb..14b30a157cd0 100644
--- a/packages/backend/src/server/api/stream/channels/user-list.ts
+++ b/packages/backend/src/server/api/stream/channels/user-list.ts
@@ -5,12 +5,11 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
-import { isInstanceMuted } from '@/misc/is-instance-muted.js';
+import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class UserListChannel extends Channel {
@@ -106,25 +105,17 @@ class UserListChannel extends Channel {
 			}
 		}
 
-		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
+		if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;
 
-		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
-		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
-		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
+		if (this.isNoteMutedOrBlocked(note)) return;
 
-		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
-
-		if (this.user && note.renoteId && !note.text) {
+		if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
 				const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
 				note.renote.myReaction = myRenoteReaction;
 			}
 		}
 
-		// 流れてきたNoteがミュートしているインスタンスに関わるものだったら無視する
-		if (isInstanceMuted(note, this.userMutedInstances)) return;
-
 		this.connection.cacheNote(note);
 
 		this.send('note', note);
diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts
index 9826068e4826..1abbb4f04494 100644
--- a/packages/backend/test/e2e/renote-mute.ts
+++ b/packages/backend/test/e2e/renote-mute.ts
@@ -63,6 +63,22 @@ describe('Renote Mute', () => {
 		assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
 	});
 
+	// #12956
+	test('タイムラインにリノートミュートしているユーザーの通常ノートのリノートが含まれる', async () => {
+		const carolNote = await post(carol, { text: 'hi' });
+		const bobRenote = await post(bob, { renoteId: carolNote.id });
+
+		// redisに追加されるのを待つ
+		await sleep(100);
+
+		const res = await api('notes/local-timeline', {}, alice);
+
+		assert.strictEqual(res.status, 200);
+		assert.strictEqual(Array.isArray(res.body), true);
+		assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+		assert.strictEqual(res.body.some((note: any) => note.id === bobRenote.id), true);
+	});
+
 	test('ストリームにリノートミュートしているユーザーのリノートが流れない', async () => {
 		const bobNote = await post(bob, { text: 'hi' });
 
@@ -86,4 +102,17 @@ describe('Renote Mute', () => {
 
 		assert.strictEqual(fired, true);
 	});
+
+	// #12956
+	test('ストリームにリノートミュートしているユーザーの通常ノートのリノートが流れてくる', async () => {
+		const carolbNote = await post(carol, { text: 'hi' });
+
+		const fired = await waitFire(
+			alice, 'localTimeline',
+			() => api('notes/create', { renoteId: carolbNote.id }, bob),
+			msg => msg.type === 'note' && msg.body.userId === bob.id,
+		);
+
+		assert.strictEqual(fired, true);
+	});
 });
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index 26bb68ec6c83..b0a70074c6a3 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -63,7 +63,7 @@ describe('Streaming', () => {
 			takumiNote = await post(takumi, { text: 'piyo' });
 
 			// Follow: ayano => kyoko
-			await api('following/create', { userId: kyoko.id }, ayano);
+			await api('following/create', { userId: kyoko.id, withReplies: false }, ayano);
 
 			// Follow: ayano => akari
 			await follow(ayano, akari);
@@ -509,6 +509,16 @@ describe('Streaming', () => {
 
 				assert.strictEqual(fired, false);
 			});
+
+			test('withReplies = falseでフォローしてる人によるリプライが流れてくる', async () => {
+				const fired = await waitFire(
+					ayano, 'globalTimeline',		// ayano:Global
+					() => api('notes/create', { text: 'foo', replyId: kanakoNote.id }, kyoko),	// kyoko posts
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,	// wait kyoko
+				);
+
+				assert.strictEqual(fired, true);
+			});
 		});
 
 		describe('UserList Timeline', () => {

From ea9aa6fdb41d3d5c0611f17fdacedee4861fdd37 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 17 Apr 2024 18:29:35 +0900
Subject: [PATCH 160/266] =?UTF-8?q?:art:=20=EF=BC=88=E3=83=9A=E3=83=BC?=
 =?UTF-8?q?=E3=82=B8=E8=A1=A8=E7=A4=BA=E9=83=A8=E4=B8=8A=E9=83=A8=E3=81=AE?=
 =?UTF-8?q?=E3=83=9C=E3=82=BF=E3=83=B3=E9=A0=86=E5=BA=8F=E3=82=92=E5=A4=89?=
 =?UTF-8?q?=E6=9B=B4=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix https://github.com/misskey-dev/misskey/pull/13724#discussion_r1568179954
---
 packages/frontend/src/pages/page.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 893c2deebf5c..e73d0320006c 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -47,8 +47,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 									<MkAvatar :user="page.user" :class="$style.avatar" indicator link preview/> <MkA :to="`/@${username}`"><MkUserName :user="page.user" :nowrap="false"/></MkA>
 								</div>
 								<div :class="$style.pageBannerTitleSubActions">
-									<button v-tooltip="i18n.ts.share" class="_button" :class="$style.generalActionButton" @click="share"><i class="ti ti-share ti-fw"></i></button>
 									<MkA v-if="page.userId === $i?.id" v-tooltip="i18n.ts._pages.editThisPage" :to="`/pages/edit/${page.id}`" class="_button" :class="$style.generalActionButton"><i class="ti ti-pencil ti-fw"></i></MkA>
+									<button v-tooltip="i18n.ts.share" class="_button" :class="$style.generalActionButton" @click="share"><i class="ti ti-share ti-fw"></i></button>
 								</div>
 							</div>
 						</div>

From cd7f7271ca5595cae95f6fb0280fac9dee77d751 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 19 Apr 2024 15:22:23 +0900
Subject: [PATCH 161/266] =?UTF-8?q?enhance:=20=E6=96=B0=E3=81=97=E3=81=84?=
 =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=87=E3=82=A3=E3=82=B7=E3=83=A7=E3=83=8A?=
 =?UTF-8?q?=E3=83=AB=E3=83=AD=E3=83=BC=E3=83=AB=E6=9D=A1=E4=BB=B6=E3=81=AE?=
 =?UTF-8?q?=E5=AE=9F=E8=A3=85=20(#13732)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: 新しいコンディショナルロールの実装

* fix: CHANGELOG.md
---
 CHANGELOG.md                                  |   6 +
 locales/index.d.ts                            |  20 +
 locales/ja-JP.yml                             |   5 +
 packages/backend/src/core/RoleService.ts      |  34 ++
 packages/backend/src/misc/json-schema.ts      |   2 +
 packages/backend/src/models/Role.ts           |  85 +++
 .../backend/src/models/json-schema/role.ts    |  17 +
 packages/backend/test/unit/RoleService.ts     | 498 +++++++++++++++---
 .../src/pages/admin/RolesEditorFormula.vue    |   5 +
 packages/misskey-js/etc/misskey-js.api.md     |   4 +
 packages/misskey-js/src/autogen/models.ts     |   1 +
 packages/misskey-js/src/autogen/types.ts      |   7 +-
 12 files changed, 617 insertions(+), 67 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 36632e5024f4..c13fa664dd53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,12 @@
 - Enhance: アンテナでBotによるノートを除外できるように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
 - Enhance: クリップのノート数を表示するように
+- Enhance: コンディショナルロールの条件として以下を新たに追加 (#13667)
+  - 猫ユーザーか
+  - botユーザーか
+  - サスペンド済みユーザーか
+  - 鍵アカウントユーザーか
+  - 「アカウントを見つけやすくする」が有効なユーザーか
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 
 ### Client
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 8e31fc8d597c..9bcd1979afbd 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -6592,6 +6592,26 @@ export interface Locale extends ILocale {
              * リモートユーザー
              */
             "isRemote": string;
+            /**
+             * 猫ユーザー
+             */
+            "isCat": string;
+            /**
+             * botユーザー
+             */
+            "isBot": string;
+            /**
+             * サスペンド済みユーザー
+             */
+            "isSuspended": string;
+            /**
+             * 鍵アカウントユーザー
+             */
+            "isLocked": string;
+            /**
+             * 「アカウントを見つけやすくする」が有効なユーザー
+             */
+            "isExplorable": string;
             /**
              * アカウント作成から~以内
              */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index f5984597929a..5f7715b210ed 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1703,6 +1703,11 @@ _role:
     roleAssignedTo: "マニュアルロールにアサイン済み"
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
+    isCat: "猫ユーザー"
+    isBot: "botユーザー"
+    isSuspended: "サスペンド済みユーザー"
+    isLocked: "鍵アカウントユーザー"
+    isExplorable: "「アカウントを見つけやすくする」が有効なユーザー"
     createdLessThan: "アカウント作成から~以内"
     createdMoreThan: "アカウント作成から~経過"
     followersLessThanOrEq: "フォロワー数が~以下"
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 09f309711445..70c537f9abdc 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -205,45 +205,79 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 	private evalCond(user: MiUser, roles: MiRole[], value: RoleCondFormulaValue): boolean {
 		try {
 			switch (value.type) {
+				// ~かつ~
 				case 'and': {
 					return value.values.every(v => this.evalCond(user, roles, v));
 				}
+				// ~または~
 				case 'or': {
 					return value.values.some(v => this.evalCond(user, roles, v));
 				}
+				// ~ではない
 				case 'not': {
 					return !this.evalCond(user, roles, value.value);
 				}
+				// マニュアルロールがアサインされている
 				case 'roleAssignedTo': {
 					return roles.some(r => r.id === value.roleId);
 				}
+				// ローカルユーザのみ
 				case 'isLocal': {
 					return this.userEntityService.isLocalUser(user);
 				}
+				// リモートユーザのみ
 				case 'isRemote': {
 					return this.userEntityService.isRemoteUser(user);
 				}
+				// サスペンド済みユーザである
+				case 'isSuspended': {
+					return user.isSuspended;
+				}
+				// 鍵アカウントユーザである
+				case 'isLocked': {
+					return user.isLocked;
+				}
+				// botユーザである
+				case 'isBot': {
+					return user.isBot;
+				}
+				// 猫である
+				case 'isCat': {
+					return user.isCat;
+				}
+				// 「ユーザを見つけやすくする」が有効なアカウント
+				case 'isExplorable': {
+					return user.isExplorable;
+				}
+				// ユーザが作成されてから指定期間経過した
 				case 'createdLessThan': {
 					return this.idService.parse(user.id).date.getTime() > (Date.now() - (value.sec * 1000));
 				}
+				// ユーザが作成されてから指定期間経っていない
 				case 'createdMoreThan': {
 					return this.idService.parse(user.id).date.getTime() < (Date.now() - (value.sec * 1000));
 				}
+				// フォロワー数が指定値以下
 				case 'followersLessThanOrEq': {
 					return user.followersCount <= value.value;
 				}
+				// フォロワー数が指定値以上
 				case 'followersMoreThanOrEq': {
 					return user.followersCount >= value.value;
 				}
+				// フォロー数が指定値以下
 				case 'followingLessThanOrEq': {
 					return user.followingCount <= value.value;
 				}
+				// フォロー数が指定値以上
 				case 'followingMoreThanOrEq': {
 					return user.followingCount >= value.value;
 				}
+				// ノート数が指定値以下
 				case 'notesLessThanOrEq': {
 					return user.notesCount <= value.value;
 				}
+				// ノート数が指定値以上
 				case 'notesMoreThanOrEq': {
 					return user.notesCount >= value.value;
 				}
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index 46b0bb2fab67..a620d7c94b8c 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -48,6 +48,7 @@ import {
 	packedRoleCondFormulaValueCreatedSchema,
 	packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
 	packedRoleCondFormulaValueSchema,
+	packedRoleCondFormulaValueUserSettingBooleanSchema,
 } from '@/models/json-schema/role.js';
 import { packedAdSchema } from '@/models/json-schema/ad.js';
 import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
@@ -97,6 +98,7 @@ export const refs = {
 	RoleCondFormulaLogics: packedRoleCondFormulaLogicsSchema,
 	RoleCondFormulaValueNot: packedRoleCondFormulaValueNot,
 	RoleCondFormulaValueIsLocalOrRemote: packedRoleCondFormulaValueIsLocalOrRemoteSchema,
+	RoleCondFormulaValueUserSettingBooleanSchema: packedRoleCondFormulaValueUserSettingBooleanSchema,
 	RoleCondFormulaValueAssignedRole: packedRoleCondFormulaValueAssignedRoleSchema,
 	RoleCondFormulaValueCreated: packedRoleCondFormulaValueCreatedSchema,
 	RoleCondFormulaFollowersOrFollowingOrNotes: packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
diff --git a/packages/backend/src/models/Role.ts b/packages/backend/src/models/Role.ts
index 058abe311863..a173971b2ce2 100644
--- a/packages/backend/src/models/Role.ts
+++ b/packages/backend/src/models/Role.ts
@@ -6,69 +6,149 @@
 import { Entity, Column, PrimaryColumn } from 'typeorm';
 import { id } from './util/id.js';
 
+/**
+ * ~かつ~
+ * 複数の条件を同時に満たす場合のみ成立とする
+ */
 type CondFormulaValueAnd = {
 	type: 'and';
 	values: RoleCondFormulaValue[];
 };
 
+/**
+ * ~または~
+ * 複数の条件のうち、いずれかを満たす場合のみ成立とする
+ */
 type CondFormulaValueOr = {
 	type: 'or';
 	values: RoleCondFormulaValue[];
 };
 
+/**
+ * ~ではない
+ * 条件を満たさない場合のみ成立とする
+ */
 type CondFormulaValueNot = {
 	type: 'not';
 	value: RoleCondFormulaValue;
 };
 
+/**
+ * ローカルユーザーのみ成立とする
+ */
 type CondFormulaValueIsLocal = {
 	type: 'isLocal';
 };
 
+/**
+ * リモートユーザーのみ成立とする
+ */
 type CondFormulaValueIsRemote = {
 	type: 'isRemote';
 };
 
+/**
+ * 既に指定のマニュアルロールにアサインされている場合のみ成立とする
+ */
 type CondFormulaValueRoleAssignedTo = {
 	type: 'roleAssignedTo';
 	roleId: string;
 };
 
+/**
+ * サスペンド済みアカウントの場合のみ成立とする
+ */
+type CondFormulaValueIsSuspended = {
+	type: 'isSuspended';
+};
+
+/**
+ * 鍵アカウントの場合のみ成立とする
+ */
+type CondFormulaValueIsLocked = {
+	type: 'isLocked';
+};
+
+/**
+ * botアカウントの場合のみ成立とする
+ */
+type CondFormulaValueIsBot = {
+	type: 'isBot';
+};
+
+/**
+ * 猫アカウントの場合のみ成立とする
+ */
+type CondFormulaValueIsCat = {
+	type: 'isCat';
+};
+
+/**
+ * 「ユーザを見つけやすくする」が有効なアカウントの場合のみ成立とする
+ */
+type CondFormulaValueIsExplorable = {
+	type: 'isExplorable';
+};
+
+/**
+ * ユーザが作成されてから指定期間経過した場合のみ成立とする
+ */
 type CondFormulaValueCreatedLessThan = {
 	type: 'createdLessThan';
 	sec: number;
 };
 
+/**
+ * ユーザが作成されてから指定期間経っていない場合のみ成立とする
+ */
 type CondFormulaValueCreatedMoreThan = {
 	type: 'createdMoreThan';
 	sec: number;
 };
 
+/**
+ * フォロワー数が指定値以下の場合のみ成立とする
+ */
 type CondFormulaValueFollowersLessThanOrEq = {
 	type: 'followersLessThanOrEq';
 	value: number;
 };
 
+/**
+ * フォロワー数が指定値以上の場合のみ成立とする
+ */
 type CondFormulaValueFollowersMoreThanOrEq = {
 	type: 'followersMoreThanOrEq';
 	value: number;
 };
 
+/**
+ * フォロー数が指定値以下の場合のみ成立とする
+ */
 type CondFormulaValueFollowingLessThanOrEq = {
 	type: 'followingLessThanOrEq';
 	value: number;
 };
 
+/**
+ * フォロー数が指定値以上の場合のみ成立とする
+ */
 type CondFormulaValueFollowingMoreThanOrEq = {
 	type: 'followingMoreThanOrEq';
 	value: number;
 };
 
+/**
+ * 投稿数が指定値以下の場合のみ成立とする
+ */
 type CondFormulaValueNotesLessThanOrEq = {
 	type: 'notesLessThanOrEq';
 	value: number;
 };
 
+/**
+ * 投稿数が指定値以上の場合のみ成立とする
+ */
 type CondFormulaValueNotesMoreThanOrEq = {
 	type: 'notesMoreThanOrEq';
 	value: number;
@@ -80,6 +160,11 @@ export type RoleCondFormulaValue = { id: string } & (
 	CondFormulaValueNot |
 	CondFormulaValueIsLocal |
 	CondFormulaValueIsRemote |
+	CondFormulaValueIsSuspended |
+	CondFormulaValueIsLocked |
+	CondFormulaValueIsBot |
+	CondFormulaValueIsCat |
+	CondFormulaValueIsExplorable |
 	CondFormulaValueRoleAssignedTo |
 	CondFormulaValueCreatedLessThan |
 	CondFormulaValueCreatedMoreThan |
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index c7702505039d..d9987a70c381 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -57,6 +57,20 @@ export const packedRoleCondFormulaValueIsLocalOrRemoteSchema = {
 	},
 } as const;
 
+export const packedRoleCondFormulaValueUserSettingBooleanSchema = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string', optional: false,
+		},
+		type: {
+			type: 'string',
+			nullable: false, optional: false,
+			enum: ['isSuspended', 'isLocked', 'isBot', 'isCat', 'isExplorable'],
+		},
+	},
+} as const;
+
 export const packedRoleCondFormulaValueAssignedRoleSchema = {
 	type: 'object',
 	properties: {
@@ -135,6 +149,9 @@ export const packedRoleCondFormulaValueSchema = {
 		{
 			ref: 'RoleCondFormulaValueIsLocalOrRemote',
 		},
+		{
+			ref: 'RoleCondFormulaValueUserSettingBooleanSchema',
+		},
 		{
 			ref: 'RoleCondFormulaValueAssignedRole',
 		},
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index 19d03570e00d..ec441735d7e6 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -3,6 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+
 process.env.NODE_ENV = 'test';
 
 import { jest } from '@jest/globals';
@@ -20,6 +22,7 @@ import { IdService } from '@/core/IdService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
 import { NotificationService } from '@/core/NotificationService.js';
+import { RoleCondFormulaValue } from '@/models/Role.js';
 import { sleep } from '../utils.js';
 import type { TestingModule } from '@nestjs/testing';
 import type { MockFunctionMetadata } from 'jest-mock';
@@ -52,12 +55,26 @@ describe('RoleService', () => {
 			id: genAidx(Date.now()),
 			updatedAt: new Date(),
 			lastUsedAt: new Date(),
+			name: '',
 			description: '',
 			...data,
 		})
 			.then(x => rolesRepository.findOneByOrFail(x.identifiers[0]));
 	}
 
+	function createConditionalRole(condFormula: RoleCondFormulaValue, data: Partial<MiRole> = {}) {
+		return createRole({
+			name: `[conditional] ${condFormula.type}`,
+			target: 'conditional',
+			condFormula: condFormula,
+			...data,
+		});
+	}
+
+	function aidx() {
+		return genAidx(Date.now());
+	}
+
 	beforeEach(async () => {
 		clock = lolex.install({
 			now: new Date(),
@@ -73,6 +90,7 @@ describe('RoleService', () => {
 				CacheService,
 				IdService,
 				GlobalEventService,
+				UserEntityService,
 				{
 					provide: NotificationService,
 					useFactory: () => ({
@@ -209,15 +227,9 @@ describe('RoleService', () => {
 			expect(result.driveCapacityMb).toBe(100);
 		});
 
-		test('conditional role', async () => {
-			const user1 = await createUser({
-				id: genAidx(Date.now() - (1000 * 60 * 60 * 24 * 365)),
-			});
-			const user2 = await createUser({
-				id: genAidx(Date.now() - (1000 * 60 * 60 * 24 * 365)),
-				followersCount: 10,
-			});
-			await createRole({
+		test('expired role', async () => {
+			const user = await createUser();
+			const role = await createRole({
 				name: 'a',
 				policies: {
 					canManageCustomEmojis: {
@@ -226,35 +238,133 @@ describe('RoleService', () => {
 						value: true,
 					},
 				},
-				target: 'conditional',
-				condFormula: {
-					id: '232a4221-9816-49a6-a967-ae0fac52ec5e',
-					type: 'and',
-					values: [{
-						id: '2a37ef43-2d93-4c4d-87f6-f2fdb7d9b530',
-						type: 'followersMoreThanOrEq',
-						value: 10,
-					}, {
-						id: '1bd67839-b126-4f92-bad0-4e285dab453b',
-						type: 'createdMoreThan',
-						sec: 60 * 60 * 24 * 7,
-					}],
-				},
 			});
-
+			await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24)));
 			metaService.fetch.mockResolvedValue({
 				policies: {
 					canManageCustomEmojis: false,
 				},
 			} as any);
 
-			const user1Policies = await roleService.getUserPolicies(user1.id);
-			const user2Policies = await roleService.getUserPolicies(user2.id);
-			expect(user1Policies.canManageCustomEmojis).toBe(false);
-			expect(user2Policies.canManageCustomEmojis).toBe(true);
+			const result = await roleService.getUserPolicies(user.id);
+			expect(result.canManageCustomEmojis).toBe(true);
+
+			clock.tick('25:00:00');
+
+			const resultAfter25h = await roleService.getUserPolicies(user.id);
+			expect(resultAfter25h.canManageCustomEmojis).toBe(false);
+
+			await roleService.assign(user.id, role.id);
+
+			// ストリーミング経由で反映されるまでちょっと待つ
+			clock.uninstall();
+			await sleep(100);
+
+			const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
+			expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
+		});
+	});
+
+	describe('conditional role', () => {
+		test('~かつ~', async () => {
+			const [user1, user2, user3, user4] = await Promise.all([
+				createUser({ isBot: true, isCat: false, isSuspended: false }),
+				createUser({ isBot: false, isCat: true, isSuspended: false }),
+				createUser({ isBot: true, isCat: true, isSuspended: false }),
+				createUser({ isBot: false, isCat: false, isSuspended: true }),
+			]);
+			const role1 = await createConditionalRole({
+				id: aidx(),
+				type: 'isBot',
+			});
+			const role2 = await createConditionalRole({
+				id: aidx(),
+				type: 'isCat',
+			});
+			const role3 = await createConditionalRole({
+				id: aidx(),
+				type: 'isSuspended',
+			});
+			const role4 = await createConditionalRole({
+				id: aidx(),
+				type: 'and',
+				values: [role1.condFormula, role2.condFormula],
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			const actual4 = await roleService.getUserRoles(user4.id);
+			expect(actual1.some(r => r.id === role4.id)).toBe(false);
+			expect(actual2.some(r => r.id === role4.id)).toBe(false);
+			expect(actual3.some(r => r.id === role4.id)).toBe(true);
+			expect(actual4.some(r => r.id === role4.id)).toBe(false);
+		});
+
+		test('~または~', async () => {
+			const [user1, user2, user3, user4] = await Promise.all([
+				createUser({ isBot: true, isCat: false, isSuspended: false }),
+				createUser({ isBot: false, isCat: true, isSuspended: false }),
+				createUser({ isBot: true, isCat: true, isSuspended: false }),
+				createUser({ isBot: false, isCat: false, isSuspended: true }),
+			]);
+			const role1 = await createConditionalRole({
+				id: aidx(),
+				type: 'isBot',
+			});
+			const role2 = await createConditionalRole({
+				id: aidx(),
+				type: 'isCat',
+			});
+			const role3 = await createConditionalRole({
+				id: aidx(),
+				type: 'isSuspended',
+			});
+			const role4 = await createConditionalRole({
+				id: aidx(),
+				type: 'or',
+				values: [role1.condFormula, role2.condFormula],
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			const actual4 = await roleService.getUserRoles(user4.id);
+			expect(actual1.some(r => r.id === role4.id)).toBe(true);
+			expect(actual2.some(r => r.id === role4.id)).toBe(true);
+			expect(actual3.some(r => r.id === role4.id)).toBe(true);
+			expect(actual4.some(r => r.id === role4.id)).toBe(false);
 		});
 
-		test('コンディショナルロール: マニュアルロールにアサイン済み', async () => {
+		test('~ではない', async () => {
+			const [user1, user2, user3] = await Promise.all([
+				createUser({ isBot: true, isCat: false, isSuspended: false }),
+				createUser({ isBot: false, isCat: true, isSuspended: false }),
+				createUser({ isBot: true, isCat: true, isSuspended: false }),
+			]);
+			const role1 = await createConditionalRole({
+				id: aidx(),
+				type: 'isBot',
+			});
+			const role2 = await createConditionalRole({
+				id: aidx(),
+				type: 'isCat',
+			});
+			const role4 = await createConditionalRole({
+				id: aidx(),
+				type: 'not',
+				value: role1.condFormula,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role4.id)).toBe(false);
+			expect(actual2.some(r => r.id === role4.id)).toBe(true);
+			expect(actual3.some(r => r.id === role4.id)).toBe(false);
+		});
+
+		test('マニュアルロールにアサイン済み', async () => {
 			const [user1, user2, role1] = await Promise.all([
 				createUser(),
 				createUser(),
@@ -262,15 +372,10 @@ describe('RoleService', () => {
 					name: 'manual role',
 				}),
 			]);
-			const role2 = await createRole({
-				name: 'conditional role',
-				target: 'conditional',
-				condFormula: {
-					// idはバックエンドのロジックに必要ない?
-					id: 'bdc612bd-9d54-4675-ae83-0499c82ea670',
-					type: 'roleAssignedTo',
-					roleId: role1.id,
-				},
+			const role2 = await createConditionalRole({
+				id: aidx(),
+				type: 'roleAssignedTo',
+				roleId: role1.id,
 			});
 			await roleService.assign(user2.id, role1.id);
 
@@ -282,41 +387,302 @@ describe('RoleService', () => {
 			expect(u2role.some(r => r.id === role2.id)).toBe(true);
 		});
 
-		test('expired role', async () => {
-			const user = await createUser();
-			const role = await createRole({
-				name: 'a',
-				policies: {
-					canManageCustomEmojis: {
-						useDefault: false,
-						priority: 0,
-						value: true,
-					},
-				},
+		test('ローカルユーザのみ', async () => {
+			const [user1, user2] = await Promise.all([
+				createUser({ host: null }),
+				createUser({ host: 'example.com' }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'isLocal',
 			});
-			await roleService.assign(user.id, role.id, new Date(Date.now() + (1000 * 60 * 60 * 24)));
-			metaService.fetch.mockResolvedValue({
-				policies: {
-					canManageCustomEmojis: false,
-				},
-			} as any);
 
-			const result = await roleService.getUserPolicies(user.id);
-			expect(result.canManageCustomEmojis).toBe(true);
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(true);
+			expect(actual2.some(r => r.id === role.id)).toBe(false);
+		});
 
-			clock.tick('25:00:00');
+		test('リモートユーザのみ', async () => {
+			const [user1, user2] = await Promise.all([
+				createUser({ host: null }),
+				createUser({ host: 'example.com' }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'isRemote',
+			});
 
-			const resultAfter25h = await roleService.getUserPolicies(user.id);
-			expect(resultAfter25h.canManageCustomEmojis).toBe(false);
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+		});
 
-			await roleService.assign(user.id, role.id);
+		test('サスペンド済みユーザである', async () => {
+			const [user1, user2] = await Promise.all([
+				createUser({ isSuspended: false }),
+				createUser({ isSuspended: true }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'isSuspended',
+			});
 
-			// ストリーミング経由で反映されるまでちょっと待つ
-			clock.uninstall();
-			await sleep(100);
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+		});
 
-			const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
-			expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
+		test('鍵アカウントユーザである', async () => {
+			const [user1, user2] = await Promise.all([
+				createUser({ isLocked: false }),
+				createUser({ isLocked: true }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'isLocked',
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+		});
+
+		test('botユーザである', async () => {
+			const [user1, user2] = await Promise.all([
+				createUser({ isBot: false }),
+				createUser({ isBot: true }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'isBot',
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+		});
+
+		test('猫である', async () => {
+			const [user1, user2] = await Promise.all([
+				createUser({ isCat: false }),
+				createUser({ isCat: true }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'isCat',
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+		});
+
+		test('「ユーザを見つけやすくする」が有効なアカウント', async () => {
+			const [user1, user2] = await Promise.all([
+				createUser({ isExplorable: false }),
+				createUser({ isExplorable: true }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'isExplorable',
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+		});
+
+		test('ユーザが作成されてから指定期間経過した', async () => {
+			const base = new Date();
+			base.setMinutes(base.getMinutes() - 5);
+
+			const d1 = new Date(base);
+			const d2 = new Date(base);
+			const d3 = new Date(base);
+			d1.setSeconds(d1.getSeconds() - 1);
+			d3.setSeconds(d3.getSeconds() + 1);
+
+			const [user1, user2, user3] = await Promise.all([
+				// 4:59
+				createUser({ id: genAidx(d1.getTime()) }),
+				// 5:00
+				createUser({ id: genAidx(d2.getTime()) }),
+				// 5:01
+				createUser({ id: genAidx(d3.getTime()) }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'createdLessThan',
+				// 5 minutes
+				sec: 300,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(false);
+			expect(actual3.some(r => r.id === role.id)).toBe(true);
+		});
+
+		test('ユーザが作成されてから指定期間経っていない', async () => {
+			const base = new Date();
+			base.setMinutes(base.getMinutes() - 5);
+
+			const d1 = new Date(base);
+			const d2 = new Date(base);
+			const d3 = new Date(base);
+			d1.setSeconds(d1.getSeconds() - 1);
+			d3.setSeconds(d3.getSeconds() + 1);
+
+			const [user1, user2, user3] = await Promise.all([
+				// 4:59
+				createUser({ id: genAidx(d1.getTime()) }),
+				// 5:00
+				createUser({ id: genAidx(d2.getTime()) }),
+				// 5:01
+				createUser({ id: genAidx(d3.getTime()) }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'createdMoreThan',
+				// 5 minutes
+				sec: 300,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(true);
+			expect(actual2.some(r => r.id === role.id)).toBe(false);
+			expect(actual3.some(r => r.id === role.id)).toBe(false);
+		});
+
+		test('フォロワー数が指定値以下', async () => {
+			const [user1, user2, user3] = await Promise.all([
+				createUser({ followersCount: 99 }),
+				createUser({ followersCount: 100 }),
+				createUser({ followersCount: 101 }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'followersLessThanOrEq',
+				value: 100,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(true);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+			expect(actual3.some(r => r.id === role.id)).toBe(false);
+		});
+
+		test('フォロワー数が指定値以下', async () => {
+			const [user1, user2, user3] = await Promise.all([
+				createUser({ followersCount: 99 }),
+				createUser({ followersCount: 100 }),
+				createUser({ followersCount: 101 }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'followersMoreThanOrEq',
+				value: 100,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+			expect(actual3.some(r => r.id === role.id)).toBe(true);
+		});
+
+		test('フォロー数が指定値以下', async () => {
+			const [user1, user2, user3] = await Promise.all([
+				createUser({ followingCount: 99 }),
+				createUser({ followingCount: 100 }),
+				createUser({ followingCount: 101 }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'followingLessThanOrEq',
+				value: 100,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(true);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+			expect(actual3.some(r => r.id === role.id)).toBe(false);
+		});
+
+		test('フォロー数が指定値以上', async () => {
+			const [user1, user2, user3] = await Promise.all([
+				createUser({ followingCount: 99 }),
+				createUser({ followingCount: 100 }),
+				createUser({ followingCount: 101 }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'followingMoreThanOrEq',
+				value: 100,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+			expect(actual3.some(r => r.id === role.id)).toBe(true);
+		});
+
+		test('ノート数が指定値以下', async () => {
+			const [user1, user2, user3] = await Promise.all([
+				createUser({ notesCount: 9 }),
+				createUser({ notesCount: 10 }),
+				createUser({ notesCount: 11 }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'notesLessThanOrEq',
+				value: 10,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(true);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+			expect(actual3.some(r => r.id === role.id)).toBe(false);
+		});
+
+		test('ノート数が指定値以上', async () => {
+			const [user1, user2, user3] = await Promise.all([
+				createUser({ notesCount: 9 }),
+				createUser({ notesCount: 10 }),
+				createUser({ notesCount: 11 }),
+			]);
+			const role = await createConditionalRole({
+				id: aidx(),
+				type: 'notesMoreThanOrEq',
+				value: 10,
+			});
+
+			const actual1 = await roleService.getUserRoles(user1.id);
+			const actual2 = await roleService.getUserRoles(user2.id);
+			const actual3 = await roleService.getUserRoles(user3.id);
+			expect(actual1.some(r => r.id === role.id)).toBe(false);
+			expect(actual2.some(r => r.id === role.id)).toBe(true);
+			expect(actual3.some(r => r.id === role.id)).toBe(true);
 		});
 	});
 
diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
index 2f5b4c47d87e..f001a4ac2031 100644
--- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue
+++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
@@ -9,6 +9,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkSelect v-model="type" :class="$style.typeSelect">
 			<option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option>
 			<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
+			<option value="isSuspended">{{ i18n.ts._role._condition.isSuspended }}</option>
+			<option value="isLocked">{{ i18n.ts._role._condition.isLocked }}</option>
+			<option value="isBot">{{ i18n.ts._role._condition.isBot }}</option>
+			<option value="isCat">{{ i18n.ts._role._condition.isCat }}</option>
+			<option value="isExplorable">{{ i18n.ts._role._condition.isExplorable }}</option>
 			<option value="roleAssignedTo">{{ i18n.ts._role._condition.roleAssignedTo }}</option>
 			<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
 			<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 360724d2a9e3..9720b04e3963 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1713,6 +1713,7 @@ declare namespace entities {
         RoleCondFormulaLogics,
         RoleCondFormulaValueNot,
         RoleCondFormulaValueIsLocalOrRemote,
+        RoleCondFormulaValueUserSettingBooleanSchema,
         RoleCondFormulaValueAssignedRole,
         RoleCondFormulaValueCreated,
         RoleCondFormulaFollowersOrFollowingOrNotes,
@@ -2745,6 +2746,9 @@ type RoleCondFormulaValueIsLocalOrRemote = components['schemas']['RoleCondFormul
 // @public (undocumented)
 type RoleCondFormulaValueNot = components['schemas']['RoleCondFormulaValueNot'];
 
+// @public (undocumented)
+type RoleCondFormulaValueUserSettingBooleanSchema = components['schemas']['RoleCondFormulaValueUserSettingBooleanSchema'];
+
 // @public (undocumented)
 type RoleLite = components['schemas']['RoleLite'];
 
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 6f61458600f4..a6e5fbe6890e 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -38,6 +38,7 @@ export type Signin = components['schemas']['Signin'];
 export type RoleCondFormulaLogics = components['schemas']['RoleCondFormulaLogics'];
 export type RoleCondFormulaValueNot = components['schemas']['RoleCondFormulaValueNot'];
 export type RoleCondFormulaValueIsLocalOrRemote = components['schemas']['RoleCondFormulaValueIsLocalOrRemote'];
+export type RoleCondFormulaValueUserSettingBooleanSchema = components['schemas']['RoleCondFormulaValueUserSettingBooleanSchema'];
 export type RoleCondFormulaValueAssignedRole = components['schemas']['RoleCondFormulaValueAssignedRole'];
 export type RoleCondFormulaValueCreated = components['schemas']['RoleCondFormulaValueCreated'];
 export type RoleCondFormulaFollowersOrFollowingOrNotes = components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index ae001cf874c6..131d20f09bd2 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4586,6 +4586,11 @@ export type components = {
       /** @enum {string} */
       type: 'isLocal' | 'isRemote';
     };
+    RoleCondFormulaValueUserSettingBooleanSchema: {
+      id: string;
+      /** @enum {string} */
+      type: 'isSuspended' | 'isLocked' | 'isBot' | 'isCat' | 'isExplorable';
+    };
     RoleCondFormulaValueAssignedRole: {
       id: string;
       /** @enum {string} */
@@ -4608,7 +4613,7 @@ export type components = {
       type: 'followersLessThanOrEq' | 'followersMoreThanOrEq' | 'followingLessThanOrEq' | 'followingMoreThanOrEq' | 'notesLessThanOrEq' | 'notesMoreThanOrEq';
       value: number;
     };
-    RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueAssignedRole'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes'];
+    RoleCondFormulaValue: components['schemas']['RoleCondFormulaLogics'] | components['schemas']['RoleCondFormulaValueNot'] | components['schemas']['RoleCondFormulaValueIsLocalOrRemote'] | components['schemas']['RoleCondFormulaValueUserSettingBooleanSchema'] | components['schemas']['RoleCondFormulaValueAssignedRole'] | components['schemas']['RoleCondFormulaValueCreated'] | components['schemas']['RoleCondFormulaFollowersOrFollowingOrNotes'];
     RoleLite: {
       /**
        * Format: id

From f9aed8f2bf994902386878d1212912caa3a57b0d Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Fri, 19 Apr 2024 19:42:01 +0900
Subject: [PATCH 162/266] =?UTF-8?q?fix:=20=E6=AD=A3=E8=A6=8F=E5=8C=96?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=E7=8A=B6?=
 =?UTF-8?q?=E6=85=8B=E3=81=AEhashtag=E3=81=8C=E9=80=A3=E5=90=88=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=81=A6=E3=81=8D=E3=81=9Fhtml=E3=81=AB=E5=90=AB?=
 =?UTF-8?q?=E3=81=BE=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E3=81=A8hashtag?=
 =?UTF-8?q?=E3=81=8C=E6=AD=A3=E3=81=97=E3=81=8Fhashtag=E3=81=AB=E5=BE=A9?=
 =?UTF-8?q?=E5=85=83=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13733)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                            | 1 +
 packages/backend/src/core/MfmService.ts | 5 ++++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c13fa664dd53..8c8bcf0ea454 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@
   - 鍵アカウントユーザーか
   - 「アカウントを見つけやすくする」が有効なユーザーか
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
+- Fix: 正規化されていない状態のhashtagが連合されてきたhtmlに含まれているとhashtagが正しくhashtagに復元されない問題を修正
 
 ### Client
 - Feat: アップロードするファイルの名前をランダム文字列にできるように
diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index c62ee5a642a4..2fb731201bd2 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -10,6 +10,7 @@ import { Window } from 'happy-dom';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { intersperse } from '@/misc/prelude/array.js';
+import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 import type { IMentionedRemoteUsers } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
 import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js';
@@ -33,6 +34,8 @@ export class MfmService {
 		// some AP servers like Pixelfed use br tags as well as newlines
 		html = html.replace(/<br\s?\/?>\r?\n/gi, '\n');
 
+		const normalizedHashtagNames = hashtagNames == null ? undefined : new Set<string>(hashtagNames.map(x => normalizeForSearch(x)));
+
 		const dom = parse5.parseFragment(html);
 
 		let text = '';
@@ -85,7 +88,7 @@ export class MfmService {
 					const href = node.attrs.find(x => x.name === 'href');
 
 					// ハッシュタグ
-					if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) {
+					if (normalizedHashtagNames && href && normalizedHashtagNames.has(normalizeForSearch(txt))) {
 						text += txt;
 					// メンション
 					} else if (txt.startsWith('@') && !(rel && rel.value.startsWith('me '))) {

From 553ba8479298fa70f4c168b679ae42f8364df17f Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Thu, 25 Apr 2024 10:34:26 +0900
Subject: [PATCH 163/266] =?UTF-8?q?AiScript=E3=81=AE=E3=83=90=E3=83=BC?=
 =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=920.18.0=E3=81=AB=E4=B8=8A?=
 =?UTF-8?q?=E3=81=92=E3=82=8B=20(#13743)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update package.json

* Update autogen files

* Update flash-edit.vue

* Update flash-edit.vue

* Update CHANGELOG.md

* revert
---
 CHANGELOG.md                                  |  1 +
 packages/frontend/package.json                |  2 +-
 .../frontend/src/pages/flash/flash-edit.vue   | 34 +++++++++----------
 pnpm-lock.yaml                                | 14 ++++----
 4 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c8bcf0ea454..87db026183b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 - Enhance: リプライにて引用がある場合テキストが空でもノートできるように
   - 引用したいノートのURLをコピーしリプライ投稿画面にペーストして添付することで達成できます
 - Enhance: フォローするかどうかの確認ダイアログを出せるように
+- Chore: AiScriptを0.18.0にバージョンアップ
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index cbf4e595920f..95980ac0fc2c 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -24,7 +24,7 @@
 		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
-		"@syuilo/aiscript": "0.17.0",
+		"@syuilo/aiscript": "0.18.0",
 		"@tabler/icons-webfont": "2.44.0",
 		"@twemoji/parser": "15.0.0",
 		"@vitejs/plugin-vue": "5.0.4",
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 3625bc11649a..bff45094a132 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -48,7 +48,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import { useRouter } from '@/router/supplier.js';
 
-const PRESET_DEFAULT = `/// @ 0.16.0
+const PRESET_DEFAULT = `/// @ 0.18.0
 
 var name = ""
 
@@ -60,13 +60,13 @@ Ui:render([
 	Ui:C:button({
 		text: "Hello"
 		onClick: @() {
-			Mk:dialog(null \`Hello, {name}!\`)
+			Mk:dialog(null, \`Hello, {name}!\`)
 		}
 	})
 ])
 `;
 
-const PRESET_OMIKUJI = `/// @ 0.16.0
+const PRESET_OMIKUJI = `/// @ 0.18.0
 // ユーザーごとに日替わりのおみくじのプリセット
 
 // 選択肢
@@ -81,11 +81,11 @@ let choices = [
 	"大凶"
 ]
 
-// シードが「ユーザーID+今日の日付」である乱数生成器を用意
-let random = Math:gen_rng(\`{USER_ID}{Date:year()}{Date:month()}{Date:day()}\`)
+// シードが「PlayID+ユーザーID+今日の日付」である乱数生成器を用意
+let random = Math:gen_rng(\`{THIS_ID}{USER_ID}{Date:year()}{Date:month()}{Date:day()}\`)
 
 // ランダムに選択肢を選ぶ
-let chosen = choices[random(0 (choices.len - 1))]
+let chosen = choices[random(0, (choices.len - 1))]
 
 // 結果のテキスト
 let result = \`今日のあなたの運勢は **{chosen}** です。\`
@@ -109,7 +109,7 @@ Ui:render([
 ])
 `;
 
-const PRESET_SHUFFLE = `/// @ 0.16.0
+const PRESET_SHUFFLE = `/// @ 0.18.0
 // 巻き戻し可能な文字シャッフルのプリセット
 
 let string = "ペペロンチーノ"
@@ -123,13 +123,13 @@ var cursor = 0
 
 @do() {
 	if (cursor != 0) {
-		results = results.slice(0 (cursor + 1))
+		results = results.slice(0, (cursor + 1))
 		cursor = 0
 	}
 
 	let chars = []
 	for (let i, length) {
-		let r = Math:rnd(0 (length - 1))
+		let r = Math:rnd(0, (length - 1))
 		chars.push(string.pick(r))
 	}
 	let result = chars.join("")
@@ -188,27 +188,27 @@ var cursor = 0
 do()
 `;
 
-const PRESET_QUIZ = `/// @ 0.16.0
+const PRESET_QUIZ = `/// @ 0.18.0
 let title = '地理クイズ'
 
 let qas = [{
 	q: 'オーストラリアの首都は?'
-	choices: ['シドニー' 'キャンベラ' 'メルボルン']
+	choices: ['シドニー', 'キャンベラ', 'メルボルン']
 	a: 'キャンベラ'
 	aDescription: '最大の都市はシドニーですが首都はキャンベラです。'
 } {
 	q: '国土面積2番目の国は?'
-	choices: ['カナダ' 'アメリカ' '中国']
+	choices: ['カナダ', 'アメリカ', '中国']
 	a: 'カナダ'
 	aDescription: '大きい順にロシア、カナダ、アメリカ、中国です。'
 } {
 	q: '二重内陸国ではないのは?'
-	choices: ['リヒテンシュタイン' 'ウズベキスタン' 'レソト']
+	choices: ['リヒテンシュタイン', 'ウズベキスタン', 'レソト']
 	a: 'レソト'
 	aDescription: 'レソトは(一重)内陸国です。'
 } {
 	q: '閘門がない運河は?'
-	choices: ['キール運河' 'スエズ運河' 'パナマ運河']
+	choices: ['キール運河', 'スエズ運河', 'パナマ運河']
 	a: 'スエズ運河'
 	aDescription: 'スエズ運河は高低差がないので閘門はありません。'
 }]
@@ -296,12 +296,12 @@ qaEls.push(Ui:C:container({
 			onClick: finish
 		})
 	]
-} 'footer'))
+}, 'footer'))
 
 Ui:render(qaEls)
 `;
 
-const PRESET_TIMELINE = `/// @ 0.16.0
+const PRESET_TIMELINE = `/// @ 0.18.0
 // APIリクエストを行いローカルタイムラインを表示するプリセット
 
 @fetch() {
@@ -315,7 +315,7 @@ const PRESET_TIMELINE = `/// @ 0.16.0
 	])
 
 	// タイムライン取得
-	let notes = Mk:api("notes/local-timeline" {})
+	let notes = Mk:api("notes/local-timeline", {})
 
 	// それぞれのノートごとにUI要素作成
 	let noteEls = []
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1dbb172b5dd7..c7625fd89f44 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -707,8 +707,8 @@ importers:
         specifier: 5.1.0
         version: 5.1.0(rollup@4.12.0)
       '@syuilo/aiscript':
-        specifier: 0.17.0
-        version: 0.17.0
+        specifier: 0.18.0
+        version: 0.18.0
       '@tabler/icons-webfont':
         specifier: 2.44.0
         version: 2.44.0
@@ -6678,7 +6678,7 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.21(typescript@5.3.3)
-      vue-component-type-helpers: 2.0.10
+      vue-component-type-helpers: 2.0.14
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -6944,8 +6944,8 @@ packages:
     dev: false
     optional: true
 
-  /@syuilo/aiscript@0.17.0:
-    resolution: {integrity: sha512-3JtQ1rWJHMxQ3153zLCXMUOwrOgjPPYGBl0dPHhR0ohm4tn7okMQRugxMCT0t3YxByemb9FfiM6TUjd0tEGxdA==}
+  /@syuilo/aiscript@0.18.0:
+    resolution: {integrity: sha512-/iY9Vv4LLjtW/KUzId1QwXC4BlpIEPCMcoT7dyRhYdyxtwhS3Hx4b/4j1HYP+n3Pq9XKyW5zvkY72/+DNu4g6Q==}
     dependencies:
       seedrandom: 3.0.5
       stringz: 2.1.0
@@ -19347,8 +19347,8 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-component-type-helpers@2.0.10:
-    resolution: {integrity: sha512-FC5fKJjDks3Ue/KRSYBdsiCaZa0kUPQfs8yQpb8W9mlO6BenV8G1z58xobeRMzevnmEcDa09LLwuXDwb4f6NMQ==}
+  /vue-component-type-helpers@2.0.14:
+    resolution: {integrity: sha512-DInfgOyXlMyliyqAAD9frK28tTfch0+tMi4qoWJcZlRxUf+NFAtraJBnAsKLep+FOyLMiajkhfyEb3xLK08i7w==}
     dev: true
 
   /vue-demi@0.14.7(vue@3.4.21):

From 85339ca751401ff856814ca21283c07a7c66f5ce Mon Sep 17 00:00:00 2001
From: Cocoa Hoto <cocoa@hoto.us>
Date: Thu, 25 Apr 2024 11:03:34 +0900
Subject: [PATCH 164/266] feat: improve emoji endpoint (#13742)

---
 packages/backend/src/server/ServerService.ts | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 671dd31eb113..1324cd1361e5 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -120,12 +120,20 @@ export class ServerService implements OnApplicationShutdown {
 				return;
 			}
 
-			const name = path.split('@')[0].replace(/\.webp$/i, '');
-			const host = path.split('@')[1]?.replace(/\.webp$/i, '');
+			const emojiPath = path.replace(/\.webp$/i, '');
+			const pathChunks = emojiPath.split('@');
+
+			if (pathChunks.length > 2) {
+				reply.code(400);
+				return;
+			}
+
+			const name = pathChunks.shift();
+			const host = pathChunks.pop();
 
 			const emoji = await this.emojisRepository.findOneBy({
 				// `@.` is the spec of ReactionService.decodeReaction
-				host: (host == null || host === '.') ? IsNull() : host,
+				host: (host === undefined || host === '.') ? IsNull() : host,
 				name: name,
 			});
 

From 6abb8c49943a0d9002118fb3b50e20940aa1e3ba Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Sat, 27 Apr 2024 12:57:00 +0900
Subject: [PATCH 165/266] Merge pull request from GHSA-m9qf-3pfj-2r86

* Add Cache-Control to Bull Board

* CHANGELOG

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                           | 1 +
 packages/backend/src/server/web/ClientServerService.ts | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87db026183b0..5933a4383c82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,7 @@
 - Fix: リプライのみの引用リノートと、CWのみの引用リノートが純粋なリノートとして誤って扱われてしまう問題を修正
 - Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
+- Fix: Add Cache-Control to Bull Board
 - Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
 - Fix: 一部のタイムラインのストリーミングでインスタンスミュートが効かない問題を修正
 - Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index b1af0c3df652..ba2f8b432409 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -202,6 +202,10 @@ export class ClientServerService {
 			// %71ueueとかでリクエストされたら困るため
 			const url = decodeURI(request.routeOptions.url);
 			if (url === bullBoardPath || url.startsWith(bullBoardPath + '/')) {
+				if (!url.startsWith(bullBoardPath + '/static/')) {
+					reply.header('Cache-Control', 'private, max-age=0, must-revalidate');
+				}
+
 				const token = request.cookies.token;
 				if (token == null) {
 					reply.code(401).send('Login required');

From f53e22d72c2e67cf4f89dec3c55c7a9a1a970dc8 Mon Sep 17 00:00:00 2001
From: salano_ym <53254905+salano-ym@users.noreply.github.com>
Date: Sat, 27 Apr 2024 16:12:00 +0900
Subject: [PATCH 166/266] add comma (#13746)

---
 packages/frontend/src/pages/flash/flash-edit.vue | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index bff45094a132..3445da26a22f 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -163,11 +163,11 @@ var cursor = 0
 						text: "←"
 						disabled: !(results.len > 1 && (results.len - cursor) > 1)
 						onClick: back
-					} {
+					}, {
 						text: "→"
 						disabled: !(results.len > 1 && cursor > 0)
 						onClick: forward
-					} {
+					}, {
 						text: "引き直す"
 						onClick: do
 					}]
@@ -196,17 +196,17 @@ let qas = [{
 	choices: ['シドニー', 'キャンベラ', 'メルボルン']
 	a: 'キャンベラ'
 	aDescription: '最大の都市はシドニーですが首都はキャンベラです。'
-} {
+}, {
 	q: '国土面積2番目の国は?'
 	choices: ['カナダ', 'アメリカ', '中国']
 	a: 'カナダ'
 	aDescription: '大きい順にロシア、カナダ、アメリカ、中国です。'
-} {
+}, {
 	q: '二重内陸国ではないのは?'
 	choices: ['リヒテンシュタイン', 'ウズベキスタン', 'レソト']
 	a: 'レソト'
 	aDescription: 'レソトは(一重)内陸国です。'
-} {
+}, {
 	q: '閘門がない運河は?'
 	choices: ['キール運河', 'スエズ運河', 'パナマ運河']
 	a: 'スエズ運河'
@@ -244,9 +244,9 @@ each (let qa, qas) {
 			})
 			Ui:C:container({
 				children: []
-			} \`{qa.id}:a\`)
+			}, \`{qa.id}:a\`)
 		]
-	} qa.id))
+	}, qa.id))
 }
 
 @finish() {

From 0a31e132c74cc2d8029cdadd103ea66a3ce16b6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 27 Apr 2024 16:48:04 +0900
Subject: [PATCH 167/266] =?UTF-8?q?fix(frontend):=20Play=E3=81=AEAiScript?=
 =?UTF-8?q?=E3=83=A9=E3=83=B3=E3=82=BF=E3=82=A4=E3=83=A0=E3=81=8C=E5=81=9C?=
 =?UTF-8?q?=E6=AD=A2=E3=81=97=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=AB=E7=94=BB?=
 =?UTF-8?q?=E9=9D=A2=E3=81=8C=E5=88=9D=E6=9C=9F=E5=8C=96=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#13747)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): PlayのAiScriptランタイムが停止したときに画面が初期化されていない問題を修正

* fix

* Update Changelog

* typo
---
 CHANGELOG.md                                |  2 +
 packages/frontend/src/pages/flash/flash.vue | 82 ++++++++++++++++-----
 2 files changed, 66 insertions(+), 18 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5933a4383c82..e4605fe74646 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 - Enhance: リプライにて引用がある場合テキストが空でもノートできるように
   - 引用したいノートのURLをコピーしリプライ投稿画面にペーストして添付することで達成できます
 - Enhance: フォローするかどうかの確認ダイアログを出せるように
+- Enhance: Playを手動でリロードできるように
 - Chore: AiScriptを0.18.0にバージョンアップ
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
@@ -50,6 +51,7 @@
 - Fix: ノート詳細ページにおいてCW付き引用リノートのCWボタンのラベルに「引用」が含まれていない問題を修正
 - Fix: ダイアログの入力で字数制限に違反していてもEnterキーが押せてしまう問題を修正
 - Fix: ダイレクト投稿の宛先が保存されない問題を修正
+- Fix: Playのページを離れたときに、Playが正常に初期化されない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 4aa3ce1672e3..40499fde0e1c 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -15,11 +15,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<MkAsUi v-if="root" :component="root" :components="components"/>
 						</div>
 						<div class="actions _panel">
-							<MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" asLike class="button" rounded primary @click="unlike()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
-							<MkButton v-else v-tooltip="i18n.ts.like" asLike class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
-							<MkButton v-tooltip="i18n.ts.shareWithNote" class="button" rounded @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></MkButton>
-							<MkButton v-tooltip="i18n.ts.copyLink" class="button" rounded @click="copyLink"><i class="ti ti-link ti-fw"></i></MkButton>
-							<MkButton v-if="isSupportShare()" v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton>
+							<div class="items">
+								<MkButton v-tooltip="i18n.ts.reload" class="button" rounded @click="reset"><i class="ti ti-reload"></i></MkButton>
+							</div>
+							<div class="items">
+								<MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" asLike class="button" rounded primary @click="unlike()"><i class="ti ti-heart"></i><span v-if="flash?.likedCount && flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
+								<MkButton v-else v-tooltip="i18n.ts.like" asLike class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash?.likedCount && flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
+								<MkButton v-tooltip="i18n.ts.copyLink" class="button" rounded @click="copyLink"><i class="ti ti-link ti-fw"></i></MkButton>
+								<MkButton v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton>
+							</div>
 						</div>
 					</div>
 					<div v-else :class="$style.ready">
@@ -49,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkA v-if="$i && $i.id === flash.userId" :to="`/play/${flash.id}/edit`" style="color: var(--accent);">{{ i18n.ts._play.editThisPage }}</MkA>
 				<MkAd :prefer="['horizontal', 'horizontal-big']"/>
 			</div>
-			<MkError v-else-if="error" @retry="fetchPage()"/>
+			<MkError v-else-if="error" @retry="fetchFlash()"/>
 			<MkLoading v-else/>
 		</Transition>
 	</MkSpacer>
@@ -94,12 +98,33 @@ function fetchFlash() {
 	});
 }
 
+function share(ev: MouseEvent) {
+	if (!flash.value) return;
+
+	os.popupMenu([
+		{
+			text: i18n.ts.shareWithNote,
+			icon: 'ti ti-pencil',
+			action: shareWithNote,
+		},
+		...(isSupportShare() ? [{
+			text: i18n.ts.share,
+			icon: 'ti ti-share',
+			action: shareWithNavigator,
+		}] : []),
+	], ev.currentTarget ?? ev.target);
+}
+
 function copyLink() {
+	if (!flash.value) return;
+
 	copyToClipboard(`${url}/play/${flash.value.id}`);
 	os.success();
 }
 
-function share() {
+function shareWithNavigator() {
+	if (!flash.value) return;
+
 	navigator.share({
 		title: flash.value.title,
 		text: flash.value.summary,
@@ -108,21 +133,28 @@ function share() {
 }
 
 function shareWithNote() {
+	if (!flash.value) return;
+
 	os.post({
-		initialText: `${flash.value.title} ${url}/play/${flash.value.id}`,
+		initialText: `${flash.value.title}\n${url}/play/${flash.value.id}`,
+		instant: true,
 	});
 }
 
 function like() {
+	if (!flash.value) return;
+
 	os.apiWithDialog('flash/like', {
 		flashId: flash.value.id,
 	}).then(() => {
-		flash.value.isLiked = true;
-		flash.value.likedCount++;
+		flash.value!.isLiked = true;
+		flash.value!.likedCount++;
 	});
 }
 
 async function unlike() {
+	if (!flash.value) return;
+
 	const confirm = await os.confirm({
 		type: 'warning',
 		text: i18n.ts.unlikeConfirm,
@@ -131,8 +163,8 @@ async function unlike() {
 	os.apiWithDialog('flash/unlike', {
 		flashId: flash.value.id,
 	}).then(() => {
-		flash.value.isLiked = false;
-		flash.value.likedCount--;
+		flash.value!.isLiked = false;
+		flash.value!.likedCount--;
 	});
 }
 
@@ -152,6 +184,7 @@ function start() {
 
 async function run() {
 	if (aiscript.value) aiscript.value.abort();
+	if (!flash.value) return;
 
 	aiscript.value = new Interpreter({
 		...createAiScriptEnv({
@@ -193,12 +226,17 @@ async function run() {
 	}
 }
 
-onDeactivated(() => {
+function reset() {
 	if (aiscript.value) aiscript.value.abort();
+	started.value = false;
+}
+
+onDeactivated(() => {
+	reset();
 });
 
 onUnmounted(() => {
-	if (aiscript.value) aiscript.value.abort();
+	reset();
 });
 
 const headerActions = computed(() => []);
@@ -265,11 +303,19 @@ definePageMetadata(() => ({
 		}
 
 		> .actions {
-			display: flex;
-			justify-content: center;
-			gap: 12px;
 			margin-top: 16px;
-			padding: 16px;
+
+			> .items {
+				display: flex;
+				justify-content: center;
+				gap: 12px;
+				padding: 16px;
+				border-bottom: 1px solid var(--divider);
+
+				&:last-child {
+					border-bottom: none;
+				}
+			}
 		}
 	}
 }

From cb5d8bdcddf76e26b9d0b80855955faa38ec6c36 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 27 Apr 2024 18:53:28 +0900
Subject: [PATCH 168/266] =?UTF-8?q?fix(backend):=20=E3=83=9A=E3=83=BC?=
 =?UTF-8?q?=E3=82=B8=E3=81=AEOGP=20URL=E3=81=8C=E9=81=95=E3=81=86=E3=81=AE?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13749)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): ページのOGP URLが違うのを修正

* Update Changelog

* typo
---
 CHANGELOG.md                                   | 1 +
 packages/backend/src/server/web/views/page.pug | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4605fe74646..a263680782c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,7 @@
 - Fix: ダイアログの入力で字数制限に違反していてもEnterキーが押せてしまう問題を修正
 - Fix: ダイレクト投稿の宛先が保存されない問題を修正
 - Fix: Playのページを離れたときに、Playが正常に初期化されない問題を修正
+- Fix: ページのOGP URLが間違っているのを修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/backend/src/server/web/views/page.pug b/packages/backend/src/server/web/views/page.pug
index 08bb08ffe7e1..03c50eca8a51 100644
--- a/packages/backend/src/server/web/views/page.pug
+++ b/packages/backend/src/server/web/views/page.pug
@@ -3,7 +3,7 @@ extends ./base
 block vars
 	- const user = page.user;
 	- const title = page.title;
-	- const url = `${config.url}/@${user.username}/${page.name}`;
+	- const url = `${config.url}/@${user.username}/pages/${page.name}`;
 
 block title
 	= `${title} | ${instanceName}`

From 7ce6a9bbaffddc6019ce2eab8b7a06c119ff2f69 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 27 Apr 2024 19:59:30 +0900
Subject: [PATCH 169/266] =?UTF-8?q?fix(frontend):=20=E3=82=B0=E3=83=AB?=
 =?UTF-8?q?=E3=83=BC=E3=83=97=E9=80=9A=E7=9F=A5=E3=81=AE=E4=BA=BA=E6=95=B0?=
 =?UTF-8?q?=E3=82=92=E3=81=A1=E3=82=83=E3=82=93=E3=81=A8=E6=95=B0=E3=81=88?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13751)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): グループ通知の人数をちゃんと数えるように

* Update Changelog
---
 CHANGELOG.md                                        |  1 +
 packages/frontend/src/components/MkNotification.vue | 11 ++++++++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a263680782c8..1a43649fdbd4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,7 @@
 - Fix: ダイレクト投稿の宛先が保存されない問題を修正
 - Fix: Playのページを離れたときに、Playが正常に初期化されない問題を修正
 - Fix: ページのOGP URLが間違っているのを修正
+- Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 0d3a5c13ba2b..73cd7cd5b398 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -58,8 +58,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
 			<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
 			<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
-			<span v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'">{{ i18n.tsx._notification.likedBySomeUsers({ n: notification.reactions.length }) }}</span>
-			<span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: notification.reactions.length }) }}</span>
+			<span v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'">{{ i18n.tsx._notification.likedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }}</span>
+			<span v-else-if="notification.type === 'reaction:grouped'">{{ i18n.tsx._notification.reactedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }}</span>
 			<span v-else-if="notification.type === 'renote:grouped'">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span>
 			<span v-else-if="notification.type === 'app'">{{ notification.header }}</span>
 			<MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/>
@@ -72,7 +72,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</MkA>
 			<MkA v-else-if="notification.type === 'renote' || notification.type === 'renote:grouped'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)">
 				<i class="ti ti-quote" :class="$style.quote"></i>
-				<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="true" :author="notification.note.renote.user"/>
+				<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="true" :author="notification.note.renote?.user"/>
 				<i class="ti ti-quote" :class="$style.quote"></i>
 			</MkA>
 			<MkA v-else-if="notification.type === 'reply'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
@@ -174,6 +174,11 @@ const rejectFollowRequest = () => {
 	followRequestDone.value = true;
 	misskeyApi('following/requests/reject', { userId: props.notification.user.id });
 };
+
+function getActualReactedUsersCount(notification: Misskey.entities.Notification) {
+	if (notification.type !== 'reaction:grouped') return 0;
+	return new Set(notification.reactions.map((reaction) => reaction.user.id)).size;
+}
 </script>
 
 <style lang="scss" module>

From 78e61c65be76f6f4d8088d6c81efc514db0e8251 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 27 Apr 2024 20:00:57 +0900
Subject: [PATCH 170/266] =?UTF-8?q?fix(frontend=5Freversi):=20=E5=85=B1?=
 =?UTF-8?q?=E6=9C=89=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=AE=E5=AE=9F=E8=A3=85?=
 =?UTF-8?q?=E3=82=92=E6=94=B9=E5=96=84=20(#13750)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend_reversi): 共有ボタンの実装を改善

* Update Changelog

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                       | 1 +
 packages/frontend/src/pages/reversi/game.board.vue | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a43649fdbd4..bc98ff0b1adf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,7 @@
 - Fix: ダイレクト投稿の宛先が保存されない問題を修正
 - Fix: Playのページを離れたときに、Playが正常に初期化されない問題を修正
 - Fix: ページのOGP URLが間違っているのを修正
+- Fix: リバーシの対局を正しく共有できないことがある問題を修正
 - Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
 
 ### Server
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index 5259dfa29a50..175ea62411d4 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -151,6 +151,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import { deepClone } from '@/scripts/clone.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { signinRequired } from '@/account.js';
+import { url } from '@/config.js';
 import { i18n } from '@/i18n.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { userPage } from '@/filters/user.js';
@@ -442,7 +443,7 @@ function autoplay() {
 
 function share() {
 	os.post({
-		initialText: `#MisskeyReversi ${location.href}`,
+		initialText: `#MisskeyReversi\n${url}/reversi/g/${game.value.id}`,
 		instant: true,
 	});
 }

From 20eb4bc29600975ea9b6d74426204b8f6871bc27 Mon Sep 17 00:00:00 2001
From: ikasoba <57828948+ikasoba@users.noreply.github.com>
Date: Sat, 27 Apr 2024 20:26:55 +0900
Subject: [PATCH 171/266] =?UTF-8?q?Fix(backend):=20ActivityPub=E3=81=A7?=
 =?UTF-8?q?=E3=81=AEHTML=E3=81=B8=E3=81=AE=E3=82=B7=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=82=BA=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1375?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* devモードでもActivityPub系エンドポイントへアクセスできるように

* ActivityPubでのHTMLのシリアライズを修正

* ハードコードしていたurlを`httpUrl`へ修正

* テストの追加
---
 packages/backend/src/core/MfmService.ts    |  8 +++++---
 packages/backend/test/unit/MfmService.ts   |  6 ++++++
 packages/frontend/vite.config.local-dev.ts | 16 ++++++++++++++++
 3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index 2fb731201bd2..9786f8b8bb7a 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -6,7 +6,7 @@
 import { URL } from 'node:url';
 import { Inject, Injectable } from '@nestjs/common';
 import * as parse5 from 'parse5';
-import { Window } from 'happy-dom';
+import { Window, XMLSerializer } from 'happy-dom';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { intersperse } from '@/misc/prelude/array.js';
@@ -247,6 +247,8 @@ export class MfmService {
 
 		const doc = window.document;
 
+		const body = doc.createElement('p');
+
 		function appendChildren(children: mfm.MfmNode[], targetElement: any): void {
 			if (children) {
 				for (const child of children.map(x => (handlers as any)[x.type](x))) targetElement.appendChild(child);
@@ -457,8 +459,8 @@ export class MfmService {
 			},
 		};
 
-		appendChildren(nodes, doc.body);
+		appendChildren(nodes, body);
 
-		return `<p>${doc.body.innerHTML}</p>`;
+		return new XMLSerializer().serializeToString(body);
 	}
 }
diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts
index f613fe9c7c45..fd4a03413bb1 100644
--- a/packages/backend/test/unit/MfmService.ts
+++ b/packages/backend/test/unit/MfmService.ts
@@ -39,6 +39,12 @@ describe('MfmService', () => {
 			const output = '<p>foo <i>bar</i></p>';
 			assert.equal(mfmService.toHtml(mfm.parse(input)), output);
 		});
+
+		test('escape', () => {
+			const input = '```\n<p>Hello, world!</p>\n```';
+			const output = '<p><pre><code>&lt;p&gt;Hello, world!&lt;/p&gt;</code></pre></p>';
+			assert.equal(mfmService.toHtml(mfm.parse(input)), output);
+		});
 	});
 
 	describe('fromHtml', () => {
diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
index 460787fd0589..f9dff13b15cc 100644
--- a/packages/frontend/vite.config.local-dev.ts
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -51,6 +51,22 @@ const devConfig = {
 			'/_info_card_': httpUrl,
 			'/bios': httpUrl,
 			'/cli': httpUrl,
+			'/inbox': httpUrl,
+			'/notes': {
+				target: httpUrl,
+				headers: {
+					'Accept': 'application/activity+json',
+				},
+			},
+			'/users': {
+				target: httpUrl,
+				headers: {
+					'Accept': 'application/activity+json',
+				},
+			},
+			'/.well-known': {
+				target: httpUrl,
+			},
 		},
 	},
 	build: {

From fe1172fbb637ad8af3688fae56b10c435b9cf497 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 27 Apr 2024 20:41:55 +0900
Subject: [PATCH 172/266] =?UTF-8?q?fix:=20=E3=83=8F=E3=82=A4=E3=83=95?=
 =?UTF-8?q?=E3=83=B3=E3=82=92=E5=90=AB=E3=82=80=E3=83=AA=E3=83=A2=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=8C=E6=8F=8F=E7=94=BB?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=20(#13715)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/core/CustomEmojiService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index edb9335b6ea7..1c75566755ad 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -20,7 +20,7 @@ import { query } from '@/misc/prelude/url.js';
 import type { Serialized } from '@/types.js';
 import { ModerationLogService } from '@/core/ModerationLogService.js';
 
-const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
+const parseEmojiStrRegexp = /^([-\w]+)(?:@([\w.-]+))?$/;
 
 @Injectable()
 export class CustomEmojiService implements OnApplicationShutdown {

From 8e8ee2ac73093b566d0b3905de884e660e67d614 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 27 Apr 2024 21:24:39 +0900
Subject: [PATCH 173/266] open links in abuse comment in new window (#13381)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: changing MkA behavior from MkMFM

* chore: open links in abuse comment in new window

* docs(changelog): 通報のコメント内のリンクをクリックした際、ウィンドウで開くように

* chore: use inject instead of prop drilling

* Revert "chore: use inject instead of prop drilling"

This reverts commit b4dd14eacf59c8079676aa6ab019fece67496d79.
---
 CHANGELOG.md                                              | 1 +
 packages/frontend/src/components/MkAbuseReport.vue        | 2 +-
 packages/frontend/src/components/MkLink.vue               | 3 +++
 packages/frontend/src/components/MkMention.vue            | 4 +++-
 packages/frontend/src/components/global/MkA.vue           | 8 +++++++-
 .../src/components/global/MkMisskeyFlavoredMarkdown.ts    | 7 ++++++-
 packages/frontend/src/components/global/MkUrl.vue         | 3 +++
 7 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc98ff0b1adf..f22cec856f95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,7 @@
   - 引用したいノートのURLをコピーしリプライ投稿画面にペーストして添付することで達成できます
 - Enhance: フォローするかどうかの確認ダイアログを出せるように
 - Enhance: Playを手動でリロードできるように
+- Enhance: 通報のコメント内のリンクをクリックした際、ウィンドウで開くように
 - Chore: AiScriptを0.18.0にバージョンアップ
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index 271b94feaada..ab65ea7ec70e 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</div>
 	<div class="detail">
 		<div>
-			<Mfm :text="report.comment"/>
+			<Mfm :text="report.comment" :linkBehavior="'window'"/>
 		</div>
 		<hr/>
 		<div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link" :behavior="'window'">@{{ report.reporter.username }}</MkA></div>
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index ca875242b443..bd1bd0e24a81 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -6,6 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <component
 	:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target"
+	:behavior="props.behavior"
 	:title="url"
 >
 	<slot></slot>
@@ -19,10 +20,12 @@ import { url as local } from '@/config.js';
 import { useTooltip } from '@/scripts/use-tooltip.js';
 import * as os from '@/os.js';
 import { isEnabledUrlPreview } from '@/instance.js';
+import { MkABehavior } from '@/components/global/MkA.vue';
 
 const props = withDefaults(defineProps<{
 	url: string;
 	rel?: null | string;
+	behavior?: MkABehavior;
 }>(), {
 });
 
diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue
index e6e8711f679c..cbefecf03a0b 100644
--- a/packages/frontend/src/components/MkMention.vue
+++ b/packages/frontend/src/components/MkMention.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }">
+<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }" :behavior="behavior">
 	<img :class="$style.icon" :src="avatarUrl" alt="">
 	<span>
 		<span>@{{ username }}</span>
@@ -21,10 +21,12 @@ import { host as localHost } from '@/config.js';
 import { $i } from '@/account.js';
 import { defaultStore } from '@/store.js';
 import { getStaticImageUrl } from '@/scripts/media-proxy.js';
+import { MkABehavior } from '@/components/global/MkA.vue';
 
 const props = defineProps<{
 	username: string;
 	host: string;
+	behavior?: MkABehavior;
 }>();
 
 const canonical = props.host === localHost ? `@${props.username}` : `@${props.username}@${toUnicode(props.host)}`;
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 1ba7cb20229f..b64acacc3214 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -9,6 +9,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 </a>
 </template>
 
+<script lang="ts">
+export type MkABehavior = 'window' | 'browser' | null;
+</script>
+
 <script lang="ts" setup>
 import { computed, shallowRef } from 'vue';
 import * as os from '@/os.js';
@@ -20,12 +24,14 @@ import { useRouter } from '@/router/supplier.js';
 const props = withDefaults(defineProps<{
 	to: string;
 	activeClass?: null | string;
-	behavior?: null | 'window' | 'browser';
+	behavior?: MkABehavior;
 }>(), {
 	activeClass: null,
 	behavior: null,
 });
 
+const linkBehaviour = props.behavior;
+
 const el = shallowRef<HTMLElement>();
 
 defineExpose({ $el: el });
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 4ed76f6bc402..a56d8bcce2df 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -16,7 +16,7 @@ import MkCode from '@/components/MkCode.vue';
 import MkCodeInline from '@/components/MkCodeInline.vue';
 import MkGoogle from '@/components/MkGoogle.vue';
 import MkSparkle from '@/components/MkSparkle.vue';
-import MkA from '@/components/global/MkA.vue';
+import MkA, {MkABehavior} from '@/components/global/MkA.vue';
 import { host } from '@/config.js';
 import { defaultStore } from '@/store.js';
 import { nyaize as doNyaize } from '@/scripts/nyaize.js';
@@ -43,6 +43,7 @@ type MfmProps = {
 	parsedNodes?: mfm.MfmNode[] | null;
 	enableEmojiMenu?: boolean;
 	enableEmojiMenuReaction?: boolean;
+	linkBehavior?: MkABehavior;
 };
 
 type MfmEvents = {
@@ -342,6 +343,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					url: token.props.url,
 					rel: 'nofollow noopener',
+					behavior: props.linkBehavior,
 				})];
 			}
 
@@ -350,6 +352,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					url: token.props.url,
 					rel: 'nofollow noopener',
+					behavior: props.linkBehavior,
 				}, genEl(token.children, scale, true))];
 			}
 
@@ -358,6 +361,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					host: (token.props.host == null && props.author && props.author.host != null ? props.author.host : token.props.host) ?? host,
 					username: token.props.username,
+					behavior: props.linkBehavior,
 				})];
 			}
 
@@ -366,6 +370,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					to: isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
 					style: 'color:var(--hashtag);',
+					behavior: props.linkBehavior,
 				}, `#${token.props.hashtag}`)];
 			}
 
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index d2945a78b950..1c2f3ccedb56 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -6,6 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <component
 	:is="self ? 'MkA' : 'a'" ref="el" :class="$style.root" class="_link" :[attr]="self ? props.url.substring(local.length) : props.url" :rel="rel ?? 'nofollow noopener'" :target="target"
+	:behavior = "props.behavior"
 	@contextmenu.stop="() => {}"
 >
 	<template v-if="!self">
@@ -31,11 +32,13 @@ import * as os from '@/os.js';
 import { useTooltip } from '@/scripts/use-tooltip.js';
 import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
 import { isEnabledUrlPreview } from '@/instance.js';
+import { MkABehavior } from '@/components/global/MkA.vue';
 
 const props = withDefaults(defineProps<{
 	url: string;
 	rel?: string;
 	showUrlPreview?: boolean;
+	behavior?: MkABehavior;
 }>(), {
 	showUrlPreview: true,
 });

From c7d7da8fc58ace9be6cf3af1040ed3a4b7309064 Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Sun, 28 Apr 2024 10:53:33 +0900
Subject: [PATCH 174/266] =?UTF-8?q?AP=20Link=E7=AD=89=E3=81=AF=E6=B7=BB?=
 =?UTF-8?q?=E4=BB=98=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E6=89=B1=E3=81=84?=
 =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=AA?=
 =?UTF-8?q?=E3=81=A9=20(#13754)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Linkは添付ファイルではない

* CHANGELOG
---
 CHANGELOG.md                                  |  1 +
 .../core/activitypub/models/ApImageService.ts | 19 +++++++-------
 .../core/activitypub/models/ApNoteService.ts  | 17 +++++-------
 packages/backend/src/core/activitypub/type.ts | 11 ++++----
 packages/backend/test/unit/activitypub.ts     | 26 ++++++++++++++-----
 5 files changed, 43 insertions(+), 31 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f22cec856f95..68015596bdcc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -73,6 +73,7 @@
 - Fix: 一部のタイムラインのストリーミングでインスタンスミュートが効かない問題を修正
 - Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
 - Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
+- Fix: AP Link等は添付ファイル扱いしないようになど (#13754)
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts
index 89b6ef23d016..369196727014 100644
--- a/packages/backend/src/core/activitypub/models/ApImageService.ts
+++ b/packages/backend/src/core/activitypub/models/ApImageService.ts
@@ -17,7 +17,7 @@ import { bindThis } from '@/decorators.js';
 import { checkHttps } from '@/misc/check-https.js';
 import { ApResolverService } from '../ApResolverService.js';
 import { ApLoggerService } from '../ApLoggerService.js';
-import type { IObject } from '../type.js';
+import { isDocument, type IObject } from '../type.js';
 
 @Injectable()
 export class ApImageService {
@@ -39,7 +39,7 @@ export class ApImageService {
 	 * Imageを作成します。
 	 */
 	@bindThis
-	public async createImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
+	public async createImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile | null> {
 		// 投稿者が凍結されていたらスキップ
 		if (actor.isSuspended) {
 			throw new Error('actor has been suspended');
@@ -47,16 +47,18 @@ export class ApImageService {
 
 		const image = await this.apResolverService.createResolver().resolve(value);
 
+		if (!isDocument(image)) return null;
+
 		if (image.url == null) {
-			throw new Error('invalid image: url not provided');
+			return null;
 		}
 
 		if (typeof image.url !== 'string') {
-			throw new Error('invalid image: unexpected type of url: ' + JSON.stringify(image.url, null, 2));
+			return null;
 		}
 
 		if (!checkHttps(image.url)) {
-			throw new Error('invalid image: unexpected schema of url: ' + image.url);
+			return null;
 		}
 
 		this.logger.info(`Creating the Image: ${image.url}`);
@@ -86,12 +88,11 @@ export class ApImageService {
 	/**
 	 * Imageを解決します。
 	 *
-	 * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ
-	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
+	 * ImageをリモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 	 */
 	@bindThis
-	public async resolveImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
-		// TODO
+	public async resolveImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile | null> {
+		// TODO: Misskeyに対象のImageが登録されていればそれを返す
 
 		// リモートサーバーからフェッチしてきて登録
 		return await this.createImage(actor, value);
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 4d64b08e15c4..05f7879983d7 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -4,7 +4,6 @@
  */
 
 import { forwardRef, Inject, Injectable } from '@nestjs/common';
-import promiseLimit from 'promise-limit';
 import { In } from 'typeorm';
 import { DI } from '@/di-symbols.js';
 import type { PollsRepository, EmojisRepository } from '@/models/_.js';
@@ -209,15 +208,13 @@ export class ApNoteService {
 		}
 
 		// 添付ファイル
-		// TODO: attachmentは必ずしもImageではない
-		// TODO: attachmentは必ずしも配列ではない
-		const limit = promiseLimit<MiDriveFile>(2);
-		const files = (await Promise.all(toArray(note.attachment).map(attach => (
-			limit(() => this.apImageService.resolveImage(actor, {
-				...attach,
-				sensitive: note.sensitive, // Noteがsensitiveなら添付もsensitiveにする
-			}))
-		))));
+		const files: MiDriveFile[] = [];
+
+		for (const attach of toArray(note.attachment)) {
+			attach.sensitive ||= note.sensitive;	// Noteがsensitiveなら添付もsensitiveにする
+			const file = await this.apImageService.resolveImage(actor, attach);
+			if (file) files.push(file);
+		}
 
 		// リプライ
 		const reply: MiNote | null = note.inReplyTo
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index b43dddad61d2..09322888d518 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -25,6 +25,7 @@ export interface IObject {
 	endTime?: Date;
 	icon?: any;
 	image?: any;
+	mediaType?: string;
 	url?: ApObject | string;
 	href?: string;
 	tag?: IObject | IObject[];
@@ -240,14 +241,14 @@ export interface IKey extends IObject {
 }
 
 export interface IApDocument extends IObject {
-	type: 'Document';
-	name: string | null;
-	mediaType: string;
+	type: 'Audio' | 'Document' | 'Image' | 'Page' | 'Video';
 }
 
-export interface IApImage extends IObject {
+export const isDocument = (object: IObject): object is IApDocument =>
+	['Audio', 'Document', 'Image', 'Page', 'Video'].includes(getApType(object));
+
+export interface IApImage extends IApDocument {
 	type: 'Image';
-	name: string | null;
 }
 
 export interface ICreate extends IActivity {
diff --git a/packages/backend/test/unit/activitypub.ts b/packages/backend/test/unit/activitypub.ts
index b4b06b06bd05..aa3f3a4ff1d9 100644
--- a/packages/backend/test/unit/activitypub.ts
+++ b/packages/backend/test/unit/activitypub.ts
@@ -17,7 +17,7 @@ import { GlobalModule } from '@/GlobalModule.js';
 import { CoreModule } from '@/core/CoreModule.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 import { LoggerService } from '@/core/LoggerService.js';
-import type { IActor, IApDocument, ICollection, IPost } from '@/core/activitypub/type.js';
+import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
 import { MiMeta, MiNote } from '@/models/_.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
 import { DownloadService } from '@/core/DownloadService.js';
@@ -295,7 +295,7 @@ describe('ActivityPub', () => {
 				await createRandomRemoteUser(resolver, personService),
 				imageObject,
 			);
-			assert.ok(!driveFile.isLink);
+			assert.ok(driveFile && !driveFile.isLink);
 
 			const sensitiveImageObject: IApDocument = {
 				type: 'Document',
@@ -308,7 +308,7 @@ describe('ActivityPub', () => {
 				await createRandomRemoteUser(resolver, personService),
 				sensitiveImageObject,
 			);
-			assert.ok(!sensitiveDriveFile.isLink);
+			assert.ok(sensitiveDriveFile && !sensitiveDriveFile.isLink);
 		});
 
 		test('cacheRemoteFiles=false disables caching', async () => {
@@ -324,7 +324,7 @@ describe('ActivityPub', () => {
 				await createRandomRemoteUser(resolver, personService),
 				imageObject,
 			);
-			assert.ok(driveFile.isLink);
+			assert.ok(driveFile && driveFile.isLink);
 
 			const sensitiveImageObject: IApDocument = {
 				type: 'Document',
@@ -337,7 +337,7 @@ describe('ActivityPub', () => {
 				await createRandomRemoteUser(resolver, personService),
 				sensitiveImageObject,
 			);
-			assert.ok(sensitiveDriveFile.isLink);
+			assert.ok(sensitiveDriveFile && sensitiveDriveFile.isLink);
 		});
 
 		test('cacheRemoteSensitiveFiles=false only affects sensitive files', async () => {
@@ -353,7 +353,7 @@ describe('ActivityPub', () => {
 				await createRandomRemoteUser(resolver, personService),
 				imageObject,
 			);
-			assert.ok(!driveFile.isLink);
+			assert.ok(driveFile && !driveFile.isLink);
 
 			const sensitiveImageObject: IApDocument = {
 				type: 'Document',
@@ -366,7 +366,19 @@ describe('ActivityPub', () => {
 				await createRandomRemoteUser(resolver, personService),
 				sensitiveImageObject,
 			);
-			assert.ok(sensitiveDriveFile.isLink);
+			assert.ok(sensitiveDriveFile && sensitiveDriveFile.isLink);
+		});
+
+		test('Link is not an attachment files', async () => {
+			const linkObject: IObject = {
+				type: 'Link',
+				href: 'https://example.com/',
+			};
+			const driveFile = await imageService.createImage(
+				await createRandomRemoteUser(resolver, personService),
+				linkObject,
+			);
+			assert.strictEqual(driveFile, null);
 		});
 	});
 });

From e2ff5f58b2357b2433313b2885e7de7923f65205 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 28 Apr 2024 10:54:20 +0900
Subject: [PATCH 175/266] lint

---
 .../frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index a56d8bcce2df..6e880fc3227b 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -16,7 +16,7 @@ import MkCode from '@/components/MkCode.vue';
 import MkCodeInline from '@/components/MkCodeInline.vue';
 import MkGoogle from '@/components/MkGoogle.vue';
 import MkSparkle from '@/components/MkSparkle.vue';
-import MkA, {MkABehavior} from '@/components/global/MkA.vue';
+import MkA, { MkABehavior } from '@/components/global/MkA.vue';
 import { host } from '@/config.js';
 import { defaultStore } from '@/store.js';
 import { nyaize as doNyaize } from '@/scripts/nyaize.js';

From 2ff90a80d453e33caee2cc39f27149d1d7386ee1 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Mon, 29 Apr 2024 15:36:01 +0900
Subject: [PATCH 176/266] fix(backend): add detailed schema to `fetch-rss`
 endpoint (#13764)

---
 .../src/server/api/endpoints/fetch-rss.ts     | 179 +++++++++++++++++-
 .../src/ui/_common_/statusbar-rss.vue         |   5 +-
 packages/frontend/src/widgets/WidgetRss.vue   |   7 +-
 .../frontend/src/widgets/WidgetRssTicker.vue  |   7 +-
 packages/misskey-js/src/autogen/types.ts      |  47 ++++-
 5 files changed, 234 insertions(+), 11 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts
index 2085b0636518..ba48b0119e93 100644
--- a/packages/backend/src/server/api/endpoints/fetch-rss.ts
+++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts
@@ -20,13 +20,188 @@ export const meta = {
 	res: {
 		type: 'object',
 		properties: {
+			image: {
+				type: 'object',
+				optional: true,
+				properties: {
+					link: {
+						type: 'string',
+						optional: true,
+					},
+					url: {
+						type: 'string',
+						optional: false,
+					},
+					title: {
+						type: 'string',
+						optional: true,
+					},
+				},
+			},
+			paginationLinks: {
+				type: 'object',
+				optional: true,
+				properties: {
+					self: {
+						type: 'string',
+						optional: true,
+					},
+					first: {
+						type: 'string',
+						optional: true,
+					},
+					next: {
+						type: 'string',
+						optional: true,
+					},
+					last: {
+						type: 'string',
+						optional: true,
+					},
+					prev: {
+						type: 'string',
+						optional: true,
+					},
+				},
+			},
+			link: {
+				type: 'string',
+				optional: true,
+			},
+			title: {
+				type: 'string',
+				optional: true,
+			},
 			items: {
 				type: 'array',
+				optional: false,
 				items: {
 					type: 'object',
+					properties: {
+						link: {
+							type: 'string',
+							optional: true,
+						},
+						guid: {
+							type: 'string',
+							optional: true,
+						},
+						title: {
+							type: 'string',
+							optional: true,
+						},
+						pubDate: {
+							type: 'string',
+							optional: true,
+						},
+						creator: {
+							type: 'string',
+							optional: true,
+						},
+						summary: {
+							type: 'string',
+							optional: true,
+						},
+						content: {
+							type: 'string',
+							optional: true,
+						},
+						isoDate: {
+							type: 'string',
+							optional: true,
+						},
+						categories: {
+							type: 'array',
+							optional: true,
+							items: {
+								type: 'string',
+							},
+						},
+						contentSnippet: {
+							type: 'string',
+							optional: true,
+						},
+						enclosure: {
+							type: 'object',
+							optional: true,
+							properties: {
+								url: {
+									type: 'string',
+									optional: false,
+								},
+								length: {
+									type: 'number',
+									optional: true,
+								},
+								type: {
+									type: 'string',
+									optional: true,
+								},
+							},
+						},
+					},
+				},
+			},
+			feedUrl: {
+				type: 'string',
+				optional: true,
+			},
+			description: {
+				type: 'string',
+				optional: true,
+			},
+			itunes: {
+				type: 'object',
+				optional: true,
+				additionalProperties: true,
+				properties: {
+					image: {
+						type: 'string',
+						optional: true,
+					},
+					owner: {
+						type: 'object',
+						optional: true,
+						properties: {
+							name: {
+								type: 'string',
+								optional: true,
+							},
+							email: {
+								type: 'string',
+								optional: true,
+							},
+						},
+					},
+					author: {
+						type: 'string',
+						optional: true,
+					},
+					summary: {
+						type: 'string',
+						optional: true,
+					},
+					explicit: {
+						type: 'string',
+						optional: true,
+					},
+					categories: {
+						type: 'array',
+						optional: true,
+						items: {
+							type: 'string',
+						},
+					},
+					keywords: {
+						type: 'array',
+						optional: true,
+						items: {
+							type: 'string',
+						},
+					},
 				},
-			}
-		}
+			},
+		},
 	},
 } as const;
 
diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue
index b973a4fd6bb1..6e1d06eec1f9 100644
--- a/packages/frontend/src/ui/_common_/statusbar-rss.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue
@@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import MarqueeText from '@/components/MkMarquee.vue';
 import { useInterval } from '@/scripts/use-interval.js';
 import { shuffle } from '@/scripts/shuffle.js';
@@ -42,13 +43,13 @@ const props = defineProps<{
 	refreshIntervalSec?: number;
 }>();
 
-const items = ref([]);
+const items = ref<Misskey.entities.FetchRssResponse['items']>([]);
 const fetching = ref(true);
 const key = ref(0);
 
 const tick = () => {
 	window.fetch(`/api/fetch-rss?url=${props.url}`, {}).then(res => {
-		res.json().then(feed => {
+		res.json().then((feed: Misskey.entities.FetchRssResponse) => {
 			if (props.shuffle) {
 				shuffle(feed.items);
 			}
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index 5d5c1188aa59..e5758662cc49 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, watch, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -64,7 +65,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-const rawItems = ref([]);
+const rawItems = ref<Misskey.entities.FetchRssResponse['items']>([]);
 const items = computed(() => rawItems.value.slice(0, widgetProps.maxEntries));
 const fetching = ref(true);
 const fetchEndpoint = computed(() => {
@@ -79,8 +80,8 @@ const tick = () => {
 
 	window.fetch(fetchEndpoint.value, {})
 		.then(res => res.json())
-		.then(feed => {
-			rawItems.value = feed.items ?? [];
+		.then((feed: Misskey.entities.FetchRssResponse) => {
+			rawItems.value = feed.items;
 			fetching.value = false;
 		});
 };
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index af220f95e234..16306ef5ba9b 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, watch, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import MarqueeText from '@/components/MkMarquee.vue';
 import { GetFormResultType } from '@/scripts/form.js';
@@ -87,7 +88,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-const rawItems = ref([]);
+const rawItems = ref<Misskey.entities.FetchRssResponse['items']>([]);
 const items = computed(() => {
 	const newItems = rawItems.value.slice(0, widgetProps.maxEntries);
 	if (widgetProps.shuffle) {
@@ -110,8 +111,8 @@ const tick = () => {
 
 	window.fetch(fetchEndpoint.value, {})
 		.then(res => res.json())
-		.then(feed => {
-			rawItems.value = feed.items ?? [];
+		.then((feed: Misskey.entities.FetchRssResponse) => {
+			rawItems.value = feed.items;
 			fetching.value = false;
 			key.value++;
 		});
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 131d20f09bd2..1b9f1304d581 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -26065,7 +26065,52 @@ export type operations = {
       200: {
         content: {
           'application/json': {
-            items: Record<string, never>[];
+            image?: {
+              link?: string;
+              url: string;
+              title?: string;
+            };
+            paginationLinks?: {
+              self?: string;
+              first?: string;
+              next?: string;
+              last?: string;
+              prev?: string;
+            };
+            link?: string;
+            title?: string;
+            items: {
+                link?: string;
+                guid?: string;
+                title?: string;
+                pubDate?: string;
+                creator?: string;
+                summary?: string;
+                content?: string;
+                isoDate?: string;
+                categories?: string[];
+                contentSnippet?: string;
+                enclosure?: {
+                  url: string;
+                  length?: number;
+                  type?: string;
+                };
+              }[];
+            feedUrl?: string;
+            description?: string;
+            itunes?: {
+              image?: string;
+              owner?: {
+                name?: string;
+                email?: string;
+              };
+              author?: string;
+              summary?: string;
+              explicit?: string;
+              categories?: string[];
+              keywords?: string[];
+              [key: string]: unknown;
+            };
           };
         };
       };

From 2017f9114fe281ac86304f3e7956589f43d9ccce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 1 May 2024 13:51:00 +0900
Subject: [PATCH 177/266] =?UTF-8?q?refactor(frontend):=20=E9=9D=9E?=
 =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3=E7=94=BB=E9=9D=A2=E3=81=A7?=
 =?UTF-8?q?=E3=81=AEmeta=E5=8F=96=E5=BE=97=E3=82=92=E6=B8=9B=E3=82=89?=
 =?UTF-8?q?=E3=81=99=20(#13776)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(frontend): 非ログイン画面でのmeta取得を減らす

* fix(frontend): サーバー供給のmetaとクライアントフォールバックで取れるmetaの型が違うのを修正

* force fetch meta at welcome.vue

* refactor
---
 .../frontend/src/components/MkFeaturedPhotos.vue     | 12 ++----------
 .../frontend/src/components/MkVisitorDashboard.vue   | 11 +++--------
 packages/frontend/src/instance.ts                    |  8 +++++---
 packages/frontend/src/pages/welcome.entrance.a.vue   |  8 ++------
 packages/frontend/src/pages/welcome.vue              | 12 ++++++------
 packages/frontend/src/ui/visitor.vue                 |  7 -------
 6 files changed, 18 insertions(+), 40 deletions(-)

diff --git a/packages/frontend/src/components/MkFeaturedPhotos.vue b/packages/frontend/src/components/MkFeaturedPhotos.vue
index 8d875790bc34..c42c692db062 100644
--- a/packages/frontend/src/components/MkFeaturedPhotos.vue
+++ b/packages/frontend/src/components/MkFeaturedPhotos.vue
@@ -4,19 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div v-if="meta" :class="$style.root" :style="{ backgroundImage: `url(${ meta.backgroundImageUrl })` }"></div>
+<div v-if="instance" :class="$style.root" :style="{ backgroundImage: `url(${ instance.backgroundImageUrl })` }"></div>
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
-import * as Misskey from 'misskey-js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
-
-const meta = ref<Misskey.entities.MetaResponse>();
-
-misskeyApi('meta', { detail: true }).then(gotMeta => {
-	meta.value = gotMeta;
-});
+import { instance } from '@/instance.js';
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index be80baa774f3..611c7be21656 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -4,19 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div v-if="meta" :class="$style.root">
+<div v-if="instance" :class="$style.root">
 	<div :class="[$style.main, $style.panel]">
 		<img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.mainIcon"/>
 		<button class="_button _acrylic" :class="$style.mainMenu" @click="showMenu"><i class="ti ti-dots"></i></button>
 		<div :class="$style.mainFg">
 			<h1 :class="$style.mainTitle">
 				<!-- 背景色によってはロゴが見えなくなるのでとりあえず無効に -->
-				<!-- <img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span> -->
+				<!-- <img class="logo" v-if="instance.logoImageUrl" :src="instance.logoImageUrl"><span v-else class="text">{{ instanceName }}</span> -->
 				<span>{{ instanceName }}</span>
 			</h1>
 			<div :class="$style.mainAbout">
 				<!-- eslint-disable-next-line vue/no-v-html -->
-				<div v-html="meta.description || i18n.ts.headlineMisskey"></div>
+				<div v-html="instance.description || i18n.ts.headlineMisskey"></div>
 			</div>
 			<div v-if="instance.disableRegistration" :class="$style.mainWarn">
 				<MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo>
@@ -66,13 +66,8 @@ import { instance } from '@/instance.js';
 import MkNumber from '@/components/MkNumber.vue';
 import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
 
-const meta = ref<Misskey.entities.MetaResponse | null>(null);
 const stats = ref<Misskey.entities.StatsResponse | null>(null);
 
-misskeyApi('meta', { detail: true }).then(_meta => {
-	meta.value = _meta;
-});
-
 misskeyApi('stats', {}).then((res) => {
 	stats.value = res;
 });
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index 22337e7eb958..7df6ec205cbb 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -28,7 +28,7 @@ if (providedAt > cachedAt) {
 
 // TODO: instanceをリアクティブにするかは再考の余地あり
 
-export const instance: Misskey.entities.MetaResponse = reactive(cachedMeta ?? {});
+export const instance: Misskey.entities.MetaDetailed = reactive(cachedMeta ?? {});
 
 export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL);
 
@@ -38,7 +38,7 @@ export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFA
 
 export const isEnabledUrlPreview = computed(() => instance.enableUrlPreview ?? true);
 
-export async function fetchInstance(force = false): Promise<void> {
+export async function fetchInstance(force = false): Promise<Misskey.entities.MetaDetailed> {
 	if (!force) {
 		const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
 
@@ -48,7 +48,7 @@ export async function fetchInstance(force = false): Promise<void> {
 	}
 
 	const meta = await misskeyApi('meta', {
-		detail: false,
+		detail: true,
 	});
 
 	for (const [k, v] of Object.entries(meta)) {
@@ -57,4 +57,6 @@ export async function fetchInstance(force = false): Promise<void> {
 
 	miLocalStorage.setItem('instance', JSON.stringify(instance));
 	miLocalStorage.setItem('instanceCachedAt', Date.now().toString());
+
+	return instance;
 }
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 6c05aad24f22..d6ba397f1b65 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -42,11 +42,11 @@ import XTimeline from './welcome.timeline.vue';
 import MarqueeText from '@/components/MkMarquee.vue';
 import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
 import misskeysvg from '/client-assets/misskey.svg';
-import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
+import { instance as meta } from '@/instance.js';
 
-const meta = ref<Misskey.entities.MetaResponse>();
 const instances = ref<Misskey.entities.FederationInstance[]>();
 
 function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
@@ -56,10 +56,6 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string
 	return getProxiedImageUrl(instance.iconUrl, 'preview');
 }
 
-misskeyApi('meta', { detail: true }).then(_meta => {
-	meta.value = _meta;
-});
-
 misskeyApiGet('federation/instances', {
 	sort: '+pubSub',
 	limit: 20,
diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue
index 9ba6a5885e30..915fe35025ea 100644
--- a/packages/frontend/src/pages/welcome.vue
+++ b/packages/frontend/src/pages/welcome.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div v-if="meta">
-	<XSetup v-if="meta.requireSetup"/>
+<div v-if="instance">
+	<XSetup v-if="instance.requireSetup"/>
 	<XEntrance v-else/>
 </div>
 </template>
@@ -16,13 +16,13 @@ import * as Misskey from 'misskey-js';
 import XSetup from './welcome.setup.vue';
 import XEntrance from './welcome.entrance.a.vue';
 import { instanceName } from '@/config.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { fetchInstance } from '@/instance.js';
 
-const meta = ref<Misskey.entities.MetaResponse | null>(null);
+const instance = ref<Misskey.entities.MetaDetailed | null>(null);
 
-misskeyApi('meta', { detail: true }).then(res => {
-	meta.value = res;
+fetchInstance(true).then((res) => {
+	instance.value = res;
 });
 
 const headerActions = computed(() => []);
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 29b305d9bc4d..80623083cfdb 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -70,11 +70,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, provide, ref, computed } from 'vue';
-import * as Misskey from 'misskey-js';
 import XCommon from './_common_/common.vue';
 import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
 import { instance } from '@/instance.js';
 import XSigninDialog from '@/components/MkSigninDialog.vue';
 import XSignupDialog from '@/components/MkSignupDialog.vue';
@@ -114,7 +112,6 @@ const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.poli
 const showMenu = ref(false);
 const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
 const narrow = ref(window.innerWidth < 1280);
-const meta = ref<Misskey.entities.MetaResponse>();
 
 const keymap = computed(() => {
 	return {
@@ -128,10 +125,6 @@ const keymap = computed(() => {
 	};
 });
 
-misskeyApi('meta', { detail: true }).then(res => {
-	meta.value = res;
-});
-
 function signin() {
 	os.popup(XSigninDialog, {
 		autoSet: true,

From 8c5e5640669c252faaf22ed8742d598ec0e2268f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 1 May 2024 13:52:59 +0900
Subject: [PATCH 178/266] fix type error

---
 packages/frontend/src/instance.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index 7df6ec205cbb..6847321d6c40 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -43,7 +43,7 @@ export async function fetchInstance(force = false): Promise<Misskey.entities.Met
 		const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
 
 		if (Date.now() - cachedAt < 1000 * 60 * 60) {
-			return;
+			return instance;
 		}
 	}
 

From ef630df443bdd24cfe0b086b0e2f94d87c4f53b7 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 1 May 2024 14:12:36 +0900
Subject: [PATCH 179/266] enhance(frontend): add contact page

---
 locales/index.d.ts                            |  4 ++
 locales/ja-JP.yml                             |  1 +
 .../src/components/MkVisitorDashboard.vue     | 39 +------------------
 packages/frontend/src/pages/contact.vue       | 24 ++++++++++++
 packages/frontend/src/router/definition.ts    |  3 ++
 packages/frontend/src/ui/_common_/common.ts   | 11 ++++--
 6 files changed, 42 insertions(+), 40 deletions(-)
 create mode 100644 packages/frontend/src/pages/contact.vue

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 9bcd1979afbd..779a5d2c3fbe 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4952,6 +4952,10 @@ export interface Locale extends ILocale {
      * フォローの際常に確認する
      */
     "alwaysConfirmFollow": string;
+    /**
+     * お問い合わせ
+     */
+    "inquiry": string;
     "_bubbleGame": {
         /**
          * 遊び方
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 5f7715b210ed..8f17215802c3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1234,6 +1234,7 @@ keepOriginalFilename: "オリジナルのファイル名を保持"
 keepOriginalFilenameDescription: "この設定をオフにすると、アップロード時にファイル名が自動でランダム文字列に置き換えられます。"
 noDescription: "説明文はありません"
 alwaysConfirmFollow: "フォローの際常に確認する"
+inquiry: "お問い合わせ"
 
 _bubbleGame:
   howToPlay: "遊び方"
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 611c7be21656..f7963f993815 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -65,6 +65,7 @@ import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import MkNumber from '@/components/MkNumber.vue';
 import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
+import { openInstanceMenu } from '@/ui/_common_/common';
 
 const stats = ref<Misskey.entities.StatsResponse | null>(null);
 
@@ -85,43 +86,7 @@ function signup() {
 }
 
 function showMenu(ev) {
-	os.popupMenu([{
-		text: i18n.ts.instanceInfo,
-		icon: 'ti ti-info-circle',
-		action: () => {
-			os.pageWindow('/about');
-		},
-	}, {
-		text: i18n.ts.aboutMisskey,
-		icon: 'ti ti-info-circle',
-		action: () => {
-			os.pageWindow('/about-misskey');
-		},
-	}, { type: 'divider' }, (instance.impressumUrl) ? {
-		text: i18n.ts.impressum,
-		icon: 'ti ti-file-invoice',
-		action: () => {
-			window.open(instance.impressumUrl!, '_blank', 'noopener');
-		},
-	} : undefined, (instance.tosUrl) ? {
-		text: i18n.ts.termsOfService,
-		icon: 'ti ti-notebook',
-		action: () => {
-			window.open(instance.tosUrl!, '_blank', 'noopener');
-		},
-	} : undefined, (instance.privacyPolicyUrl) ? {
-		text: i18n.ts.privacyPolicy,
-		icon: 'ti ti-shield-lock',
-		action: () => {
-			window.open(instance.privacyPolicyUrl!, '_blank', 'noopener');
-		},
-	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, {
-		text: i18n.ts.help,
-		icon: 'ti ti-help-circle',
-		action: () => {
-			window.open('https://misskey-hub.net/docs/for-users/', '_blank', 'noopener');
-		},
-	}], ev.currentTarget ?? ev.target);
+	openInstanceMenu(ev);
 }
 
 function exploreOtherServers() {
diff --git a/packages/frontend/src/pages/contact.vue b/packages/frontend/src/pages/contact.vue
new file mode 100644
index 000000000000..3a694a713277
--- /dev/null
+++ b/packages/frontend/src/pages/contact.vue
@@ -0,0 +1,24 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+	<template #header><MkPageHeader/></template>
+	<MkSpacer :contentMax="600" :marginMin="20">
+		<div>{{ instance.maintainerEmail }}</div>
+	</MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { instance } from '@/instance.js';
+
+definePageMetadata(() => ({
+	title: i18n.ts.inquiry,
+	icon: 'ti ti-help-circle',
+}));
+</script>
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index c9f03b738f4c..c5b576f505c9 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -197,6 +197,9 @@ const routes: RouteDef[] = [{
 	path: '/about',
 	component: page(() => import('@/pages/about.vue')),
 	hash: 'initialTab',
+}, {
+	path: '/contact',
+	component: page(() => import('@/pages/contact.vue')),
 }, {
 	path: '/about-misskey',
 	component: page(() => import('@/pages/about-misskey.vue')),
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 9b510a629241..839fa5faf8a4 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -79,7 +79,12 @@ export function openInstanceMenu(ev: MouseEvent) {
 		text: i18n.ts.tools,
 		icon: 'ti ti-tool',
 		children: toolsMenuItems(),
-	}, { type: 'divider' }, (instance.impressumUrl) ? {
+	}, { type: 'divider' }, {
+		type: 'link',
+		text: i18n.ts.inquiry,
+		icon: 'ti ti-help-circle',
+		to: '/contact',
+	}, (instance.impressumUrl) ? {
 		text: i18n.ts.impressum,
 		icon: 'ti ti-file-invoice',
 		action: () => {
@@ -98,8 +103,8 @@ export function openInstanceMenu(ev: MouseEvent) {
 			window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
 		},
 	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, {
-		text: i18n.ts.help,
-		icon: 'ti ti-help-circle',
+		text: i18n.ts.document,
+		icon: 'ti ti-bulb',
 		action: () => {
 			window.open('https://misskey-hub.net/docs/for-users/', '_blank', 'noopener');
 		},

From 9f66f229537915f47da8e6e08e92a78be390f454 Mon Sep 17 00:00:00 2001
From: taiy <53635909+taiyme@users.noreply.github.com>
Date: Wed, 1 May 2024 15:29:38 +0900
Subject: [PATCH 180/266] =?UTF-8?q?fix(frontend):=20=E9=80=A3=E5=90=88?=
 =?UTF-8?q?=E3=81=AA=E3=81=97=E3=81=AE=E7=8A=B6=E6=85=8B=E3=81=AE=E8=AA=AD?=
 =?UTF-8?q?=E3=81=BF=E6=9B=B8=E3=81=8D=E3=81=8C=E3=81=A7=E3=81=8D=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=20(#13777)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: 連合なしの状態の読み書きができない問題

* update changelog

* fix types: https://github.com/misskey-dev/misskey/pull/13777#discussion_r1585901601
---
 CHANGELOG.md                                          | 1 +
 packages/frontend/src/components/MkPostForm.vue       | 8 ++++++--
 packages/frontend/src/components/MkPostFormDialog.vue | 6 ++++--
 packages/frontend/src/scripts/get-note-menu.ts        | 5 ++---
 packages/frontend/src/store.ts                        | 4 ++--
 5 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68015596bdcc..4b65550daf18 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,6 +56,7 @@
 - Fix: ページのOGP URLが間違っているのを修正
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
 - Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
+- Fix: 連合なしの状態の読み書きができない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index d7efca9de9c9..7dbc1272986f 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -156,6 +156,7 @@ const props = withDefaults(defineProps<{
 	initialVisibleUsers: () => [],
 	autofocus: true,
 	mock: false,
+	initialLocalOnly: undefined,
 });
 
 provide('mock', props.mock);
@@ -185,8 +186,8 @@ watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
 const showAddMfmFunction = ref(defaultStore.state.enableQuickAddMfmFunction);
 watch(showAddMfmFunction, () => defaultStore.set('enableQuickAddMfmFunction', showAddMfmFunction.value));
 const cw = ref<string | null>(props.initialCw ?? null);
-const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
-const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
+const localOnly = ref(props.initialLocalOnly ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly));
+const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility));
 const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]);
 if (props.initialVisibleUsers) {
 	props.initialVisibleUsers.forEach(pushVisibleUser);
@@ -518,6 +519,9 @@ async function toggleLocalOnly() {
 	}
 
 	localOnly.value = !localOnly.value;
+	if (defaultStore.state.rememberNoteVisibility) {
+		defaultStore.set('localOnly', localOnly.value);
+	}
 }
 
 async function toggleReactionAcceptance() {
diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue
index 6331dfed2994..ac37cb31bc9b 100644
--- a/packages/frontend/src/components/MkPostFormDialog.vue
+++ b/packages/frontend/src/components/MkPostFormDialog.vue
@@ -15,7 +15,7 @@ import * as Misskey from 'misskey-js';
 import MkModal from '@/components/MkModal.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
 
-const props = defineProps<{
+const props = withDefaults(defineProps<{
 	reply?: Misskey.entities.Note;
 	renote?: Misskey.entities.Note;
 	channel?: any; // TODO
@@ -31,7 +31,9 @@ const props = defineProps<{
 	instant?: boolean;
 	fixed?: boolean;
 	autofocus?: boolean;
-}>();
+}>(), {
+	initialLocalOnly: undefined,
+});
 
 const emit = defineEmits<{
 	(ev: 'closed'): void;
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 87921bc67f5b..2cd21c1edc2e 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -492,10 +492,9 @@ export function getNoteMenu(props: {
 	};
 }
 
-type Visibility = 'public' | 'home' | 'followers' | 'specified';
+type Visibility = (typeof Misskey.noteVisibilities)[number];
 
-// defaultStore.state.visibilityがstringなためstringも受け付けている
-function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility {
+function smallerVisibility(a: Visibility, b: Visibility): Visibility {
 	if (a === 'specified' || b === 'specified') return 'specified';
 	if (a === 'followers' || b === 'followers') return 'followers';
 	if (a === 'home' || b === 'home') return 'home';
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index e6a348b79fe9..e8eb5a1ed7db 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -94,7 +94,7 @@ export const defaultStore = markRaw(new Storage('base', {
 	},
 	defaultNoteVisibility: {
 		where: 'account',
-		default: 'public',
+		default: 'public' as (typeof Misskey.noteVisibilities)[number],
 	},
 	defaultNoteLocalOnly: {
 		where: 'account',
@@ -150,7 +150,7 @@ export const defaultStore = markRaw(new Storage('base', {
 	},
 	visibility: {
 		where: 'deviceAccount',
-		default: 'public' as 'public' | 'home' | 'followers' | 'specified',
+		default: 'public' as (typeof Misskey.noteVisibilities)[number],
 	},
 	localOnly: {
 		where: 'deviceAccount',

From d2a5bb39e344fcb84a24ae60faafe4694b227b88 Mon Sep 17 00:00:00 2001
From: Daiki Mizukami <tesaguriguma@gmail.com>
Date: Wed, 1 May 2024 07:33:58 +0000
Subject: [PATCH 181/266] Merge pull request from GHSA-2vxv-pv3m-3wvj

* fix: normalize incoming signed activities

* Tweak style

* Update CHANGELOG.md

* Log compacted activity as well

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  2 +
 packages/backend/src/core/CoreModule.ts       | 12 ++---
 .../src/core/activitypub/ApRendererService.ts | 45 +++----------------
 ...LdSignatureService.ts => JsonLdService.ts} | 32 ++++++++-----
 .../src/core/activitypub/misc/contexts.ts     | 39 +++++++++++++++-
 .../queue/processors/InboxProcessorService.ts | 44 +++++++++++++-----
 packages/backend/test/unit/activitypub.ts     | 42 +++++++++++++++++
 7 files changed, 146 insertions(+), 70 deletions(-)
 rename packages/backend/src/core/activitypub/{LdSignatureService.ts => JsonLdService.ts} (83%)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b65550daf18..4394ab0c552a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ### Note
 - コントロールパネル内にあるサマリープロキシの設定個所がセキュリティから全般へ変更となります。
+- 悪意のある第三者がリモートユーザーになりすましたアクティビティを受け取れてしまう問題を修正しました。詳しくは[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-2vxv-pv3m-3wvj)をご覧ください。
 
 ### General
 - Enhance: URLプレビューの有効化・無効化を設定できるように #13569
@@ -61,6 +62,7 @@
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
 - Enhance: misskey-dev/summaly@5.1.0の取り込み(プレビュー生成処理の効率化)
+- Fix: リモートから配送されたアクティビティにJSON-LD compactionをかける
 - Fix: フォローリクエストを作成する際に既存のものは削除するように  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
 - Fix: エンドポイント`notes/translate`のエラーを改善
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 2c27d33c0649..595315587241 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -127,7 +127,7 @@ import { ApMfmService } from './activitypub/ApMfmService.js';
 import { ApRendererService } from './activitypub/ApRendererService.js';
 import { ApRequestService } from './activitypub/ApRequestService.js';
 import { ApResolverService } from './activitypub/ApResolverService.js';
-import { LdSignatureService } from './activitypub/LdSignatureService.js';
+import { JsonLdService } from './activitypub/JsonLdService.js';
 import { RemoteLoggerService } from './RemoteLoggerService.js';
 import { RemoteUserResolveService } from './RemoteUserResolveService.js';
 import { WebfingerService } from './WebfingerService.js';
@@ -266,7 +266,7 @@ const $ApMfmService: Provider = { provide: 'ApMfmService', useExisting: ApMfmSer
 const $ApRendererService: Provider = { provide: 'ApRendererService', useExisting: ApRendererService };
 const $ApRequestService: Provider = { provide: 'ApRequestService', useExisting: ApRequestService };
 const $ApResolverService: Provider = { provide: 'ApResolverService', useExisting: ApResolverService };
-const $LdSignatureService: Provider = { provide: 'LdSignatureService', useExisting: LdSignatureService };
+const $JsonLdService: Provider = { provide: 'JsonLdService', useExisting: JsonLdService };
 const $RemoteLoggerService: Provider = { provide: 'RemoteLoggerService', useExisting: RemoteLoggerService };
 const $RemoteUserResolveService: Provider = { provide: 'RemoteUserResolveService', useExisting: RemoteUserResolveService };
 const $WebfingerService: Provider = { provide: 'WebfingerService', useExisting: WebfingerService };
@@ -406,7 +406,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		ApRendererService,
 		ApRequestService,
 		ApResolverService,
-		LdSignatureService,
+		JsonLdService,
 		RemoteLoggerService,
 		RemoteUserResolveService,
 		WebfingerService,
@@ -542,7 +542,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$ApRendererService,
 		$ApRequestService,
 		$ApResolverService,
-		$LdSignatureService,
+		$JsonLdService,
 		$RemoteLoggerService,
 		$RemoteUserResolveService,
 		$WebfingerService,
@@ -678,7 +678,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		ApRendererService,
 		ApRequestService,
 		ApResolverService,
-		LdSignatureService,
+		JsonLdService,
 		RemoteLoggerService,
 		RemoteUserResolveService,
 		WebfingerService,
@@ -813,7 +813,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$ApRendererService,
 		$ApRequestService,
 		$ApResolverService,
-		$LdSignatureService,
+		$JsonLdService,
 		$RemoteLoggerService,
 		$RemoteUserResolveService,
 		$WebfingerService,
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index d7fb977a99d3..d3553b6f7303 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -28,8 +28,9 @@ import { bindThis } from '@/decorators.js';
 import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 import { isNotNull } from '@/misc/is-not-null.js';
 import { IdService } from '@/core/IdService.js';
-import { LdSignatureService } from './LdSignatureService.js';
+import { JsonLdService } from './JsonLdService.js';
 import { ApMfmService } from './ApMfmService.js';
+import { CONTEXT } from './misc/contexts.js';
 import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
 
 @Injectable()
@@ -56,7 +57,7 @@ export class ApRendererService {
 		private customEmojiService: CustomEmojiService,
 		private userEntityService: UserEntityService,
 		private driveFileEntityService: DriveFileEntityService,
-		private ldSignatureService: LdSignatureService,
+		private jsonLdService: JsonLdService,
 		private userKeypairService: UserKeypairService,
 		private apMfmService: ApMfmService,
 		private mfmService: MfmService,
@@ -617,48 +618,16 @@ export class ApRendererService {
 			x.id = `${this.config.url}/${randomUUID()}`;
 		}
 
-		return Object.assign({
-			'@context': [
-				'https://www.w3.org/ns/activitystreams',
-				'https://w3id.org/security/v1',
-				{
-					Key: 'sec:Key',
-					// as non-standards
-					manuallyApprovesFollowers: 'as:manuallyApprovesFollowers',
-					sensitive: 'as:sensitive',
-					Hashtag: 'as:Hashtag',
-					quoteUrl: 'as:quoteUrl',
-					// Mastodon
-					toot: 'http://joinmastodon.org/ns#',
-					Emoji: 'toot:Emoji',
-					featured: 'toot:featured',
-					discoverable: 'toot:discoverable',
-					// schema
-					schema: 'http://schema.org#',
-					PropertyValue: 'schema:PropertyValue',
-					value: 'schema:value',
-					// Misskey
-					misskey: 'https://misskey-hub.net/ns#',
-					'_misskey_content': 'misskey:_misskey_content',
-					'_misskey_quote': 'misskey:_misskey_quote',
-					'_misskey_reaction': 'misskey:_misskey_reaction',
-					'_misskey_votes': 'misskey:_misskey_votes',
-					'_misskey_summary': 'misskey:_misskey_summary',
-					'isCat': 'misskey:isCat',
-					// vcard
-					vcard: 'http://www.w3.org/2006/vcard/ns#',
-				},
-			],
-		}, x as T & { id: string });
+		return Object.assign({ '@context': CONTEXT }, x as T & { id: string });
 	}
 
 	@bindThis
 	public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> {
 		const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
-		const ldSignature = this.ldSignatureService.use();
-		ldSignature.debug = false;
-		activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`);
+		const jsonLd = this.jsonLdService.use();
+		jsonLd.debug = false;
+		activity = await jsonLd.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`);
 
 		return activity;
 	}
diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/JsonLdService.ts
similarity index 83%
rename from packages/backend/src/core/activitypub/LdSignatureService.ts
rename to packages/backend/src/core/activitypub/JsonLdService.ts
index 9de184336f0f..100d4fa19fe5 100644
--- a/packages/backend/src/core/activitypub/LdSignatureService.ts
+++ b/packages/backend/src/core/activitypub/JsonLdService.ts
@@ -7,14 +7,14 @@ import * as crypto from 'node:crypto';
 import { Injectable } from '@nestjs/common';
 import { HttpRequestService } from '@/core/HttpRequestService.js';
 import { bindThis } from '@/decorators.js';
-import { CONTEXTS } from './misc/contexts.js';
+import { CONTEXT, PRELOADED_CONTEXTS } from './misc/contexts.js';
 import { validateContentTypeSetAsJsonLD } from './misc/validator.js';
 import type { JsonLdDocument } from 'jsonld';
-import type { JsonLd, RemoteDocument } from 'jsonld/jsonld-spec.js';
+import type { JsonLd as JsonLdObject, RemoteDocument } from 'jsonld/jsonld-spec.js';
 
-// RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017
+// RsaSignature2017 implementation is based on https://github.com/transmute-industries/RsaSignature2017
 
-class LdSignature {
+class JsonLd {
 	public debug = false;
 	public preLoad = true;
 	public loderTimeout = 5000;
@@ -89,10 +89,18 @@ class LdSignature {
 	}
 
 	@bindThis
-	public async normalize(data: JsonLdDocument): Promise<string> {
+	public async compact(data: any, context: any = CONTEXT): Promise<JsonLdDocument> {
 		const customLoader = this.getLoader();
 		// XXX: Importing jsonld dynamically since Jest frequently fails to import it statically
 		// https://github.com/misskey-dev/misskey/pull/9894#discussion_r1103753595
+		return (await import('jsonld')).default.compact(data, context, {
+			documentLoader: customLoader,
+		});
+	}
+
+	@bindThis
+	public async normalize(data: JsonLdDocument): Promise<string> {
+		const customLoader = this.getLoader();
 		return (await import('jsonld')).default.normalize(data, {
 			documentLoader: customLoader,
 		});
@@ -104,11 +112,11 @@ class LdSignature {
 			if (!/^https?:\/\//.test(url)) throw new Error(`Invalid URL ${url}`);
 
 			if (this.preLoad) {
-				if (url in CONTEXTS) {
+				if (url in PRELOADED_CONTEXTS) {
 					if (this.debug) console.debug(`HIT: ${url}`);
 					return {
 						contextUrl: undefined,
-						document: CONTEXTS[url],
+						document: PRELOADED_CONTEXTS[url],
 						documentUrl: url,
 					};
 				}
@@ -125,7 +133,7 @@ class LdSignature {
 	}
 
 	@bindThis
-	private async fetchDocument(url: string): Promise<JsonLd> {
+	private async fetchDocument(url: string): Promise<JsonLdObject> {
 		const json = await this.httpRequestService.send(
 			url,
 			{
@@ -146,7 +154,7 @@ class LdSignature {
 			}
 		});
 
-		return json as JsonLd;
+		return json as JsonLdObject;
 	}
 
 	@bindThis
@@ -158,14 +166,14 @@ class LdSignature {
 }
 
 @Injectable()
-export class LdSignatureService {
+export class JsonLdService {
 	constructor(
 		private httpRequestService: HttpRequestService,
 	) {
 	}
 
 	@bindThis
-	public use(): LdSignature {
-		return new LdSignature(this.httpRequestService);
+	public use(): JsonLd {
+		return new JsonLd(this.httpRequestService);
 	}
 }
diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts
index 88afdefcd3d7..feb8c42c563c 100644
--- a/packages/backend/src/core/activitypub/misc/contexts.ts
+++ b/packages/backend/src/core/activitypub/misc/contexts.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import type { JsonLd } from 'jsonld/jsonld-spec.js';
+import type { Context, JsonLd } from 'jsonld/jsonld-spec.js';
 
 /* eslint:disable:quotemark indent */
 const id_v1 = {
@@ -526,7 +526,42 @@ const activitystreams = {
 	},
 } satisfies JsonLd;
 
-export const CONTEXTS: Record<string, JsonLd> = {
+const context_iris = [
+	'https://www.w3.org/ns/activitystreams',
+	'https://w3id.org/security/v1',
+];
+
+const extension_context_definition = {
+	Key: 'sec:Key',
+	// as non-standards
+	manuallyApprovesFollowers: 'as:manuallyApprovesFollowers',
+	sensitive: 'as:sensitive',
+	Hashtag: 'as:Hashtag',
+	quoteUrl: 'as:quoteUrl',
+	// Mastodon
+	toot: 'http://joinmastodon.org/ns#',
+	Emoji: 'toot:Emoji',
+	featured: 'toot:featured',
+	discoverable: 'toot:discoverable',
+	// schema
+	schema: 'http://schema.org#',
+	PropertyValue: 'schema:PropertyValue',
+	value: 'schema:value',
+	// Misskey
+	misskey: 'https://misskey-hub.net/ns#',
+	'_misskey_content': 'misskey:_misskey_content',
+	'_misskey_quote': 'misskey:_misskey_quote',
+	'_misskey_reaction': 'misskey:_misskey_reaction',
+	'_misskey_votes': 'misskey:_misskey_votes',
+	'_misskey_summary': 'misskey:_misskey_summary',
+	'isCat': 'misskey:isCat',
+	// vcard
+	vcard: 'http://www.w3.org/2006/vcard/ns#',
+} satisfies Context;
+
+export const CONTEXT: (string | Context)[] = [...context_iris, extension_context_definition];
+
+export const PRELOADED_CONTEXTS: Record<string, JsonLd> = {
 	'https://w3id.org/identity/v1': id_v1,
 	'https://w3id.org/security/v1': security_v1,
 	'https://www.w3.org/ns/activitystreams': activitystreams,
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 3addead0587b..1d05f4ade10c 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -15,13 +15,14 @@ import InstanceChart from '@/core/chart/charts/instance.js';
 import ApRequestChart from '@/core/chart/charts/ap-request.js';
 import FederationChart from '@/core/chart/charts/federation.js';
 import { getApId } from '@/core/activitypub/type.js';
+import type { IActivity } from '@/core/activitypub/type.js';
 import type { MiRemoteUser } from '@/models/User.js';
 import type { MiUserPublickey } from '@/models/UserPublickey.js';
 import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
 import { StatusError } from '@/misc/status-error.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
-import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js';
+import { JsonLdService } from '@/core/activitypub/JsonLdService.js';
 import { ApInboxService } from '@/core/activitypub/ApInboxService.js';
 import { bindThis } from '@/decorators.js';
 import { IdentifiableError } from '@/misc/identifiable-error.js';
@@ -38,7 +39,7 @@ export class InboxProcessorService {
 		private apInboxService: ApInboxService,
 		private federatedInstanceService: FederatedInstanceService,
 		private fetchInstanceMetadataService: FetchInstanceMetadataService,
-		private ldSignatureService: LdSignatureService,
+		private jsonLdService: JsonLdService,
 		private apPersonService: ApPersonService,
 		private apDbResolverService: ApDbResolverService,
 		private instanceChart: InstanceChart,
@@ -52,7 +53,7 @@ export class InboxProcessorService {
 	@bindThis
 	public async process(job: Bull.Job<InboxJobData>): Promise<string> {
 		const signature = job.data.signature;	// HTTP-signature
-		const activity = job.data.activity;
+		let activity = job.data.activity;
 
 		//#region Log
 		const info = Object.assign({}, activity);
@@ -110,20 +111,21 @@ export class InboxProcessorService {
 		// また、signatureのsignerは、activity.actorと一致する必要がある
 		if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
 			// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
-			if (activity.signature) {
-				if (activity.signature.type !== 'RsaSignature2017') {
-					throw new Bull.UnrecoverableError(`skip: unsupported LD-signature type ${activity.signature.type}`);
+			const ldSignature = activity.signature;
+			if (ldSignature) {
+				if (ldSignature.type !== 'RsaSignature2017') {
+					throw new Bull.UnrecoverableError(`skip: unsupported LD-signature type ${ldSignature.type}`);
 				}
 
-				// activity.signature.creator: https://example.oom/users/user#main-key
+				// ldSignature.creator: https://example.oom/users/user#main-key
 				// みたいになっててUserを引っ張れば公開キーも入ることを期待する
-				if (activity.signature.creator) {
-					const candicate = activity.signature.creator.replace(/#.*/, '');
+				if (ldSignature.creator) {
+					const candicate = ldSignature.creator.replace(/#.*/, '');
 					await this.apPersonService.resolvePerson(candicate).catch(() => null);
 				}
 
 				// keyIdからLD-Signatureのユーザーを取得
-				authUser = await this.apDbResolverService.getAuthUserFromKeyId(activity.signature.creator);
+				authUser = await this.apDbResolverService.getAuthUserFromKeyId(ldSignature.creator);
 				if (authUser == null) {
 					throw new Bull.UnrecoverableError('skip: LD-Signatureのユーザーが取得できませんでした');
 				}
@@ -132,13 +134,31 @@ export class InboxProcessorService {
 					throw new Bull.UnrecoverableError('skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした');
 				}
 
+				const jsonLd = this.jsonLdService.use();
+
 				// LD-Signature検証
-				const ldSignature = this.ldSignatureService.use();
-				const verified = await ldSignature.verifyRsaSignature2017(activity, authUser.key.keyPem).catch(() => false);
+				const verified = await jsonLd.verifyRsaSignature2017(activity, authUser.key.keyPem).catch(() => false);
 				if (!verified) {
 					throw new Bull.UnrecoverableError('skip: LD-Signatureの検証に失敗しました');
 				}
 
+				// アクティビティを正規化
+				delete activity.signature;
+				try {
+					activity = await jsonLd.compact(activity) as IActivity;
+				} catch (e) {
+					throw new Bull.UnrecoverableError(`skip: failed to compact activity: ${e}`);
+				}
+				// TODO: 元のアクティビティと非互換な形に正規化される場合は転送をスキップする
+				// https://github.com/mastodon/mastodon/blob/664b0ca/app/services/activitypub/process_collection_service.rb#L24-L29
+				activity.signature = ldSignature;
+
+				//#region Log
+				const compactedInfo = Object.assign({}, activity);
+				delete compactedInfo['@context'];
+				this.logger.debug(`compacted: ${JSON.stringify(compactedInfo, null, 2)}`);
+				//#endregion
+
 				// もう一度actorチェック
 				if (authUser.user.uri !== activity.actor) {
 					throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`);
diff --git a/packages/backend/test/unit/activitypub.ts b/packages/backend/test/unit/activitypub.ts
index aa3f3a4ff1d9..696260810677 100644
--- a/packages/backend/test/unit/activitypub.ts
+++ b/packages/backend/test/unit/activitypub.ts
@@ -13,6 +13,8 @@ import { ApImageService } from '@/core/activitypub/models/ApImageService.js';
 import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
 import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
+import { JsonLdService } from '@/core/activitypub/JsonLdService.js';
+import { CONTEXT } from '@/core/activitypub/misc/contexts.js';
 import { GlobalModule } from '@/GlobalModule.js';
 import { CoreModule } from '@/core/CoreModule.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
@@ -88,6 +90,7 @@ describe('ActivityPub', () => {
 	let noteService: ApNoteService;
 	let personService: ApPersonService;
 	let rendererService: ApRendererService;
+	let jsonLdService: JsonLdService;
 	let resolver: MockResolver;
 
 	const metaInitial = {
@@ -128,6 +131,7 @@ describe('ActivityPub', () => {
 		personService = app.get<ApPersonService>(ApPersonService);
 		rendererService = app.get<ApRendererService>(ApRendererService);
 		imageService = app.get<ApImageService>(ApImageService);
+		jsonLdService = app.get<JsonLdService>(JsonLdService);
 		resolver = new MockResolver(await app.resolve<LoggerService>(LoggerService));
 
 		// Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error
@@ -381,4 +385,42 @@ describe('ActivityPub', () => {
 			assert.strictEqual(driveFile, null);
 		});
 	});
+
+	describe('JSON-LD', () =>{
+		test('Compaction', async () => {
+			const jsonLd = jsonLdService.use();
+
+			const object = {
+				'@context': [
+					'https://www.w3.org/ns/activitystreams',
+					{
+						_misskey_quote: 'https://misskey-hub.net/ns#_misskey_quote',
+						unknown: 'https://example.org/ns#unknown',
+						undefined: null,
+					},
+				],
+				id: 'https://example.com/notes/42',
+				type: 'Note',
+				attributedTo: 'https://example.com/users/1',
+				to: ['https://www.w3.org/ns/activitystreams#Public'],
+				content: 'test test foo',
+				_misskey_quote: 'https://example.com/notes/1',
+				unknown: 'test test bar',
+				undefined: 'test test baz',
+			};
+			const compacted = await jsonLd.compact(object);
+
+			assert.deepStrictEqual(compacted, {
+				'@context': CONTEXT,
+				id: 'https://example.com/notes/42',
+				type: 'Note',
+				attributedTo: 'https://example.com/users/1',
+				to: 'as:Public',
+				content: 'test test foo',
+				_misskey_quote: 'https://example.com/notes/1',
+				'https://example.org/ns#unknown': 'test test bar',
+				// undefined: 'test test baz',
+			});
+		});
+	});
 });

From 9c057e6854c22b4bc908485c08364a8a38091167 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Wed, 1 May 2024 16:39:16 +0900
Subject: [PATCH 182/266] fix(frontend): fix Storybook type errors (#13779)

* fix(frontend): fix Storybook type errors

* fix: `hasReduce` doesn't work in args
---
 packages/frontend/.storybook/fakes.ts         |   10 +-
 packages/frontend/.storybook/generate.tsx     |   26 +-
 packages/frontend/.storybook/main.ts          |    2 +-
 packages/frontend/.storybook/mocks.ts         |    3 +-
 packages/frontend/package.json                |   38 +-
 .../components/MkAccountMoved.stories.impl.ts |   15 +-
 .../MkAnnouncementDialog.stories.impl.ts      |   20 +-
 .../MkSignupDialog.rules.stories.impl.ts      |    6 +
 .../components/global/MkAd.stories.impl.ts    |   22 +-
 .../global/MkAvatar.stories.impl.ts           |    3 +-
 .../global/MkCondensedLine.stories.impl.ts    |    2 +
 .../components/global/MkError.stories.meta.ts |    7 +-
 .../global/MkPageHeader.stories.impl.ts       |    5 +-
 .../components/global/MkPageHeader.tabs.vue   |    1 -
 .../components/global/MkTime.stories.impl.ts  |   14 +-
 .../global/MkUserName.stories.impl.ts         |    2 +-
 pnpm-lock.yaml                                | 1510 +++++++++--------
 17 files changed, 953 insertions(+), 733 deletions(-)

diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index 48c9e0261d8e..3a24ccb248ed 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -27,7 +27,7 @@ export function galleryPost(isSensitive = false) {
 		id: 'somepostid',
 		createdAt: '2016-12-28T22:49:51.000Z',
 		updatedAt: '2016-12-28T22:49:51.000Z',
-		userid: 'someuserid',
+		userId: 'someuserid',
 		user: userDetailed(),
 		title: 'Some post title',
 		description: 'Some post description',
@@ -75,9 +75,8 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi
 		avatarUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
 		avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay',
 		avatarDecorations: [],
-		emojis: [],
+		emojis: {},
 		bannerBlurhash: 'eQA^IW^-MH8w9tE8I=S^o{$*R4RikXtSxutRozjEnNR.RQadoyozog',
-		bannerColor: '#000000',
 		bannerUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true',
 		birthday: '2014-06-20',
 		createdAt: '2016-12-28T22:49:51.000Z',
@@ -118,11 +117,16 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi
 		publicReactions: false,
 		securityKeys: false,
 		twoFactorEnabled: false,
+		usePasswordLessLogin: false,
 		twoFactorBackupCodesStock: 'none',
 		updatedAt: null,
+		lastFetchedAt: null,
 		uri: null,
 		url: null,
+		movedTo: null,
+		alsoKnownAs: null,
 		notify: 'none',
+		memo: null
 	};
 }
 
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index 1e925aede618..d74c83a50007 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -82,23 +82,16 @@ function h<T extends estree.Node>(
 	return Object.assign(props || {}, { type }) as T;
 }
 
-declare global {
-	namespace JSX {
-		type Element = estree.Node;
-		type ElementClass = never;
-		type ElementAttributesProperty = never;
-		type ElementChildrenAttribute = never;
-		type IntrinsicAttributes = never;
-		type IntrinsicClassAttributes<T> = never;
-		type IntrinsicElements = {
-			[T in keyof typeof generator as ToKebab<SplitCamel<Uncapitalize<T>>>]: {
-				[K in keyof Omit<
-					Parameters<(typeof generator)[T]>[0],
-					'type'
-				>]?: Parameters<(typeof generator)[T]>[0][K];
-			};
+declare namespace h.JSX {
+	type Element = estree.Node;
+	type IntrinsicElements = {
+		[T in keyof typeof generator as ToKebab<SplitCamel<Uncapitalize<T>>>]: {
+			[K in keyof Omit<
+				Parameters<(typeof generator)[T]>[0],
+				'type'
+			>]?: Parameters<(typeof generator)[T]>[0][K];
 		};
-	}
+	};
 }
 
 function toStories(component: string): Promise<string> {
@@ -388,6 +381,7 @@ function toStories(component: string): Promise<string> {
 		'/* eslint-disable @typescript-eslint/explicit-function-return-type */\n' +
 			'/* eslint-disable import/no-default-export */\n' +
 			'/* eslint-disable import/no-duplicates */\n' +
+			'/* eslint-disable import/order */\n' +
 			generate(program, { generator }) +
 			(hasImplStories ? readFileSync(`${implStories}.ts`, 'utf-8') : ''),
 		{
diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts
index 0a87488573ed..d3822942cd46 100644
--- a/packages/frontend/.storybook/main.ts
+++ b/packages/frontend/.storybook/main.ts
@@ -34,7 +34,7 @@ const config = {
 		disableTelemetry: true,
 	},
 	async viteFinal(config) {
-		const replacePluginForIsChromatic = config.plugins?.findIndex((plugin) => plugin && (plugin as Partial<Plugin>)?.name === 'replace') ?? -1;
+		const replacePluginForIsChromatic = config.plugins?.findIndex((plugin: Plugin) => plugin && plugin.name === 'replace') ?? -1;
 		if (~replacePluginForIsChromatic) {
 			config.plugins?.splice(replacePluginForIsChromatic, 1);
 		}
diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts
index 817b0125e7e9..29cb112ccbf5 100644
--- a/packages/frontend/.storybook/mocks.ts
+++ b/packages/frontend/.storybook/mocks.ts
@@ -6,7 +6,8 @@
 import { type SharedOptions, http, HttpResponse } from 'msw';
 
 export const onUnhandledRequest = ((req, print) => {
-	if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) {
+	const url = new URL(req.url);
+	if (url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(url.pathname)) {
 		return
 	}
 	print.warning()
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 95980ac0fc2c..43a7759fa60c 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -78,24 +78,24 @@
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@misskey-dev/summaly": "5.0.3",
-		"@storybook/addon-actions": "8.0.0-beta.6",
-		"@storybook/addon-essentials": "8.0.0-beta.6",
-		"@storybook/addon-interactions": "8.0.0-beta.6",
-		"@storybook/addon-links": "8.0.0-beta.6",
-		"@storybook/addon-mdx-gfm": "8.0.0-beta.6",
-		"@storybook/addon-storysource": "8.0.0-beta.6",
-		"@storybook/blocks": "8.0.0-beta.6",
-		"@storybook/components": "8.0.0-beta.6",
-		"@storybook/core-events": "8.0.0-beta.6",
-		"@storybook/manager-api": "8.0.0-beta.6",
-		"@storybook/preview-api": "8.0.0-beta.6",
-		"@storybook/react": "8.0.0-beta.6",
-		"@storybook/react-vite": "8.0.0-beta.6",
-		"@storybook/test": "8.0.0-beta.6",
-		"@storybook/theming": "8.0.0-beta.6",
-		"@storybook/types": "8.0.0-beta.6",
-		"@storybook/vue3": "8.0.0-beta.6",
-		"@storybook/vue3-vite": "8.0.0-beta.6",
+		"@storybook/addon-actions": "8.0.9",
+		"@storybook/addon-essentials": "8.0.9",
+		"@storybook/addon-interactions": "8.0.9",
+		"@storybook/addon-links": "8.0.9",
+		"@storybook/addon-mdx-gfm": "8.0.9",
+		"@storybook/addon-storysource": "8.0.9",
+		"@storybook/blocks": "8.0.9",
+		"@storybook/components": "8.0.9",
+		"@storybook/core-events": "8.0.9",
+		"@storybook/manager-api": "8.0.9",
+		"@storybook/preview-api": "8.0.9",
+		"@storybook/react": "8.0.9",
+		"@storybook/react-vite": "8.0.9",
+		"@storybook/test": "8.0.9",
+		"@storybook/theming": "8.0.9",
+		"@storybook/types": "8.0.9",
+		"@storybook/vue3": "8.0.9",
+		"@storybook/vue3-vite": "8.0.9",
 		"@testing-library/vue": "8.0.2",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
@@ -129,7 +129,7 @@
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
 		"start-server-and-test": "2.0.3",
-		"storybook": "8.0.0-beta.6",
+		"storybook": "8.0.9",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
 		"vite-plugin-turbosnap": "1.0.3",
 		"vitest": "0.34.6",
diff --git a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
index f1cfdc157a84..cad26de6e2a7 100644
--- a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
+++ b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
@@ -4,7 +4,10 @@
  */
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
 import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import { userDetailed } from '../../.storybook/fakes.js';
 import MkAccountMoved from './MkAccountMoved.vue';
 export const Default = {
@@ -29,10 +32,18 @@ export const Default = {
 		};
 	},
 	args: {
-		username: userDetailed().username,
-		host: userDetailed().host,
+		movedTo: userDetailed().id,
 	},
 	parameters: {
 		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/users/show', async ({ request }) => {
+					action('POST /api/users/show')(await request.json());
+					return HttpResponse.json(userDetailed());
+				}),
+			],
+		},
 	},
 } satisfies StoryObj<typeof MkAccountMoved>;
diff --git a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts
index ffa4e56f5f26..bf3ddb935b20 100644
--- a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts
+++ b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts
@@ -4,7 +4,10 @@
  */
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
 import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import MkAnnouncementDialog from './MkAnnouncementDialog.vue';
 export const Default = {
 	render(args) {
@@ -23,8 +26,13 @@ export const Default = {
 						...this.args,
 					};
 				},
+				events() {
+					return {
+						closed: action('closed'),
+					};
+				},
 			},
-			template: '<MkAnnouncementDialog v-bind="props" />',
+			template: '<MkAnnouncementDialog v-bind="props" v-on="events" />',
 		};
 	},
 	args: {
@@ -38,10 +46,20 @@ export const Default = {
 			imageUrl: null,
 			display: 'dialog',
 			needConfirmationToRead: false,
+			silence: false,
 			forYou: true,
 		},
 	},
 	parameters: {
 		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/i/read-announcement', async ({ request }) => {
+					action('POST /api/i/read-announcement')(await request.json());
+					return HttpResponse.json();
+				}),
+			],
+		},
 	},
 } satisfies StoryObj<typeof MkAnnouncementDialog>;
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
index fcd1ffde3e94..9df3ec0c30c3 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
+++ b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
@@ -51,13 +51,16 @@ export const Empty = {
 		expect(buttons.at(-1)).toBeEnabled();
 	},
 	args: {
+		// @ts-expect-error serverRules is for test
 		serverRules: [],
 		tosUrl: null,
 	},
 	decorators: [
 		(_, context) => ({
 			setup() {
+				// @ts-expect-error serverRules is for test
 				instance.serverRules = context.args.serverRules;
+				// @ts-expect-error tosUrl is for test
 				instance.tosUrl = context.args.tosUrl;
 				onBeforeUnmount(() => {
 					// FIXME: 呼び出されない
@@ -76,6 +79,7 @@ export const ServerRulesOnly = {
 	...Empty,
 	args: {
 		...Empty.args,
+		// @ts-expect-error serverRules is for test
 		serverRules: [
 			'ルール',
 		],
@@ -85,6 +89,7 @@ export const TOSOnly = {
 	...Empty,
 	args: {
 		...Empty.args,
+		// @ts-expect-error tosUrl is for test
 		tosUrl: 'https://example.com/tos',
 	},
 } satisfies StoryObj<typeof MkSignupServerRules>;
@@ -92,6 +97,7 @@ export const ServerRulesAndTOS = {
 	...Empty,
 	args: {
 		...Empty.args,
+		// @ts-expect-error serverRules is for test
 		serverRules: ServerRulesOnly.args.serverRules,
 		tosUrl: TOSOnly.args.tosUrl,
 	},
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index f6cdc2bf23d4..a1d274382fa3 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -4,8 +4,10 @@
  */
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { expect, userEvent, waitFor, within } from '@storybook/test';
 import { StoryObj } from '@storybook/vue3';
 import MkAd from './MkAd.vue';
+import { i18n } from '@/i18n.js';
 
 let lock: Promise<undefined> | undefined;
 
@@ -30,7 +32,6 @@ const common = {
 			template: '<MkAd v-bind="props" />',
 		};
 	},
-	/* FIXME: disabled because it still didn’t pass after applying #11267
 	async play({ canvasElement, args }) {
 		if (lock) {
 			console.warn('This test is unexpectedly running twice in parallel, fix it!');
@@ -44,7 +45,7 @@ const common = {
 		try {
 			const canvas = within(canvasElement);
 			const a = canvas.getByRole<HTMLAnchorElement>('link');
-			await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
+			// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
 			const img = within(a).getByRole('img');
 			await expect(img).toBeInTheDocument();
 			let buttons = canvas.getAllByRole<HTMLButtonElement>('button');
@@ -52,13 +53,14 @@ const common = {
 			const i = buttons[0];
 			await expect(i).toBeInTheDocument();
 			await userEvent.click(i);
-			await waitFor(() => expect(canvasElement).toHaveTextContent(i18n.ts._ad.back));
+			// await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
 			await expect(a).not.toBeInTheDocument();
 			await expect(i).not.toBeInTheDocument();
 			buttons = canvas.getAllByRole<HTMLButtonElement>('button');
-			await expect(buttons).toHaveLength(args.__hasReduce ? 2 : 1);
-			const reduce = args.__hasReduce ? buttons[0] : null;
-			const back = buttons[args.__hasReduce ? 1 : 0];
+			const hasReduceFrequency = args.specify?.ratio !== 0;
+			await expect(buttons).toHaveLength(hasReduceFrequency ? 2 : 1);
+			const reduce = hasReduceFrequency ? buttons[0] : null;
+			const back = buttons[hasReduceFrequency ? 1 : 0];
 			if (reduce) {
 				await expect(reduce).toBeInTheDocument();
 				await expect(reduce).toHaveTextContent(i18n.ts._ad.reduceFrequencyOfThisAd);
@@ -80,15 +82,16 @@ const common = {
 			lock = undefined;
 		}
 	},
-	 */
 	args: {
 		prefer: [],
 		specify: {
 			id: 'someadid',
-			radio: 1,
+			ratio: 1,
 			url: '#test',
+			place: '',
+			imageUrl: '',
+			dayOfWeek: 7,
 		},
-		__hasReduce: true,
 	},
 	parameters: {
 		layout: 'centered',
@@ -138,6 +141,5 @@ export const ZeroRatio = {
 			...Square.args.specify,
 			ratio: 0,
 		},
-		__hasReduce: false,
 	},
 } satisfies StoryObj<typeof MkAd>;
diff --git a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
index 933754ec4c46..9d2de9f0be81 100644
--- a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
@@ -33,7 +33,7 @@ const common = {
 	},
 	decorators: [
 		(Story, context) => ({
-			// eslint-disable-next-line quotes
+			// @ts-expect-error size is for test
 			template: `<div :style="{ display: 'grid', width: '${context.args.size}px', height: '${context.args.size}px' }"><story/></div>`,
 		}),
 	],
@@ -45,6 +45,7 @@ export const ProfilePage = {
 	...common,
 	args: {
 		...common.args,
+		// @ts-expect-error size is for test
 		size: 120,
 		indicator: true,
 	},
diff --git a/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts b/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts
index e4e90cddd521..e15dcba76022 100644
--- a/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkCondensedLine.stories.impl.ts
@@ -28,6 +28,7 @@ export const Default = {
 		};
 	},
 	args: {
+		// @ts-expect-error text is for test
 		text: 'This is a condensed line.',
 	},
 	parameters: {
@@ -41,4 +42,5 @@ export const ContainerIs100px = {
 			template: '<div style="width: 100px;"><story/></div>',
 		}),
 	],
+	// @ts-expect-error text is for test
 } satisfies StoryObj<typeof MkCondensedLine>;
diff --git a/packages/frontend/src/components/global/MkError.stories.meta.ts b/packages/frontend/src/components/global/MkError.stories.meta.ts
index 1abbc56f509d..cd7fada18911 100644
--- a/packages/frontend/src/components/global/MkError.stories.meta.ts
+++ b/packages/frontend/src/components/global/MkError.stories.meta.ts
@@ -3,8 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { Meta } from '@storybook/vue3';
+import MkError from './MkError.vue';
+
 export const argTypes = {
-	retry: {
+	onRetry: {
 		action: 'retry',
 	},
-};
+} satisfies Meta<typeof MkError>['argTypes'];
diff --git a/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts
index eb74e874dd0e..1d079edd2c57 100644
--- a/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkPageHeader.stories.impl.ts
@@ -33,7 +33,6 @@ export const Empty = {
 		await waitFor(async () => await wait);
 	},
 	args: {
-		static: true,
 		tabs: [],
 	},
 	parameters: {
@@ -71,8 +70,8 @@ export const IconOnly = {
 		...Icon.args,
 		tabs: [
 			{
-				...Icon.args.tabs[0],
-				title: undefined,
+				key: Icon.args.tabs[0].key,
+				icon: Icon.args.tabs[0].icon,
 				iconOnly: true,
 			},
 		],
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index e93b09721ab2..fcc46cc34569 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -38,7 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts">
 export type Tab = {
 	key: string;
-	title: string;
 	onClick?: (ev: MouseEvent) => void;
 } & (
 	| {
diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts
index 355c83911399..ffd4a849a2ed 100644
--- a/packages/frontend/src/components/global/MkTime.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts
@@ -60,7 +60,7 @@ export const RelativeFuture = {
 export const AbsoluteFuture = {
 	...Empty,
 	async play({ canvasElement, args }) {
-		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time));
+		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(typeof args.time === 'string' ? new Date(args.time) : args.time ?? undefined));
 	},
 	args: {
 		...Empty.args,
@@ -97,7 +97,7 @@ export const RelativeNow = {
 export const AbsoluteNow = {
 	...Empty,
 	async play({ canvasElement, args }) {
-		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time));
+		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(typeof args.time === 'string' ? new Date(args.time) : args.time ?? undefined));
 	},
 	args: {
 		...Empty.args,
@@ -136,7 +136,7 @@ export const RelativeOneHourAgo = {
 export const AbsoluteOneHourAgo = {
 	...Empty,
 	async play({ canvasElement, args }) {
-		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time));
+		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(typeof args.time === 'string' ? new Date(args.time) : args.time ?? undefined));
 	},
 	args: {
 		...Empty.args,
@@ -175,7 +175,7 @@ export const RelativeOneDayAgo = {
 export const AbsoluteOneDayAgo = {
 	...Empty,
 	async play({ canvasElement, args }) {
-		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time));
+		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(typeof args.time === 'string' ? new Date(args.time) : args.time ?? undefined));
 	},
 	args: {
 		...Empty.args,
@@ -214,7 +214,7 @@ export const RelativeOneWeekAgo = {
 export const AbsoluteOneWeekAgo = {
 	...Empty,
 	async play({ canvasElement, args }) {
-		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time));
+		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(typeof args.time === 'string' ? new Date(args.time) : args.time ?? undefined));
 	},
 	args: {
 		...Empty.args,
@@ -253,7 +253,7 @@ export const RelativeOneMonthAgo = {
 export const AbsoluteOneMonthAgo = {
 	...Empty,
 	async play({ canvasElement, args }) {
-		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time));
+		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(typeof args.time === 'string' ? new Date(args.time) : args.time ?? undefined));
 	},
 	args: {
 		...Empty.args,
@@ -292,7 +292,7 @@ export const RelativeOneYearAgo = {
 export const AbsoluteOneYearAgo = {
 	...Empty,
 	async play({ canvasElement, args }) {
-		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(args.time));
+		await expect(canvasElement).toHaveTextContent(dateTimeFormat.format(typeof args.time === 'string' ? new Date(args.time) : args.time ?? undefined));
 	},
 	args: {
 		...Empty.args,
diff --git a/packages/frontend/src/components/global/MkUserName.stories.impl.ts b/packages/frontend/src/components/global/MkUserName.stories.impl.ts
index 88bf4f4e6c1d..e39061c291f5 100644
--- a/packages/frontend/src/components/global/MkUserName.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkUserName.stories.impl.ts
@@ -30,7 +30,7 @@ export const Default = {
 		};
 	},
 	async play({ canvasElement }) {
-		await expect(canvasElement).toHaveTextContent(userDetailed().name);
+		await expect(canvasElement).toHaveTextContent(userDetailed().name as string);
 	},
 	args: {
 		user: userDetailed(),
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c7625fd89f44..8e5cc2d6999b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -864,59 +864,59 @@ importers:
         specifier: 5.0.3
         version: 5.0.3
       '@storybook/addon-actions':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6
+        specifier: 8.0.9
+        version: 8.0.9
       '@storybook/addon-essentials':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.9
+        version: 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-interactions':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6
+        specifier: 8.0.9
+        version: 8.0.9(vitest@0.34.6)
       '@storybook/addon-links':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(react@18.2.0)
+        specifier: 8.0.9
+        version: 8.0.9(react@18.2.0)
       '@storybook/addon-mdx-gfm':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6
+        specifier: 8.0.9
+        version: 8.0.9
       '@storybook/addon-storysource':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6
+        specifier: 8.0.9
+        version: 8.0.9
       '@storybook/blocks':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.9
+        version: 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       '@storybook/components':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.9
+        version: 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6
+        specifier: 8.0.9
+        version: 8.0.9
       '@storybook/manager-api':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.9
+        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6
+        specifier: 8.0.9
+        version: 8.0.9
       '@storybook/react':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+        specifier: 8.0.9
+        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       '@storybook/react-vite':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4)
+        specifier: 8.0.9
+        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4)
       '@storybook/test':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(vitest@0.34.6)
+        specifier: 8.0.9
+        version: 8.0.9(vitest@0.34.6)
       '@storybook/theming':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.9
+        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)
       '@storybook/types':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6
+        specifier: 8.0.9
+        version: 8.0.9
       '@storybook/vue3':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(vue@3.4.21)
+        specifier: 8.0.9
+        version: 8.0.9(vue@3.4.21)
       '@storybook/vue3-vite':
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21)
+        specifier: 8.0.9
+        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21)
       '@testing-library/vue':
         specifier: 8.0.2
         version: 8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21)
@@ -1017,11 +1017,11 @@ importers:
         specifier: 2.0.3
         version: 2.0.3
       storybook:
-        specifier: 8.0.0-beta.6
-        version: 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+        specifier: 8.0.9
+        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
-        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9)(@storybook/components@8.0.9)(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9)(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9)(@storybook/types@8.0.9)(react-dom@18.2.0)(react@18.2.0)
       vite-plugin-turbosnap:
         specifier: 1.0.3
         version: 1.0.3
@@ -1964,14 +1964,14 @@ packages:
     resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.5
+      '@babel/types': 7.24.0
     dev: true
 
   /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15:
     resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.5
+      '@babel/types': 7.24.0
     dev: true
 
   /@babel/helper-compilation-targets@7.22.15:
@@ -1991,48 +1991,48 @@ packages:
     dependencies:
       '@babel/compat-data': 7.23.5
       '@babel/helper-validator-option': 7.23.5
-      browserslist: 4.22.2
+      browserslist: 4.23.0
       lru-cache: 5.1.1
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5):
+  /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.24.0):
     resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-function-name': 7.23.0
       '@babel/helper-member-expression-to-functions': 7.23.0
       '@babel/helper-optimise-call-expression': 7.22.5
-      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5)
+      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.5):
+  /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.0):
     resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
       regexpu-core: 5.3.2
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.5):
+  /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/core': 7.24.0
+      '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-plugin-utils': 7.22.5
       debug: 4.3.4(supports-color@8.1.1)
       lodash.debounce: 4.0.8
@@ -2065,7 +2065,7 @@ packages:
     resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.5
+      '@babel/types': 7.24.0
     dev: true
 
   /@babel/helper-module-imports@7.22.15:
@@ -2107,7 +2107,7 @@ packages:
     resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.5
+      '@babel/types': 7.24.0
     dev: true
 
   /@babel/helper-plugin-utils@7.22.5:
@@ -2115,25 +2115,25 @@ packages:
     engines: {node: '>=6.9.0'}
     dev: true
 
-  /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.5):
+  /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.0):
     resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-wrap-function': 7.22.20
     dev: true
 
-  /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.5):
+  /@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0):
     resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-member-expression-to-functions': 7.23.0
       '@babel/helper-optimise-call-expression': 7.22.5
@@ -2150,7 +2150,7 @@ packages:
     resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.5
+      '@babel/types': 7.24.0
     dev: true
 
   /@babel/helper-split-export-declaration@7.22.6:
@@ -2178,8 +2178,8 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/helper-function-name': 7.23.0
-      '@babel/template': 7.22.15
-      '@babel/types': 7.23.5
+      '@babel/template': 7.24.0
+      '@babel/types': 7.24.0
     dev: true
 
   /@babel/helpers@7.23.5:
@@ -2228,46 +2228,46 @@ packages:
       '@babel/types': 7.24.0
     dev: true
 
-  /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.13.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.5):
+  /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0):
     resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
     dev: true
 
   /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5):
@@ -2279,6 +2279,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0):
+    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
     peerDependencies:
@@ -2297,61 +2306,70 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.5):
+  /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0):
+    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0):
     resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.5):
+  /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.5):
+  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -2364,6 +2382,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.0):
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
     peerDependencies:
@@ -2373,6 +2400,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
     engines: {node: '>=6.9.0'}
@@ -2383,6 +2419,16 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.5):
     resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
     peerDependencies:
@@ -2392,6 +2438,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0):
+    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
     peerDependencies:
@@ -2401,6 +2456,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5):
     resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
     peerDependencies:
@@ -2410,6 +2474,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0):
+    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
     peerDependencies:
@@ -2419,6 +2492,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
     peerDependencies:
@@ -2428,6 +2510,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
     peerDependencies:
@@ -2437,13 +2528,22 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.5):
+  /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0):
     resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -2457,6 +2557,16 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0):
+    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
     engines: {node: '>=6.9.0'}
@@ -2467,708 +2577,718 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.5):
+  /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0):
+    resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.24.0
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.0):
     resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.5)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.5)
+      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-module-imports': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.5)
+      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.12.0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.5)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-classes@7.23.5(@babel/core@7.23.5):
+  /@babel/plugin-transform-classes@7.23.5(@babel/core@7.24.0):
     resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-function-name': 7.23.0
       '@babel/helper-optimise-call-expression': 7.22.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5)
+      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
       '@babel/helper-split-export-declaration': 7.22.6
       globals: 11.12.0
     dev: true
 
-  /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/template': 7.22.15
+      '@babel/template': 7.24.0
     dev: true
 
-  /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/core': 7.24.0
+      '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-function-name': 7.23.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-literals@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.5)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-simple-access': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-hoist-variables': 7.22.5
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-validator-identifier': 7.22.20
     dev: true
 
-  /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.5):
+  /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.0):
     resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.5)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
       '@babel/compat-data': 7.23.5
-      '@babel/core': 7.23.5
-      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/core': 7.24.0
+      '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5)
+      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.5):
+  /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.24.0):
     resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.5)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       regenerator-transform: 0.15.2
     dev: true
 
-  /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-spread@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-typescript@7.23.5(@babel/core@7.23.5):
+  /@babel/plugin-transform-typescript@7.23.5(@babel/core@7.24.0):
     resolution: {integrity: sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.5):
+  /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/preset-env@7.23.5(@babel/core@7.23.5):
+  /@babel/preset-env@7.23.5(@babel/core@7.24.0):
     resolution: {integrity: sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
       '@babel/compat-data': 7.23.5
-      '@babel/core': 7.23.5
-      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/core': 7.24.0
+      '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-validator-option': 7.23.5
-      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.5)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.5)
-      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.5)
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.5)
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.5)
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.5)
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.5)
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5)
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.5)
-      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5)
-      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.5)
-      '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.23.5)
-      '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.5)
-      '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.5)
-      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.5)
-      babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.5)
-      babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.23.5)
-      babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.5)
+      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.0)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.0)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0)
+      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.0)
+      '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.24.0)
+      '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.24.0)
+      '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.24.0)
+      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.0)
+      babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.24.0)
+      babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.24.0)
+      babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.24.0)
       core-js-compat: 3.33.3
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@babel/preset-flow@7.23.3(@babel/core@7.23.5):
+  /@babel/preset-flow@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-validator-option': 7.23.5
-      '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.24.0)
     dev: true
 
-  /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.5):
+  /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.0):
     resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
     peerDependencies:
       '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/types': 7.23.5
+      '@babel/types': 7.24.0
       esutils: 2.0.3
     dev: true
 
-  /@babel/preset-typescript@7.23.3(@babel/core@7.23.5):
+  /@babel/preset-typescript@7.23.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-validator-option': 7.23.5
-      '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.23.5)
+      '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.24.0)
     dev: true
 
-  /@babel/register@7.22.15(@babel/core@7.23.5):
+  /@babel/register@7.22.15(@babel/core@7.24.0):
     resolution: {integrity: sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       clone-deep: 4.0.1
       find-cache-dir: 2.1.0
       make-dir: 2.1.0
@@ -4557,17 +4677,6 @@ packages:
       - supports-color
     dev: true
 
-  /@jest/types@27.5.1:
-    resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==}
-    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
-    dependencies:
-      '@types/istanbul-lib-coverage': 2.0.4
-      '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.11.22
-      '@types/yargs': 16.0.5
-      chalk: 4.1.2
-    dev: true
-
   /@jest/types@29.6.3:
     resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -5878,10 +5987,10 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@storybook/addon-actions@8.0.0-beta.6:
-    resolution: {integrity: sha512-g+X2M6Awg21vkXzRP7hWBYCdbXnxJ3BJWsP7BblYmPo2J7eJDzhQascNyTmSr0pb1/7nv+tworGviXThgvlUgw==}
+  /@storybook/addon-actions@8.0.9:
+    resolution: {integrity: sha512-+I3VTvlKdj8puHeS2tyaOVv9syDiNLneVZbTfqN+UDOK2i42NwvZr8PVwjTzMlEj9eePJdCZgiipz55xwts5bw==}
     dependencies:
-      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.9
       '@storybook/global': 5.0.0
       '@types/uuid': 9.0.8
       dequal: 2.0.3
@@ -5889,18 +5998,18 @@ packages:
       uuid: 9.0.1
     dev: true
 
-  /@storybook/addon-backgrounds@8.0.0-beta.6:
-    resolution: {integrity: sha512-C8MS635knAOSat5JbkpZXOiAqkDm1bKWvuVqiQfbX2into45/aAuyN3mYxveGIRTRjPJCv/UpostkLSNvfH/NQ==}
+  /@storybook/addon-backgrounds@8.0.9:
+    resolution: {integrity: sha512-pCDecACrVyxPaJKEWS0sHsRb8xw+IPCSxDM1TkjaAQ6zZ468A/dcUnqW+LVK8bSXgQwWzn23wqnqPFSy5yptuQ==}
     dependencies:
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-controls@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-G96MH7yU/KShq3lTrkgtU1IbNQXLVc3BG7miaLqzQgWFN8SSAivlu3vk1Vffui3+3Dv52WZhMKi3hueNfnM1Xw==}
+  /@storybook/addon-controls@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-wWdmd62UP/sfPm8M7aJjEA+kEXTUIR/QsYi9PoYBhBZcXiikZ4kNan7oD7GfsnzGGKHrBVfwQhO+TqaENGYytA==}
     dependencies:
-      '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
       lodash: 4.17.21
       ts-dedent: 2.2.0
     transitivePeerDependencies:
@@ -5911,22 +6020,22 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-docs@8.0.0-beta.6:
-    resolution: {integrity: sha512-VLys4EuL8XVhmu1QxUiUG5keID8v/FsC5L71Y0Wcf5D+ll6ZD8vCqEtbMY3TiJJ9NqqNIcmcG3bG6JVXOYcD8g==}
+  /@storybook/addon-docs@8.0.9:
+    resolution: {integrity: sha512-x7hX7UuzJtClu6XwU3SfpyFhuckVcgqgD6BU6Ihxl0zs+i4xp6iKVXYSnHFMRM1sgoeT8TjPxab35Ke8w8BVRw==}
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
       '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.2.0)
-      '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-plugin': 8.0.0-beta.6
-      '@storybook/csf-tools': 8.0.0-beta.6
+      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 8.0.9
+      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-plugin': 8.0.9
+      '@storybook/csf-tools': 8.0.9
       '@storybook/global': 5.0.0
-      '@storybook/node-logger': 8.0.0-beta.6
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/node-logger': 8.0.9
+      '@storybook/preview-api': 8.0.9
+      '@storybook/react-dom-shim': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.9
       '@types/react': 18.0.28
       fs-extra: 11.1.1
       react: 18.2.0
@@ -5939,22 +6048,22 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-essentials@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-6Vjf03c0oIavXqOK9DIN0UeH0iJFmBoVrFt1mTwydMxchyJBSP785MSd9DuFhLdYZPQTMHaR4/JhOIjdDV8mbA==}
-    dependencies:
-      '@storybook/addon-actions': 8.0.0-beta.6
-      '@storybook/addon-backgrounds': 8.0.0-beta.6
-      '@storybook/addon-controls': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-docs': 8.0.0-beta.6
-      '@storybook/addon-highlight': 8.0.0-beta.6
-      '@storybook/addon-measure': 8.0.0-beta.6
-      '@storybook/addon-outline': 8.0.0-beta.6
-      '@storybook/addon-toolbars': 8.0.0-beta.6
-      '@storybook/addon-viewport': 8.0.0-beta.6
-      '@storybook/core-common': 8.0.0-beta.6
-      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 8.0.0-beta.6
-      '@storybook/preview-api': 8.0.0-beta.6
+  /@storybook/addon-essentials@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-mwAgdfrOsTuTDcagvM7veBh+iayZIWmKOazzkhrIWbhYcrXOsweigD2UOVeHgAiAzJK49znr4FXTCKcE1hOWcw==}
+    dependencies:
+      '@storybook/addon-actions': 8.0.9
+      '@storybook/addon-backgrounds': 8.0.9
+      '@storybook/addon-controls': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-docs': 8.0.9
+      '@storybook/addon-highlight': 8.0.9
+      '@storybook/addon-measure': 8.0.9
+      '@storybook/addon-outline': 8.0.9
+      '@storybook/addon-toolbars': 8.0.9
+      '@storybook/addon-viewport': 8.0.9
+      '@storybook/core-common': 8.0.9
+      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 8.0.9
+      '@storybook/preview-api': 8.0.9
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@types/react'
@@ -5964,80 +6073,87 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-highlight@8.0.0-beta.6:
-    resolution: {integrity: sha512-U+qz4TNLrw24t1eZ2Zmhl2FZKZKiwHbibq4qR5ruAFe9W5/aMHqPuBB0POroaGu3P+tyDP2G46dckMNXVraiWA==}
+  /@storybook/addon-highlight@8.0.9:
+    resolution: {integrity: sha512-vaRHGDbx7dpNpQECAHk5wczlZO3ntstprGlqnZt0o7ylz6xB5+pTQwTuIFty0hwKv+3TPcskzzifATUyEOEmyg==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/addon-interactions@8.0.0-beta.6:
-    resolution: {integrity: sha512-KSigq+7vCA1tnj31MjhM7xaqickR1guZdjyXVRx7gi7qbdhSuCQv52gAkVpDapwlEuvGFCCYxzt7tmcn6dkLZQ==}
+  /@storybook/addon-interactions@8.0.9(vitest@0.34.6):
+    resolution: {integrity: sha512-AMIdNcyM6DDAWvMitBJMqp1iPZND8AXB4QT4VZHGMKG2ngHNKktriEKpTfcRkfKPGTJs9T+71dWfm6/R4tticw==}
     dependencies:
       '@storybook/global': 5.0.0
-      '@storybook/types': 8.0.0-beta.6
-      jest-mock: 27.5.1
+      '@storybook/instrumenter': 8.0.9
+      '@storybook/test': 8.0.9(vitest@0.34.6)
+      '@storybook/types': 8.0.9
       polished: 4.2.2
       ts-dedent: 2.2.0
+    transitivePeerDependencies:
+      - '@jest/globals'
+      - '@types/bun'
+      - '@types/jest'
+      - jest
+      - vitest
     dev: true
 
-  /@storybook/addon-links@8.0.0-beta.6(react@18.2.0):
-    resolution: {integrity: sha512-+5knw5CHEb23n6Bm9Xp9nmoLRqWZ3QVGb1gNI3mGwmkpLwesohFR4fW7OrdRmzYHpS0PyYToZyfTCMYrmjBDvg==}
+  /@storybook/addon-links@8.0.9(react@18.2.0):
+    resolution: {integrity: sha512-FVt+AdW3JFSqbJzkKiqKsMRWqHXqEvCBqFs7lNfk3OW0w0jfv1iREtrxE0dVdJoUFQC9V/2Im/EpJ7UB3C2bNQ==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
     peerDependenciesMeta:
       react:
         optional: true
     dependencies:
-      '@storybook/csf': 0.1.2
+      '@storybook/csf': 0.1.6
       '@storybook/global': 5.0.0
       react: 18.2.0
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-mdx-gfm@8.0.0-beta.6:
-    resolution: {integrity: sha512-b4pb59rrX+C/oYFeEiHb8jJn0h9WZSkHVkLIgaj0G64Nd9OpyKZXMbGpDxwMq4LTi1w65Wddi1UUQbUVVDNHRw==}
+  /@storybook/addon-mdx-gfm@8.0.9:
+    resolution: {integrity: sha512-AoEx+OGKANtVZgKyWKrQhGpMpDuc2S7PnOlNLUiDYzmj8ABAGPmEJmqeb/VHVgqLQSjhOW1fMsQ4fYsecvMxTQ==}
     dependencies:
-      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/node-logger': 8.0.9
       remark-gfm: 4.0.0
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/addon-measure@8.0.0-beta.6:
-    resolution: {integrity: sha512-D+KzWRULcbwR8/ysD7Qbw4uWBn9gwNm9s3IeVuhupawUb3u+H4XfVCOW2rA5qry/x8aroKOhAmyKd9v4i+l3pg==}
+  /@storybook/addon-measure@8.0.9:
+    resolution: {integrity: sha512-91svOOGEXmGG4USglwXLE3wtlUVgtbKJVxTKX7xRI+AC5JEEaKByVzP17/X8Qn/8HilUL7AfSQ0kCoqtPSJ5cA==}
     dependencies:
       '@storybook/global': 5.0.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-outline@8.0.0-beta.6:
-    resolution: {integrity: sha512-U+5TFTj+gtkIiIJCk6h7zbrP588CUipzVVsiDTSLl4pc+H3ylGTGncq3ZGtOyl+DCoBsQCgKxy2YWQtKHrESOw==}
+  /@storybook/addon-outline@8.0.9:
+    resolution: {integrity: sha512-fQ+jm356TgUnz81IxsC99/aOesbLw3N5OQRJpo/A6kqbLMzlq3ybVzuXYCKC3f0ArgQRNh4NoMeJBMRFMtaWRw==}
     dependencies:
       '@storybook/global': 5.0.0
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-storysource@8.0.0-beta.6:
-    resolution: {integrity: sha512-J9sCZ5/KQW2hbfKsom8LmgSWJxw+Kp/7LjIHGevFfov/i9DR8i9xbh5htUwC9fx+vWGR87tez03b+oUJbyHPog==}
+  /@storybook/addon-storysource@8.0.9:
+    resolution: {integrity: sha512-5m3K2Rs4fQtKtqwrq4CDS1jK2wzWOlnxhE2ArX5XTWytb1am65CEPxfYTEQkvZH9oPGwX3cXytPCziynqysFMQ==}
     dependencies:
-      '@storybook/source-loader': 8.0.0-beta.6
+      '@storybook/source-loader': 8.0.9
       estraverse: 5.3.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-toolbars@8.0.0-beta.6:
-    resolution: {integrity: sha512-ClT5spwh6S1rUvyFEIFQndE3VK6tpwI2cyIW4E20LajtfUmj3dOfJQX/ZbnhEH3sDBsCm97ysZ/mNR0mbBHZrg==}
+  /@storybook/addon-toolbars@8.0.9:
+    resolution: {integrity: sha512-nNSBnnBOhQ+EJwkrIkK4ZBYPcozNmEH770CZ/6NK85SUJ6WEBZapE6ru33jIUokFGEvlOlNCeai0GUc++cQP8w==}
     dev: true
 
-  /@storybook/addon-viewport@8.0.0-beta.6:
-    resolution: {integrity: sha512-KNYGM6nVrz/Ej25W3lcpaxxJDYVXBYeGl60FWN/WlqRnjo4c4Fyufl6Xev2plQ3eI8jIvWEdGNC/Z/NQnDx1+Q==}
+  /@storybook/addon-viewport@8.0.9:
+    resolution: {integrity: sha512-Ao4+D56cO7biaw+iTlMU1FBec1idX0cmdosDeCFZin06MSawcPkeBlRBeruaSQYdLes8TBMdZPFgfuqI5yIk6g==}
     dependencies:
       memoizerific: 1.11.3
     dev: true
 
-  /@storybook/blocks@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-QkrWT0BELNv3UGv/dtNuB/ROZn0f9VpERbadhXLE/oNXMJLalyjEbRGM635l0lDeoqjYnWHl+tuM6DTe1Xpk2w==}
+  /@storybook/blocks@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-F2zSrfSwzTFN7qW3zB80tG+EXtmfmCDC6Ird0F7tolszb6tOqJcAcBOwQbE2O0wI63sLu21qxzXgaKBMkiWvJg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6047,18 +6163,18 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/channels': 8.0.0-beta.6
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/csf': 0.1.2
-      '@storybook/docs-tools': 8.0.0-beta.6
+      '@storybook/channels': 8.0.9
+      '@storybook/client-logger': 8.0.9
+      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 8.0.9
+      '@storybook/csf': 0.1.6
+      '@storybook/docs-tools': 8.0.9
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 8.0.9
+      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.9
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
@@ -6079,18 +6195,18 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-manager@8.0.0-beta.6:
-    resolution: {integrity: sha512-bB/gSsPIpU22Tc6YTjPZdw1RM6nrsuJJ9aYXGqEJTqA4l4lBUN7fwIZQ1x/pS+5LbeUO0J9lAhGXurS+m8rI2A==}
+  /@storybook/builder-manager@8.0.9:
+    resolution: {integrity: sha512-/PxDwZIfMc/PSRZcasb6SIdGr3azIlenzx7dBF7Imt8i4jLHiAf1t00GvghlfJsvsrn4DNp95rbRbXTDyTj7tQ==}
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 8.0.0-beta.6
-      '@storybook/manager': 8.0.0-beta.6
-      '@storybook/node-logger': 8.0.0-beta.6
+      '@storybook/core-common': 8.0.9
+      '@storybook/manager': 8.0.9
+      '@storybook/node-logger': 8.0.9
       '@types/ejs': 3.1.2
-      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.20)
+      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.19.11)
       browser-assert: 1.2.1
       ejs: 3.1.9
-      esbuild: 0.18.20
+      esbuild: 0.19.11
       esbuild-plugin-alias: 0.2.1
       express: 4.18.2
       fs-extra: 11.1.1
@@ -6101,8 +6217,8 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4):
-    resolution: {integrity: sha512-3P5uTZqwwcUW64Hep/VtJXpQYi5vTkmqAjwZvr8gmzr37NYq3YT/PiSGn4CaZswSx5Z/lSYq3In8oIwmj/a1/g==}
+  /@storybook/builder-vite@8.0.9(typescript@5.3.3)(vite@5.1.4):
+    resolution: {integrity: sha512-7hEQFZIIz7VvxdySDpPE96iMvZxQvRZcRdhaNGeE+8Y2pyc3DgYE4WY3sjr+LUoB0a6TYLpAIKqbXwtLz0R+PQ==}
     peerDependencies:
       '@preact/preset-vite': '*'
       typescript: '>= 4.3.x'
@@ -6116,15 +6232,15 @@ packages:
       vite-plugin-glimmerx:
         optional: true
     dependencies:
-      '@storybook/channels': 8.0.0-beta.6
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/core-common': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/csf-plugin': 8.0.0-beta.6
-      '@storybook/node-logger': 8.0.0-beta.6
-      '@storybook/preview': 8.0.0-beta.6
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/channels': 8.0.9
+      '@storybook/client-logger': 8.0.9
+      '@storybook/core-common': 8.0.9
+      '@storybook/core-events': 8.0.9
+      '@storybook/csf-plugin': 8.0.9
+      '@storybook/node-logger': 8.0.9
+      '@storybook/preview': 8.0.9
+      '@storybook/preview-api': 8.0.9
+      '@storybook/types': 8.0.9
       '@types/find-cache-dir': 3.2.1
       browser-assert: 1.2.1
       es-module-lexer: 0.9.3
@@ -6140,32 +6256,31 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/channels@8.0.0-beta.6:
-    resolution: {integrity: sha512-DjwJhty45gQifo+TvGqddLX+NX1iGTmZyGLxlqPMpdp+x/yq8WwVZ316Q7tLt6z6fyAmsroc3ma5p1iLhqpV7g==}
+  /@storybook/channels@8.0.9:
+    resolution: {integrity: sha512-7Lcfyy5CsLWWGhMPO9WG4jZ/Alzp0AjepFhEreYHRPtQrfttp6qMAjE/g1aHgun0qHCYWxwqIG4NLR/hqDNrXQ==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.9
+      '@storybook/core-events': 8.0.9
       '@storybook/global': 5.0.0
-      qs: 6.11.1
       telejson: 7.2.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/cli@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-sREQYnPds2bwQS7FLbRy7oaxGvOmYhPEYVf93pWKyo/qwSWyXEXbqGCGT6bNhSl/xzqXX7VryLDmuOoHmVTh1g==}
+  /@storybook/cli@8.0.9(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-lilYTKn8F5YOePijqfRYFa5v2mHVIJxPCIgTn+OXAmAFbcizZ6P8P6niU4J/NXulgx68Ln1M7hYhFtTP25hVTw==}
     hasBin: true
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/types': 7.23.5
+      '@babel/core': 7.24.0
+      '@babel/types': 7.24.0
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 8.0.0-beta.6
-      '@storybook/core-common': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-tools': 8.0.0-beta.6
-      '@storybook/node-logger': 8.0.0-beta.6
-      '@storybook/telemetry': 8.0.0-beta.6
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/codemod': 8.0.9
+      '@storybook/core-common': 8.0.9
+      '@storybook/core-events': 8.0.9
+      '@storybook/core-server': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-tools': 8.0.9
+      '@storybook/node-logger': 8.0.9
+      '@storybook/telemetry': 8.0.9
+      '@storybook/types': 8.0.9
       '@types/semver': 7.5.8
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
@@ -6186,7 +6301,7 @@ packages:
       prettier: 3.2.5
       prompts: 2.4.2
       read-pkg-up: 7.0.1
-      semver: 7.5.4
+      semver: 7.6.0
       strip-json-comments: 3.1.1
       tempy: 1.0.1
       tiny-invariant: 1.3.1
@@ -6201,47 +6316,47 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@8.0.0-beta.6:
-    resolution: {integrity: sha512-XX9CSWt9NDO/1K8tTYV+yuj0ur4HznM1Vc5mY5AwT5xh0RP5HtWZ+VoJfrWYXlBoRXaj0gf8si+FO+lSW82DcQ==}
+  /@storybook/client-logger@8.0.9:
+    resolution: {integrity: sha512-LzV/RHkbf07sRc1Jc0ff36RlapKf9Ul7/+9VMvVbI3hshH1CpmrZK4t/tsIdpX/EVOdJ1Gg5cES06PnleOAIPA==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/codemod@8.0.0-beta.6:
-    resolution: {integrity: sha512-ttQYDkhKmtU6Qbg+Kgn4K2XXf8XMpa2euuC6PmYffBD7/qLiGfABfBc4FHKRv4yScnvKK7Ehy7K0lvipfg6tXw==}
+  /@storybook/codemod@8.0.9:
+    resolution: {integrity: sha512-VBeGpSZSQpL6iyLLqceJSNGhdCqcNwv+xC/aWdDFOkmuE1YfbmNNwpa9QYv4ZFJ2QjUsm4iTWG60qK+9NXeSKA==}
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
-      '@babel/types': 7.23.5
-      '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 8.0.0-beta.6
-      '@storybook/node-logger': 8.0.0-beta.6
-      '@storybook/types': 8.0.0-beta.6
+      '@babel/core': 7.24.0
+      '@babel/preset-env': 7.23.5(@babel/core@7.24.0)
+      '@babel/types': 7.24.0
+      '@storybook/csf': 0.1.6
+      '@storybook/csf-tools': 8.0.9
+      '@storybook/node-logger': 8.0.9
+      '@storybook/types': 8.0.9
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
       jscodeshift: 0.15.1(@babel/preset-env@7.23.5)
       lodash: 4.17.21
       prettier: 3.2.5
-      recast: 0.23.4
+      recast: 0.23.6
       tiny-invariant: 1.3.1
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/components@8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-J3aJtPgaSco0sefvRMBLFsWbslhKMhaS3U+5baRqlV5bjPLZN+d4P18gP1RMaw/coh6DiKEQJZuHRoPIOdt4CA==}
+  /@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-JcwBGADzIJs0PSzqykrrD2KHzNG9wtexUOKuidt+FSv9szpUhe3qBAXIHpdfBRl7mOJ9TRZ5rt+mukEnfncdzA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
       '@radix-ui/react-slot': 1.0.2(@types/react@18.0.28)(react@18.2.0)
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/csf': 0.1.2
+      '@storybook/client-logger': 8.0.9
+      '@storybook/csf': 0.1.6
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.9
       memoizerific: 1.11.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -6250,19 +6365,19 @@ packages:
       - '@types/react'
     dev: true
 
-  /@storybook/core-common@8.0.0-beta.6:
-    resolution: {integrity: sha512-Mah4Kx/VBNhHaX6neYHTiVwfD93yf3LVVfLTS9WcJFOpek74EAAqbARV3vzOn/utOI75N7yu2PCVoKi5KkDoVw==}
+  /@storybook/core-common@8.0.9:
+    resolution: {integrity: sha512-Jmue+sfHFb4GTYBzyWYw1MygoJiQSfISIrKmNIzAmZ+oR9EOr+jpu/i/bH+uetZ2Hqg1AGhj1VB7OtJp9HQyWw==}
     dependencies:
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/csf-tools': 8.0.0-beta.6
-      '@storybook/node-logger': 8.0.0-beta.6
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/core-events': 8.0.9
+      '@storybook/csf-tools': 8.0.9
+      '@storybook/node-logger': 8.0.9
+      '@storybook/types': 8.0.9
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      esbuild: 0.18.20
-      esbuild-register: 3.5.0(esbuild@0.18.20)
+      esbuild: 0.19.11
+      esbuild-register: 3.5.0(esbuild@0.19.11)
       execa: 5.1.1
       file-system-cache: 2.3.0
       find-cache-dir: 3.3.2
@@ -6276,7 +6391,7 @@ packages:
       pkg-dir: 5.0.0
       pretty-hrtime: 1.0.3
       resolve-from: 5.0.0
-      semver: 7.5.4
+      semver: 7.6.0
       tempy: 1.0.1
       tiny-invariant: 1.3.1
       ts-dedent: 2.2.0
@@ -6286,32 +6401,32 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@8.0.0-beta.6:
-    resolution: {integrity: sha512-ZyEVkOJ5gGGTfHjyasyeZgNGoeVJwVkLFRpV6cUl8hzOT29R5iDsf5PbJdrpF1x2pm1oLumeRckYQ7sYhr+R/w==}
+  /@storybook/core-events@8.0.9:
+    resolution: {integrity: sha512-DxSUx7wG9Qe3OFUBnv3OrYq48J8UWNo2DUR5/JecJCtp3n++L4fAEW3J0IF5FfxpQDMQSp1yTNjZ2PaWCMd2ag==}
     dependencies:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/core-server@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-0ciJTWZs+mCnQOUzB3WuSfkhwXKpO033M5iYK92PKu9A6KSrwdc/WCwIJHeBNnIpmxC0GEh9j6/CgIsWehwJvg==}
+  /@storybook/core-server@8.0.9(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-BIe1T5YUBl0GYxEjRoTQsvXD2pyuzL8rPTUD41zlzSQM0R8U6Iant9SzRms4u0+rKUm2mGxxKuODlUo5ewqaGA==}
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@babel/core': 7.24.0
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 8.0.0-beta.6
-      '@storybook/channels': 8.0.0-beta.6
-      '@storybook/core-common': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 8.0.0-beta.6
+      '@storybook/builder-manager': 8.0.9
+      '@storybook/channels': 8.0.9
+      '@storybook/core-common': 8.0.9
+      '@storybook/core-events': 8.0.9
+      '@storybook/csf': 0.1.6
+      '@storybook/csf-tools': 8.0.9
       '@storybook/docs-mdx': 3.0.0
       '@storybook/global': 5.0.0
-      '@storybook/manager': 8.0.0-beta.6
-      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 8.0.0-beta.6
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/telemetry': 8.0.0-beta.6
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/manager': 8.0.9
+      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 8.0.9
+      '@storybook/preview-api': 8.0.9
+      '@storybook/telemetry': 8.0.9
+      '@storybook/types': 8.0.9
       '@types/detect-port': 1.3.2
       '@types/node': 18.17.15
       '@types/pretty-hrtime': 1.0.1
@@ -6330,7 +6445,7 @@ packages:
       pretty-hrtime: 1.0.3
       prompts: 2.4.2
       read-pkg-up: 7.0.1
-      semver: 7.5.4
+      semver: 7.6.0
       telejson: 7.2.0
       tiny-invariant: 1.3.1
       ts-dedent: 2.2.0
@@ -6347,33 +6462,33 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/csf-plugin@8.0.0-beta.6:
-    resolution: {integrity: sha512-cYI/4OndODf0utV0DxJs8AOKbmjCG+pEgxQGcmPtGnkSmEuieUwpQpN7v+fEIN7IPUQLYvs0wspR0njZQAIzyA==}
+  /@storybook/csf-plugin@8.0.9:
+    resolution: {integrity: sha512-pXaNCNi++kxKsqSWwvx215fPx8cNqvepLVxQ7B69qXLHj80DHn0Q3DFBO3sLXNiQMJ2JK4OYcTxMfuOiyzszKw==}
     dependencies:
-      '@storybook/csf-tools': 8.0.0-beta.6
+      '@storybook/csf-tools': 8.0.9
       unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf-tools@8.0.0-beta.6:
-    resolution: {integrity: sha512-wwzbE6f8ykrvIeZlXYTba0IA8D5GPSyZ4L0+PqRAYHm3ozu0DXqtm4USDHKrjYAzuD+W+fG/6qIOQmsWYbNmpA==}
+  /@storybook/csf-tools@8.0.9:
+    resolution: {integrity: sha512-PiNMhL97giLytTdQwuhsZ92buVk4gy9H/8DtrDhUc45/1OmF95gogm6T2Yap729SIFwgpOcuq/U3aVo6d6swVQ==}
     dependencies:
-      '@babel/generator': 7.23.5
-      '@babel/parser': 7.23.9
-      '@babel/traverse': 7.23.5
-      '@babel/types': 7.23.5
-      '@storybook/csf': 0.1.2
-      '@storybook/types': 8.0.0-beta.6
+      '@babel/generator': 7.23.6
+      '@babel/parser': 7.24.0
+      '@babel/traverse': 7.24.0
+      '@babel/types': 7.24.0
+      '@storybook/csf': 0.1.6
+      '@storybook/types': 8.0.9
       fs-extra: 11.1.1
-      recast: 0.23.4
+      recast: 0.23.6
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf@0.1.2:
-    resolution: {integrity: sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==}
+  /@storybook/csf@0.1.6:
+    resolution: {integrity: sha512-JjWnBptVhBYJ14yq+cHs66BXjykRUWQ5TlD1RhPxMOtavynYyV/Q+QR98/N+XB+mcPtFMm5I2DvNkpj0/Dk8Mw==}
     dependencies:
       type-fest: 2.19.0
     dev: true
@@ -6382,12 +6497,13 @@ packages:
     resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==}
     dev: true
 
-  /@storybook/docs-tools@8.0.0-beta.6:
-    resolution: {integrity: sha512-fSKXEu0vegzqC2HT1RaOKqi0+W/vIn+qa5D+dZHkj2BnceYxWAGYsX9ZZPHW6DUvvwp0WZp1vz57nPUhsLvcQg==}
+  /@storybook/docs-tools@8.0.9:
+    resolution: {integrity: sha512-OzogAeOmeHea/MxSPKRBWtOQVNSpoq+OOpimO9YRA5h5GBRJ2TUOGT44Gny6QT4ll5AvQA8fIiq9KezKcLekAg==}
     dependencies:
-      '@storybook/core-common': 8.0.0-beta.6
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/core-common': 8.0.9
+      '@storybook/core-events': 8.0.9
+      '@storybook/preview-api': 8.0.9
+      '@storybook/types': 8.0.9
       '@types/doctrine': 0.0.3
       assert: 2.1.0
       doctrine: 3.0.0
@@ -6412,29 +6528,30 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/instrumenter@8.0.0-beta.6:
-    resolution: {integrity: sha512-xJ3qkvj8dce7nJEa6hmp4PDDZJMBuP5UlSKPidiMAfEsB0MeUbDulTFNDb0t1DwcH9ywinDl8TilSzG4+r1kDA==}
+  /@storybook/instrumenter@8.0.9:
+    resolution: {integrity: sha512-Gw74dgpTU/2p7FG0s7DuVdqCbJ2MEcSuRJjDo7HcXRYcvWp7I6Ly+C0v7N5VaoS+kbBVerAhLKIHZgG/LZf1og==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.6
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
+      '@storybook/channels': 8.0.9
+      '@storybook/client-logger': 8.0.9
+      '@storybook/core-events': 8.0.9
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@vitest/utils': 0.34.6
+      '@storybook/preview-api': 8.0.9
+      '@vitest/utils': 1.5.3
       util: 0.12.5
     dev: true
 
-  /@storybook/manager-api@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-kOGOT/yGFgKzld9IL1HREouFwZ0LpFuXZZOHBih5ydK8XT+bkWF6e3SiqthB3qtqpd0eVLAbNiPfY9R8t3qfWg==}
+  /@storybook/manager-api@8.0.9(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-99b3yKArDSvfabXL7QE3nA95e4DdW/5H/ZCcr6/E2qCQJayZ6G1v/WWamKXbiaTpkndulFmcb/+ZmnDXcweIIQ==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.6
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/csf': 0.1.2
+      '@storybook/channels': 8.0.9
+      '@storybook/client-logger': 8.0.9
+      '@storybook/core-events': 8.0.9
+      '@storybook/csf': 0.1.6
       '@storybook/global': 5.0.0
-      '@storybook/router': 8.0.0-beta.6
-      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/router': 8.0.9
+      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.9
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
@@ -6446,23 +6563,23 @@ packages:
       - react-dom
     dev: true
 
-  /@storybook/manager@8.0.0-beta.6:
-    resolution: {integrity: sha512-FeQ2/CIasSOgcTMEE3QYMFa92KeMnfEMyUVO4hHEmPh3SqPsz6OOv8p0bQvN0SWWBgZarbhFR0dKC3W10yYrXg==}
+  /@storybook/manager@8.0.9:
+    resolution: {integrity: sha512-+NnRo+5JQFGNqveKrLtC0b+Z08Tae4m44iq292bPeZMpr9OkFsIkU0PBPsHTHPkrqC/zZXRNsCsTEgvu3p2OIA==}
     dev: true
 
-  /@storybook/node-logger@8.0.0-beta.6:
-    resolution: {integrity: sha512-nmBlmZ8wzJiU1/ubhUmFeWQaJPBv6l6s0Cndk04omPSjROa+O1whoPhDTVGvWC28zm17tmAYVcQRujkdoi+YBA==}
+  /@storybook/node-logger@8.0.9:
+    resolution: {integrity: sha512-5ajMdZFrYrjGLJOVDq7dlEQNFsgeLHymt4dCK9MulL/ciXykmXUZXE3Bye0wFy+I2qqDVvrvR8uzCvSFvm5MAQ==}
     dev: true
 
-  /@storybook/preview-api@8.0.0-beta.6:
-    resolution: {integrity: sha512-V07MF1ArjBGi2EPSjrEW8pjCoW/TIwxNDilcO9cD12LHrDQGXuo/iKyR47TGUYmcJ/u1I2Eu9cjyVj9DVyppag==}
+  /@storybook/preview-api@8.0.9:
+    resolution: {integrity: sha512-zHfX34bkAMzzmE7vbDzaqFwSW6ExiBD0HiO1L/IsHF55f0f7xV7IH8uJyFRrDTvAoW3ReSxZDMvvPpeydFPKGA==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.6
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/csf': 0.1.2
+      '@storybook/channels': 8.0.9
+      '@storybook/client-logger': 8.0.9
+      '@storybook/core-events': 8.0.9
+      '@storybook/csf': 0.1.6
       '@storybook/global': 5.0.0
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/types': 8.0.9
       '@types/qs': 6.9.7
       dequal: 2.0.3
       lodash: 4.17.21
@@ -6473,12 +6590,12 @@ packages:
       util-deprecate: 1.0.2
     dev: true
 
-  /@storybook/preview@8.0.0-beta.6:
-    resolution: {integrity: sha512-tp3Wyvjsbf5r5RhbCQSafArQWJAir1bmIJWGG2S4o2E3YT6TlHFpR078tNJtgXqsPyG0yhF9vhRRkDczrPX/Gw==}
+  /@storybook/preview@8.0.9:
+    resolution: {integrity: sha512-tFsR8xc8AYBZZrZw8enklFbSQt7ZAV+rv20BoxwDhd3q7fjXyK7O4moGPqUwBZ7rukTG13nPoISxr+VXAk/HYA==}
     dev: true
 
-  /@storybook/react-dom-shim@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-l14oDKAW2jyrXynHKP6SoNGal78gXcWCgj0zLwSDWpKgAFWC7SuIneuxLv6weU1D4+f9Y9FBrz+K3CCaMgMtOA==}
+  /@storybook/react-dom-shim@8.0.9(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-8011KlRuG3obr5pZZ7bcEyYYNWF3tR596YadoMd267NPoHKvwAbKL1L/DNgb6kiYjZDUf9QfaKSCWW31k0kcRQ==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6487,8 +6604,8 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4):
-    resolution: {integrity: sha512-Tvz25pTXmhncDxprjIYsnXc68Lfa9idDybpRTRRbtvjsJyVpZogUdgz2/kddGNTuX3mqz6vmTMWiLiIVh+ytQA==}
+  /@storybook/react-vite@8.0.9(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4):
+    resolution: {integrity: sha512-FT5KeulUH6grfzOJOxJCxpv9+81UVDrT9UPcgiFhQT9rKtsgmltezThwbHknByZNw3WWnf+ieidMLEis9hd73A==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6497,12 +6614,16 @@ packages:
     dependencies:
       '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.4)
       '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
-      '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4)
-      '@storybook/react': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+      '@storybook/builder-vite': 8.0.9(typescript@5.3.3)(vite@5.1.4)
+      '@storybook/node-logger': 8.0.9
+      '@storybook/react': 8.0.9(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+      find-up: 5.0.0
       magic-string: 0.30.7
       react: 18.2.0
       react-docgen: 7.0.1
       react-dom: 18.2.0(react@18.2.0)
+      resolve: 1.22.8
+      tsconfig-paths: 4.2.0
       vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
     transitivePeerDependencies:
       - '@preact/preset-vite'
@@ -6513,8 +6634,8 @@ packages:
       - vite-plugin-glimmerx
     dev: true
 
-  /@storybook/react@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-69B0c08HDYHEgZRRnkB+3z4dY/HO/GMSiRzRCNpzI0SBQzk1YwDzG9MOtkNgGqzdLK3e3DveSXb5Uyy1cB0ZiQ==}
+  /@storybook/react@8.0.9(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-NeQ6suZG3HKikwe3Tx9cAIaRx7uP8FKCmlVvIiBg4LTTI5orCt94PPakvuZukZcbkqvcCnEBkebAzwUpn8PiJw==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6524,12 +6645,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/docs-tools': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.9
+      '@storybook/docs-tools': 8.0.9
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/react-dom-shim': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/preview-api': 8.0.9
+      '@storybook/react-dom-shim': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.9
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 18.17.15
@@ -6543,7 +6664,7 @@ packages:
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0)
-      semver: 7.5.4
+      semver: 7.6.0
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       typescript: 5.3.3
@@ -6553,30 +6674,30 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/router@8.0.0-beta.6:
-    resolution: {integrity: sha512-JjLyDaVzCH3kmNsOkuJ8/U2bPIoReZZ/QsgHJdfvm22T2wKNjQ+lfNrQptBgNybfi1o/Tmn9VbCdRqurSlh9Dw==}
+  /@storybook/router@8.0.9:
+    resolution: {integrity: sha512-aAOWxbM9J4mt+cp4o88T2PB29mgBBTOzU37/pUsTHYnKnR9XI4npXEXdN8Gv+ryqM0kj0AbBpz/llFlnR2MNNA==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.9
       memoizerific: 1.11.3
       qs: 6.11.1
     dev: true
 
-  /@storybook/source-loader@8.0.0-beta.6:
-    resolution: {integrity: sha512-cYtjnuJZgm8MS9SsNsbuhuFz2d7j6BKRLZByBUqELrK+ftup0qqOWM+78w26qn3nPgA8myZXWxGa+V/Pjxio5w==}
+  /@storybook/source-loader@8.0.9:
+    resolution: {integrity: sha512-FDnpxIGE5nIYT15pvYe6rz95TSBrdLcDll7lOHNyZisWt19MI3wZU3YkVsFNRBuFrebo+FjVU3wHyoV81ur1Qw==}
     dependencies:
-      '@storybook/csf': 0.1.2
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/csf': 0.1.6
+      '@storybook/types': 8.0.9
       estraverse: 5.3.0
       lodash: 4.17.21
       prettier: 3.2.5
     dev: true
 
-  /@storybook/telemetry@8.0.0-beta.6:
-    resolution: {integrity: sha512-3CU5Sdj8eVm0tb35GriMkDrxJyTpdGcfU/hgUnsuw+I4eHYdZsc4Boh9uXWTVNsaBaoqbD/MP1aqbfxkElqPxQ==}
+  /@storybook/telemetry@8.0.9:
+    resolution: {integrity: sha512-AGGfcup06t+wxhBIkHd0iybieOh9PDVZQJ9oPct5JGB39+ni9wvs0WOD+MYlHbsjp8id7+aGkh6mYuYOvfck+Q==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/core-common': 8.0.0-beta.6
-      '@storybook/csf-tools': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.9
+      '@storybook/core-common': 8.0.9
+      '@storybook/csf-tools': 8.0.9
       chalk: 4.1.2
       detect-package-manager: 2.0.1
       fetch-retry: 5.0.4
@@ -6587,19 +6708,18 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/test@8.0.0-beta.6(vitest@0.34.6):
-    resolution: {integrity: sha512-GcV76EX3U77G+k8+0V+jAa/sJQZEuNb/4W+g/RaqGLRCEG73UADzkgRuFm60UQUBGtltvvRZU9sIPVbFTJFxuA==}
+  /@storybook/test@8.0.9(vitest@0.34.6):
+    resolution: {integrity: sha512-bRd5tBJnPzR6UKbDXONWnFWtdkNOY99HMLDUWe5fTRo50GwkrpFBVqPflhdkruEeof0kAbBUbnoN2CIYgtnAFw==}
     dependencies:
-      '@storybook/client-logger': 8.0.0-beta.6
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/instrumenter': 8.0.0-beta.6
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@testing-library/dom': 9.3.3
+      '@storybook/client-logger': 8.0.9
+      '@storybook/core-events': 8.0.9
+      '@storybook/instrumenter': 8.0.9
+      '@storybook/preview-api': 8.0.9
+      '@testing-library/dom': 9.3.4
       '@testing-library/jest-dom': 6.4.2(vitest@0.34.6)
-      '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.3)
-      '@vitest/expect': 1.1.3
-      '@vitest/spy': 1.2.2
-      chai: 4.3.10
+      '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4)
+      '@vitest/expect': 1.3.1
+      '@vitest/spy': 1.5.3
       util: 0.12.5
     transitivePeerDependencies:
       - '@jest/globals'
@@ -6609,8 +6729,8 @@ packages:
       - vitest
     dev: true
 
-  /@storybook/theming@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-WXvDbV257fKbHM5jHd7hOHefRSBnyZec08NGpcVOG6muJjLu8nPjazcYgISqFc97MkFmxvEDPFfX8CvBEeefzA==}
+  /@storybook/theming@8.0.9(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-jgfDuYoiNMMirQiASN3Eg0hGDXsEtpdAcMxyShqYGwu9elxgD9yUnYC2nSckYsM74a3ZQ3JaViZ9ZFSe2FHmeQ==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6621,35 +6741,35 @@ packages:
         optional: true
     dependencies:
       '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
-      '@storybook/client-logger': 8.0.0-beta.6
+      '@storybook/client-logger': 8.0.9
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@8.0.0-beta.6:
-    resolution: {integrity: sha512-w3jq8mBcxir4P0RK3gQePeUJ0rXbnUbCKg91YBOKeitmU0+4jSr4e1EwTWOYgsyz7KtikzSNr8JXtMQn2TJD5A==}
+  /@storybook/types@8.0.9:
+    resolution: {integrity: sha512-ew0EXzk9k4B557P1qIWYrnvUcgaE0WWA5qQS0AU8l+fRTp5nvl9O3SP/zNIB0SN1qDFO7dXr3idTNTyIikTcEQ==}
     dependencies:
-      '@storybook/channels': 8.0.0-beta.6
+      '@storybook/channels': 8.0.9
       '@types/express': 4.17.17
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21):
-    resolution: {integrity: sha512-Pf9W7hcHjx1FE3JmhY1iSxGq9k/Tp5n/obOCd4FJGUdIttPYFclG9km49DrCJtNfhK7M6+d2QTZ6Uds4ORWZPg==}
+  /@storybook/vue3-vite@8.0.9(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21):
+    resolution: {integrity: sha512-IkzYsEyCo5HIvLWbJeGrBu/VIN4u+LvdIAz7vcFqVVXBtTUhy+9/8caLx8fdnM0FWgKcBRQs8HnjBB2V0lOFcg==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 8.0.0-beta.6(typescript@5.3.3)(vite@5.1.4)
-      '@storybook/core-server': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/vue3': 8.0.0-beta.6(vue@3.4.21)
+      '@storybook/builder-vite': 8.0.9(typescript@5.3.3)(vite@5.1.4)
+      '@storybook/core-server': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/vue3': 8.0.9(vue@3.4.21)
       find-package-json: 1.2.0
       magic-string: 0.30.7
       typescript: 5.3.3
       vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
-      vue-component-meta: 1.8.27(typescript@5.3.3)
+      vue-component-meta: 2.0.16(typescript@5.3.3)
       vue-docgen-api: 4.75.1(vue@3.4.21)
     transitivePeerDependencies:
       - '@preact/preset-vite'
@@ -6663,22 +6783,22 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@8.0.0-beta.6(vue@3.4.21):
-    resolution: {integrity: sha512-027KDM1f6y0XzMK1yE5W4JKY/VsbGpr1kj0mvEKxaPUYgBJV9wTHADWgmluiJS/e/MWrCCZql5mE+D9lVJUjoA==}
+  /@storybook/vue3@8.0.9(vue@3.4.21):
+    resolution: {integrity: sha512-EqVdS62YbOCAE0wJrQKW0sHpM90be8N8Mvmj+HzB0QYhJNtFqP9ehwbcTfwEKtaVGudisHgGBOzNoSKDlxFaag==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       vue: ^3.0.0
     dependencies:
-      '@storybook/docs-tools': 8.0.0-beta.6
+      '@storybook/docs-tools': 8.0.9
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/types': 8.0.0-beta.6
-      '@vue/compiler-core': 3.4.18
+      '@storybook/preview-api': 8.0.9
+      '@storybook/types': 8.0.9
+      '@vue/compiler-core': 3.4.21
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.21(typescript@5.3.3)
-      vue-component-type-helpers: 2.0.14
+      vue-component-type-helpers: 2.0.16
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -7108,6 +7228,20 @@ packages:
       pretty-format: 27.5.1
     dev: true
 
+  /@testing-library/dom@9.3.4:
+    resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==}
+    engines: {node: '>=14'}
+    dependencies:
+      '@babel/code-frame': 7.23.5
+      '@babel/runtime': 7.23.4
+      '@types/aria-query': 5.0.1
+      aria-query: 5.1.3
+      chalk: 4.1.2
+      dom-accessibility-api: 0.5.16
+      lz-string: 1.5.0
+      pretty-format: 27.5.1
+    dev: true
+
   /@testing-library/jest-dom@6.4.2(vitest@0.34.6):
     resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==}
     engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
@@ -7140,13 +7274,13 @@ packages:
       vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
     dev: true
 
-  /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.3):
+  /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4):
     resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==}
     engines: {node: '>=12', npm: '>=6'}
     peerDependencies:
       '@testing-library/dom': '>=7.21.4'
     dependencies:
-      '@testing-library/dom': 9.3.3
+      '@testing-library/dom': 9.3.4
     dev: true
 
   /@testing-library/vue@8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21):
@@ -7296,7 +7430,7 @@ packages:
   /@types/cross-spawn@6.0.2:
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
     dependencies:
-      '@types/node': 20.11.22
+      '@types/node': 20.11.28
     dev: true
 
   /@types/debug@4.1.12:
@@ -7382,13 +7516,13 @@ packages:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.11.22
+      '@types/node': 20.11.28
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.11.22
+      '@types/node': 20.11.28
     dev: true
 
   /@types/hast@3.0.4:
@@ -7793,12 +7927,6 @@ packages:
     resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
     dev: true
 
-  /@types/yargs@16.0.5:
-    resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==}
-    dependencies:
-      '@types/yargs-parser': 21.0.0
-    dev: true
-
   /@types/yargs@17.0.19:
     resolution: {integrity: sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==}
     dependencies:
@@ -8120,11 +8248,11 @@ packages:
       chai: 4.3.10
     dev: true
 
-  /@vitest/expect@1.1.3:
-    resolution: {integrity: sha512-MnJqsKc1Ko04lksF9XoRJza0bGGwTtqfbyrsYv5on4rcEkdo+QgUdITenBQBUltKzdxW7K3rWh+nXRULwsdaVg==}
+  /@vitest/expect@1.3.1:
+    resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
     dependencies:
-      '@vitest/spy': 1.1.3
-      '@vitest/utils': 1.1.3
+      '@vitest/spy': 1.3.1
+      '@vitest/utils': 1.3.1
       chai: 4.3.10
     dev: true
 
@@ -8150,14 +8278,14 @@ packages:
       tinyspy: 2.2.0
     dev: true
 
-  /@vitest/spy@1.1.3:
-    resolution: {integrity: sha512-Ec0qWyGS5LhATFQtldvChPTAHv08yHIOZfiNcjwRQbFPHpkih0md9KAbs7TfeIfL7OFKoe7B/6ukBTqByubXkQ==}
+  /@vitest/spy@1.3.1:
+    resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
     dependencies:
       tinyspy: 2.2.0
     dev: true
 
-  /@vitest/spy@1.2.2:
-    resolution: {integrity: sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g==}
+  /@vitest/spy@1.5.3:
+    resolution: {integrity: sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==}
     dependencies:
       tinyspy: 2.2.0
     dev: true
@@ -8170,8 +8298,17 @@ packages:
       pretty-format: 29.7.0
     dev: true
 
-  /@vitest/utils@1.1.3:
-    resolution: {integrity: sha512-Dyt3UMcdElTll2H75vhxfpZu03uFpXRCHxWnzcrFjZxT1kTbq8ALUYIeBgGolo1gldVdI0YSlQRacsqxTwNqwg==}
+  /@vitest/utils@1.3.1:
+    resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
+    dependencies:
+      diff-sequences: 29.6.3
+      estree-walker: 3.0.3
+      loupe: 2.3.7
+      pretty-format: 29.7.0
+    dev: true
+
+  /@vitest/utils@1.5.3:
+    resolution: {integrity: sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==}
     dependencies:
       diff-sequences: 29.6.3
       estree-walker: 3.0.3
@@ -8185,12 +8322,24 @@ packages:
       '@volar/source-map': 1.11.1
     dev: true
 
+  /@volar/language-core@2.2.0:
+    resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==}
+    dependencies:
+      '@volar/source-map': 2.2.0
+    dev: true
+
   /@volar/source-map@1.11.1:
     resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==}
     dependencies:
       muggle-string: 0.3.1
     dev: true
 
+  /@volar/source-map@2.2.0:
+    resolution: {integrity: sha512-HQlPRlHOVqCCHK8wI76ZldHkEwKsjp7E6idUc36Ekni+KJDNrqgSqPvyHQixybXPHNU7CI9Uxd9/IkxO7LuNBw==}
+    dependencies:
+      muggle-string: 0.4.1
+    dev: true
+
   /@volar/typescript@1.11.1:
     resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==}
     dependencies:
@@ -8198,6 +8347,13 @@ packages:
       path-browserify: 1.0.1
     dev: true
 
+  /@volar/typescript@2.2.0:
+    resolution: {integrity: sha512-wC6l4zLiiCLxF+FGaHCbWlQYf4vMsnRxYhcI6WgvaNppOD6r1g+Ef1RKRJUApALWU46Yy/JDU/TbdV6w/X6Liw==}
+    dependencies:
+      '@volar/language-core': 2.2.0
+      path-browserify: 1.0.1
+    dev: true
+
   /@vue/compiler-core@3.4.18:
     resolution: {integrity: sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==}
     dependencies:
@@ -8269,6 +8425,24 @@ packages:
       vue-template-compiler: 2.7.14
     dev: true
 
+  /@vue/language-core@2.0.16(typescript@5.3.3):
+    resolution: {integrity: sha512-Bc2sexRH99pznOph8mLw2BlRZ9edm7tW51kcBXgx8adAoOcZUWJj3UNSsdQ6H9Y8meGz7BoazVrVo/jUukIsPw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@volar/language-core': 2.2.0
+      '@vue/compiler-dom': 3.4.21
+      '@vue/shared': 3.4.21
+      computeds: 0.0.1
+      minimatch: 9.0.3
+      path-browserify: 1.0.1
+      typescript: 5.3.3
+      vue-template-compiler: 2.7.14
+    dev: true
+
   /@vue/reactivity@3.4.21:
     resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
     dependencies:
@@ -8326,13 +8500,13 @@ packages:
     requiresBuild: true
     dev: false
 
-  /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.18.20):
+  /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.19.11):
     resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==}
     engines: {node: '>=14.15.0'}
     peerDependencies:
       esbuild: '>=0.10.0'
     dependencies:
-      esbuild: 0.18.20
+      esbuild: 0.19.11
       tslib: 2.6.2
     dev: true
 
@@ -8847,12 +9021,12 @@ packages:
     resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
     dev: false
 
-  /babel-core@7.0.0-bridge.0(@babel/core@7.23.5):
+  /babel-core@7.0.0-bridge.0(@babel/core@7.24.0):
     resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.0
     dev: true
 
   /babel-jest@29.7.0(@babel/core@7.23.5):
@@ -8896,38 +9070,38 @@ packages:
       '@types/babel__traverse': 7.20.0
     dev: true
 
-  /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.5):
+  /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.24.0):
     resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
     dependencies:
       '@babel/compat-data': 7.23.5
-      '@babel/core': 7.23.5
-      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.23.5):
+  /babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.24.0):
     resolution: {integrity: sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
       core-js-compat: 3.33.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.5):
+  /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.24.0):
     resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -9167,7 +9341,6 @@ packages:
       electron-to-chromium: 1.4.686
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.23.0)
-    dev: false
 
   /bser@2.1.1:
     resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
@@ -9363,7 +9536,6 @@ packages:
 
   /caniuse-lite@1.0.30001591:
     resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==}
-    dev: false
 
   /canonicalize@1.0.8:
     resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
@@ -9887,7 +10059,7 @@ packages:
   /core-js-compat@3.33.3:
     resolution: {integrity: sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==}
     dependencies:
-      browserslist: 4.22.2
+      browserslist: 4.23.0
     dev: true
 
   /core-js@3.29.1:
@@ -10614,7 +10786,6 @@ packages:
 
   /electron-to-chromium@1.4.686:
     resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==}
-    dev: false
 
   /emittery@0.13.1:
     resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@@ -10789,13 +10960,13 @@ packages:
     resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==}
     dev: true
 
-  /esbuild-register@3.5.0(esbuild@0.18.20):
+  /esbuild-register@3.5.0(esbuild@0.19.11):
     resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==}
     peerDependencies:
       esbuild: '>=0.12 <1'
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
-      esbuild: 0.18.20
+      esbuild: 0.19.11
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -13392,14 +13563,6 @@ packages:
       stack-utils: 2.0.6
     dev: true
 
-  /jest-mock@27.5.1:
-    resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==}
-    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
-    dependencies:
-      '@jest/types': 27.5.1
-      '@types/node': 20.11.22
-    dev: true
-
   /jest-mock@29.7.0:
     resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -13688,18 +13851,18 @@ packages:
       '@babel/preset-env':
         optional: true
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/parser': 7.23.9
-      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
-      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.5)
-      '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
-      '@babel/preset-flow': 7.23.3(@babel/core@7.23.5)
-      '@babel/preset-typescript': 7.23.3(@babel/core@7.23.5)
-      '@babel/register': 7.22.15(@babel/core@7.23.5)
-      babel-core: 7.0.0-bridge.0(@babel/core@7.23.5)
+      '@babel/core': 7.24.0
+      '@babel/parser': 7.24.0
+      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
+      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0)
+      '@babel/preset-env': 7.23.5(@babel/core@7.24.0)
+      '@babel/preset-flow': 7.23.3(@babel/core@7.24.0)
+      '@babel/preset-typescript': 7.23.3(@babel/core@7.24.0)
+      '@babel/register': 7.22.15(@babel/core@7.24.0)
+      babel-core: 7.0.0-bridge.0(@babel/core@7.24.0)
       chalk: 4.1.2
       flow-parser: 0.202.0
       graceful-fs: 4.2.11
@@ -14995,6 +15158,10 @@ packages:
     resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
     dev: true
 
+  /muggle-string@0.4.1:
+    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+    dev: true
+
   /multer@1.4.4-lts.1:
     resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==}
     engines: {node: '>= 6.0.0'}
@@ -16819,9 +16986,9 @@ packages:
     resolution: {integrity: sha512-rCz0HBIT0LWbIM+///LfRrJoTKftIzzwsYDf0ns5KwaEjejMHQRtphcns+IXFHDNY9pnz6G8l/JbbI6pD4EAIA==}
     engines: {node: '>=16.14.0'}
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/traverse': 7.23.5
-      '@babel/types': 7.23.5
+      '@babel/core': 7.24.0
+      '@babel/traverse': 7.24.0
+      '@babel/types': 7.24.0
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
       '@types/doctrine': 0.0.9
@@ -16971,6 +17138,17 @@ packages:
       tslib: 2.6.2
     dev: true
 
+  /recast@0.23.6:
+    resolution: {integrity: sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==}
+    engines: {node: '>= 4'}
+    dependencies:
+      ast-types: 0.16.1
+      esprima: 4.0.1
+      source-map: 0.6.1
+      tiny-invariant: 1.3.3
+      tslib: 2.6.2
+    dev: true
+
   /reconnecting-websocket@4.4.0:
     resolution: {integrity: sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==}
     dev: false
@@ -17972,11 +18150,11 @@ packages:
     resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
     dev: true
 
-  /storybook@8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-8d9gpKPDY9Ix64f0560rXIifmnuoswDdvSdTz4NXHGvPt7WrKNmaDTvWGyt1/fbTbv2dvvVp7bsWPgq1KGbrcg==}
+  /storybook@8.0.9(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-/Mvij0Br5bUwJpCvqAUZMEDIWmdRxEyllvVj8Ukw5lIWJePxfpSsz4px5jg9+R6B9tO8sQSqjg4HJvQ/pZk8Tg==}
     hasBin: true
     dependencies:
-      '@storybook/cli': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/cli': 8.0.9(react-dom@18.2.0)(react@18.2.0)
     transitivePeerDependencies:
       - '@babel/preset-env'
       - bufferutil
@@ -18429,6 +18607,10 @@ packages:
     resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
     dev: true
 
+  /tiny-invariant@1.3.3:
+    resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+    dev: true
+
   /tiny-lru@10.0.1:
     resolution: {integrity: sha512-Vst+6kEsWvb17Zpz14sRJV/f8bUWKhqm6Dc+v08iShmIJ/WxqWytHzCTd6m88pS33rE2zpX34TRmOpAJPloNCA==}
     engines: {node: '>=6'}
@@ -18612,7 +18794,6 @@ packages:
       json5: 2.2.3
       minimist: 1.2.8
       strip-bom: 3.0.0
-    dev: false
 
   /tsd@0.30.7:
     resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==}
@@ -19027,7 +19208,6 @@ packages:
       browserslist: 4.23.0
       escalade: 3.1.1
       picocolors: 1.0.0
-    dev: false
 
   /uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -19324,19 +19504,19 @@ packages:
       vscode-languageserver-protocol: 3.17.5
     dev: false
 
-  /vue-component-meta@1.8.27(typescript@5.3.3):
-    resolution: {integrity: sha512-j3WJsyQHP4TDlvnjHc/eseo0/eVkf0FaCpkqGwez5zD+Tj31onBzWZEXTnWKs8xRj0n3dMNYdy3SpiS6NubSvg==}
+  /vue-component-meta@2.0.16(typescript@5.3.3):
+    resolution: {integrity: sha512-IyIMClUMYcKxAL34GqdPbR4V45MUeHXqQiZlHxeYMV5Qcqp4M+CEmtGpF//XBSS138heDkYkceHAtJQjLUB1Lw==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@volar/typescript': 1.11.1
-      '@vue/language-core': 1.8.27(typescript@5.3.3)
+      '@volar/typescript': 2.2.0
+      '@vue/language-core': 2.0.16(typescript@5.3.3)
       path-browserify: 1.0.1
       typescript: 5.3.3
-      vue-component-type-helpers: 1.8.27
+      vue-component-type-helpers: 2.0.16
     dev: true
 
   /vue-component-type-helpers@1.8.27:
@@ -19347,8 +19527,8 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-component-type-helpers@2.0.14:
-    resolution: {integrity: sha512-DInfgOyXlMyliyqAAD9frK28tTfch0+tMi4qoWJcZlRxUf+NFAtraJBnAsKLep+FOyLMiajkhfyEb3xLK08i7w==}
+  /vue-component-type-helpers@2.0.16:
+    resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
     dev: true
 
   /vue-demi@0.14.7(vue@3.4.21):
@@ -19371,9 +19551,9 @@ packages:
     peerDependencies:
       vue: '>=2'
     dependencies:
-      '@babel/parser': 7.23.9
-      '@babel/types': 7.23.5
-      '@vue/compiler-dom': 3.4.18
+      '@babel/parser': 7.24.0
+      '@babel/types': 7.24.0
+      '@vue/compiler-dom': 3.4.21
       '@vue/compiler-sfc': 3.4.21
       ast-types: 0.16.1
       hash-sum: 2.0.0
@@ -19893,7 +20073,7 @@ packages:
       vscode-languageclient: 9.0.1
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.0-beta.6)(@storybook/components@8.0.0-beta.6)(@storybook/core-events@8.0.0-beta.6)(@storybook/manager-api@8.0.0-beta.6)(@storybook/preview-api@8.0.0-beta.6)(@storybook/theming@8.0.0-beta.6)(@storybook/types@8.0.0-beta.6)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9)(@storybook/components@8.0.9)(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9)(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9)(@storybook/types@8.0.9)(react-dom@18.2.0)(react@18.2.0):
     resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
     id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640
     name: storybook-addon-misskey-theme
@@ -19914,13 +20094,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/blocks': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 8.0.0-beta.6(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 8.0.0-beta.6
-      '@storybook/manager-api': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 8.0.0-beta.6
-      '@storybook/theming': 8.0.0-beta.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.0-beta.6
+      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 8.0.9
+      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 8.0.9
+      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 8.0.9
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true

From c530a46e547791b22ecf12fe1b9e952f7df0a58c Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Wed, 1 May 2024 17:13:20 +0900
Subject: [PATCH 183/266] =?UTF-8?q?enhance(backend):=20=E3=83=89=E3=83=A9?=
 =?UTF-8?q?=E3=82=A4=E3=83=96=E3=81=AE=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?=
 =?UTF-8?q?=E3=81=8CNSFW=E3=81=8B=E3=81=A9=E3=81=86=E3=81=8B=E5=80=8B?=
 =?UTF-8?q?=E5=88=A5=E3=81=AB=E9=80=A3=E5=90=88=E3=81=95=E3=82=8C=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#13756)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): ノートのattachmentにおいて、attach.sensitiveが元から存在する場合はそれを尊重する

* docs: update changelog (per misskey-dev#13756)

* feat(backend,apub): renderDocumentがsensitiveを連合するようにする
per https://github.com/misskey-dev/misskey/issues/13755#issuecomment-2081303014

* chore(backend): 追加したコメントを削除

* docs: changelogをより丁寧にする

* docs: changelogの項目名をPRに合わせる

* docs: tweak

apply suggestion from mei23
---
 CHANGELOG.md                                                  | 2 ++
 packages/backend/src/core/activitypub/ApRendererService.ts    | 1 +
 packages/backend/src/core/activitypub/models/ApNoteService.ts | 2 +-
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4394ab0c552a..1f3ae412effb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -77,6 +77,8 @@
 - Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
 - Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
 - Fix: AP Link等は添付ファイル扱いしないようになど (#13754)
+- Enhance: ドライブのファイルがNSFWかどうか個別に連合されるように (#13756)
+  - 可能な場合、ノートの添付ファイルのセンシティブ判定がファイル単位になります
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index d3553b6f7303..4fc724b5480e 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -167,6 +167,7 @@ export class ApRendererService {
 			mediaType: file.webpublicType ?? file.type,
 			url: this.driveFileEntityService.getPublicUrl(file),
 			name: file.comment,
+			sensitive: file.isSensitive,
 		};
 	}
 
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 05f7879983d7..4e361b57bcf5 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -211,7 +211,7 @@ export class ApNoteService {
 		const files: MiDriveFile[] = [];
 
 		for (const attach of toArray(note.attachment)) {
-			attach.sensitive ||= note.sensitive;	// Noteがsensitiveなら添付もsensitiveにする
+			attach.sensitive ??= note.sensitive;
 			const file = await this.apImageService.resolveImage(actor, attach);
 			if (file) files.push(file);
 		}

From 053e7626e41a7001a49287b2d51933151efaf4c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 4 May 2024 13:21:40 +0900
Subject: [PATCH 184/266] =?UTF-8?q?enhance(frontend=5Fais):=20PostForm?=
 =?UTF-8?q?=E7=B3=BB=E3=81=AE=E8=A8=AD=E5=AE=9A=E9=A0=85=E7=9B=AE=E3=82=92?=
 =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20(#13788)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend_ais): PostForm系の設定項目を追加

* Update Changelog
---
 CHANGELOG.md                                 |  1 +
 packages/frontend/src/components/MkAsUi.vue  |  4 ++
 packages/frontend/src/scripts/aiscript/ui.ts | 62 ++++++++++----------
 3 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f3ae412effb..c54fa6ed78c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@
 - Enhance: フォローするかどうかの確認ダイアログを出せるように
 - Enhance: Playを手動でリロードできるように
 - Enhance: 通報のコメント内のリンクをクリックした際、ウィンドウで開くように
+- Enhance: `Ui:C:postForm` および `Ui:C:postFormButton` に `localOnly` と `visibility` を設定できるように
 - Chore: AiScriptを0.18.0にバージョンアップ
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index 5eb77740be15..18e8e7542e3f 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -44,6 +44,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:instant="true"
 			:initialText="c.form?.text"
 			:initialCw="c.form?.cw"
+			:initialVisibility="c.form?.visibility"
+			:initialLocalOnly="c.form?.localOnly"
 		/>
 	</div>
 	<MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened">
@@ -111,6 +113,8 @@ function openPostForm() {
 	os.post({
 		initialText: form.text,
 		initialCw: form.cw,
+		initialVisibility: form.visibility,
+		initialLocalOnly: form.localOnly,
 		instant: true,
 	});
 }
diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts
index f2493264d37a..fa3fcac2e79a 100644
--- a/packages/frontend/src/scripts/aiscript/ui.ts
+++ b/packages/frontend/src/scripts/aiscript/ui.ts
@@ -6,6 +6,7 @@
 import { utils, values } from '@syuilo/aiscript';
 import { v4 as uuid } from 'uuid';
 import { ref, Ref } from 'vue';
+import * as Misskey from 'misskey-js';
 
 export type AsUiComponentBase = {
 	id: string;
@@ -115,23 +116,24 @@ export type AsUiFolder = AsUiComponentBase & {
 	opened?: boolean;
 };
 
+type PostFormPropsForAsUi = {
+	text: string;
+	cw?: string;
+	visibility?: (typeof Misskey.noteVisibilities)[number];
+	localOnly?: boolean;
+};
+
 export type AsUiPostFormButton = AsUiComponentBase & {
 	type: 'postFormButton';
 	text?: string;
 	primary?: boolean;
 	rounded?: boolean;
-	form?: {
-		text: string;
-		cw?: string;
-	};
+	form?: PostFormPropsForAsUi;
 };
 
 export type AsUiPostForm = AsUiComponentBase & {
 	type: 'postForm';
-	form?: {
-		text: string;
-		cw?: string;
-	};
+	form?: PostFormPropsForAsUi;
 };
 
 export type AsUiComponent = AsUiRoot | AsUiContainer | AsUiText | AsUiMfm | AsUiButton | AsUiButtons | AsUiSwitch | AsUiTextarea | AsUiTextInput | AsUiNumberInput | AsUiSelect | AsUiFolder | AsUiPostFormButton | AsUiPostForm;
@@ -447,6 +449,24 @@ function getFolderOptions(def: values.Value | undefined): Omit<AsUiFolder, 'id'
 	};
 }
 
+function getPostFormProps(form: values.VObj): PostFormPropsForAsUi {
+	const text = form.value.get('text');
+	utils.assertString(text);
+	const cw = form.value.get('cw');
+	if (cw) utils.assertString(cw);
+	const visibility = form.value.get('visibility');
+	if (visibility) utils.assertString(visibility);
+	const localOnly = form.value.get('localOnly');
+	if (localOnly) utils.assertBoolean(localOnly);
+
+	return {
+		text: text.value,
+		cw: cw?.value,
+		visibility: (visibility?.value && (Misskey.noteVisibilities as readonly string[]).includes(visibility.value)) ? visibility.value as typeof Misskey.noteVisibilities[number] : undefined,
+		localOnly: localOnly?.value,
+	};
+}
+
 function getPostFormButtonOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiPostFormButton, 'id' | 'type'> {
 	utils.assertObject(def);
 
@@ -459,22 +479,11 @@ function getPostFormButtonOptions(def: values.Value | undefined, call: (fn: valu
 	const form = def.value.get('form');
 	if (form) utils.assertObject(form);
 
-	const getForm = () => {
-		const text = form!.value.get('text');
-		utils.assertString(text);
-		const cw = form!.value.get('cw');
-		if (cw) utils.assertString(cw);
-		return {
-			text: text.value,
-			cw: cw?.value,
-		};
-	};
-
 	return {
 		text: text?.value,
 		primary: primary?.value,
 		rounded: rounded?.value,
-		form: form ? getForm() : {
+		form: form ? getPostFormProps(form) : {
 			text: '',
 		},
 	};
@@ -486,19 +495,8 @@ function getPostFormOptions(def: values.Value | undefined, call: (fn: values.VFn
 	const form = def.value.get('form');
 	if (form) utils.assertObject(form);
 
-	const getForm = () => {
-		const text = form!.value.get('text');
-		utils.assertString(text);
-		const cw = form!.value.get('cw');
-		if (cw) utils.assertString(cw);
-		return {
-			text: text.value,
-			cw: cw?.value,
-		};
-	};
-
 	return {
-		form: form ? getForm() : {
+		form: form ? getPostFormProps(form) : {
 			text: '',
 		},
 	};

From eef7fcdd45b12556c07c0cff31ee7c37a0e9d12f Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 4 May 2024 19:40:17 +0900
Subject: [PATCH 185/266] chore(frontend): ui tweak

---
 packages/frontend/src/pages/admin-user.vue       | 2 +-
 packages/frontend/src/pages/admin/roles.role.vue | 2 +-
 packages/frontend/src/scripts/get-user-menu.ts   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 2cef55df6c9e..f57aa51b5b54 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -416,7 +416,7 @@ async function assignRole() {
 	if (canceled) return;
 
 	const { canceled: canceled2, result: period } = await os.select({
-		title: i18n.ts.period,
+		title: i18n.ts.period + ': ' + roles.find(r => r.id === roleId)!.name,
 		items: [{
 			value: 'indefinitely', text: i18n.ts.indefinitely,
 		}, {
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index ab8005045bdd..8b3c906d8a6e 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -119,7 +119,7 @@ async function assign() {
 	const user = await os.selectUser({ includeSelf: true });
 
 	const { canceled: canceled2, result: period } = await os.select({
-		title: i18n.ts.period,
+		title: i18n.ts.period + ': ' + role.name,
 		items: [{
 			value: 'indefinitely', text: i18n.ts.indefinitely,
 		}, {
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index c14f75f38275..3e031d232ff9 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -272,7 +272,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 						text: r.name,
 						action: async () => {
 							const { canceled, result: period } = await os.select({
-								title: i18n.ts.period,
+								title: i18n.ts.period + ': ' + r.name,
 								items: [{
 									value: 'indefinitely', text: i18n.ts.indefinitely,
 								}, {

From 2b21c1936212b6e1288d545b71544888e84ce8ab Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 4 May 2024 20:56:14 +0900
Subject: [PATCH 186/266] update deps (#13624)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* update deps

* Update package.json

* update deps

* build: pass --strip-leading-paths to restore 0.2.x behavior (#13684)

* :v:

* :v:

* pureimageの代わりに@napi-rs/canvasを使う (#13748)

* pureimageの代わりに@napi-rs/canvasを使う

* remove writestream

* remove createtemp

* wip

* Update ClientServerService.ts

* update pnpm to 9.x

* update deps

* re: update pnpm to 9.x

* update node

* :v:

---------

Co-authored-by: anatawa12 <anatawa12@icloud.com>
Co-authored-by: tamaina <tamaina@hotmail.co.jp>
---
 .devcontainer/devcontainer.json               |     2 +-
 .../workflows/check-misskey-js-autogen.yml    |     2 +-
 .github/workflows/get-api-diff.yml            |     4 +-
 .github/workflows/lint.yml                    |     6 +-
 .github/workflows/on-release-created.yml      |     4 +-
 .github/workflows/storybook.yml               |     2 +-
 .github/workflows/test-backend.yml            |     8 +-
 .github/workflows/test-frontend.yml           |     8 +-
 .github/workflows/test-misskey-js.yml         |     2 +-
 .github/workflows/test-production.yml         |     4 +-
 .github/workflows/validate-api-json.yml       |     4 +-
 .node-version                                 |     2 +-
 Dockerfile                                    |     2 +-
 package.json                                  |    24 +-
 packages/backend/package.json                 |   114 +-
 packages/backend/src/core/WebAuthnService.ts  |    22 +-
 packages/backend/src/misc/gen-identicon.ts    |     9 +-
 packages/backend/src/server/ServerService.ts  |     5 +-
 .../server/api/endpoints/i/2fa/key-done.ts    |     6 +-
 .../src/server/web/ClientServerService.ts     |     5 +
 .../backend/src/server/web/views/base.pug     |     2 +-
 .../frontend/.storybook/preview-head.html     |     2 +-
 packages/frontend/package.json                |    64 +-
 packages/frontend/src/_dev_boot_.ts           |     2 +-
 packages/misskey-js/package.json              |    14 +-
 packages/sw/package.json                      |     6 +-
 pnpm-lock.yaml                                | 24340 +++++++++-------
 scripts/build-assets.mjs                      |     2 +-
 28 files changed, 14056 insertions(+), 10611 deletions(-)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index f8d9905ecdf6..182ee2fbb2cc 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -8,7 +8,7 @@
 			"version": "8.9.2"
 		},
 		"ghcr.io/devcontainers/features/node:1": {
-			"version": "20.10.0"
+			"version": "20.12.2"
 		}
 	},
 	"forwardPorts": [3000],
diff --git a/.github/workflows/check-misskey-js-autogen.yml b/.github/workflows/check-misskey-js-autogen.yml
index 4aa0646b7b89..9052b2e3722e 100644
--- a/.github/workflows/check-misskey-js-autogen.yml
+++ b/.github/workflows/check-misskey-js-autogen.yml
@@ -26,7 +26,7 @@ jobs:
       - name: setup pnpm
         uses: pnpm/action-setup@v3
         with:
-          version: 8
+          version: 9
 
       - name: setup node
         id: setup-node
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index e737b89b42e5..146e0686e584 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -18,7 +18,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
         api-json-name: [api-base.json, api-head.json]
         include:
           - api-json-name: api-base.json
@@ -34,7 +34,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v3
       with:
-        version: 8
+        version: 9
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 9b3f85fe1db9..9a269014ab82 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -29,7 +29,7 @@ jobs:
         submodules: true
     - uses: pnpm/action-setup@v3
       with:
-        version: 8
+        version: 9
         run_install: false
     - uses: actions/setup-node@v4.0.2
       with:
@@ -56,7 +56,7 @@ jobs:
         submodules: true
     - uses: pnpm/action-setup@v3
       with:
-        version: 7
+        version: 9
         run_install: false
     - uses: actions/setup-node@v4.0.2
       with:
@@ -82,7 +82,7 @@ jobs:
         submodules: true
     - uses: pnpm/action-setup@v3
       with:
-        version: 7
+        version: 9
         run_install: false
     - uses: actions/setup-node@v4.0.2
       with:
diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml
index 069534bd53c5..52463d7542f9 100644
--- a/.github/workflows/on-release-created.yml
+++ b/.github/workflows/on-release-created.yml
@@ -17,7 +17,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
 
     steps:
       - uses: actions/checkout@v4.1.1
@@ -26,7 +26,7 @@ jobs:
       - name: Install pnpm
         uses: pnpm/action-setup@v3
         with:
-          version: 8
+          version: 9
           run_install: false
       - name: Use Node.js ${{ matrix.node-version }}
         uses: actions/setup-node@v4.0.2
diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml
index ca82f4bcf3de..3bc354b331c7 100644
--- a/.github/workflows/storybook.yml
+++ b/.github/workflows/storybook.yml
@@ -36,7 +36,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v3
       with:
-        version: 8
+        version: 9
         run_install: false
     - name: Use Node.js 20.x
       uses: actions/setup-node@v4.0.2
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index a803db4508a5..525cd0916b0f 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -21,7 +21,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
 
     services:
       postgres:
@@ -43,7 +43,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v3
       with:
-        version: 8
+        version: 9
         run_install: false
     - name: Install FFmpeg
       uses: FedericoCarboni/setup-ffmpeg@v3
@@ -73,7 +73,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
 
     services:
       postgres:
@@ -95,7 +95,7 @@ jobs:
       - name: Install pnpm
         uses: pnpm/action-setup@v3
         with:
-          version: 8
+          version: 9
           run_install: false
       - name: Use Node.js ${{ matrix.node-version }}
         uses: actions/setup-node@v4.0.2
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 1e020b73680b..9df3c983935a 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -26,7 +26,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
 
     steps:
     - uses: actions/checkout@v4.1.1
@@ -35,7 +35,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v3
       with:
-        version: 8
+        version: 9
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
@@ -64,7 +64,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
         browser: [chrome]
 
     services:
@@ -93,7 +93,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v3
       with:
-        version: 7
+        version: 9
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index f73bd0b08f7c..2589d908b8d4 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -20,7 +20,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
         # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
 
     steps:
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index 77af08b6fe0f..24a530e07370 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -16,7 +16,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
 
     steps:
     - uses: actions/checkout@v4.1.1
@@ -25,7 +25,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v3
       with:
-        version: 8
+        version: 9
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml
index 36ed8d273f65..229c447893bc 100644
--- a/.github/workflows/validate-api-json.yml
+++ b/.github/workflows/validate-api-json.yml
@@ -17,7 +17,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.10.0]
+        node-version: [20.12.2]
 
     steps:
     - uses: actions/checkout@v4.1.1
@@ -26,7 +26,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v3
       with:
-        version: 8
+        version: 9
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
diff --git a/.node-version b/.node-version
index d5a159609d09..87834047a6fa 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-20.10.0
+20.12.2
diff --git a/Dockerfile b/Dockerfile
index ee3a30a3c17d..9fc2d611cd82 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
 # syntax = docker/dockerfile:1.4
 
-ARG NODE_VERSION=20.10.0-bullseye
+ARG NODE_VERSION=20.12.2-bullseye
 
 # build assets & compile TypeScript
 
diff --git a/package.json b/package.json
index 84d6db5124a3..23e0ea0ee5b9 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
 		"type": "git",
 		"url": "https://github.com/misskey-dev/misskey.git"
 	},
-	"packageManager": "pnpm@8.15.4",
+	"packageManager": "pnpm@9.0.6",
 	"workspaces": [
 		"packages/frontend",
 		"packages/backend",
@@ -48,24 +48,24 @@
 		"lodash": "4.17.21"
 	},
 	"dependencies": {
-		"cssnano": "6.0.5",
+		"cssnano": "6.1.2",
 		"execa": "8.0.1",
 		"fast-glob": "3.3.2",
 		"ignore-walk": "6.0.4",
 		"js-yaml": "4.1.0",
-		"postcss": "8.4.35",
-		"tar": "6.2.0",
-		"terser": "5.28.1",
-		"typescript": "5.3.3",
-		"esbuild": "0.19.11",
-		"glob": "10.3.10"
+		"postcss": "8.4.38",
+		"tar": "6.2.1",
+		"terser": "5.30.3",
+		"typescript": "5.4.5",
+		"esbuild": "0.20.2",
+		"glob": "10.3.12"
 	},
 	"devDependencies": {
-		"@types/node": "^20.11.28",
-		"@typescript-eslint/eslint-plugin": "7.1.0",
-		"@typescript-eslint/parser": "7.1.0",
+		"@types/node": "20.12.7",
+		"@typescript-eslint/eslint-plugin": "7.7.1",
+		"@typescript-eslint/parser": "7.7.1",
 		"cross-env": "7.0.3",
-		"cypress": "13.6.6",
+		"cypress": "13.7.3",
 		"eslint": "8.57.0",
 		"ncp": "2.0.0",
 		"start-server-and-test": "2.0.3"
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 7f70ae0c975a..23b3bfdb8bfe 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -12,9 +12,9 @@
 		"migrate": "pnpm typeorm migration:run -d ormconfig.js",
 		"revert": "pnpm typeorm migration:revert -d ormconfig.js",
 		"check:connect": "node ./scripts/check_connect.js",
-		"build": "swc src -d built -D",
-		"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc",
-		"watch:swc": "swc src -d built -D -w",
+		"build": "swc src -d built -D --strip-leading-paths",
+		"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths",
+		"watch:swc": "swc src -d built -D -w --strip-leading-paths",
 		"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
 		"watch": "node ./scripts/watch.mjs",
 		"restart": "pnpm build && pnpm start",
@@ -67,38 +67,39 @@
 	"dependencies": {
 		"@aws-sdk/client-s3": "3.412.0",
 		"@aws-sdk/lib-storage": "3.412.0",
-		"@bull-board/api": "5.14.2",
-		"@bull-board/fastify": "5.14.2",
-		"@bull-board/ui": "5.14.2",
-		"@discordapp/twemoji": "15.0.2",
+		"@bull-board/api": "5.17.0",
+		"@bull-board/fastify": "5.17.0",
+		"@bull-board/ui": "5.17.0",
+		"@discordapp/twemoji": "15.0.3",
 		"@fastify/accepts": "4.3.0",
 		"@fastify/cookie": "9.3.1",
-		"@fastify/cors": "8.5.0",
-		"@fastify/express": "2.3.0",
-		"@fastify/http-proxy": "9.3.0",
-		"@fastify/multipart": "8.1.0",
-		"@fastify/static": "6.12.0",
-		"@fastify/view": "8.2.0",
+		"@fastify/cors": "9.0.1",
+		"@fastify/express": "3.0.0",
+		"@fastify/http-proxy": "9.5.0",
+		"@fastify/multipart": "8.2.0",
+		"@fastify/static": "7.0.3",
+		"@fastify/view": "9.1.0",
 		"@misskey-dev/sharp-read-bmp": "1.2.0",
 		"@misskey-dev/summaly": "5.1.0",
-		"@nestjs/common": "10.3.3",
-		"@nestjs/core": "10.3.3",
-		"@nestjs/testing": "10.3.3",
+		"@napi-rs/canvas": "^0.1.52",
+		"@nestjs/common": "10.3.8",
+		"@nestjs/core": "10.3.8",
+		"@nestjs/testing": "10.3.8",
 		"@peertube/http-signature": "1.7.0",
-		"@simplewebauthn/server": "9.0.3",
+		"@simplewebauthn/server": "10.0.0",
 		"@sinonjs/fake-timers": "11.2.2",
-		"@smithy/node-http-handler": "2.1.10",
-		"@swc/cli": "0.1.63",
-		"@swc/core": "1.3.107",
-		"@twemoji/parser": "15.0.0",
+		"@smithy/node-http-handler": "2.5.0",
+		"@swc/cli": "0.3.12",
+		"@swc/core": "1.4.17",
+		"@twemoji/parser": "15.1.1",
 		"accepts": "1.3.8",
-		"ajv": "8.12.0",
-		"archiver": "6.0.1",
-		"async-mutex": "0.4.1",
+		"ajv": "8.13.0",
+		"archiver": "7.0.1",
+		"async-mutex": "0.5.0",
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "5.4.0",
+		"bullmq": "5.7.8",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.2",
 		"chalk": "5.3.0",
@@ -109,85 +110,84 @@
 		"content-disposition": "0.5.4",
 		"date-fns": "2.30.0",
 		"deep-email-validator": "0.1.21",
-		"fastify": "4.25.2",
+		"fastify": "4.26.2",
 		"fastify-raw-body": "4.3.0",
 		"feed": "4.2.2",
 		"file-type": "19.0.0",
 		"fluent-ffmpeg": "2.1.2",
 		"form-data": "4.0.0",
-		"got": "14.2.0",
-		"happy-dom": "10.0.3",
+		"got": "14.2.1",
+		"happy-dom": "14.7.1",
 		"hpagent": "1.2.0",
 		"htmlescape": "1.1.1",
-		"http-link-header": "1.1.2",
-		"ioredis": "5.3.2",
+		"http-link-header": "1.1.3",
+		"ioredis": "5.4.1",
 		"ip-cidr": "3.1.0",
-		"ipaddr.js": "2.1.0",
+		"ipaddr.js": "2.2.0",
 		"is-svg": "5.0.0",
 		"js-yaml": "4.1.0",
-		"jsdom": "23.2.0",
+		"jsdom": "24.0.0",
 		"json5": "2.2.3",
 		"jsonld": "8.3.2",
 		"jsrsasign": "11.1.0",
-		"meilisearch": "0.37.0",
+		"meilisearch": "0.38.0",
 		"mfm-js": "0.24.0",
 		"microformats-parser": "2.0.2",
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",
 		"misskey-reversi": "workspace:*",
 		"ms": "3.0.0-canary.1",
-		"nanoid": "5.0.6",
+		"nanoid": "5.0.7",
 		"nested-property": "4.0.0",
 		"node-fetch": "3.3.2",
-		"nodemailer": "6.9.10",
+		"nodemailer": "6.9.13",
 		"nsfwjs": "2.4.2",
 		"oauth": "0.10.0",
 		"oauth2orize": "1.12.0",
 		"oauth2orize-pkce": "0.1.2",
 		"os-utils": "0.0.14",
-		"otpauth": "9.2.2",
+		"otpauth": "9.2.3",
 		"parse5": "7.1.2",
-		"pg": "8.11.3",
+		"pg": "8.11.5",
 		"pkce-challenge": "4.1.0",
 		"probe-image-size": "7.2.3",
 		"promise-limit": "2.7.0",
 		"pug": "3.0.2",
 		"punycode": "2.3.1",
-		"pureimage": "0.3.17",
 		"qrcode": "1.5.3",
 		"random-seed": "0.3.0",
 		"ratelimiter": "3.4.1",
-		"re2": "1.20.9",
+		"re2": "1.20.10",
 		"redis-lock": "0.1.4",
-		"reflect-metadata": "0.2.1",
+		"reflect-metadata": "0.2.2",
 		"rename": "1.0.4",
 		"rss-parser": "3.13.0",
 		"rxjs": "7.8.1",
-		"sanitize-html": "2.12.1",
+		"sanitize-html": "2.13.0",
 		"secure-json-parse": "2.7.0",
-		"sharp": "0.33.2",
+		"sharp": "0.33.3",
 		"slacc": "0.0.10",
 		"strict-event-emitter-types": "2.0.0",
 		"stringz": "2.1.0",
-		"systeminformation": "5.22.0",
+		"systeminformation": "5.22.7",
 		"tinycolor2": "1.6.0",
-		"tmp": "0.2.2",
+		"tmp": "0.2.3",
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
 		"typeorm": "0.3.20",
-		"typescript": "5.3.3",
+		"typescript": "5.4.5",
 		"ulid": "2.3.0",
 		"vary": "1.1.2",
 		"web-push": "3.6.7",
-		"ws": "8.16.0",
+		"ws": "8.17.0",
 		"xev": "3.0.2"
 	},
 	"devDependencies": {
 		"@jest/globals": "29.7.0",
 		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@nestjs/platform-express": "10.3.3",
-		"@simplewebauthn/types": "9.0.1",
-		"@swc/jest": "0.2.31",
+		"@nestjs/platform-express": "10.3.8",
+		"@simplewebauthn/types": "10.0.0",
+		"@swc/jest": "0.2.36",
 		"@types/accepts": "1.3.7",
 		"@types/archiver": "6.0.2",
 		"@types/bcryptjs": "2.4.6",
@@ -197,20 +197,20 @@
 		"@types/fluent-ffmpeg": "2.1.24",
 		"@types/htmlescape": "^1.1.3",
 		"@types/http-link-header": "1.0.5",
-		"@types/jest": "29.5.11",
+		"@types/jest": "29.5.12",
 		"@types/js-yaml": "4.0.9",
 		"@types/jsdom": "21.1.6",
 		"@types/jsonld": "1.5.13",
-		"@types/jsrsasign": "10.5.12",
+		"@types/jsrsasign": "10.5.14",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.11.22",
+		"@types/node": "20.12.7",
 		"@types/node-fetch": "3.0.3",
-		"@types/nodemailer": "6.4.14",
+		"@types/nodemailer": "6.4.15",
 		"@types/oauth": "0.9.4",
-		"@types/oauth2orize": "1.11.3",
+		"@types/oauth2orize": "1.11.5",
 		"@types/oauth2orize-pkce": "0.1.2",
-		"@types/pg": "8.11.2",
+		"@types/pg": "8.11.5",
 		"@types/pug": "2.0.10",
 		"@types/punycode": "2.1.4",
 		"@types/qrcode": "1.5.5",
@@ -226,8 +226,8 @@
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "7.1.0",
-		"@typescript-eslint/parser": "7.1.0",
+		"@typescript-eslint/eslint-plugin": "7.7.1",
+		"@typescript-eslint/parser": "7.7.1",
 		"aws-sdk-client-mock": "3.0.1",
 		"cross-env": "7.0.3",
 		"eslint": "8.57.0",
diff --git a/packages/backend/src/core/WebAuthnService.ts b/packages/backend/src/core/WebAuthnService.ts
index 42fbed21107a..ec9f4484a4c0 100644
--- a/packages/backend/src/core/WebAuthnService.ts
+++ b/packages/backend/src/core/WebAuthnService.ts
@@ -10,7 +10,7 @@ import {
 	generateRegistrationOptions, verifyAuthenticationResponse,
 	verifyRegistrationResponse,
 } from '@simplewebauthn/server';
-import { AttestationFormat, isoCBOR } from '@simplewebauthn/server/helpers';
+import { AttestationFormat, isoCBOR, isoUint8Array } from '@simplewebauthn/server/helpers';
 import { DI } from '@/di-symbols.js';
 import type { UserSecurityKeysRepository } from '@/models/_.js';
 import type { Config } from '@/config.js';
@@ -49,7 +49,7 @@ export class WebAuthnService {
 		const instance = await this.metaService.fetch();
 		return {
 			origin: this.config.url,
-			rpId: this.config.host,
+			rpId: this.config.hostname,
 			rpName: instance.name ?? this.config.host,
 			rpIcon: instance.iconUrl ?? undefined,
 		};
@@ -65,13 +65,12 @@ export class WebAuthnService {
 		const registrationOptions = await generateRegistrationOptions({
 			rpName: relyingParty.rpName,
 			rpID: relyingParty.rpId,
-			userID: userId,
+			userID: isoUint8Array.fromUTF8String(userId),
 			userName: userName,
 			userDisplayName: userDisplayName,
 			attestationType: 'indirect',
-			excludeCredentials: keys.map(key => (<PublicKeyCredentialDescriptorFuture>{
-				id: Buffer.from(key.id, 'base64url'),
-				type: 'public-key',
+			excludeCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{
+				id: key.id,
 				transports: key.transports ?? undefined,
 			})),
 			authenticatorSelection: {
@@ -87,7 +86,7 @@ export class WebAuthnService {
 
 	@bindThis
 	public async verifyRegistration(userId: MiUser['id'], response: RegistrationResponseJSON): Promise<{
-		credentialID: Uint8Array;
+		credentialID: string;
 		credentialPublicKey: Uint8Array;
 		attestationObject: Uint8Array;
 		fmt: AttestationFormat;
@@ -144,6 +143,7 @@ export class WebAuthnService {
 
 	@bindThis
 	public async initiateAuthentication(userId: MiUser['id']): Promise<PublicKeyCredentialRequestOptionsJSON> {
+		const relyingParty = await this.getRelyingParty();
 		const keys = await this.userSecurityKeysRepository.findBy({
 			userId: userId,
 		});
@@ -153,9 +153,9 @@ export class WebAuthnService {
 		}
 
 		const authenticationOptions = await generateAuthenticationOptions({
-			allowCredentials: keys.map(key => (<PublicKeyCredentialDescriptorFuture>{
-				id: Buffer.from(key.id, 'base64url'),
-				type: 'public-key',
+			rpID: relyingParty.rpId,
+			allowCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{
+				id: key.id,
 				transports: key.transports ?? undefined,
 			})),
 			userVerification: 'preferred',
@@ -219,7 +219,7 @@ export class WebAuthnService {
 				expectedOrigin: relyingParty.origin,
 				expectedRPID: relyingParty.rpId,
 				authenticator: {
-					credentialID: Buffer.from(key.id, 'base64url'),
+					credentialID: key.id,
 					credentialPublicKey: Buffer.from(key.publicKey, 'base64url'),
 					counter: key.counter,
 					transports: key.transports ? key.transports as AuthenticatorTransportFuture[] : undefined,
diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts
index 62a8ab8ace45..342e0f8602c6 100644
--- a/packages/backend/src/misc/gen-identicon.ts
+++ b/packages/backend/src/misc/gen-identicon.ts
@@ -8,9 +8,8 @@
  * https://en.wikipedia.org/wiki/Identicon
  */
 
-import * as p from 'pureimage';
+import { createCanvas } from '@napi-rs/canvas';
 import gen from 'random-seed';
-import type { WriteStream } from 'node:fs';
 
 const size = 128; // px
 const n = 5; // resolution
@@ -45,9 +44,9 @@ const sideN = Math.floor(n / 2);
 /**
  * Generate buffer of an identicon by seed
  */
-export function genIdenticon(seed: string, stream: WriteStream): Promise<void> {
+export async function genIdenticon(seed: string): Promise<Buffer> {
 	const rand = gen.create(seed);
-	const canvas = p.make(size, size, undefined);
+	const canvas = createCanvas(size, size);
 	const ctx = canvas.getContext('2d');
 
 	const bgColors = colors[rand(colors.length)];
@@ -101,5 +100,5 @@ export function genIdenticon(seed: string, stream: WriteStream): Promise<void> {
 		}
 	}
 
-	return p.encodePNGToStream(canvas, stream);
+	return await canvas.encode('png');
 }
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 1324cd1361e5..da17a88e0351 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -18,7 +18,6 @@ import { DI } from '@/di-symbols.js';
 import type Logger from '@/logger.js';
 import * as Acct from '@/misc/acct.js';
 import { genIdenticon } from '@/misc/gen-identicon.js';
-import { createTemp } from '@/misc/create-temp.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
@@ -192,9 +191,7 @@ export class ServerService implements OnApplicationShutdown {
 			reply.header('Cache-Control', 'public, max-age=86400');
 
 			if ((await this.metaService.fetch()).enableIdenticonGeneration) {
-				const [temp, cleanup] = await createTemp();
-				await genIdenticon(request.params.x, fs.createWriteStream(temp));
-				return fs.createReadStream(temp).on('close', () => cleanup());
+				return await genIdenticon(request.params.x);
 			} else {
 				return reply.redirect('/static-assets/avatar.png');
 			}
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
index 5f738420f2eb..65eece5b9705 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
@@ -96,10 +96,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			}
 
 			const keyInfo = await this.webAuthnService.verifyRegistration(me.id, ps.credential);
+			const keyId = keyInfo.credentialID;
 
-			const credentialId = Buffer.from(keyInfo.credentialID).toString('base64url');
 			await this.userSecurityKeysRepository.insert({
-				id: credentialId,
+				id: keyId,
 				userId: me.id,
 				name: ps.name,
 				publicKey: Buffer.from(keyInfo.credentialPublicKey).toString('base64url'),
@@ -116,7 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			}));
 
 			return {
-				id: credentialId,
+				id: keyId,
 				name: ps.name,
 			};
 		});
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index ba2f8b432409..1394616752bb 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -199,6 +199,11 @@ export class ClientServerService {
 
 		// Authenticate
 		fastify.addHook('onRequest', async (request, reply) => {
+			if (request.routeOptions.url == null) {
+				reply.code(404).send('Not found');
+				return;
+			}
+
 			// %71ueueとかでリクエストされたら困るため
 			const url = decodeURI(request.routeOptions.url);
 			if (url === bullBoardPath || url.startsWith(bullBoardPath + '/')) {
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 123336809be0..1d9146e22a37 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -36,7 +36,7 @@ html
 		link(rel='prefetch' href=infoImageUrl)
 		link(rel='prefetch' href=notFoundImageUrl)
 		//- https://github.com/misskey-dev/misskey/issues/9842
-		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.44.0')
+		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v3.3.0')
 		link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
 
 		if !config.clientManifestExists
diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html
index e50c48824332..4722fe7f5f46 100644
--- a/packages/frontend/.storybook/preview-head.html
+++ b/packages/frontend/.storybook/preview-head.html
@@ -5,7 +5,7 @@
 
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
-<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.44.0/tabler-icons.min.css">
+<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.3.0/tabler-icons.min.css">
 <link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
 <style>
 	html {
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 43a7759fa60c..a482373200ef 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -17,7 +17,7 @@
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"dependencies": {
-		"@discordapp/twemoji": "15.0.2",
+		"@discordapp/twemoji": "15.0.3",
 		"@github/webauthn-json": "2.1.1",
 		"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
 		"@misskey-dev/browser-image-resizer": "2024.1.0",
@@ -25,23 +25,23 @@
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
 		"@syuilo/aiscript": "0.18.0",
-		"@tabler/icons-webfont": "2.44.0",
-		"@twemoji/parser": "15.0.0",
+		"@tabler/icons-webfont": "3.3.0",
+		"@twemoji/parser": "15.1.1",
 		"@vitejs/plugin-vue": "5.0.4",
-		"@vue/compiler-sfc": "3.4.21",
+		"@vue/compiler-sfc": "3.4.26",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.4",
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
 		"buraha": "0.0.1",
-		"canvas-confetti": "1.9.2",
+		"canvas-confetti": "1.9.3",
 		"chart.js": "4.4.2",
 		"chartjs-adapter-date-fns": "3.0.0",
 		"chartjs-chart-matrix": "2.0.1",
 		"chartjs-plugin-gradient": "0.6.1",
 		"chartjs-plugin-zoom": "2.0.1",
-		"chromatic": "11.0.0",
+		"chromatic": "11.3.0",
 		"compare-versions": "6.1.0",
-		"cropperjs": "2.0.0-beta.4",
+		"cropperjs": "2.0.0-beta.5",
 		"date-fns": "2.30.0",
 		"escape-regexp": "0.0.1",
 		"estree-walker": "3.0.3",
@@ -57,27 +57,27 @@
 		"misskey-reversi": "workspace:*",
 		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
-		"rollup": "4.12.0",
-		"sanitize-html": "2.12.1",
-		"sass": "1.71.1",
-		"shiki": "1.2.0",
+		"rollup": "4.17.2",
+		"sanitize-html": "2.13.0",
+		"sass": "1.76.0",
+		"shiki": "1.4.0",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
-		"three": "0.162.0",
+		"three": "0.164.1",
 		"throttle-debounce": "5.0.0",
 		"tinycolor2": "1.6.0",
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
-		"typescript": "5.3.3",
+		"typescript": "5.4.5",
 		"uuid": "9.0.1",
-		"v-code-diff": "1.9.0",
-		"vite": "5.1.4",
-		"vue": "3.4.21",
+		"v-code-diff": "1.11.0",
+		"vite": "5.2.11",
+		"vue": "3.4.26",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@misskey-dev/summaly": "5.0.3",
+		"@misskey-dev/summaly": "5.1.0",
 		"@storybook/addon-actions": "8.0.9",
 		"@storybook/addon-essentials": "8.0.9",
 		"@storybook/addon-interactions": "8.0.9",
@@ -96,46 +96,46 @@
 		"@storybook/types": "8.0.9",
 		"@storybook/vue3": "8.0.9",
 		"@storybook/vue3-vite": "8.0.9",
-		"@testing-library/vue": "8.0.2",
+		"@testing-library/vue": "8.0.3",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
 		"@types/matter-js": "0.19.6",
-		"@types/micromatch": "4.0.6",
-		"@types/node": "20.11.22",
+		"@types/micromatch": "4.0.7",
+		"@types/node": "20.12.7",
 		"@types/punycode": "2.1.4",
 		"@types/sanitize-html": "2.11.0",
 		"@types/throttle-debounce": "5.0.2",
 		"@types/tinycolor2": "1.4.6",
 		"@types/uuid": "9.0.8",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "7.1.0",
-		"@typescript-eslint/parser": "7.1.0",
+		"@typescript-eslint/eslint-plugin": "7.7.1",
+		"@typescript-eslint/parser": "7.7.1",
 		"@vitest/coverage-v8": "0.34.6",
-		"@vue/runtime-core": "3.4.21",
+		"@vue/runtime-core": "3.4.26",
 		"acorn": "8.11.3",
 		"cross-env": "7.0.3",
-		"cypress": "13.6.6",
+		"cypress": "13.8.1",
 		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
-		"eslint-plugin-vue": "9.22.0",
+		"eslint-plugin-vue": "9.25.0",
 		"fast-glob": "3.3.2",
-		"happy-dom": "13.6.2",
+		"happy-dom": "14.7.1",
 		"intersection-observer": "0.12.2",
 		"micromatch": "4.0.5",
-		"msw": "2.1.7",
-		"msw-storybook-addon": "2.0.0-beta.1",
+		"msw": "2.2.14",
+		"msw-storybook-addon": "2.0.1",
 		"nodemon": "3.1.0",
 		"prettier": "3.2.5",
-		"react": "18.2.0",
-		"react-dom": "18.2.0",
+		"react": "18.3.1",
+		"react-dom": "18.3.1",
 		"start-server-and-test": "2.0.3",
 		"storybook": "8.0.9",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
 		"vite-plugin-turbosnap": "1.0.3",
 		"vitest": "0.34.6",
 		"vitest-fetch-mock": "0.2.2",
-		"vue-component-type-helpers": "1.8.27",
+		"vue-component-type-helpers": "2.0.16",
 		"vue-eslint-parser": "9.4.2",
-		"vue-tsc": "1.8.27"
+		"vue-tsc": "2.0.16"
 	}
 }
diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts
index eceec76c5148..7c6e537fbc17 100644
--- a/packages/frontend/src/_dev_boot_.ts
+++ b/packages/frontend/src/_dev_boot_.ts
@@ -6,7 +6,7 @@
 // devモードで起動される際(index.htmlを使うとき)はrouterが暴発してしまってうまく読み込めない。
 // よって、devモードとして起動されるときはビルド時に組み込む形としておく。
 // (pnpm start時はpugファイルの中で静的リソースとして読み込むようになっており、この問題は起こっていない)
-import '@tabler/icons-webfont/tabler-icons.scss';
+import '@tabler/icons-webfont/dist/tabler-icons.scss';
 
 await main();
 
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index a9c75c95c28e..6badc7f3eea8 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -33,13 +33,13 @@
 		"url": "git+https://github.com/misskey-dev/misskey.js.git"
 	},
 	"devDependencies": {
-		"@microsoft/api-extractor": "7.39.1",
+		"@microsoft/api-extractor": "7.43.1",
 		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@swc/jest": "0.2.31",
+		"@swc/jest": "0.2.36",
 		"@types/jest": "29.5.12",
-		"@types/node": "20.11.22",
-		"@typescript-eslint/eslint-plugin": "7.1.0",
-		"@typescript-eslint/parser": "7.1.0",
+		"@types/node": "20.12.7",
+		"@typescript-eslint/eslint-plugin": "7.7.1",
+		"@typescript-eslint/parser": "7.7.1",
 		"eslint": "8.57.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
@@ -49,9 +49,9 @@
 		"nodemon": "3.1.0",
 		"execa": "8.0.1",
 		"tsd": "0.30.7",
-		"typescript": "5.3.3",
+		"typescript": "5.4.5",
 		"esbuild": "0.19.11",
-		"glob": "10.3.10"
+		"glob": "10.3.12"
 	},
 	"files": [
 		"built"
diff --git a/packages/sw/package.json b/packages/sw/package.json
index bac0cc1ff501..cb59a7023862 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,18 +9,18 @@
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"dependencies": {
-		"esbuild": "0.19.11",
+		"esbuild": "0.20.2",
 		"idb-keyval": "6.2.1",
 		"misskey-js": "workspace:*"
 	},
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@typescript-eslint/parser": "7.1.0",
+		"@typescript-eslint/parser": "7.7.1",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
 		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
 		"nodemon": "3.1.0",
-		"typescript": "5.3.3"
+		"typescript": "5.4.5"
 	},
 	"type": "module"
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8e5cc2d6999b..9ddc1f11604d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.0'
+lockfileVersion: '9.0'
 
 settings:
   autoInstallPeers: true
@@ -13,11 +13,11 @@ importers:
   .:
     dependencies:
       cssnano:
-        specifier: 6.0.5
-        version: 6.0.5(postcss@8.4.35)
+        specifier: 6.1.2
+        version: 6.1.2(postcss@8.4.38)
       esbuild:
-        specifier: 0.19.11
-        version: 0.19.11
+        specifier: 0.20.2
+        version: 0.20.2
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -25,8 +25,8 @@ importers:
         specifier: 3.3.2
         version: 3.3.2
       glob:
-        specifier: 10.3.10
-        version: 10.3.10
+        specifier: 10.3.12
+        version: 10.3.12
       ignore-walk:
         specifier: 6.0.4
         version: 6.0.4
@@ -34,37 +34,37 @@ importers:
         specifier: 4.1.0
         version: 4.1.0
       postcss:
-        specifier: 8.4.35
-        version: 8.4.35
+        specifier: 8.4.38
+        version: 8.4.38
       tar:
-        specifier: 6.2.0
-        version: 6.2.0
+        specifier: 6.2.1
+        version: 6.2.1
       terser:
-        specifier: 5.28.1
-        version: 5.28.1
+        specifier: 5.30.3
+        version: 5.30.3
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.5
+        version: 5.4.5
     optionalDependencies:
       '@tensorflow/tfjs-core':
         specifier: 4.4.0
-        version: 4.4.0
+        version: 4.4.0(encoding@0.1.13)
     devDependencies:
       '@types/node':
-        specifier: ^20.11.28
-        version: 20.11.28
+        specifier: 20.12.7
+        version: 20.12.7
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/parser':
-        specifier: 7.1.0
-        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.6.6
-        version: 13.6.6
+        specifier: 13.7.3
+        version: 13.7.3
       eslint:
         specifier: 8.57.0
         version: 8.57.0
@@ -84,17 +84,17 @@ importers:
         specifier: 3.412.0
         version: 3.412.0(@aws-sdk/client-s3@3.412.0)
       '@bull-board/api':
-        specifier: 5.14.2
-        version: 5.14.2(@bull-board/ui@5.14.2)
+        specifier: 5.17.0
+        version: 5.17.0(@bull-board/ui@5.17.0)
       '@bull-board/fastify':
-        specifier: 5.14.2
-        version: 5.14.2
+        specifier: 5.17.0
+        version: 5.17.0
       '@bull-board/ui':
-        specifier: 5.14.2
-        version: 5.14.2
+        specifier: 5.17.0
+        version: 5.17.0
       '@discordapp/twemoji':
-        specifier: 15.0.2
-        version: 15.0.2
+        specifier: 15.0.3
+        version: 15.0.3
       '@fastify/accepts':
         specifier: 4.3.0
         version: 4.3.0
@@ -102,71 +102,74 @@ importers:
         specifier: 9.3.1
         version: 9.3.1
       '@fastify/cors':
-        specifier: 8.5.0
-        version: 8.5.0
+        specifier: 9.0.1
+        version: 9.0.1
       '@fastify/express':
-        specifier: 2.3.0
-        version: 2.3.0
+        specifier: 3.0.0
+        version: 3.0.0
       '@fastify/http-proxy':
-        specifier: 9.3.0
-        version: 9.3.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 9.5.0
+        version: 9.5.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       '@fastify/multipart':
-        specifier: 8.1.0
-        version: 8.1.0
-      '@fastify/static':
-        specifier: 6.12.0
-        version: 6.12.0
-      '@fastify/view':
         specifier: 8.2.0
         version: 8.2.0
+      '@fastify/static':
+        specifier: 7.0.3
+        version: 7.0.3
+      '@fastify/view':
+        specifier: 9.1.0
+        version: 9.1.0
       '@misskey-dev/sharp-read-bmp':
         specifier: 1.2.0
         version: 1.2.0
       '@misskey-dev/summaly':
         specifier: 5.1.0
         version: 5.1.0
+      '@napi-rs/canvas':
+        specifier: ^0.1.52
+        version: 0.1.52
       '@nestjs/common':
-        specifier: 10.3.3
-        version: 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
+        specifier: 10.3.8
+        version: 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
       '@nestjs/core':
-        specifier: 10.3.3
-        version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
+        specifier: 10.3.8
+        version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
       '@nestjs/testing':
-        specifier: 10.3.3
-        version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3)
+        specifier: 10.3.8
+        version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8))
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
       '@simplewebauthn/server':
-        specifier: 9.0.3
-        version: 9.0.3
+        specifier: 10.0.0
+        version: 10.0.0(encoding@0.1.13)
       '@sinonjs/fake-timers':
         specifier: 11.2.2
         version: 11.2.2
       '@smithy/node-http-handler':
-        specifier: 2.1.10
-        version: 2.1.10
+        specifier: 2.5.0
+        version: 2.5.0
       '@swc/cli':
-        specifier: 0.1.63
-        version: 0.1.63(@swc/core@1.3.107)(chokidar@3.5.3)
+        specifier: 0.3.12
+        version: 0.3.12(@swc/core@1.4.17)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.3.107
-        version: 1.3.107
+        specifier: 1.4.17
+        version: 1.4.17
       '@twemoji/parser':
-        specifier: 15.0.0
-        version: 15.0.0
+        specifier: 15.1.1
+        version: 15.1.1
       accepts:
         specifier: 1.3.8
         version: 1.3.8
       ajv:
-        specifier: 8.12.0
-        version: 8.12.0
+        specifier: 8.13.0
+        version: 8.13.0
       archiver:
-        specifier: 6.0.1
-        version: 6.0.1
+        specifier: 7.0.1
+        version: 7.0.1
       async-mutex:
-        specifier: 0.4.1
-        version: 0.4.1
+        specifier: 0.5.0
+        version: 0.5.0
       bcryptjs:
         specifier: 2.4.3
         version: 2.4.3
@@ -177,8 +180,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 5.4.0
-        version: 5.4.0
+        specifier: 5.7.8
+        version: 5.7.8
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -210,8 +213,8 @@ importers:
         specifier: 0.1.21
         version: 0.1.21
       fastify:
-        specifier: 4.25.2
-        version: 4.25.2
+        specifier: 4.26.2
+        version: 4.26.2
       fastify-raw-body:
         specifier: 4.3.0
         version: 4.3.0
@@ -228,11 +231,11 @@ 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
       happy-dom:
-        specifier: 10.0.3
-        version: 10.0.3
+        specifier: 14.7.1
+        version: 14.7.1
       hpagent:
         specifier: 1.2.0
         version: 1.2.0
@@ -240,17 +243,17 @@ importers:
         specifier: 1.1.1
         version: 1.1.1
       http-link-header:
-        specifier: 1.1.2
-        version: 1.1.2
+        specifier: 1.1.3
+        version: 1.1.3
       ioredis:
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.4.1
+        version: 5.4.1
       ip-cidr:
         specifier: 3.1.0
         version: 3.1.0
       ipaddr.js:
-        specifier: 2.1.0
-        version: 2.1.0
+        specifier: 2.2.0
+        version: 2.2.0
       is-svg:
         specifier: 5.0.0
         version: 5.0.0
@@ -258,20 +261,20 @@ importers:
         specifier: 4.1.0
         version: 4.1.0
       jsdom:
-        specifier: 23.2.0
-        version: 23.2.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 24.0.0
+        version: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       json5:
         specifier: 2.2.3
         version: 2.2.3
       jsonld:
         specifier: 8.3.2
-        version: 8.3.2
+        version: 8.3.2(web-streams-polyfill@3.2.1)
       jsrsasign:
         specifier: 11.1.0
         version: 11.1.0
       meilisearch:
-        specifier: 0.37.0
-        version: 0.37.0
+        specifier: 0.38.0
+        version: 0.38.0(encoding@0.1.13)
       mfm-js:
         specifier: 0.24.0
         version: 0.24.0
@@ -291,8 +294,8 @@ importers:
         specifier: 3.0.0-canary.1
         version: 3.0.0-canary.1
       nanoid:
-        specifier: 5.0.6
-        version: 5.0.6
+        specifier: 5.0.7
+        version: 5.0.7
       nested-property:
         specifier: 4.0.0
         version: 4.0.0
@@ -300,11 +303,11 @@ importers:
         specifier: 3.3.2
         version: 3.3.2
       nodemailer:
-        specifier: 6.9.10
-        version: 6.9.10
+        specifier: 6.9.13
+        version: 6.9.13
       nsfwjs:
         specifier: 2.4.2
-        version: 2.4.2(@tensorflow/tfjs@4.4.0)
+        version: 2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5))
       oauth:
         specifier: 0.10.0
         version: 0.10.0
@@ -318,14 +321,14 @@ importers:
         specifier: 0.0.14
         version: 0.0.14
       otpauth:
-        specifier: 9.2.2
-        version: 9.2.2
+        specifier: 9.2.3
+        version: 9.2.3
       parse5:
         specifier: 7.1.2
         version: 7.1.2
       pg:
-        specifier: 8.11.3
-        version: 8.11.3
+        specifier: 8.11.5
+        version: 8.11.5
       pkce-challenge:
         specifier: 4.1.0
         version: 4.1.0
@@ -341,9 +344,6 @@ importers:
       punycode:
         specifier: 2.3.1
         version: 2.3.1
-      pureimage:
-        specifier: 0.3.17
-        version: 0.3.17
       qrcode:
         specifier: 1.5.3
         version: 1.5.3
@@ -354,14 +354,14 @@ importers:
         specifier: 3.4.1
         version: 3.4.1
       re2:
-        specifier: 1.20.9
-        version: 1.20.9
+        specifier: 1.20.10
+        version: 1.20.10
       redis-lock:
         specifier: 0.1.4
         version: 0.1.4
       reflect-metadata:
-        specifier: 0.2.1
-        version: 0.2.1
+        specifier: 0.2.2
+        version: 0.2.2
       rename:
         specifier: 1.0.4
         version: 1.0.4
@@ -372,14 +372,14 @@ importers:
         specifier: 7.8.1
         version: 7.8.1
       sanitize-html:
-        specifier: 2.12.1
-        version: 2.12.1
+        specifier: 2.13.0
+        version: 2.13.0
       secure-json-parse:
         specifier: 2.7.0
         version: 2.7.0
       sharp:
-        specifier: 0.33.2
-        version: 0.33.2
+        specifier: 0.33.3
+        version: 0.33.3
       slacc:
         specifier: 0.0.10
         version: 0.0.10
@@ -390,14 +390,14 @@ importers:
         specifier: 2.1.0
         version: 2.1.0
       systeminformation:
-        specifier: 5.22.0
-        version: 5.22.0
+        specifier: 5.22.7
+        version: 5.22.7
       tinycolor2:
         specifier: 1.6.0
         version: 1.6.0
       tmp:
-        specifier: 0.2.2
-        version: 0.2.2
+        specifier: 0.2.3
+        version: 0.2.3
       tsc-alias:
         specifier: 1.8.8
         version: 1.8.8
@@ -406,10 +406,10 @@ importers:
         version: 4.2.0
       typeorm:
         specifier: 0.3.20
-        version: 0.3.20(ioredis@5.3.2)(pg@8.11.3)
+        version: 0.3.20(ioredis@5.4.1)(pg@8.11.5)
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.5
+        version: 5.4.5
       ulid:
         specifier: 2.3.0
         version: 2.3.0
@@ -420,8 +420,8 @@ importers:
         specifier: 3.6.7
         version: 3.6.7
       ws:
-        specifier: 8.16.0
-        version: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 8.17.0
+        version: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xev:
         specifier: 3.0.2
         version: 3.0.2
@@ -464,10 +464,10 @@ importers:
         version: 1.3.56
       '@tensorflow/tfjs':
         specifier: 4.4.0
-        version: 4.4.0(seedrandom@3.0.5)
+        version: 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
       '@tensorflow/tfjs-node':
         specifier: 4.4.0
-        version: 4.4.0(seedrandom@3.0.5)
+        version: 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
       bufferutil:
         specifier: 4.0.7
         version: 4.0.7
@@ -519,16 +519,16 @@ importers:
         version: 29.7.0
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
       '@nestjs/platform-express':
-        specifier: 10.3.3
-        version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
+        specifier: 10.3.8
+        version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)
       '@simplewebauthn/types':
-        specifier: 9.0.1
-        version: 9.0.1
+        specifier: 10.0.0
+        version: 10.0.0
       '@swc/jest':
-        specifier: 0.2.31
-        version: 0.2.31(@swc/core@1.3.107)
+        specifier: 0.2.36
+        version: 0.2.36(@swc/core@1.4.17)
       '@types/accepts':
         specifier: 1.3.7
         version: 1.3.7
@@ -557,8 +557,8 @@ importers:
         specifier: 1.0.5
         version: 1.0.5
       '@types/jest':
-        specifier: 29.5.11
-        version: 29.5.11
+        specifier: 29.5.12
+        version: 29.5.12
       '@types/js-yaml':
         specifier: 4.0.9
         version: 4.0.9
@@ -569,8 +569,8 @@ importers:
         specifier: 1.5.13
         version: 1.5.13
       '@types/jsrsasign':
-        specifier: 10.5.12
-        version: 10.5.12
+        specifier: 10.5.14
+        version: 10.5.14
       '@types/mime-types':
         specifier: 2.1.4
         version: 2.1.4
@@ -578,26 +578,26 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.11.22
-        version: 20.11.22
+        specifier: 20.12.7
+        version: 20.12.7
       '@types/node-fetch':
         specifier: 3.0.3
         version: 3.0.3
       '@types/nodemailer':
-        specifier: 6.4.14
-        version: 6.4.14
+        specifier: 6.4.15
+        version: 6.4.15
       '@types/oauth':
         specifier: 0.9.4
         version: 0.9.4
       '@types/oauth2orize':
-        specifier: 1.11.3
-        version: 1.11.3
+        specifier: 1.11.5
+        version: 1.11.5
       '@types/oauth2orize-pkce':
         specifier: 0.1.2
         version: 0.1.2
       '@types/pg':
-        specifier: 8.11.2
-        version: 8.11.2
+        specifier: 8.11.5
+        version: 8.11.5
       '@types/pug':
         specifier: 2.0.10
         version: 2.0.10
@@ -644,11 +644,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/parser':
-        specifier: 7.1.0
-        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       aws-sdk-client-mock:
         specifier: 3.0.1
         version: 3.0.1
@@ -660,7 +660,7 @@ importers:
         version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -669,7 +669,7 @@ importers:
         version: 9.0.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.11.22)
+        version: 29.7.0(@types/node@20.12.7)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
@@ -686,8 +686,8 @@ importers:
   packages/frontend:
     dependencies:
       '@discordapp/twemoji':
-        specifier: 15.0.2
-        version: 15.0.2
+        specifier: 15.0.3
+        version: 15.0.3
       '@github/webauthn-json':
         specifier: 2.1.1
         version: 2.1.1
@@ -699,31 +699,31 @@ importers:
         version: 2024.1.0
       '@rollup/plugin-json':
         specifier: 6.1.0
-        version: 6.1.0(rollup@4.12.0)
+        version: 6.1.0(rollup@4.17.2)
       '@rollup/plugin-replace':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.12.0)
+        version: 5.0.5(rollup@4.17.2)
       '@rollup/pluginutils':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.12.0)
+        version: 5.1.0(rollup@4.17.2)
       '@syuilo/aiscript':
         specifier: 0.18.0
         version: 0.18.0
       '@tabler/icons-webfont':
-        specifier: 2.44.0
-        version: 2.44.0
+        specifier: 3.3.0
+        version: 3.3.0
       '@twemoji/parser':
-        specifier: 15.0.0
-        version: 15.0.0
+        specifier: 15.1.1
+        version: 15.1.1
       '@vitejs/plugin-vue':
         specifier: 5.0.4
-        version: 5.0.4(vite@5.1.4)(vue@3.4.21)
+        version: 5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))
       '@vue/compiler-sfc':
-        specifier: 3.4.21
-        version: 3.4.21
+        specifier: 3.4.26
+        version: 3.4.26
       aiscript-vscode:
         specifier: github:aiscript-dev/aiscript-vscode#v0.1.4
-        version: github.com/aiscript-dev/aiscript-vscode/3f79d6f0550369267220aa67702287948d885424
+        version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424
       astring:
         specifier: 1.8.6
         version: 1.8.6
@@ -734,8 +734,8 @@ importers:
         specifier: 0.0.1
         version: 0.0.1
       canvas-confetti:
-        specifier: 1.9.2
-        version: 1.9.2
+        specifier: 1.9.3
+        version: 1.9.3
       chart.js:
         specifier: 4.4.2
         version: 4.4.2
@@ -752,14 +752,14 @@ importers:
         specifier: 2.0.1
         version: 2.0.1(chart.js@4.4.2)
       chromatic:
-        specifier: 11.0.0
-        version: 11.0.0
+        specifier: 11.3.0
+        version: 11.3.0
       compare-versions:
         specifier: 6.1.0
         version: 6.1.0
       cropperjs:
-        specifier: 2.0.0-beta.4
-        version: 2.0.0-beta.4
+        specifier: 2.0.0-beta.5
+        version: 2.0.0-beta.5
       date-fns:
         specifier: 2.30.0
         version: 2.30.0
@@ -806,17 +806,17 @@ importers:
         specifier: 2.3.1
         version: 2.3.1
       rollup:
-        specifier: 4.12.0
-        version: 4.12.0
+        specifier: 4.17.2
+        version: 4.17.2
       sanitize-html:
-        specifier: 2.12.1
-        version: 2.12.1
+        specifier: 2.13.0
+        version: 2.13.0
       sass:
-        specifier: 1.71.1
-        version: 1.71.1
+        specifier: 1.76.0
+        version: 1.76.0
       shiki:
-        specifier: 1.2.0
-        version: 1.2.0
+        specifier: 1.4.0
+        version: 1.4.0
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -824,8 +824,8 @@ importers:
         specifier: 3.1.0
         version: 3.1.0
       three:
-        specifier: 0.162.0
-        version: 0.162.0
+        specifier: 0.164.1
+        version: 0.164.1
       throttle-debounce:
         specifier: 5.0.0
         version: 5.0.0
@@ -839,42 +839,42 @@ importers:
         specifier: 4.2.0
         version: 4.2.0
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.5
+        version: 5.4.5
       uuid:
         specifier: 9.0.1
         version: 9.0.1
       v-code-diff:
-        specifier: 1.9.0
-        version: 1.9.0(vue@3.4.21)
+        specifier: 1.11.0
+        version: 1.11.0(vue@3.4.26(typescript@5.4.5))
       vite:
-        specifier: 5.1.4
-        version: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+        specifier: 5.2.11
+        version: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
       vue:
-        specifier: 3.4.21
-        version: 3.4.21(typescript@5.3.3)
+        specifier: 3.4.26
+        version: 3.4.26(typescript@5.4.5)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.4.21)
+        version: 4.1.0(vue@3.4.26(typescript@5.4.5))
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
       '@misskey-dev/summaly':
-        specifier: 5.0.3
-        version: 5.0.3
+        specifier: 5.1.0
+        version: 5.1.0
       '@storybook/addon-actions':
         specifier: 8.0.9
         version: 8.0.9
       '@storybook/addon-essentials':
         specifier: 8.0.9
-        version: 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        version: 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/addon-interactions':
         specifier: 8.0.9
-        version: 8.0.9(vitest@0.34.6)
+        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@storybook/addon-links':
         specifier: 8.0.9
-        version: 8.0.9(react@18.2.0)
+        version: 8.0.9(react@18.3.1)
       '@storybook/addon-mdx-gfm':
         specifier: 8.0.9
         version: 8.0.9
@@ -883,43 +883,43 @@ importers:
         version: 8.0.9
       '@storybook/blocks':
         specifier: 8.0.9
-        version: 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        version: 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/components':
         specifier: 8.0.9
-        version: 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+        version: 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/core-events':
         specifier: 8.0.9
         version: 8.0.9
       '@storybook/manager-api':
         specifier: 8.0.9
-        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)
+        version: 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/preview-api':
         specifier: 8.0.9
         version: 8.0.9
       '@storybook/react':
         specifier: 8.0.9
-        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
       '@storybook/react-vite':
         specifier: 8.0.9
-        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4)
+        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
       '@storybook/test':
         specifier: 8.0.9
-        version: 8.0.9(vitest@0.34.6)
+        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@storybook/theming':
         specifier: 8.0.9
-        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)
+        version: 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/types':
         specifier: 8.0.9
         version: 8.0.9
       '@storybook/vue3':
         specifier: 8.0.9
-        version: 8.0.9(vue@3.4.21)
+        version: 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.4.5))
       '@storybook/vue3-vite':
         specifier: 8.0.9
-        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21)
+        version: 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))
       '@testing-library/vue':
-        specifier: 8.0.2
-        version: 8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21)
+        specifier: 8.0.3
+        version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -930,11 +930,11 @@ importers:
         specifier: 0.19.6
         version: 0.19.6
       '@types/micromatch':
-        specifier: 4.0.6
-        version: 4.0.6
+        specifier: 4.0.7
+        version: 4.0.7
       '@types/node':
-        specifier: 20.11.22
-        version: 20.11.22
+        specifier: 20.12.7
+        version: 20.12.7
       '@types/punycode':
         specifier: 2.1.4
         version: 2.1.4
@@ -954,17 +954,17 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/parser':
-        specifier: 7.1.0
-        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       '@vitest/coverage-v8':
         specifier: 0.34.6
-        version: 0.34.6(vitest@0.34.6)
+        version: 0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@vue/runtime-core':
-        specifier: 3.4.21
-        version: 3.4.21
+        specifier: 3.4.26
+        version: 3.4.26
       acorn:
         specifier: 8.11.3
         version: 8.11.3
@@ -972,23 +972,23 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.6.6
-        version: 13.6.6
+        specifier: 13.8.1
+        version: 13.8.1
       eslint:
         specifier: 8.57.0
         version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
       eslint-plugin-vue:
-        specifier: 9.22.0
-        version: 9.22.0(eslint@8.57.0)
+        specifier: 9.25.0
+        version: 9.25.0(eslint@8.57.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
       happy-dom:
-        specifier: 13.6.2
-        version: 13.6.2
+        specifier: 14.7.1
+        version: 14.7.1
       intersection-observer:
         specifier: 0.12.2
         version: 0.12.2
@@ -996,11 +996,11 @@ importers:
         specifier: 4.0.5
         version: 4.0.5
       msw:
-        specifier: 2.1.7
-        version: 2.1.7(typescript@5.3.3)
+        specifier: 2.2.14
+        version: 2.2.14(typescript@5.4.5)
       msw-storybook-addon:
-        specifier: 2.0.0-beta.1
-        version: 2.0.0-beta.1(msw@2.1.7)
+        specifier: 2.0.1
+        version: 2.0.1(msw@2.2.14(typescript@5.4.5))
       nodemon:
         specifier: 3.1.0
         version: 3.1.0
@@ -1008,38 +1008,38 @@ importers:
         specifier: 3.2.5
         version: 3.2.5
       react:
-        specifier: 18.2.0
-        version: 18.2.0
+        specifier: 18.3.1
+        version: 18.3.1
       react-dom:
-        specifier: 18.2.0
-        version: 18.2.0(react@18.2.0)
+        specifier: 18.3.1
+        version: 18.3.1(react@18.3.1)
       start-server-and-test:
         specifier: 2.0.3
         version: 2.0.3
       storybook:
         specifier: 8.0.9
-        version: 8.0.9(react-dom@18.2.0)(react@18.2.0)
+        version: 8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
-        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9)(@storybook/components@8.0.9)(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9)(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9)(@storybook/types@8.0.9)(react-dom@18.2.0)(react@18.2.0)
+        version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       vite-plugin-turbosnap:
         specifier: 1.0.3
         version: 1.0.3
       vitest:
         specifier: 0.34.6
-        version: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
+        version: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
       vitest-fetch-mock:
         specifier: 0.2.2
-        version: 0.2.2(vitest@0.34.6)
+        version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       vue-component-type-helpers:
-        specifier: 1.8.27
-        version: 1.8.27
+        specifier: 2.0.16
+        version: 2.0.16
       vue-eslint-parser:
         specifier: 9.4.2
         version: 9.4.2(eslint@8.57.0)
       vue-tsc:
-        specifier: 1.8.27
-        version: 1.8.27(typescript@5.3.3)
+        specifier: 2.0.16
+        version: 2.0.16(typescript@5.4.5)
 
   packages/misskey-bubble-game:
     dependencies:
@@ -1055,7 +1055,7 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0))(eslint@8.57.0)
       '@types/matter-js':
         specifier: 0.19.6
         version: 0.19.6
@@ -1067,7 +1067,7 @@ importers:
         version: 3.0.8
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
         version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
@@ -1100,26 +1100,26 @@ importers:
         version: 4.4.0
     devDependencies:
       '@microsoft/api-extractor':
-        specifier: 7.39.1
-        version: 7.39.1(@types/node@20.11.22)
+        specifier: 7.43.1
+        version: 7.43.1(@types/node@20.12.7)
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
       '@swc/jest':
-        specifier: 0.2.31
-        version: 0.2.31(@swc/core@1.3.107)
+        specifier: 0.2.36
+        version: 0.2.36(@swc/core@1.4.17)
       '@types/jest':
         specifier: 29.5.12
         version: 29.5.12
       '@types/node':
-        specifier: 20.11.22
-        version: 20.11.22
+        specifier: 20.12.7
+        version: 20.12.7
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/parser':
-        specifier: 7.1.0
-        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
@@ -1130,14 +1130,14 @@ importers:
         specifier: 8.0.1
         version: 8.0.1
       glob:
-        specifier: 10.3.10
-        version: 10.3.10
+        specifier: 10.3.12
+        version: 10.3.12
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.11.22)
+        version: 29.7.0(@types/node@20.12.7)
       jest-fetch-mock:
         specifier: 3.0.3
-        version: 3.0.3
+        version: 3.0.3(encoding@0.1.13)
       jest-websocket-mock:
         specifier: 2.5.0
         version: 2.5.0
@@ -1154,14 +1154,14 @@ importers:
         specifier: 0.30.7
         version: 0.30.7
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.5
+        version: 5.4.5
 
   packages/misskey-js/generator:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: ^1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.11.0)(@typescript-eslint/parser@6.11.0)(eslint-plugin-import@2.29.1)(eslint@8.53.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3))(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0))(eslint@8.53.0)
       '@readme/openapi-parser':
         specifier: 2.5.0
         version: 2.5.0(openapi-types@12.1.3)
@@ -1170,7 +1170,7 @@ importers:
         version: 20.9.1
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.3)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.11.0
         version: 6.11.0(eslint@8.53.0)(typescript@5.3.3)
@@ -1201,13 +1201,13 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0))(eslint@8.57.0)
       '@types/node':
         specifier: 20.11.5
         version: 20.11.5
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
         version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
@@ -1233,8 +1233,8 @@ importers:
   packages/sw:
     dependencies:
       esbuild:
-        specifier: 0.19.11
-        version: 0.19.11
+        specifier: 0.20.2
+        version: 0.20.2
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -1244,299 +1244,10149 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
       '@typescript-eslint/parser':
-        specifier: 7.1.0
-        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+        specifier: 7.7.1
+        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
-        version: /@types/serviceworker@0.0.67
+        version: '@types/serviceworker@0.0.67'
       eslint:
         specifier: 8.57.0
         version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
       nodemon:
         specifier: 3.1.0
         version: 3.1.0
       typescript:
-        specifier: 5.3.3
-        version: 5.3.3
+        specifier: 5.4.5
+        version: 5.4.5
 
 packages:
 
-  /@aashutoshrathi/word-wrap@1.2.6:
+  '@aashutoshrathi/word-wrap@1.2.6':
     resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
-  /@adobe/css-tools@4.3.3:
+  '@adobe/css-tools@4.3.3':
     resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==}
-    dev: true
 
-  /@ampproject/remapping@2.2.1:
+  '@aiscript-dev/aiscript-languageserver@https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz':
+    resolution: {tarball: https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz}
+    version: 0.1.6
+    hasBin: true
+
+  '@ampproject/remapping@2.2.1':
     resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
     engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/gen-mapping': 0.3.2
-      '@jridgewell/trace-mapping': 0.3.18
-    dev: true
 
-  /@apidevtools/openapi-schemas@2.1.0:
+  '@apidevtools/openapi-schemas@2.1.0':
     resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==}
     engines: {node: '>=10'}
-    dev: true
 
-  /@apidevtools/swagger-methods@3.0.2:
+  '@apidevtools/swagger-methods@3.0.2':
     resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
-    dev: true
-
-  /@asamuzakjp/dom-selector@2.0.2:
-    resolution: {integrity: sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==}
-    dependencies:
-      bidi-js: 1.0.3
-      css-tree: 2.3.1
-      is-potential-custom-element-name: 1.0.1
-    dev: false
 
-  /@aw-web-design/x-default-browser@1.4.126:
+  '@aw-web-design/x-default-browser@1.4.126':
     resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==}
     hasBin: true
-    dependencies:
-      default-browser-id: 3.0.0
-    dev: true
 
-  /@aws-crypto/crc32@3.0.0:
+  '@aws-crypto/crc32@3.0.0':
     resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==}
-    dependencies:
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-crypto/crc32c@3.0.0:
+  '@aws-crypto/crc32c@3.0.0':
     resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==}
-    dependencies:
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-crypto/ie11-detection@3.0.0:
+  '@aws-crypto/ie11-detection@3.0.0':
     resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==}
-    dependencies:
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-crypto/sha1-browser@3.0.0:
+  '@aws-crypto/sha1-browser@3.0.0':
     resolution: {integrity: sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==}
-    dependencies:
-      '@aws-crypto/ie11-detection': 3.0.0
-      '@aws-crypto/supports-web-crypto': 3.0.0
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      '@aws-sdk/util-locate-window': 3.208.0
-      '@aws-sdk/util-utf8-browser': 3.259.0
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-crypto/sha256-browser@3.0.0:
+  '@aws-crypto/sha256-browser@3.0.0':
     resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==}
-    dependencies:
-      '@aws-crypto/ie11-detection': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-crypto/supports-web-crypto': 3.0.0
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      '@aws-sdk/util-locate-window': 3.208.0
-      '@aws-sdk/util-utf8-browser': 3.259.0
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-crypto/sha256-js@3.0.0:
+  '@aws-crypto/sha256-js@3.0.0':
     resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==}
-    dependencies:
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-crypto/supports-web-crypto@3.0.0:
+  '@aws-crypto/supports-web-crypto@3.0.0':
     resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==}
-    dependencies:
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-crypto/util@3.0.0:
+  '@aws-crypto/util@3.0.0':
     resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==}
-    dependencies:
-      '@aws-sdk/types': 3.413.0
-      '@aws-sdk/util-utf8-browser': 3.259.0
-      tslib: 1.14.1
-    dev: false
 
-  /@aws-sdk/client-s3@3.412.0:
+  '@aws-sdk/client-s3@3.412.0':
     resolution: {integrity: sha512-sNrlx9sSBmFUCqMgTznwk9Fee3PJat0nZ3RIDR5Crhsld/eexxrqb6TYKsxzFfBfXTL/oPh+/S5driRV2xsB8A==}
     engines: {node: '>=14.0.0'}
-    dependencies:
-      '@aws-crypto/sha1-browser': 3.0.0
-      '@aws-crypto/sha256-browser': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/client-sts': 3.410.0
-      '@aws-sdk/credential-provider-node': 3.410.0
-      '@aws-sdk/middleware-bucket-endpoint': 3.410.0
-      '@aws-sdk/middleware-expect-continue': 3.410.0
-      '@aws-sdk/middleware-flexible-checksums': 3.410.0
-      '@aws-sdk/middleware-host-header': 3.410.0
-      '@aws-sdk/middleware-location-constraint': 3.410.0
-      '@aws-sdk/middleware-logger': 3.410.0
-      '@aws-sdk/middleware-recursion-detection': 3.410.0
-      '@aws-sdk/middleware-sdk-s3': 3.410.0
-      '@aws-sdk/middleware-signing': 3.410.0
-      '@aws-sdk/middleware-ssec': 3.410.0
-      '@aws-sdk/middleware-user-agent': 3.410.0
-      '@aws-sdk/signature-v4-multi-region': 3.412.0
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@aws-sdk/util-user-agent-browser': 3.410.0
-      '@aws-sdk/util-user-agent-node': 3.410.0
-      '@aws-sdk/xml-builder': 3.310.0
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/eventstream-serde-browser': 2.0.8
-      '@smithy/eventstream-serde-config-resolver': 2.0.8
-      '@smithy/eventstream-serde-node': 2.0.8
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/hash-blob-browser': 2.0.8
-      '@smithy/hash-node': 2.0.8
-      '@smithy/hash-stream-node': 2.0.8
-      '@smithy/invalid-dependency': 2.0.8
-      '@smithy/md5-js': 2.0.8
-      '@smithy/middleware-content-length': 2.0.10
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/middleware-retry': 2.0.11
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.10
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-body-length-browser': 2.0.0
-      '@smithy/util-body-length-node': 2.1.0
-      '@smithy/util-defaults-mode-browser': 2.0.9
-      '@smithy/util-defaults-mode-node': 2.0.11
-      '@smithy/util-retry': 2.0.1
-      '@smithy/util-stream': 2.0.11
-      '@smithy/util-utf8': 2.0.0
-      '@smithy/util-waiter': 2.0.8
-      fast-xml-parser: 4.2.5
-      tslib: 2.6.2
-    transitivePeerDependencies:
-      - aws-crt
-    dev: false
 
-  /@aws-sdk/client-sso@3.410.0:
+  '@aws-sdk/client-sso@3.410.0':
     resolution: {integrity: sha512-MC9GrgwtlOuSL2WS3DRM3dQ/5y+49KSMMJRH6JiEcU5vE0dX/OtEcX+VfEwpi73x5pSfIjm7xnzjzOFx+sQBIg==}
     engines: {node: '>=14.0.0'}
-    dependencies:
-      '@aws-crypto/sha256-browser': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/middleware-host-header': 3.410.0
-      '@aws-sdk/middleware-logger': 3.410.0
-      '@aws-sdk/middleware-recursion-detection': 3.410.0
-      '@aws-sdk/middleware-user-agent': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@aws-sdk/util-user-agent-browser': 3.410.0
-      '@aws-sdk/util-user-agent-node': 3.410.0
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/hash-node': 2.0.8
-      '@smithy/invalid-dependency': 2.0.8
-      '@smithy/middleware-content-length': 2.0.10
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/middleware-retry': 2.0.11
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.10
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-body-length-browser': 2.0.0
-      '@smithy/util-body-length-node': 2.1.0
-      '@smithy/util-defaults-mode-browser': 2.0.9
-      '@smithy/util-defaults-mode-node': 2.0.11
-      '@smithy/util-retry': 2.0.1
-      '@smithy/util-utf8': 2.0.0
-      tslib: 2.6.2
-    transitivePeerDependencies:
-      - aws-crt
-    dev: false
 
-  /@aws-sdk/client-sts@3.410.0:
+  '@aws-sdk/client-sts@3.410.0':
     resolution: {integrity: sha512-e6VMrBJtnTxxUXwDmkADGIvyppmDMFf4+cGGA68tVCUm1cFNlCI6M/67bVSIPN/WVKAAfhEL5O2vVXCM7aatYg==}
     engines: {node: '>=14.0.0'}
-    dependencies:
-      '@aws-crypto/sha256-browser': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/credential-provider-node': 3.410.0
-      '@aws-sdk/middleware-host-header': 3.410.0
-      '@aws-sdk/middleware-logger': 3.410.0
-      '@aws-sdk/middleware-recursion-detection': 3.410.0
-      '@aws-sdk/middleware-sdk-sts': 3.410.0
-      '@aws-sdk/middleware-signing': 3.410.0
-      '@aws-sdk/middleware-user-agent': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@aws-sdk/util-user-agent-browser': 3.410.0
-      '@aws-sdk/util-user-agent-node': 3.410.0
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/hash-node': 2.0.8
-      '@smithy/invalid-dependency': 2.0.8
-      '@smithy/middleware-content-length': 2.0.10
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/middleware-retry': 2.0.11
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.10
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-body-length-browser': 2.0.0
-      '@smithy/util-body-length-node': 2.1.0
-      '@smithy/util-defaults-mode-browser': 2.0.9
-      '@smithy/util-defaults-mode-node': 2.0.11
-      '@smithy/util-retry': 2.0.1
-      '@smithy/util-utf8': 2.0.0
-      fast-xml-parser: 4.2.5
-      tslib: 2.6.2
-    transitivePeerDependencies:
-      - aws-crt
-    dev: false
 
-  /@aws-sdk/credential-provider-env@3.410.0:
+  '@aws-sdk/credential-provider-env@3.410.0':
     resolution: {integrity: sha512-c7TB9LbN0PkFOsXI0lcRJnqPNOmc4VBvrHf8jP/BkTDg4YUoKQKOFd4d0SqzODmlZiAyoMQVZTR4ISZo95Zj4Q==}
     engines: {node: '>=14.0.0'}
-    dependencies:
-      '@aws-sdk/types': 3.410.0
+
+  '@aws-sdk/credential-provider-ini@3.410.0':
+    resolution: {integrity: sha512-D8rcr5bRCFD0f42MPQ7K6TWZq5d3pfqrKINL1/bpfkK5BJbvq1BGYmR88UC6CLpTRtZ1LHY2HgYG0fp/2zjjww==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/credential-provider-node@3.410.0':
+    resolution: {integrity: sha512-0wmVm33T/j1FS7MZ/j+WsPlgSc0YnCXnpbWSov1Mn6R86SHI2b2JhdIPRRE4XbGfyW2QGNUl2CwoZVaqhXeF5g==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/credential-provider-process@3.410.0':
+    resolution: {integrity: sha512-BMju1hlDCDNkkSZpKF5SQ8G0WCLRj6/Jvw9QmudLHJuVwYJXEW1r2AsVMg98OZ3hB9G+MAvHruHZIbMiNmUMXQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/credential-provider-sso@3.410.0':
+    resolution: {integrity: sha512-zEaoY/sY+KYTlQUkp9dvveAHf175b8RIt0DsQkDrRPtrg/RBHR00r5rFvz9+nrwsR8546RaBU7h/zzTaQGhmcA==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/credential-provider-web-identity@3.410.0':
+    resolution: {integrity: sha512-cE0l8LmEHdWbDkdPNgrfdYSgp4/cIVXrjUKI1QCATA729CrHZ/OQjB/maOBOrMHO9YTiggko887NkslVvwVB7w==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/lib-storage@3.412.0':
+    resolution: {integrity: sha512-uAdVtNuip06rJOs28zVrYXLNeHfKraxvJRTzTA+DW1dXkzh70GTKqDKHWH9IJkW/xMTE6wGSM+fDs8jsMOn/yA==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      '@aws-sdk/client-s3': ^3.0.0
+
+  '@aws-sdk/middleware-bucket-endpoint@3.410.0':
+    resolution: {integrity: sha512-pUGrpFgCKf9fDHu01JJhhw+MUImheS0HFlZwNG37OMubkxUAbCdmYGewGxfTCUvWyZJtx9bVjrSu6gG7w+RARg==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-expect-continue@3.410.0':
+    resolution: {integrity: sha512-e5YqGCNmW99GZjEPPujJ02RlEZql19U40oORysBhVF7mKz8BBvF3s8l37tvu37oxebDEkh1u/2cm2+ggOXxLjQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-flexible-checksums@3.410.0':
+    resolution: {integrity: sha512-IK7KlvEKtrQVBfmAp/MmGd0wbWLuN2GZwwfAmsU0qFb0f5vOVUbKDsu6tudtDKCBG9uXyTEsx3/QGvoK2zDy+g==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-host-header@3.410.0':
+    resolution: {integrity: sha512-ED/OVcyITln5rrxnajZP+V0PN1nug+gSDHJDqdDo/oLy7eiDr/ZWn3nlWW7WcMplQ1/Jnb+hK0UetBp/25XooA==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-location-constraint@3.410.0':
+    resolution: {integrity: sha512-jAftSpOpw/5AdpOJ/cGiXCb+Vv22KXR5QZmxmllUDsnlm18672tpRaI2plmu/1d98CVvqhY61eSklFMrIf2c4w==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-logger@3.410.0':
+    resolution: {integrity: sha512-YtmKYCVtBfScq3/UFJk+aSZOktKJBNZL9DaSc2aPcy/goCVsYDOkGwtHk0jIkC1JRSNCkVTqL7ya60sSr8zaQQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-recursion-detection@3.410.0':
+    resolution: {integrity: sha512-KWaes5FLzRqj28vaIEE4Bimpga2E596WdPF2HaH6zsVMJddoRDsc3ZX9ZhLOGrXzIO1RqBd0QxbLrM0S/B2aOQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-sdk-s3@3.410.0':
+    resolution: {integrity: sha512-K2sG2V1ZkezYMCIy3uMt0MwtflcfIwLptwm0iFLaYitiINZQ1tcslk9ggAjyTHg0rslDSI4/zjkhy8VHFOV7HA==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-sdk-sts@3.410.0':
+    resolution: {integrity: sha512-YfBpctDocRR4CcROoDueJA7D+aMLBV8nTFfmVNdLLLgyuLZ/AUR11VQSu1lf9gQZKl8IpKE/BLf2fRE/qV1ZuA==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-signing@3.410.0':
+    resolution: {integrity: sha512-KBAZ/eoAJUSJv5us2HsKwK2OszG2s9FEyKpEhgnHLcbbKzW873zHBH5GcOGEQu4AWArTy2ndzJu3FF+9/J9hJQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-ssec@3.410.0':
+    resolution: {integrity: sha512-DNsjVTXoxIh+PuW9o45CFaMiconbuZRm19MC3NA1yNCaCj3ZxD5OdXAutq6UjQdrx8UG4EjUlCJEEvBKmboITw==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-user-agent@3.410.0':
+    resolution: {integrity: sha512-ZayDtLfvCZUohSxQc/49BfoU/y6bDHLfLdyyUJbJ54Sv8zQcrmdyKvCBFUZwE6tHQgAmv9/ZT18xECMl+xiONA==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/signature-v4-multi-region@3.412.0':
+    resolution: {integrity: sha512-ijxOeYpNDuk2T940S9HYcZ1C+wTP9vqp1Cw37zw9whVY2mKV3Vr7i+44D4FQ5HhWULgdwhjD7IctbNxPIPzUZQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/token-providers@3.410.0':
+    resolution: {integrity: sha512-d5Nc0xydkH/X0LA1HDyhGY5sEv4LuADFk+QpDtT8ogLilcre+b1jpdY8Sih/gd1KoGS1H+d1tz2hSGwUHAbUbw==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/types@3.410.0':
+    resolution: {integrity: sha512-D7iaUCszv/v04NDaZUmCmekamy6VD/lKozm/3gS9+dkfU6cC2CsNoUfPV8BlV6dPdw0oWgF91am3I1stdvfVrQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/types@3.413.0':
+    resolution: {integrity: sha512-j1xib0f/TazIFc5ySIKOlT1ujntRbaoG4LJFeEezz4ji03/wSJMI8Vi4KjzpBp8J1tTu0oRDnsxRIGixsUBeYQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/util-arn-parser@3.310.0':
+    resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/util-endpoints@3.410.0':
+    resolution: {integrity: sha512-iNiqJyC7N3+8zFwnXUqcWSxrZecVZLToo1iTQQdeYL2af1IcOtRgb7n8jpAI/hmXhBSx2+3RI+Y7pxyFo1vu+w==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/util-locate-window@3.208.0':
+    resolution: {integrity: sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==}
+    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/util-user-agent-browser@3.410.0':
+    resolution: {integrity: sha512-i1G/XGpXGMRT2zEiAhi1xucJsfCWk8nNYjk/LbC0sA+7B9Huri96YAzVib12wkHPsJQvZxZC6CpQDIHWm4lXMA==}
+
+  '@aws-sdk/util-user-agent-node@3.410.0':
+    resolution: {integrity: sha512-bK70t1jHRl8HrJXd4hEIwc5PBZ7U0w+81AKFnanIVKZwZedd6nLibUXDTK14z/Jp2GFcBqd4zkt2YLGkRt/U4A==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      aws-crt: '>=1.0.0'
+    peerDependenciesMeta:
+      aws-crt:
+        optional: true
+
+  '@aws-sdk/util-utf8-browser@3.259.0':
+    resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==}
+
+  '@aws-sdk/xml-builder@3.310.0':
+    resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==}
+    engines: {node: '>=14.0.0'}
+
+  '@babel/code-frame@7.23.5':
+    resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.23.5':
+    resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.23.5':
+    resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.24.0':
+    resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.23.5':
+    resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.23.6':
+    resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-annotate-as-pure@7.22.5':
+    resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-builder-binary-assignment-operator-visitor@7.22.15':
+    resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.22.15':
+    resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.23.6':
+    resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-create-class-features-plugin@7.23.5':
+    resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-create-regexp-features-plugin@7.22.15':
+    resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-define-polyfill-provider@0.4.3':
+    resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  '@babel/helper-environment-visitor@7.22.20':
+    resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-function-name@7.23.0':
+    resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-hoist-variables@7.22.5':
+    resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-member-expression-to-functions@7.23.0':
+    resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.22.15':
+    resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.23.3':
+    resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-optimise-call-expression@7.22.5':
+    resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-plugin-utils@7.22.5':
+    resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-remap-async-to-generator@7.22.20':
+    resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-replace-supers@7.22.20':
+    resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-simple-access@7.22.5':
+    resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.22.5':
+    resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-split-export-declaration@7.22.6':
+    resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-string-parser@7.23.4':
+    resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.22.20':
+    resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.23.5':
+    resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-wrap-function@7.22.20':
+    resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.23.5':
+    resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.24.0':
+    resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/highlight@7.23.4':
+    resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.23.9':
+    resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/parser@7.24.0':
+    resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/parser@7.24.5':
+    resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3':
+    resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3':
+    resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.13.0
+
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3':
+    resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2':
+    resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-async-generators@7.8.4':
+    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-bigint@7.8.3':
+    resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-class-properties@7.12.13':
+    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-class-static-block@7.14.5':
+    resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-dynamic-import@7.8.3':
+    resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-export-namespace-from@7.8.3':
+    resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-flow@7.23.3':
+    resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-assertions@7.23.3':
+    resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-attributes@7.23.3':
+    resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-import-meta@7.10.4':
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-json-strings@7.8.3':
+    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-jsx@7.23.3':
+    resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4':
+    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
+    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-numeric-separator@7.10.4':
+    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-object-rest-spread@7.8.3':
+    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-optional-catch-binding@7.8.3':
+    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-optional-chaining@7.8.3':
+    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-private-property-in-object@7.14.5':
+    resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-top-level-await@7.14.5':
+    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-typescript@7.23.3':
+    resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-unicode-sets-regex@7.18.6':
+    resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-transform-arrow-functions@7.23.3':
+    resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-async-generator-functions@7.23.4':
+    resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-async-to-generator@7.23.3':
+    resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-block-scoped-functions@7.23.3':
+    resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-block-scoping@7.23.4':
+    resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-class-properties@7.23.3':
+    resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-class-static-block@7.23.4':
+    resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.12.0
+
+  '@babel/plugin-transform-classes@7.23.5':
+    resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-computed-properties@7.23.3':
+    resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-destructuring@7.23.3':
+    resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-dotall-regex@7.23.3':
+    resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-duplicate-keys@7.23.3':
+    resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-dynamic-import@7.23.4':
+    resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-exponentiation-operator@7.23.3':
+    resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-export-namespace-from@7.23.4':
+    resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-flow-strip-types@7.23.3':
+    resolution: {integrity: sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-for-of@7.23.3':
+    resolution: {integrity: sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-function-name@7.23.3':
+    resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-json-strings@7.23.4':
+    resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-literals@7.23.3':
+    resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-logical-assignment-operators@7.23.4':
+    resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-member-expression-literals@7.23.3':
+    resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-amd@7.23.3':
+    resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-commonjs@7.23.3':
+    resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-systemjs@7.23.3':
+    resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-modules-umd@7.23.3':
+    resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-named-capturing-groups-regex@7.22.5':
+    resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-transform-new-target@7.23.3':
+    resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-nullish-coalescing-operator@7.23.4':
+    resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-numeric-separator@7.23.4':
+    resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-object-rest-spread@7.23.4':
+    resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-object-super@7.23.3':
+    resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-optional-catch-binding@7.23.4':
+    resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-optional-chaining@7.23.4':
+    resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-parameters@7.23.3':
+    resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-private-methods@7.23.3':
+    resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-private-property-in-object@7.23.4':
+    resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-property-literals@7.23.3':
+    resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-regenerator@7.23.3':
+    resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-reserved-words@7.23.3':
+    resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-shorthand-properties@7.23.3':
+    resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-spread@7.23.3':
+    resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-sticky-regex@7.23.3':
+    resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-template-literals@7.23.3':
+    resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typeof-symbol@7.23.3':
+    resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-typescript@7.23.5':
+    resolution: {integrity: sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-escapes@7.23.3':
+    resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-property-regex@7.23.3':
+    resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-regex@7.23.3':
+    resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-unicode-sets-regex@7.23.3':
+    resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/preset-env@7.23.5':
+    resolution: {integrity: sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/preset-flow@7.23.3':
+    resolution: {integrity: sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/preset-modules@0.1.6-no-external-plugins':
+    resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
+
+  '@babel/preset-typescript@7.23.3':
+    resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/register@7.22.15':
+    resolution: {integrity: sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/regjsgen@0.8.0':
+    resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
+
+  '@babel/runtime@7.23.4':
+    resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/template@7.22.15':
+    resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/template@7.24.0':
+    resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.23.5':
+    resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.24.0':
+    resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.23.5':
+    resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.24.0':
+    resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
+    engines: {node: '>=6.9.0'}
+
+  '@base2/pretty-print-object@1.0.1':
+    resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==}
+
+  '@bcoe/v8-coverage@0.2.3':
+    resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+
+  '@bull-board/api@5.17.0':
+    resolution: {integrity: sha512-qU+AiZIaYa//rkt1x7jDowtYa8u7/dLsDfEWgenZMkgvUszZ1kxJszdCtGapsDTVyPmnXgTRxpOWcR6sAYwSNQ==}
+    peerDependencies:
+      '@bull-board/ui': 5.17.0
+
+  '@bull-board/fastify@5.17.0':
+    resolution: {integrity: sha512-73YrPc7ERTWSOQRgBP6a7BPscWfcHd8U+Zq0auMdL/KkjPhG9GxapbfnovGZDDahJL/p/4YQb6ULu03zdtOrEA==}
+
+  '@bull-board/ui@5.17.0':
+    resolution: {integrity: sha512-Vj+yWPjrjx3Iqh2N/ZBDhK2d2yJD44dfvIxm+SnXQb4ne312j117TpViInceysxGtbbAOlAW6hq6JvsDoRl7KQ==}
+
+  '@bundled-es-modules/cookie@2.0.0':
+    resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==}
+
+  '@bundled-es-modules/statuses@1.0.1':
+    resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==}
+
+  '@canvas/image-data@1.0.0':
+    resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==}
+
+  '@colors/colors@1.5.0':
+    resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
+    engines: {node: '>=0.1.90'}
+
+  '@cropper/element-canvas@2.0.0-beta.5':
+    resolution: {integrity: sha512-TS+NTVQAyLKBFLIjzFcaFK6V5GaNCNSp8FBjApTD/AosV/dPRlNCsgmdJ/BugwJTJUSowVnLrPmulI35z4npAg==}
+
+  '@cropper/element-crosshair@2.0.0-beta.5':
+    resolution: {integrity: sha512-en3EjiqS/O/uVPVLUanx2ZxvE2n3J5VxGABvBTwQimX4c3kNixq8TUVlsaLdcG7jbehxFpT3S19+tiuZudHqxg==}
+
+  '@cropper/element-grid@2.0.0-beta.5':
+    resolution: {integrity: sha512-uKQExNTOMOGo5d6Tv1NJDbjJHRR/0NgqeROUSt2J8g9ymPP+/MoFdCCf+Nj/KM5pk7/fBEV3HhzUnO8jh1hZfQ==}
+
+  '@cropper/element-handle@2.0.0-beta.5':
+    resolution: {integrity: sha512-SlaV5/qbEBQLHnuaGD8J0EqSp797m/MMB8V10EUZpv6cznSRxg/SXOj6ROE0ePzo5+0i96Dw+8ZukLilDgc1Xw==}
+
+  '@cropper/element-image@2.0.0-beta.5':
+    resolution: {integrity: sha512-369ztVaoRS2DN8SaiHZ/bRCz0Snw9ss7PZrX1OQK86fwVhCoeRqCHj48ayfLMdchx+J3RbM5f2g8ePf7ejwOKw==}
+
+  '@cropper/element-selection@2.0.0-beta.5':
+    resolution: {integrity: sha512-l8DvOBAZYytTarpkfhCglhxD+zDQ2acVwIzGwp5r9xR+ERleJHxr2rHYVhowRHT/JZRd94DJBlye91c1uO/XGg==}
+
+  '@cropper/element-shade@2.0.0-beta.5':
+    resolution: {integrity: sha512-LGSVLAD1lasFrS+Pd7JnQSJRCMSNnc40UCcjLhscDuRcRHK/ViMglnwCfFxeGnS26kugbDLF5IbYDCLCbykUog==}
+
+  '@cropper/element-viewer@2.0.0-beta.5':
+    resolution: {integrity: sha512-i4cc+L+j8Gq1L8g1BQWfQ842QxH5T9v2EkIeGuW66SVSBglafxu8gxmSOyRD3hDAMHM3wbJ+XVmFwBHZzlYCvQ==}
+
+  '@cropper/element@2.0.0-beta.5':
+    resolution: {integrity: sha512-+pHX/iYw+Y/HxgpcjvSPBc3+hvJaycznbZdWifnChmDkpLStd6Xu9gO2ful9sSL0uGSjQxUYV4xPyikYJOnfug==}
+
+  '@cropper/elements@2.0.0-beta.5':
+    resolution: {integrity: sha512-KWa5/dmJpLcKDJpNlbEQzO9Shz+f4aB0I3y97CqqTf8JSGS6CEKOd9uLywd1eow1r4O0Hwo65ktXPwAEhMWDZg==}
+
+  '@cropper/utils@2.0.0-beta.5':
+    resolution: {integrity: sha512-xE7Klel/4WSjhLUeldfROwbWqV/1A3YKrQLqTrs5/X0ath7B05Fmvhr3TNFvN51v2KSx46Ug6xDJzmbg772m1g==}
+
+  '@cypress/request@3.0.0':
+    resolution: {integrity: sha512-GKFCqwZwMYmL3IBoNeR2MM1SnxRIGERsQOTWeQKoYBt2JLqcqiy7JXqO894FLrpjZYqGxW92MNwRH2BN56obdQ==}
+    engines: {node: '>= 6'}
+
+  '@cypress/xvfb@1.2.4':
+    resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
+
+  '@digitalbazaar/http-client@3.4.1':
+    resolution: {integrity: sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==}
+    engines: {node: '>=14.0'}
+
+  '@discordapp/twemoji@15.0.3':
+    resolution: {integrity: sha512-5t0LLrNaSqViG0cSaomWwfR0+3fWqok+xLq40M8hJHxNX7s8gIoyNZYybQJo+s5/rGMjgdldpt8Ox8MapGvBUA==}
+
+  '@discoveryjs/json-ext@0.5.7':
+    resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
+    engines: {node: '>=10.0.0'}
+
+  '@emnapi/runtime@1.1.1':
+    resolution: {integrity: sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==}
+
+  '@emotion/use-insertion-effect-with-fallbacks@1.0.1':
+    resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
+    peerDependencies:
+      react: '>=16.8.0'
+
+  '@esbuild/aix-ppc64@0.19.11':
+    resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/aix-ppc64@0.20.2':
+    resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.18.20':
+    resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm64@0.19.11':
+    resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm64@0.20.2':
+    resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.18.20':
+    resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-arm@0.19.11':
+    resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-arm@0.20.2':
+    resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.18.20':
+    resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/android-x64@0.19.11':
+    resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/android-x64@0.20.2':
+    resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.18.20':
+    resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-arm64@0.19.11':
+    resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-arm64@0.20.2':
+    resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.18.20':
+    resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.19.11':
+    resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.20.2':
+    resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.18.20':
+    resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-arm64@0.19.11':
+    resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-arm64@0.20.2':
+    resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.18.20':
+    resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.19.11':
+    resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.20.2':
+    resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.18.20':
+    resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm64@0.19.11':
+    resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm64@0.20.2':
+    resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.18.20':
+    resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.19.11':
+    resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.20.2':
+    resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.18.20':
+    resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.19.11':
+    resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.20.2':
+    resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.18.20':
+    resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.19.11':
+    resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.20.2':
+    resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.18.20':
+    resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.19.11':
+    resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.20.2':
+    resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.18.20':
+    resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.19.11':
+    resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.20.2':
+    resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.18.20':
+    resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.19.11':
+    resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.20.2':
+    resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.18.20':
+    resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.19.11':
+    resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.20.2':
+    resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.18.20':
+    resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.19.11':
+    resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.20.2':
+    resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-x64@0.18.20':
+    resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.19.11':
+    resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.20.2':
+    resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-x64@0.18.20':
+    resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.19.11':
+    resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.20.2':
+    resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/sunos-x64@0.18.20':
+    resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/sunos-x64@0.19.11':
+    resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/sunos-x64@0.20.2':
+    resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.18.20':
+    resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-arm64@0.19.11':
+    resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-arm64@0.20.2':
+    resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.18.20':
+    resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.19.11':
+    resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.20.2':
+    resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.18.20':
+    resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.19.11':
+    resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.20.2':
+    resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
+  '@eslint-community/eslint-utils@4.4.0':
+    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+  '@eslint-community/regexpp@4.10.0':
+    resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  '@eslint-community/regexpp@4.6.2':
+    resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  '@eslint/eslintrc@2.1.4':
+    resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  '@eslint/js@8.53.0':
+    resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  '@eslint/js@8.57.0':
+    resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  '@fal-works/esbuild-plugin-global-externals@2.1.2':
+    resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==}
+
+  '@fastify/accept-negotiator@1.0.0':
+    resolution: {integrity: sha512-4R/N2KfYeld7A5LGkai+iUFMahXcxxYbDp+XS2B1yuL3cdmZLJ9TlCnNzT3q5xFTqsYm0GPpinLUwfSwjcVjyA==}
+    engines: {node: '>=14'}
+
+  '@fastify/accepts@4.3.0':
+    resolution: {integrity: sha512-QK4FoqXdwwPmaPOLL6NrxsyaXVvdviYVoS6ltHyOLdFlUyREIaMykHQIp+x0aJz9hB3B3n/Ht6QRdvBeGkptGQ==}
+
+  '@fastify/ajv-compiler@3.5.0':
+    resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==}
+
+  '@fastify/busboy@2.1.0':
+    resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==}
+    engines: {node: '>=14'}
+
+  '@fastify/cookie@9.3.1':
+    resolution: {integrity: sha512-h1NAEhB266+ZbZ0e9qUE6NnNR07i7DnNXWG9VbbZ8uC6O/hxHpl+Zoe5sw1yfdZ2U6XhToUGDnzQtWJdCaPwfg==}
+
+  '@fastify/cors@9.0.1':
+    resolution: {integrity: sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==}
+
+  '@fastify/deepmerge@1.3.0':
+    resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==}
+
+  '@fastify/error@3.4.0':
+    resolution: {integrity: sha512-e/mafFwbK3MNqxUcFBLgHhgxsF8UT1m8aj0dAlqEa2nJEgPsRtpHTZ3ObgrgkZ2M1eJHPTwgyUl/tXkvabsZdQ==}
+
+  '@fastify/express@3.0.0':
+    resolution: {integrity: sha512-Ug6aulXCUiHgMyrHVYQqnQbGdsAV0aTad6nZxbOr6w3QjKn1mdQS3Kyzvc+I0xMjZ9yIyMUWHSooHgZ0l7nOng==}
+
+  '@fastify/fast-json-stringify-compiler@4.3.0':
+    resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==}
+
+  '@fastify/http-proxy@9.5.0':
+    resolution: {integrity: sha512-1iqIdV10d5k9YtfHq9ylX5zt1NiM50fG+rIX40qt00R694sqWso3ukyTFZVk33SDoSiBW8roB7n11RUVUoN+Ag==}
+
+  '@fastify/multipart@8.2.0':
+    resolution: {integrity: sha512-OZ8nsyyoS2TV7Yeu3ZdrdDGsKUTAbfjrKC9jSxGgT2qdgek+BxpWX31ZubTrWMNZyU5xwk4ox6AvTjAbYWjrWg==}
+
+  '@fastify/reply-from@9.0.1':
+    resolution: {integrity: sha512-q9vFNUiXZTY1x8omDPe59os2MYq+3y7KgO/kZoXpZlnud+45Nd8Ot/svEvrUATzjkizIggfS4K8LR9zXDyZZKg==}
+
+  '@fastify/send@2.0.1':
+    resolution: {integrity: sha512-8jdouu0o5d0FMq1+zCKeKXc1tmOQ5tTGYdQP3MpyF9+WWrZT1KCBdh6hvoEYxOm3oJG/akdE9BpehLiJgYRvGw==}
+
+  '@fastify/static@6.12.0':
+    resolution: {integrity: sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==}
+
+  '@fastify/static@7.0.3':
+    resolution: {integrity: sha512-2tmTdF+uFCykasutaO6k4/wOt7eXyi7m3dGuCPo5micXzv0qt6ttb/nWnDYL/BlXjYGfp1JI4a1gyluTIylvQA==}
+
+  '@fastify/view@8.2.0':
+    resolution: {integrity: sha512-hBSiBofCnJNlPHEMZWpO1SL84eqOaqujJ1hR3jntFyZZCkweH5jMs12DKYyGesjVll7SJFRRxPUBB8kmUmneRQ==}
+
+  '@fastify/view@9.1.0':
+    resolution: {integrity: sha512-jRTGDljs/uB2p8bf6c1x4stGjP7H84VQkhbtDgCx55Mxf9Fplud5UZIHubvL4BTTX8jNYEzP1FpNAOBi7vibxg==}
+
+  '@github/webauthn-json@2.1.1':
+    resolution: {integrity: sha512-XrftRn4z75SnaJOmZQbt7Mk+IIjqVHw+glDGOxuHwXkZBZh/MBoRS7MHjSZMDaLhT4RjN2VqiEU7EOYleuJWSQ==}
+    hasBin: true
+
+  '@hapi/boom@10.0.1':
+    resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==}
+
+  '@hapi/bourne@3.0.0':
+    resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
+
+  '@hapi/hoek@10.0.1':
+    resolution: {integrity: sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw==}
+
+  '@hapi/hoek@11.0.2':
+    resolution: {integrity: sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==}
+
+  '@hapi/hoek@9.3.0':
+    resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
+
+  '@hapi/topo@5.1.0':
+    resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
+
+  '@hapi/wreck@18.0.1':
+    resolution: {integrity: sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==}
+
+  '@hexagon/base64@1.1.27':
+    resolution: {integrity: sha512-PdUmzpvcUM3Rh39kvz9RdbPVYhMjBjdV7Suw7ZduP7urRLsZR8l5tzgSWKm7TExwBYDFwTnYrZbnE0rQ3N5NLQ==}
+
+  '@humanwhocodes/config-array@0.11.13':
+    resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
+    engines: {node: '>=10.10.0'}
+
+  '@humanwhocodes/config-array@0.11.14':
+    resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
+    engines: {node: '>=10.10.0'}
+
+  '@humanwhocodes/module-importer@1.0.1':
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+
+  '@humanwhocodes/momoa@2.0.4':
+    resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==}
+    engines: {node: '>=10.10.0'}
+
+  '@humanwhocodes/object-schema@2.0.1':
+    resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
+
+  '@humanwhocodes/object-schema@2.0.2':
+    resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
+
+  '@img/sharp-darwin-arm64@0.33.3':
+    resolution: {integrity: sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@img/sharp-darwin-x64@0.33.3':
+    resolution: {integrity: sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@img/sharp-libvips-darwin-arm64@1.0.2':
+    resolution: {integrity: sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==}
+    engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@img/sharp-libvips-darwin-x64@1.0.2':
+    resolution: {integrity: sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==}
+    engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@img/sharp-libvips-linux-arm64@1.0.2':
+    resolution: {integrity: sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==}
+    engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@img/sharp-libvips-linux-arm@1.0.2':
+    resolution: {integrity: sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==}
+    engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm]
+    os: [linux]
+
+  '@img/sharp-libvips-linux-s390x@1.0.2':
+    resolution: {integrity: sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==}
+    engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@img/sharp-libvips-linux-x64@1.0.2':
+    resolution: {integrity: sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==}
+    engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+
+  '@img/sharp-libvips-linuxmusl-arm64@1.0.2':
+    resolution: {integrity: sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==}
+    engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@img/sharp-libvips-linuxmusl-x64@1.0.2':
+    resolution: {integrity: sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==}
+    engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+
+  '@img/sharp-linux-arm64@0.33.3':
+    resolution: {integrity: sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@img/sharp-linux-arm@0.33.3':
+    resolution: {integrity: sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==}
+    engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm]
+    os: [linux]
+
+  '@img/sharp-linux-s390x@0.33.3':
+    resolution: {integrity: sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==}
+    engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@img/sharp-linux-x64@0.33.3':
+    resolution: {integrity: sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==}
+    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+
+  '@img/sharp-linuxmusl-arm64@0.33.3':
+    resolution: {integrity: sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==}
+    engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@img/sharp-linuxmusl-x64@0.33.3':
+    resolution: {integrity: sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==}
+    engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [linux]
+
+  '@img/sharp-wasm32@0.33.3':
+    resolution: {integrity: sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==}
+    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [wasm32]
+
+  '@img/sharp-win32-ia32@0.33.3':
+    resolution: {integrity: sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==}
+    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@img/sharp-win32-x64@0.33.3':
+    resolution: {integrity: sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==}
+    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+    cpu: [x64]
+    os: [win32]
+
+  '@inquirer/confirm@3.1.6':
+    resolution: {integrity: sha512-Mj4TU29g6Uy+37UtpA8UpEOI2icBfpCwSW1QDtfx60wRhUy90s/kHPif2OXSSvuwDQT1lhAYRWUfkNf9Tecxvg==}
+    engines: {node: '>=18'}
+
+  '@inquirer/core@8.1.0':
+    resolution: {integrity: sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==}
+    engines: {node: '>=18'}
+
+  '@inquirer/figures@1.0.1':
+    resolution: {integrity: sha512-mtup3wVKia3ZwULPHcbs4Mor8Voi+iIXEWD7wCNbIO6lYR62oPCTQyrddi5OMYVXHzeCSoneZwJuS8sBvlEwDw==}
+    engines: {node: '>=18'}
+
+  '@inquirer/type@1.3.1':
+    resolution: {integrity: sha512-Pe3PFccjPVJV1vtlfVvm9OnlbxqdnP5QcscFEFEnK5quChf1ufZtM0r8mR5ToWHMxZOh0s8o/qp9ANGRTo/DAw==}
+    engines: {node: '>=18'}
+
+  '@intlify/core-base@9.13.1':
+    resolution: {integrity: sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==}
+    engines: {node: '>= 16'}
+
+  '@intlify/message-compiler@9.13.1':
+    resolution: {integrity: sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==}
+    engines: {node: '>= 16'}
+
+  '@intlify/shared@9.13.1':
+    resolution: {integrity: sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==}
+    engines: {node: '>= 16'}
+
+  '@ioredis/commands@1.2.0':
+    resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
+
+  '@isaacs/cliui@8.0.2':
+    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+    engines: {node: '>=12'}
+
+  '@istanbuljs/load-nyc-config@1.1.0':
+    resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+    engines: {node: '>=8'}
+
+  '@istanbuljs/schema@0.1.3':
+    resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+    engines: {node: '>=8'}
+
+  '@jest/console@29.7.0':
+    resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/core@29.7.0':
+    resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  '@jest/create-cache-key-function@29.7.0':
+    resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/environment@29.7.0':
+    resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/expect-utils@29.7.0':
+    resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/expect@29.7.0':
+    resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/fake-timers@29.7.0':
+    resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/globals@29.7.0':
+    resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/reporters@29.7.0':
+    resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  '@jest/schemas@29.6.3':
+    resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/source-map@29.6.3':
+    resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/test-result@29.7.0':
+    resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/test-sequencer@29.7.0':
+    resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/transform@29.7.0':
+    resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@jest/types@29.6.3':
+    resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.0':
+    resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
+    peerDependencies:
+      typescript: '>= 4.3.x'
+      vite: ^3.0.0 || ^4.0.0 || ^5.0.0
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@jridgewell/gen-mapping@0.3.2':
+    resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/resolve-uri@3.1.0':
+    resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/set-array@1.1.2':
+    resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/source-map@0.3.5':
+    resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
+
+  '@jridgewell/sourcemap-codec@1.4.14':
+    resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
+
+  '@jridgewell/sourcemap-codec@1.4.15':
+    resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
+
+  '@jridgewell/trace-mapping@0.3.18':
+    resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
+
+  '@jsdevtools/ono@7.1.3':
+    resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
+
+  '@kurkle/color@0.3.2':
+    resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
+
+  '@levischuck/tiny-cbor@0.2.2':
+    resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==}
+
+  '@lukeed/csprng@1.0.1':
+    resolution: {integrity: sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==}
+    engines: {node: '>=8'}
+
+  '@lukeed/ms@2.0.1':
+    resolution: {integrity: sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==}
+    engines: {node: '>=8'}
+
+  '@mapbox/node-pre-gyp@1.0.9':
+    resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
+    hasBin: true
+
+  '@mcaptcha/core-glue@0.1.0-alpha-5':
+    resolution: {integrity: sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA==}
+
+  '@mcaptcha/vanilla-glue@0.1.0-alpha-3':
+    resolution: {integrity: sha512-GT6TJBgmViGXcXiT5VOr+h/6iOnThSlZuCoOWncubyTZU9R3cgU5vWPkF7G6Ob6ee2CBe3yqBxxk24CFVGTVXw==}
+
+  '@mdx-js/react@3.0.1':
+    resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==}
+    peerDependencies:
+      '@types/react': '>=16'
+      react: '>=16'
+
+  '@microsoft/api-extractor-model@7.28.14':
+    resolution: {integrity: sha512-Bery/c8A8SsKPSvA82cTTuy/+OcxZbLRmKhPkk91/AJOQzxZsShcrmHFAGeiEqSIrv1nPZ3tKq9kfMLdCHmsqg==}
+
+  '@microsoft/api-extractor@7.43.1':
+    resolution: {integrity: sha512-ohg40SsvFFgzHFAtYq5wKJc8ZDyY46bphjtnSvhSSlXpPTG7GHwyyXkn48UZiUCBwr2WC7TRC1Jfwz7nreuiyQ==}
+    hasBin: true
+
+  '@microsoft/tsdoc-config@0.16.2':
+    resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==}
+
+  '@microsoft/tsdoc@0.14.2':
+    resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
+
+  '@misskey-dev/browser-image-resizer@2024.1.0':
+    resolution: {integrity: sha512-4EnO0zLW5NDtng3Gaz5MuT761uiuoOuplwX18wBqgj8w56LTU5BjLn/vbHwDIIe0j2gwqDYhMb7bDjmr1/Fomg==}
+
+  '@misskey-dev/eslint-plugin@1.0.0':
+    resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==}
+    peerDependencies:
+      '@typescript-eslint/eslint-plugin': '>= 6'
+      '@typescript-eslint/parser': '>= 6'
+      eslint: '>= 3'
+      eslint-plugin-import: '>= 2'
+
+  '@misskey-dev/sharp-read-bmp@1.2.0':
+    resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
+
+  '@misskey-dev/summaly@5.1.0':
+    resolution: {integrity: sha512-WAUrgX3/z4h4aI8Y/WVwmJcJ6Fa1Zf2LJCSS651t9MHoWVGABLsQ2KCXRGmlpk4i+cMDNIwweObUroosE7j8rg==}
+
+  '@mole-inc/bin-wrapper@8.0.1':
+    resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2':
+    resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2':
+    resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2':
+    resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==}
+    cpu: [arm64]
+    os: [linux]
+
+  '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2':
+    resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==}
+    cpu: [arm]
+    os: [linux]
+
+  '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2':
+    resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==}
+    cpu: [x64]
+    os: [linux]
+
+  '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2':
+    resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==}
+    cpu: [x64]
+    os: [win32]
+
+  '@mswjs/cookies@1.1.0':
+    resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==}
+    engines: {node: '>=18'}
+
+  '@mswjs/interceptors@0.26.15':
+    resolution: {integrity: sha512-HM47Lu1YFmnYHKMBynFfjCp0U/yRskHj/8QEJW0CBEPOlw8Gkmjfll+S9b8M7V5CNDw2/ciRxjjnWeaCiblSIQ==}
+    engines: {node: '>=18'}
+
+  '@napi-rs/canvas-android-arm64@0.1.52':
+    resolution: {integrity: sha512-x/K471KbASPVh5mfBUxokza66J0FNIlOgMNANWAf5C8HiATb487KecEhSkUQvvTS3WLYC9uSqIPHFgwF+tir3w==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [android]
+
+  '@napi-rs/canvas-darwin-arm64@0.1.52':
+    resolution: {integrity: sha512-4OgVRD7TW02q5Q7lWLLjT+pYJ9ZHkQUTBOuXbPQ5wB0Wnh3RIq/aMY6thoXDZDzdR5vV3a5TUtbZUJ0aqLq3NA==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@napi-rs/canvas-darwin-x64@0.1.52':
+    resolution: {integrity: sha512-3fgeGJ3j2X6Mtmn0QYf3iA+A6y1ePnsayakc2emEokzf03ErrPczONw3vjnTQo53JLPMzEnfPGAffdktU/ssPA==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@napi-rs/canvas-linux-arm-gnueabihf@0.1.52':
+    resolution: {integrity: sha512-aaDEEK5XwHUrPt0q4SR8l7Va0vtn50KmSs+itxP+o7RNk3Nuch8fINHOXyhMyhwNYgv1tfiJVyHsJhD0E6lXGA==}
+    engines: {node: '>= 10'}
+    cpu: [arm]
+    os: [linux]
+
+  '@napi-rs/canvas-linux-arm64-gnu@0.1.52':
+    resolution: {integrity: sha512-tzuwM7Amt5mkrp4csQjYWkFzwFdiCm7RNdJ5usX8syzKSXmozqWzLHjzo/2ozdSQNUy6wyzRrxkG4Rh6g0OpOA==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@napi-rs/canvas-linux-arm64-musl@0.1.52':
+    resolution: {integrity: sha512-HQCtJlDT0dFp3uUZVzZOZ1VLMO7lbLRc548MjMxPpojit2ZdGopFzJ8jDSr4iszHrTO1SM1AxPaCM3pRvCAtjw==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@napi-rs/canvas-linux-x64-gnu@0.1.52':
+    resolution: {integrity: sha512-z5sBEw0PVWPH/MIQL8hOR8C3YYVlu8lqtRUcYajigMfXAhbMiNqDWTjuIWGMz3nIydDjZmn8KTxw/D4a0HFPqQ==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@napi-rs/canvas-linux-x64-musl@0.1.52':
+    resolution: {integrity: sha512-G1+JdWFhHLyHhULJS51xTEhB7EL0ZiAUQwQaRi4/w75OOYDQ91O+o4miaxDHiV0hZuxBhHtZU6ftV2Zl3RMguw==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@napi-rs/canvas-win32-x64-msvc@0.1.52':
+    resolution: {integrity: sha512-hMI626VsCC/wv29qHF78N7TSG+auatOp08DHln0Zdif5y1NJ14NU/rNUhzlTW8Zc6ssw+AMDJ3KKYYWYYg1aoA==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [win32]
+
+  '@napi-rs/canvas@0.1.52':
+    resolution: {integrity: sha512-xeW9EghZLDPZuqWJ4l1+eG3ld0i9J7SpV2zlgi34MPt/FE9K2XWGCfnLr0gHGOBkcI3YOVhI13I0HqRAkMPdVw==}
+    engines: {node: '>= 10'}
+
+  '@ndelangen/get-tarball@3.0.7':
+    resolution: {integrity: sha512-NqGfTZIZpRFef1GoVaShSSRwDC3vde3ThtTeqFdcYd6ipKqnfEVhjK2hUeHjCQUcptyZr2TONqcloFXM+5QBrQ==}
+
+  '@nestjs/common@10.3.8':
+    resolution: {integrity: sha512-P+vPEIvqx2e+fonsYVlFXKvoChyJ8Tq+lfpqdVFqblovHbFr3kZ/nYX0cPs+XuW6bnRT8tz0SSR9XBGU43kJhw==}
+    peerDependencies:
+      class-transformer: '*'
+      class-validator: '*'
+      reflect-metadata: ^0.1.12 || ^0.2.0
+      rxjs: ^7.1.0
+    peerDependenciesMeta:
+      class-transformer:
+        optional: true
+      class-validator:
+        optional: true
+
+  '@nestjs/core@10.3.8':
+    resolution: {integrity: sha512-AxF4tpYLDNn5Wfb3C4bNaaHJ4pREH5FJrSisR2A5zkYpQFORFs0Tc36lOFPMwBTy8Iv2wUwWLUVc5ftBnxEv4w==}
+    peerDependencies:
+      '@nestjs/common': ^10.0.0
+      '@nestjs/microservices': ^10.0.0
+      '@nestjs/platform-express': ^10.0.0
+      '@nestjs/websockets': ^10.0.0
+      reflect-metadata: ^0.1.12 || ^0.2.0
+      rxjs: ^7.1.0
+    peerDependenciesMeta:
+      '@nestjs/microservices':
+        optional: true
+      '@nestjs/platform-express':
+        optional: true
+      '@nestjs/websockets':
+        optional: true
+
+  '@nestjs/platform-express@10.3.8':
+    resolution: {integrity: sha512-sifLoxgEJvAgbim1UuW6wyScMfkS9SVQRH+lN33N/9ZvZSjO6NSDLOe+wxqsnZkia+QrjFC0qy0ITRAsggfqbg==}
+    peerDependencies:
+      '@nestjs/common': ^10.0.0
+      '@nestjs/core': ^10.0.0
+
+  '@nestjs/testing@10.3.8':
+    resolution: {integrity: sha512-hpX9das2TdFTKQ4/2ojhjI6YgXtCfXRKui3A4Qaj54VVzc5+mtK502Jj18Vzji98o9MVS6skmYu+S/UvW3U6Fw==}
+    peerDependencies:
+      '@nestjs/common': ^10.0.0
+      '@nestjs/core': ^10.0.0
+      '@nestjs/microservices': ^10.0.0
+      '@nestjs/platform-express': ^10.0.0
+    peerDependenciesMeta:
+      '@nestjs/microservices':
+        optional: true
+      '@nestjs/platform-express':
+        optional: true
+
+  '@nodelib/fs.scandir@2.1.5':
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.stat@2.0.5':
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.walk@1.2.8':
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+
+  '@npmcli/agent@2.2.0':
+    resolution: {integrity: sha512-2yThA1Es98orMkpSLVqlDZAMPK3jHJhifP2gnNUdk1754uZ8yI5c+ulCoVG+WlntQA6MzhrURMXjSd9Z7dJ2/Q==}
+    engines: {node: ^16.14.0 || >=18.0.0}
+
+  '@npmcli/fs@3.1.0':
+    resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  '@nsfw-filter/gif-frames@1.0.2':
+    resolution: {integrity: sha512-XZrbJWEN8YfVla5i+PD4Wj51rRlJ8OgnXiPjjOt/OsrbsCR9GZRD4jr953oNWcwiRaoIcOCFWQNMQukO7Yb1dA==}
+
+  '@nsfw-filter/save-pixels@2.3.4':
+    resolution: {integrity: sha512-dRZXwrXadMvxwJYKChrDBqC6GNvxVqlmdkyvZJO5DV65qyBsHZw8bPg9CnX7EgpxGl6+4ba/MAdHDLxs2XoD0Q==}
+
+  '@nuxtjs/opencollective@0.3.2':
+    resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==}
+    engines: {node: '>=8.0.0', npm: '>=5.0.0'}
+    hasBin: true
+
+  '@one-ini/wasm@0.1.1':
+    resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+
+  '@open-draft/deferred-promise@2.2.0':
+    resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
+
+  '@open-draft/logger@0.3.0':
+    resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==}
+
+  '@open-draft/until@2.1.0':
+    resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
+
+  '@peculiar/asn1-android@2.3.10':
+    resolution: {integrity: sha512-z9Rx9cFJv7UUablZISe7uksNbFJCq13hO0yEAOoIpAymALTLlvUOSLnGiQS7okPaM5dP42oTLhezH6XDXRXjGw==}
+
+  '@peculiar/asn1-ecc@2.3.8':
+    resolution: {integrity: sha512-Ah/Q15y3A/CtxbPibiLM/LKcMbnLTdUdLHUgdpB5f60sSvGkXzxJCu5ezGTFHogZXWNX3KSmYqilCrfdmBc6pQ==}
+
+  '@peculiar/asn1-rsa@2.3.8':
+    resolution: {integrity: sha512-ES/RVEHu8VMYXgrg3gjb1m/XG0KJWnV4qyZZ7mAg7rrF3VTmRbLxO8mk+uy0Hme7geSMebp+Wvi2U6RLLEs12Q==}
+
+  '@peculiar/asn1-schema@2.3.8':
+    resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==}
+
+  '@peculiar/asn1-x509@2.3.8':
+    resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==}
+
+  '@peertube/http-signature@1.7.0':
+    resolution: {integrity: sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==}
+    engines: {node: '>=0.10'}
+
+  '@pkgjs/parseargs@0.11.0':
+    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+    engines: {node: '>=14'}
+
+  '@radix-ui/react-compose-refs@1.0.1':
+    resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-slot@1.0.2':
+    resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@readme/better-ajv-errors@1.6.0':
+    resolution: {integrity: sha512-9gO9rld84Jgu13kcbKRU+WHseNhaVt76wYMeRDGsUGYxwJtI3RmEJ9LY9dZCYQGI8eUZLuxb5qDja0nqklpFjQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      ajv: 4.11.8 - 8
+
+  '@readme/json-schema-ref-parser@1.2.0':
+    resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==}
+
+  '@readme/openapi-parser@2.5.0':
+    resolution: {integrity: sha512-IbymbOqRuUzoIgxfAAR7XJt2FWl6n2yqN09fF5adacGm7W03siA3bj1Emql0X9D2T+RpBYz3x9zDsMhuoMP62A==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      openapi-types: '>=7'
+
+  '@rollup/plugin-json@6.1.0':
+    resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+
+  '@rollup/plugin-replace@5.0.5':
+    resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+
+  '@rollup/pluginutils@5.1.0':
+    resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
+
+  '@rollup/rollup-android-arm-eabi@4.17.2':
+    resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.17.2':
+    resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.17.2':
+    resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.17.2':
+    resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.17.2':
+    resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==}
+    cpu: [arm]
+    os: [linux]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.17.2':
+    resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==}
+    cpu: [arm]
+    os: [linux]
+
+  '@rollup/rollup-linux-arm64-gnu@4.17.2':
+    resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==}
+    cpu: [arm64]
+    os: [linux]
+
+  '@rollup/rollup-linux-arm64-musl@4.17.2':
+    resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==}
+    cpu: [arm64]
+    os: [linux]
+
+  '@rollup/rollup-linux-powerpc64le-gnu@4.17.2':
+    resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.17.2':
+    resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@rollup/rollup-linux-s390x-gnu@4.17.2':
+    resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==}
+    cpu: [s390x]
+    os: [linux]
+
+  '@rollup/rollup-linux-x64-gnu@4.17.2':
+    resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==}
+    cpu: [x64]
+    os: [linux]
+
+  '@rollup/rollup-linux-x64-musl@4.17.2':
+    resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==}
+    cpu: [x64]
+    os: [linux]
+
+  '@rollup/rollup-win32-arm64-msvc@4.17.2':
+    resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.17.2':
+    resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.17.2':
+    resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==}
+    cpu: [x64]
+    os: [win32]
+
+  '@rushstack/node-core-library@4.1.0':
+    resolution: {integrity: sha512-qz4JFBZJCf1YN5cAXa1dP6Mki/HrsQxc/oYGAGx29dF2cwF2YMxHoly0FBhMw3IEnxo5fMj0boVfoHVBkpkx/w==}
+    peerDependencies:
+      '@types/node': '*'
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+
+  '@rushstack/rig-package@0.5.2':
+    resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==}
+
+  '@rushstack/terminal@0.10.1':
+    resolution: {integrity: sha512-C6Vi/m/84IYJTkfzmXr1+W8Wi3MmBjVF/q3za91Gb3VYjKbpALHVxY6FgH625AnDe5Z0Kh4MHKWA3Z7bqgAezA==}
+    peerDependencies:
+      '@types/node': '*'
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+
+  '@rushstack/ts-command-line@4.19.2':
+    resolution: {integrity: sha512-cqmXXmBEBlzo9WtyUrHtF9e6kl0LvBY7aTSVX4jfnBfXWZQWnPq9JTFPlQZ+L/ZwjZ4HrNwQsOVvhe9oOucZkw==}
+
+  '@shikijs/core@1.4.0':
+    resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==}
+
+  '@sideway/address@4.1.4':
+    resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
+
+  '@sideway/formula@3.0.1':
+    resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
+
+  '@sideway/pinpoint@2.0.0':
+    resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
+
+  '@simplewebauthn/server@10.0.0':
+    resolution: {integrity: sha512-w5eIoiF7ltg1sgggjY5Tx654j+DBuyEx2B3869jjmPp0xl2Z4BUP4kJ3yJ6DnZIv+ZYYntT3E6nZXNjPOQbrtw==}
+    engines: {node: '>=20.0.0'}
+
+  '@simplewebauthn/types@10.0.0':
+    resolution: {integrity: sha512-SFXke7xkgPRowY2E+8djKbdEznTVnD5R6GO7GPTthpHrokLvNKw8C3lFZypTxLI7KkCfGPfhtqB3d7OVGGa9jQ==}
+
+  '@sinclair/typebox@0.27.8':
+    resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
+
+  '@sindresorhus/is@4.6.0':
+    resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
+    engines: {node: '>=10'}
+
+  '@sindresorhus/is@5.3.0':
+    resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
+    engines: {node: '>=14.16'}
+
+  '@sindresorhus/is@6.1.0':
+    resolution: {integrity: sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==}
+    engines: {node: '>=16'}
+
+  '@sinonjs/commons@2.0.0':
+    resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==}
+
+  '@sinonjs/commons@3.0.0':
+    resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==}
+
+  '@sinonjs/fake-timers@10.3.0':
+    resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+
+  '@sinonjs/fake-timers@11.2.2':
+    resolution: {integrity: sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==}
+
+  '@sinonjs/samsam@8.0.0':
+    resolution: {integrity: sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==}
+
+  '@sinonjs/text-encoding@0.7.2':
+    resolution: {integrity: sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==}
+
+  '@smithy/abort-controller@2.0.14':
+    resolution: {integrity: sha512-zXtteuYLWbSXnzI3O6xq3FYvigYZFW8mdytGibfarLL2lxHto9L3ILtGVnVGmFZa7SDh62l39EnU5hesLN87Fw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/abort-controller@2.2.0':
+    resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/chunked-blob-reader-native@2.0.0':
+    resolution: {integrity: sha512-HM8V2Rp1y8+1343tkZUKZllFhEQPNmpNdgFAncbTsxkZ18/gqjk23XXv3qGyXWp412f3o43ZZ1UZHVcHrpRnCQ==}
+
+  '@smithy/chunked-blob-reader@2.0.0':
+    resolution: {integrity: sha512-k+J4GHJsMSAIQPChGBrjEmGS+WbPonCXesoqP9fynIqjn7rdOThdH8FAeCmokP9mxTYKQAKoHCLPzNlm6gh7Wg==}
+
+  '@smithy/config-resolver@2.0.9':
+    resolution: {integrity: sha512-QBkGPLUqyPmis9Erz8v4q5lo/ErnF7+GD5WZHa6JZiXopUPfaaM+B21n8gzS5xCkIXZmnwzNQhObP9xQPu8oqQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/credential-provider-imds@2.0.11':
+    resolution: {integrity: sha512-uJJs8dnM5iXkn8a2GaKvlKMhcOJ+oJPYqY9gY3CM/EieCVObIDjxUtR/g8lU/k/A+OauA78GzScAfulmFjPOYA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/eventstream-codec@2.0.8':
+    resolution: {integrity: sha512-onO4to8ujCKn4m5XagReT9Nc6FlNG5vveuvjp1H7AtaG7njdet1LOl6/jmUOkskF2C/w+9jNw3r9Ak+ghOvN0A==}
+
+  '@smithy/eventstream-serde-browser@2.0.8':
+    resolution: {integrity: sha512-/RGlkKUnC0sd+xKBKH/2APSBRmVMZTeLOKZMhrZmrO+ONoU+DwyMr/RLJ6WnmBKN+2ebjffM4pcIJTKLNNDD8g==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/eventstream-serde-config-resolver@2.0.8':
+    resolution: {integrity: sha512-EyAEj258eMUv9zcMvBbqrInh2eHRYuiwQAjXDMxZFCyP+JePzQB6O++3wFwjQeRKMFFgZipNgnEXfReII4+NAw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/eventstream-serde-node@2.0.8':
+    resolution: {integrity: sha512-FMBatSUSKwh6aguKVJokXfJaV8nqsuCkCZHb9MP9zah0ZF+ohbTLeeed7DQGeTVBueVIVWEzIsShPxtxBv7MMQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/eventstream-serde-universal@2.0.8':
+    resolution: {integrity: sha512-6InMXH8BUKoEDa6CAuxR4Gn8Gf2vBfVtjA9A6zDKZClYHT+ANUJS+2EtOBc5wECJJGk4KLn5ajQyrt9MBv5lcw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/fetch-http-handler@2.1.4':
+    resolution: {integrity: sha512-SL24M9W5ERByoXaVicRx+bj9GJVujDnPn+QO7GY7adhY0mPGa6DSF58pVKsgIh4r5Tx/k3SWCPlH4BxxSxA/fQ==}
+
+  '@smithy/hash-blob-browser@2.0.8':
+    resolution: {integrity: sha512-IgvRlBMfg/qLg321a59T1yTdEEbaizLrEVsU3DHj65DAO4lFRMF5f+l7vuV+je6m1G9wSD5GQXLturX8qlGb4g==}
+
+  '@smithy/hash-node@2.0.8':
+    resolution: {integrity: sha512-yZL/nmxZzjZV5/QX5JWSgXlt0HxuMTwFO89CS++jOMMPiCMZngf6VYmtNdccs8IIIAMmfQeTzwu07XgUE/Zd3Q==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/hash-stream-node@2.0.8':
+    resolution: {integrity: sha512-82zC6I9ZJycbEZH8TVyXyBx9c2ZIPQDgBvM0x5AFPUl/i1AxwKKX+lwYRnzgkF//cYhIIoJaCfJ9mjSMPRGvCQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/invalid-dependency@2.0.8':
+    resolution: {integrity: sha512-88VOS7W3KzUz/bNRc+Sl/F/CDIasFspEE4G39YZRHIh9YmsXF7GUyVaAKURfMNulTie62ayk6BHC9O0nOBAVgQ==}
+
+  '@smithy/is-array-buffer@2.0.0':
+    resolution: {integrity: sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/md5-js@2.0.8':
+    resolution: {integrity: sha512-1VVECXEiuJvjXv+mudiaUFKYwgDLOWz5MTTy8RzbrPiU3GiOb3/o5/urdkYpqmgoMfxdvxxOw/Adjv2dV2q2Yg==}
+
+  '@smithy/middleware-content-length@2.0.10':
+    resolution: {integrity: sha512-EGSbysyA4jH0p3xI6G0jdXoj9Iz9GUnAta6aEaHtXm3wVWtenRf80y2TeVvNkVSr5jwKOdSCjKIRI2l1A/oZLA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/middleware-endpoint@2.0.8':
+    resolution: {integrity: sha512-yOpogfG2d2V0cbJdAJ6GLAWkNOc9pVsL5hZUfXcxJu408N3CUCsXzIAFF6+70ZKSE+lCfG3GFErcSXv/UfUbjw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/middleware-retry@2.0.11':
+    resolution: {integrity: sha512-pknfokumZ+wvBERSuKAI2vVr+aK3ZgPiWRg6+0ZG4kKJogBRpPmDGWw+Jht0izS9ZaEbIobNzueIb4wD33JJVg==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/middleware-serde@2.0.8':
+    resolution: {integrity: sha512-Is0sm+LiNlgsc0QpstDzifugzL9ehno1wXp109GgBgpnKTK3j+KphiparBDI4hWTtH9/7OUsxuspNqai2yyhcg==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/middleware-stack@2.0.1':
+    resolution: {integrity: sha512-UexsfY6/oQZRjTQL56s9AKtMcR60tBNibSgNYX1I2WXaUaXg97W9JCkFyth85TzBWKDBTyhLfenrukS/kyu54A==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/node-config-provider@2.0.11':
+    resolution: {integrity: sha512-CaR1dciSSGKttjhcefpytYjsfI/Yd5mqL8am4wfmyFCDxSiPsvnEWHl8UjM/RbcAjX0klt+CeIKPSHEc0wGvJA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/node-http-handler@2.5.0':
+    resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/property-provider@2.0.9':
+    resolution: {integrity: sha512-25pPZ8f8DeRwYI5wbPRZaoMoR+3vrw8DwbA0TjP+GsdiB2KxScndr4HQehiJ5+WJ0giOTWhLz0bd+7Djv1qpUQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/protocol-http@3.0.10':
+    resolution: {integrity: sha512-6+tjNk7rXW7YTeGo9qwxXj/2BFpJTe37kTj3EnZCoX/nH+NP/WLA7O83fz8XhkGqsaAhLUPo/bB12vvd47nsmg==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/protocol-http@3.3.0':
+    resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/querystring-builder@2.0.14':
+    resolution: {integrity: sha512-lQ4pm9vTv9nIhl5jt6uVMPludr6syE2FyJmHsIJJuOD7QPIJnrf9HhUGf1iHh9KJ4CUv21tpOU3X6s0rB6uJ0g==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/querystring-builder@2.2.0':
+    resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/querystring-parser@2.0.8':
+    resolution: {integrity: sha512-ArbanNuR7O/MmTd90ZqhDqGOPPDYmxx3huHxD+R3cuCnazcK/1tGQA+SnnR5307T7ZRb5WTpB6qBggERuibVSA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/service-error-classification@2.0.1':
+    resolution: {integrity: sha512-QHa9+t+v4s0cMuDCcbjIJN67mNZ42/+fc3jKe8P6ZMPXZl5ksKk6a8vhZ/m494GZng5eFTc3OePv+NF9cG83yg==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/shared-ini-file-loader@2.0.10':
+    resolution: {integrity: sha512-jWASteSezRKohJ7GdA7pHDvmr7Q7tw3b5mu3xLHIkZy/ICftJ+O7aqNaF8wklhI7UNFoQ7flFRM3Rd0KA+1BbQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/signature-v4@2.0.5':
+    resolution: {integrity: sha512-ABIzXmUDXK4n2c9cXjQLELgH2RdtABpYKT+U131e2I6RbCypFZmxIHmIBufJzU2kdMCQ3+thBGDWorAITFW04A==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/smithy-client@2.1.5':
+    resolution: {integrity: sha512-7S865uKzsxApM8W8Q6zkij7tcUFgaG8PuADMFdMt1yL/ku3d0+s6Zwrg3N7iXCPM08Gu/mf0BIfTXIu/9i450Q==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/types@2.12.0':
+    resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/types@2.6.0':
+    resolution: {integrity: sha512-PgqxJq2IcdMF9iAasxcqZqqoOXBHufEfmbEUdN1pmJrJltT42b0Sc8UiYSWWzKkciIp9/mZDpzYi4qYG1qqg6g==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/url-parser@2.0.8':
+    resolution: {integrity: sha512-wQw7j004ScCrBRJ+oNPXlLE9mtofxyadSZ9D8ov/rHkyurS7z1HTNuyaGRj6OvKsEk0SVQsuY0C9+EfM75XTkw==}
+
+  '@smithy/util-base64@2.0.0':
+    resolution: {integrity: sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-body-length-browser@2.0.0':
+    resolution: {integrity: sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==}
+
+  '@smithy/util-body-length-node@2.1.0':
+    resolution: {integrity: sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-buffer-from@2.0.0':
+    resolution: {integrity: sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-config-provider@2.0.0':
+    resolution: {integrity: sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-defaults-mode-browser@2.0.9':
+    resolution: {integrity: sha512-JONLJVQWT8165XoSV36ERn3SVlZLJJ4D6IeGsCSePv65Uxa93pzSLE0UMSR9Jwm4zix7rst9AS8W5QIypZWP8Q==}
+    engines: {node: '>= 10.0.0'}
+
+  '@smithy/util-defaults-mode-node@2.0.11':
+    resolution: {integrity: sha512-tmqjNsfj+bgZN6jXBe6efZnukzILA7BUytHkzqikuRLNtR+0VVchQHvawD0w6vManh76rO81ydhioe7i4oBzuA==}
+    engines: {node: '>= 10.0.0'}
+
+  '@smithy/util-hex-encoding@2.0.0':
+    resolution: {integrity: sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-middleware@2.0.1':
+    resolution: {integrity: sha512-LnsBMi0Mg3gfz/TpNGLv2Jjcz2ra1OX5HR/4IaCepIYmtPQzqMWDdhX/XTW1LS8OZ0xbQuyQPcHkQ+2XkhWOVQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-retry@2.0.1':
+    resolution: {integrity: sha512-naj4X0IafJ9yJnVJ58QgSMkCNLjyQOnyrnKh/T0f+0UOUxJiT8vuFn/hS7B/pNqbo2STY7PyJ4J4f+5YqxwNtA==}
+    engines: {node: '>= 14.0.0'}
+
+  '@smithy/util-stream@2.0.11':
+    resolution: {integrity: sha512-2MeWfqSpZKdmEJ+tH8CJQSgzLWhH5cmdE24X7JB0hiamXrOmswWGGuPvyj/9sQCTclo57pNxLR2p7KrP8Ahiyg==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-uri-escape@2.0.0':
+    resolution: {integrity: sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-uri-escape@2.2.0':
+    resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-utf8@2.0.0':
+    resolution: {integrity: sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==}
+    engines: {node: '>=14.0.0'}
+
+  '@smithy/util-waiter@2.0.8':
+    resolution: {integrity: sha512-t9yaoofNhdEhNlyDeV5al/JJEFJ62HIQBGktgCUE63MvKn6imnbkh1qISsYMyMYVLwhWCpZ3Xa3R1LA+SnWcng==}
+    engines: {node: '>=14.0.0'}
+
+  '@sqltools/formatter@1.2.5':
+    resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
+
+  '@storybook/addon-actions@8.0.9':
+    resolution: {integrity: sha512-+I3VTvlKdj8puHeS2tyaOVv9syDiNLneVZbTfqN+UDOK2i42NwvZr8PVwjTzMlEj9eePJdCZgiipz55xwts5bw==}
+
+  '@storybook/addon-backgrounds@8.0.9':
+    resolution: {integrity: sha512-pCDecACrVyxPaJKEWS0sHsRb8xw+IPCSxDM1TkjaAQ6zZ468A/dcUnqW+LVK8bSXgQwWzn23wqnqPFSy5yptuQ==}
+
+  '@storybook/addon-controls@8.0.9':
+    resolution: {integrity: sha512-wWdmd62UP/sfPm8M7aJjEA+kEXTUIR/QsYi9PoYBhBZcXiikZ4kNan7oD7GfsnzGGKHrBVfwQhO+TqaENGYytA==}
+
+  '@storybook/addon-docs@8.0.9':
+    resolution: {integrity: sha512-x7hX7UuzJtClu6XwU3SfpyFhuckVcgqgD6BU6Ihxl0zs+i4xp6iKVXYSnHFMRM1sgoeT8TjPxab35Ke8w8BVRw==}
+
+  '@storybook/addon-essentials@8.0.9':
+    resolution: {integrity: sha512-mwAgdfrOsTuTDcagvM7veBh+iayZIWmKOazzkhrIWbhYcrXOsweigD2UOVeHgAiAzJK49znr4FXTCKcE1hOWcw==}
+
+  '@storybook/addon-highlight@8.0.9':
+    resolution: {integrity: sha512-vaRHGDbx7dpNpQECAHk5wczlZO3ntstprGlqnZt0o7ylz6xB5+pTQwTuIFty0hwKv+3TPcskzzifATUyEOEmyg==}
+
+  '@storybook/addon-interactions@8.0.9':
+    resolution: {integrity: sha512-AMIdNcyM6DDAWvMitBJMqp1iPZND8AXB4QT4VZHGMKG2ngHNKktriEKpTfcRkfKPGTJs9T+71dWfm6/R4tticw==}
+
+  '@storybook/addon-links@8.0.9':
+    resolution: {integrity: sha512-FVt+AdW3JFSqbJzkKiqKsMRWqHXqEvCBqFs7lNfk3OW0w0jfv1iREtrxE0dVdJoUFQC9V/2Im/EpJ7UB3C2bNQ==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      react:
+        optional: true
+
+  '@storybook/addon-mdx-gfm@8.0.9':
+    resolution: {integrity: sha512-AoEx+OGKANtVZgKyWKrQhGpMpDuc2S7PnOlNLUiDYzmj8ABAGPmEJmqeb/VHVgqLQSjhOW1fMsQ4fYsecvMxTQ==}
+
+  '@storybook/addon-measure@8.0.9':
+    resolution: {integrity: sha512-91svOOGEXmGG4USglwXLE3wtlUVgtbKJVxTKX7xRI+AC5JEEaKByVzP17/X8Qn/8HilUL7AfSQ0kCoqtPSJ5cA==}
+
+  '@storybook/addon-outline@8.0.9':
+    resolution: {integrity: sha512-fQ+jm356TgUnz81IxsC99/aOesbLw3N5OQRJpo/A6kqbLMzlq3ybVzuXYCKC3f0ArgQRNh4NoMeJBMRFMtaWRw==}
+
+  '@storybook/addon-storysource@8.0.9':
+    resolution: {integrity: sha512-5m3K2Rs4fQtKtqwrq4CDS1jK2wzWOlnxhE2ArX5XTWytb1am65CEPxfYTEQkvZH9oPGwX3cXytPCziynqysFMQ==}
+
+  '@storybook/addon-toolbars@8.0.9':
+    resolution: {integrity: sha512-nNSBnnBOhQ+EJwkrIkK4ZBYPcozNmEH770CZ/6NK85SUJ6WEBZapE6ru33jIUokFGEvlOlNCeai0GUc++cQP8w==}
+
+  '@storybook/addon-viewport@8.0.9':
+    resolution: {integrity: sha512-Ao4+D56cO7biaw+iTlMU1FBec1idX0cmdosDeCFZin06MSawcPkeBlRBeruaSQYdLes8TBMdZPFgfuqI5yIk6g==}
+
+  '@storybook/blocks@8.0.9':
+    resolution: {integrity: sha512-F2zSrfSwzTFN7qW3zB80tG+EXtmfmCDC6Ird0F7tolszb6tOqJcAcBOwQbE2O0wI63sLu21qxzXgaKBMkiWvJg==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      react:
+        optional: true
+      react-dom:
+        optional: true
+
+  '@storybook/builder-manager@8.0.9':
+    resolution: {integrity: sha512-/PxDwZIfMc/PSRZcasb6SIdGr3azIlenzx7dBF7Imt8i4jLHiAf1t00GvghlfJsvsrn4DNp95rbRbXTDyTj7tQ==}
+
+  '@storybook/builder-vite@8.0.9':
+    resolution: {integrity: sha512-7hEQFZIIz7VvxdySDpPE96iMvZxQvRZcRdhaNGeE+8Y2pyc3DgYE4WY3sjr+LUoB0a6TYLpAIKqbXwtLz0R+PQ==}
+    peerDependencies:
+      '@preact/preset-vite': '*'
+      typescript: '>= 4.3.x'
+      vite: ^4.0.0 || ^5.0.0
+      vite-plugin-glimmerx: '*'
+    peerDependenciesMeta:
+      '@preact/preset-vite':
+        optional: true
+      typescript:
+        optional: true
+      vite-plugin-glimmerx:
+        optional: true
+
+  '@storybook/channels@8.0.9':
+    resolution: {integrity: sha512-7Lcfyy5CsLWWGhMPO9WG4jZ/Alzp0AjepFhEreYHRPtQrfttp6qMAjE/g1aHgun0qHCYWxwqIG4NLR/hqDNrXQ==}
+
+  '@storybook/cli@8.0.9':
+    resolution: {integrity: sha512-lilYTKn8F5YOePijqfRYFa5v2mHVIJxPCIgTn+OXAmAFbcizZ6P8P6niU4J/NXulgx68Ln1M7hYhFtTP25hVTw==}
+    hasBin: true
+
+  '@storybook/client-logger@8.0.9':
+    resolution: {integrity: sha512-LzV/RHkbf07sRc1Jc0ff36RlapKf9Ul7/+9VMvVbI3hshH1CpmrZK4t/tsIdpX/EVOdJ1Gg5cES06PnleOAIPA==}
+
+  '@storybook/codemod@8.0.9':
+    resolution: {integrity: sha512-VBeGpSZSQpL6iyLLqceJSNGhdCqcNwv+xC/aWdDFOkmuE1YfbmNNwpa9QYv4ZFJ2QjUsm4iTWG60qK+9NXeSKA==}
+
+  '@storybook/components@8.0.9':
+    resolution: {integrity: sha512-JcwBGADzIJs0PSzqykrrD2KHzNG9wtexUOKuidt+FSv9szpUhe3qBAXIHpdfBRl7mOJ9TRZ5rt+mukEnfncdzA==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+  '@storybook/core-common@8.0.9':
+    resolution: {integrity: sha512-Jmue+sfHFb4GTYBzyWYw1MygoJiQSfISIrKmNIzAmZ+oR9EOr+jpu/i/bH+uetZ2Hqg1AGhj1VB7OtJp9HQyWw==}
+
+  '@storybook/core-events@8.0.9':
+    resolution: {integrity: sha512-DxSUx7wG9Qe3OFUBnv3OrYq48J8UWNo2DUR5/JecJCtp3n++L4fAEW3J0IF5FfxpQDMQSp1yTNjZ2PaWCMd2ag==}
+
+  '@storybook/core-server@8.0.9':
+    resolution: {integrity: sha512-BIe1T5YUBl0GYxEjRoTQsvXD2pyuzL8rPTUD41zlzSQM0R8U6Iant9SzRms4u0+rKUm2mGxxKuODlUo5ewqaGA==}
+
+  '@storybook/csf-plugin@8.0.9':
+    resolution: {integrity: sha512-pXaNCNi++kxKsqSWwvx215fPx8cNqvepLVxQ7B69qXLHj80DHn0Q3DFBO3sLXNiQMJ2JK4OYcTxMfuOiyzszKw==}
+
+  '@storybook/csf-tools@8.0.9':
+    resolution: {integrity: sha512-PiNMhL97giLytTdQwuhsZ92buVk4gy9H/8DtrDhUc45/1OmF95gogm6T2Yap729SIFwgpOcuq/U3aVo6d6swVQ==}
+
+  '@storybook/csf@0.1.6':
+    resolution: {integrity: sha512-JjWnBptVhBYJ14yq+cHs66BXjykRUWQ5TlD1RhPxMOtavynYyV/Q+QR98/N+XB+mcPtFMm5I2DvNkpj0/Dk8Mw==}
+
+  '@storybook/docs-mdx@3.0.0':
+    resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==}
+
+  '@storybook/docs-tools@8.0.9':
+    resolution: {integrity: sha512-OzogAeOmeHea/MxSPKRBWtOQVNSpoq+OOpimO9YRA5h5GBRJ2TUOGT44Gny6QT4ll5AvQA8fIiq9KezKcLekAg==}
+
+  '@storybook/global@5.0.0':
+    resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
+
+  '@storybook/icons@1.2.5':
+    resolution: {integrity: sha512-m3jnuE+zmkZy6K+cdUDzAoUuCJyl0fWCAXPCji7VZCH1TzFohyvnPqhc9JMkQpanej2TOW3wWXaplPzHghcBSg==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+  '@storybook/instrumenter@8.0.9':
+    resolution: {integrity: sha512-Gw74dgpTU/2p7FG0s7DuVdqCbJ2MEcSuRJjDo7HcXRYcvWp7I6Ly+C0v7N5VaoS+kbBVerAhLKIHZgG/LZf1og==}
+
+  '@storybook/manager-api@8.0.9':
+    resolution: {integrity: sha512-99b3yKArDSvfabXL7QE3nA95e4DdW/5H/ZCcr6/E2qCQJayZ6G1v/WWamKXbiaTpkndulFmcb/+ZmnDXcweIIQ==}
+
+  '@storybook/manager@8.0.9':
+    resolution: {integrity: sha512-+NnRo+5JQFGNqveKrLtC0b+Z08Tae4m44iq292bPeZMpr9OkFsIkU0PBPsHTHPkrqC/zZXRNsCsTEgvu3p2OIA==}
+
+  '@storybook/node-logger@8.0.9':
+    resolution: {integrity: sha512-5ajMdZFrYrjGLJOVDq7dlEQNFsgeLHymt4dCK9MulL/ciXykmXUZXE3Bye0wFy+I2qqDVvrvR8uzCvSFvm5MAQ==}
+
+  '@storybook/preview-api@8.0.9':
+    resolution: {integrity: sha512-zHfX34bkAMzzmE7vbDzaqFwSW6ExiBD0HiO1L/IsHF55f0f7xV7IH8uJyFRrDTvAoW3ReSxZDMvvPpeydFPKGA==}
+
+  '@storybook/preview@8.0.9':
+    resolution: {integrity: sha512-tFsR8xc8AYBZZrZw8enklFbSQt7ZAV+rv20BoxwDhd3q7fjXyK7O4moGPqUwBZ7rukTG13nPoISxr+VXAk/HYA==}
+
+  '@storybook/react-dom-shim@8.0.9':
+    resolution: {integrity: sha512-8011KlRuG3obr5pZZ7bcEyYYNWF3tR596YadoMd267NPoHKvwAbKL1L/DNgb6kiYjZDUf9QfaKSCWW31k0kcRQ==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+  '@storybook/react-vite@8.0.9':
+    resolution: {integrity: sha512-FT5KeulUH6grfzOJOxJCxpv9+81UVDrT9UPcgiFhQT9rKtsgmltezThwbHknByZNw3WWnf+ieidMLEis9hd73A==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      vite: ^4.0.0 || ^5.0.0
+
+  '@storybook/react@8.0.9':
+    resolution: {integrity: sha512-NeQ6suZG3HKikwe3Tx9cAIaRx7uP8FKCmlVvIiBg4LTTI5orCt94PPakvuZukZcbkqvcCnEBkebAzwUpn8PiJw==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      typescript: '>= 4.2.x'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@storybook/router@8.0.9':
+    resolution: {integrity: sha512-aAOWxbM9J4mt+cp4o88T2PB29mgBBTOzU37/pUsTHYnKnR9XI4npXEXdN8Gv+ryqM0kj0AbBpz/llFlnR2MNNA==}
+
+  '@storybook/source-loader@8.0.9':
+    resolution: {integrity: sha512-FDnpxIGE5nIYT15pvYe6rz95TSBrdLcDll7lOHNyZisWt19MI3wZU3YkVsFNRBuFrebo+FjVU3wHyoV81ur1Qw==}
+
+  '@storybook/telemetry@8.0.9':
+    resolution: {integrity: sha512-AGGfcup06t+wxhBIkHd0iybieOh9PDVZQJ9oPct5JGB39+ni9wvs0WOD+MYlHbsjp8id7+aGkh6mYuYOvfck+Q==}
+
+  '@storybook/test@8.0.9':
+    resolution: {integrity: sha512-bRd5tBJnPzR6UKbDXONWnFWtdkNOY99HMLDUWe5fTRo50GwkrpFBVqPflhdkruEeof0kAbBUbnoN2CIYgtnAFw==}
+
+  '@storybook/theming@8.0.9':
+    resolution: {integrity: sha512-jgfDuYoiNMMirQiASN3Eg0hGDXsEtpdAcMxyShqYGwu9elxgD9yUnYC2nSckYsM74a3ZQ3JaViZ9ZFSe2FHmeQ==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      react:
+        optional: true
+      react-dom:
+        optional: true
+
+  '@storybook/types@8.0.9':
+    resolution: {integrity: sha512-ew0EXzk9k4B557P1qIWYrnvUcgaE0WWA5qQS0AU8l+fRTp5nvl9O3SP/zNIB0SN1qDFO7dXr3idTNTyIikTcEQ==}
+
+  '@storybook/vue3-vite@8.0.9':
+    resolution: {integrity: sha512-IkzYsEyCo5HIvLWbJeGrBu/VIN4u+LvdIAz7vcFqVVXBtTUhy+9/8caLx8fdnM0FWgKcBRQs8HnjBB2V0lOFcg==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      vite: ^4.0.0 || ^5.0.0
+
+  '@storybook/vue3@8.0.9':
+    resolution: {integrity: sha512-EqVdS62YbOCAE0wJrQKW0sHpM90be8N8Mvmj+HzB0QYhJNtFqP9ehwbcTfwEKtaVGudisHgGBOzNoSKDlxFaag==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      vue: ^3.0.0
+
+  '@swc/cli@0.3.12':
+    resolution: {integrity: sha512-h7bvxT+4+UDrLWJLFHt6V+vNAcUNii2G4aGSSotKz1ECEk4MyEh5CWxmeSscwuz5K3i+4DWTgm4+4EyMCQKn+g==}
+    engines: {node: '>= 16.14.0'}
+    hasBin: true
+    peerDependencies:
+      '@swc/core': ^1.2.66
+      chokidar: 3.5.3
+    peerDependenciesMeta:
+      chokidar:
+        optional: true
+
+  '@swc/core-android-arm64@1.3.11':
+    resolution: {integrity: sha512-M7FamR3kFpVTyTw73FzKcOZmS7/TWHX75eqtwBTaU9fW4shf0KTLr/h9DnMxNKAnwUMeub/lqlINUe5EKFIKwQ==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [android]
+
+  '@swc/core-darwin-arm64@1.3.56':
+    resolution: {integrity: sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@swc/core-darwin-arm64@1.4.17':
+    resolution: {integrity: sha512-HVl+W4LezoqHBAYg2JCqR+s9ife9yPfgWSj37iIawLWzOmuuJ7jVdIB7Ee2B75bEisSEKyxRlTl6Y1Oq3owBgw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@swc/core-darwin-x64@1.3.56':
+    resolution: {integrity: sha512-VH5saqYFasdRXJy6RAT+MXm0+IjkMZvOkohJwUei+oA65cKJofQwrJ1jZro8yOJFYvUSI3jgNRGsdBkmo/4hMw==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@swc/core-darwin-x64@1.4.17':
+    resolution: {integrity: sha512-WYRO9Fdzq4S/he8zjW5I95G1zcvyd9yyD3Tgi4/ic84P5XDlSMpBDpBLbr/dCPjmSg7aUXxNQqKqGkl6dQxYlA==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@swc/core-freebsd-x64@1.3.11':
+    resolution: {integrity: sha512-02uqYktPp6WmZfZ2Crc/yIVOcgANtjo8ciHcT7yLHvz7v+S7gx1I2tyNGUFtTX5hcR2IFNGrL8Yj4DvpTABFHg==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@swc/core-linux-arm-gnueabihf@1.3.56':
+    resolution: {integrity: sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==}
+    engines: {node: '>=10'}
+    cpu: [arm]
+    os: [linux]
+
+  '@swc/core-linux-arm-gnueabihf@1.4.17':
+    resolution: {integrity: sha512-cgbvpWOvtMH0XFjvwppUCR+Y+nf6QPaGu6AQ5hqCP+5Lv2zO5PG0RfasC4zBIjF53xgwEaaWmGP5/361P30X8Q==}
+    engines: {node: '>=10'}
+    cpu: [arm]
+    os: [linux]
+
+  '@swc/core-linux-arm64-gnu@1.3.56':
+    resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@swc/core-linux-arm64-gnu@1.4.17':
+    resolution: {integrity: sha512-l7zHgaIY24cF9dyQ/FOWbmZDsEj2a9gRFbmgx2u19e3FzOPuOnaopFj0fRYXXKCmtdx+anD750iBIYnTR+pq/Q==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@swc/core-linux-arm64-musl@1.3.56':
+    resolution: {integrity: sha512-9gxL09BIiAv8zY0DjfnFf19bo8+P4T9tdhzPwcm+1yPJcY5yr1+YFWLNFzz01agtOj6VlZ2/wUJTaOfdjjtc+A==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@swc/core-linux-arm64-musl@1.4.17':
+    resolution: {integrity: sha512-qhH4gr9gAlVk8MBtzXbzTP3BJyqbAfUOATGkyUtohh85fPXQYuzVlbExix3FZXTwFHNidGHY8C+ocscI7uDaYw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@swc/core-linux-x64-gnu@1.3.56':
+    resolution: {integrity: sha512-n0ORNknl50vMRkll3BDO1E4WOqY6iISlPV1ZQCRLWQ6YQ2q8/WAryBxc2OAybcGHBUFkxyACpJukeU1QZ/9tNw==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@swc/core-linux-x64-gnu@1.4.17':
+    resolution: {integrity: sha512-vRDFATL1oN5oZMImkwbgSHEkp8xG1ofEASBypze01W1Tqto8t+yo6gsp69wzCZBlxldsvPpvFZW55Jq0Rn+UnA==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@swc/core-linux-x64-musl@1.3.56':
+    resolution: {integrity: sha512-r+D34WLAOAlJtfw1gaVWpHRwCncU9nzW9i7w9kSw4HpWYnHJOz54jLGSEmNsrhdTCz1VK2ar+V2ktFUsrlGlDA==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@swc/core-linux-x64-musl@1.4.17':
+    resolution: {integrity: sha512-zQNPXAXn3nmPqv54JVEN8k2JMEcMTQ6veVuU0p5O+A7KscJq+AGle/7ZQXzpXSfUCXlLMX4wvd+rwfGhh3J4cw==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@swc/core-win32-arm64-msvc@1.3.56':
+    resolution: {integrity: sha512-29Yt75Is6X24z3x8h/xZC1HnDPkPpyLH9mDQiM6Cuc0I9mVr1XSriPEUB2N/awf5IE4SA8c+3IVq1DtKWbkJIw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@swc/core-win32-arm64-msvc@1.4.17':
+    resolution: {integrity: sha512-z86n7EhOwyzxwm+DLE5NoLkxCTme2lq7QZlDjbQyfCxOt6isWz8rkW5QowTX8w9Rdmk34ncrjSLvnHOeLY17+w==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@swc/core-win32-ia32-msvc@1.3.56':
+    resolution: {integrity: sha512-mplp0zbYDrcHtfvkniXlXdB04e2qIjz2Gq/XHKr4Rnc6xVORJjjXF91IemXKpavx2oZYJws+LNJL7UFQ8jyCdQ==}
+    engines: {node: '>=10'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@swc/core-win32-ia32-msvc@1.4.17':
+    resolution: {integrity: sha512-JBwuSTJIgiJJX6wtr4wmXbfvOswHFj223AumUrK544QV69k60FJ9q2adPW9Csk+a8wm1hLxq4HKa2K334UHJ/g==}
+    engines: {node: '>=10'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@swc/core-win32-x64-msvc@1.3.56':
+    resolution: {integrity: sha512-zp8MBnrw/bjdLenO/ifYzHrImSjKunqL0C2IF4LXYNRfcbYFh2NwobsVQMZ20IT0474lKRdlP8Oxdt+bHuXrzA==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [win32]
+
+  '@swc/core-win32-x64-msvc@1.4.17':
+    resolution: {integrity: sha512-jFkOnGQamtVDBm3MF5Kq1lgW8vx4Rm1UvJWRUfg+0gx7Uc3Jp3QMFeMNw/rDNQYRDYPG3yunCC+2463ycd5+dg==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [win32]
+
+  '@swc/core@1.4.17':
+    resolution: {integrity: sha512-tq+mdWvodMBNBBZbwFIMTVGYHe9N7zvEaycVVjfvAx20k1XozHbHhRv+9pEVFJjwRxLdXmtvFZd3QZHRAOpoNQ==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@swc/helpers': ^0.5.0
+    peerDependenciesMeta:
+      '@swc/helpers':
+        optional: true
+
+  '@swc/counter@0.1.3':
+    resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+  '@swc/jest@0.2.36':
+    resolution: {integrity: sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==}
+    engines: {npm: '>= 7.0.0'}
+    peerDependencies:
+      '@swc/core': '*'
+
+  '@swc/types@0.1.5':
+    resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
+
+  '@swc/wasm@1.2.130':
+    resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
+
+  '@syuilo/aiscript@0.18.0':
+    resolution: {integrity: sha512-/iY9Vv4LLjtW/KUzId1QwXC4BlpIEPCMcoT7dyRhYdyxtwhS3Hx4b/4j1HYP+n3Pq9XKyW5zvkY72/+DNu4g6Q==}
+
+  '@szmarczak/http-timer@4.0.6':
+    resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
+    engines: {node: '>=10'}
+
+  '@szmarczak/http-timer@5.0.1':
+    resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
+    engines: {node: '>=14.16'}
+
+  '@tabler/icons-webfont@3.3.0':
+    resolution: {integrity: sha512-vMsxtwTXdC4QH4uDajZjBYThILEI0dP+Mn1s4XZkVtnJ793IF31i3596nYetWhAJnrED0UJ0HQWSbmqVxVQbxA==}
+
+  '@tabler/icons@3.3.0':
+    resolution: {integrity: sha512-PLVe9d7b59sKytbx00KgeGhQG3N176Ezv8YMmsnSz4s0ifDzMWlp/h2wEfQZ0ZNe8e377GY2OW6kovUe3Rnd0g==}
+
+  '@tensorflow/tfjs-backend-cpu@4.4.0':
+    resolution: {integrity: sha512-d4eln500/qNym78z9IrUUzF0ITBoJGLrxV8xd92kLVoXhg35Mm+zqUXShjFcrH8joOHOFuST0qZ0TbDDqcPzPA==}
+    engines: {yarn: '>= 1.3.2'}
+    peerDependencies:
+      '@tensorflow/tfjs-core': 4.4.0
+
+  '@tensorflow/tfjs-backend-webgl@4.4.0':
+    resolution: {integrity: sha512-TzQKvfAPgGt9cMG+5bVoTckoG1xr/PVJM/uODkPvzcMqi3j97kuWDXwkYJIgXldStmfiKkU7f5CmyD3Cq3E6BA==}
+    engines: {yarn: '>= 1.3.2'}
+    peerDependencies:
+      '@tensorflow/tfjs-core': 4.4.0
+
+  '@tensorflow/tfjs-converter@4.4.0':
+    resolution: {integrity: sha512-JUjpRStrAuw37tgPd5UENu0UjQVuJT09yF7KpOur4BriJ0uQqrbEZHMPHmvUtr5nYzkqlXJTuXIyxvEY/olNpg==}
+    peerDependencies:
+      '@tensorflow/tfjs-core': 4.4.0
+
+  '@tensorflow/tfjs-core@4.4.0':
+    resolution: {integrity: sha512-Anxpc7cAOA0Q7EUXdTbQKMg3reFvrdkgDlaYzH9ZfkMq2CgLV4Au6E/s6HmbYn/VrAtWy9mLY5c/lLJqh4764g==}
+    engines: {yarn: '>= 1.3.2'}
+
+  '@tensorflow/tfjs-data@4.4.0':
+    resolution: {integrity: sha512-aY4eq4cgrsrXeBU6ABZAAN3tV0fG4YcHd0z+cYuNXnCo+VEQLJnPmhn+xymZ4VQZQH4GXbVS4dV9pXMclFNRFw==}
+    peerDependencies:
+      '@tensorflow/tfjs-core': 4.4.0
+      seedrandom: ^3.0.5
+
+  '@tensorflow/tfjs-layers@4.4.0':
+    resolution: {integrity: sha512-OGC7shfiD9Gc698hINHK4y9slOJvu5m54tVNm4xf+WSNrw/avvgpar6yyoL5bakYIZNQvFNK75Yr8VRPR7oPeQ==}
+    peerDependencies:
+      '@tensorflow/tfjs-core': 4.4.0
+
+  '@tensorflow/tfjs-node@4.4.0':
+    resolution: {integrity: sha512-+JSAddsupjSQUDZeb7QGOFkL3Tty3kjPHx8ethiYFzwTZJHCMvM7wZJd0Fqnjxym6A0KpsmB7SPZgwRRXVIlPA==}
+    engines: {node: '>=8.11.0'}
+
+  '@tensorflow/tfjs@4.4.0':
+    resolution: {integrity: sha512-EmCsnzdvawyk4b+4JKaLLuicHcJQRZtL1zSy9AWJLiiHTbDDseYgLxfaCEfLk8v2bUe7SBXwl3n3B7OjgvH11Q==}
+    hasBin: true
+
+  '@testing-library/dom@9.3.3':
+    resolution: {integrity: sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==}
+    engines: {node: '>=14'}
+
+  '@testing-library/dom@9.3.4':
+    resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==}
+    engines: {node: '>=14'}
+
+  '@testing-library/jest-dom@6.4.2':
+    resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==}
+    engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+    peerDependencies:
+      '@jest/globals': '>= 28'
+      '@types/bun': latest
+      '@types/jest': '>= 28'
+      jest: '>= 28'
+      vitest: '>= 0.32'
+    peerDependenciesMeta:
+      '@jest/globals':
+        optional: true
+      '@types/bun':
+        optional: true
+      '@types/jest':
+        optional: true
+      jest:
+        optional: true
+      vitest:
+        optional: true
+
+  '@testing-library/user-event@14.5.2':
+    resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==}
+    engines: {node: '>=12', npm: '>=6'}
+    peerDependencies:
+      '@testing-library/dom': '>=7.21.4'
+
+  '@testing-library/vue@8.0.3':
+    resolution: {integrity: sha512-wSsbNlZ69ZFQgVlHMtc/ZC/g9BHO7MhyDrd4nHyfEubtMr3kToN/w4/BsSBknGIF8w9UmPbsgbIuq/CbdBHzCA==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@vue/compiler-sfc': '>= 3'
+      vue: '>= 3'
+    peerDependenciesMeta:
+      '@vue/compiler-sfc':
+        optional: true
+
+  '@tokenizer/token@0.3.0':
+    resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
+
+  '@trysound/sax@0.2.0':
+    resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
+    engines: {node: '>=10.13.0'}
+
+  '@tsd/typescript@5.3.3':
+    resolution: {integrity: sha512-CQlfzol0ldaU+ftWuG52vH29uRoKboLinLy84wS8TQOu+m+tWoaUfk4svL4ij2V8M5284KymJBlHUusKj6k34w==}
+    engines: {node: '>=14.17'}
+
+  '@twemoji/parser@15.0.0':
+    resolution: {integrity: sha512-lh9515BNsvKSNvyUqbj5yFu83iIDQ77SwVcsN/SnEGawczhsKU6qWuogewN1GweTi5Imo5ToQ9s+nNTf97IXvg==}
+
+  '@twemoji/parser@15.1.1':
+    resolution: {integrity: sha512-CChRzIu6ngkCJOmURBlYEdX5DZSu+bBTtqR60XjBkFrmvplKW7OQsea+i8XwF4bLVlUXBO7ZmHhRPDzfQyLwwg==}
+
+  '@types/accepts@1.3.7':
+    resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
+
+  '@types/archiver@6.0.2':
+    resolution: {integrity: sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==}
+
+  '@types/argparse@1.0.38':
+    resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
+
+  '@types/aria-query@5.0.1':
+    resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==}
+
+  '@types/babel__core@7.20.0':
+    resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
+
+  '@types/babel__generator@7.6.4':
+    resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
+
+  '@types/babel__template@7.4.1':
+    resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
+
+  '@types/babel__traverse@7.20.0':
+    resolution: {integrity: sha512-TBOjqAGf0hmaqRwpii5LLkJLg7c6OMm4nHLmpsUxwk9bBHtoTC6dAHdVWdGv4TBxj2CZOZY8Xfq8WmfoVi7n4Q==}
+
+  '@types/bcryptjs@2.4.6':
+    resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==}
+
+  '@types/body-parser@1.19.5':
+    resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
+
+  '@types/braces@3.0.1':
+    resolution: {integrity: sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==}
+
+  '@types/cacheable-request@6.0.3':
+    resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
+
+  '@types/chai-subset@1.3.5':
+    resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==}
+
+  '@types/chai@4.3.11':
+    resolution: {integrity: sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==}
+
+  '@types/color-convert@2.0.3':
+    resolution: {integrity: sha512-2Q6wzrNiuEvYxVQqhh7sXM2mhIhvZR/Paq4FdsQkOMgWsCIkKvSGj8Le1/XalulrmgOzPMqNa0ix+ePY4hTrfg==}
+
+  '@types/color-name@1.1.1':
+    resolution: {integrity: sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==}
+
+  '@types/connect@3.4.35':
+    resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
+
+  '@types/content-disposition@0.5.8':
+    resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==}
+
+  '@types/cookie@0.6.0':
+    resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+
+  '@types/cross-spawn@6.0.2':
+    resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
+
+  '@types/debug@4.1.12':
+    resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+
+  '@types/detect-port@1.3.2':
+    resolution: {integrity: sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==}
+
+  '@types/disposable-email-domains@1.0.2':
+    resolution: {integrity: sha512-SDKwyYTjk3y5aZBxxc38yRecpJPjsqn57STz1bNxYYlv4k11bBe7QB8w4llXDTmQXKT1mFvgGmJv+8Zdu3YmJw==}
+
+  '@types/doctrine@0.0.3':
+    resolution: {integrity: sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==}
+
+  '@types/doctrine@0.0.9':
+    resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==}
+
+  '@types/ejs@3.1.2':
+    resolution: {integrity: sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==}
+
+  '@types/emscripten@1.39.7':
+    resolution: {integrity: sha512-tLqYV94vuqDrXh515F/FOGtBcRMTPGvVV1LzLbtYDcQmmhtpf/gLYf+hikBbQk8MzOHNz37wpFfJbYAuSn8HqA==}
+
+  '@types/escape-regexp@0.0.3':
+    resolution: {integrity: sha512-FQMYUxaf1dVeWLUzJFSvfdDugfOpDyM13p67QfyMdagxSkBa689opkr/q9uR/VWyrWrl0jAyQaSPKxX9MpAXFw==}
+
+  '@types/escodegen@0.0.6':
+    resolution: {integrity: sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig==}
+
+  '@types/eslint@7.29.0':
+    resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==}
+
+  '@types/estree@0.0.51':
+    resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
+
+  '@types/estree@1.0.5':
+    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+
+  '@types/express-serve-static-core@4.17.33':
+    resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
+
+  '@types/express@4.17.17':
+    resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==}
+
+  '@types/find-cache-dir@3.2.1':
+    resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==}
+
+  '@types/fluent-ffmpeg@2.1.24':
+    resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
+
+  '@types/glob@7.2.0':
+    resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
+
+  '@types/graceful-fs@4.1.6':
+    resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
+
+  '@types/hast@3.0.4':
+    resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
+  '@types/htmlescape@1.1.3':
+    resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==}
+
+  '@types/http-cache-semantics@4.0.4':
+    resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+
+  '@types/http-link-header@1.0.5':
+    resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
+
+  '@types/istanbul-lib-coverage@2.0.4':
+    resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
+
+  '@types/istanbul-lib-report@3.0.0':
+    resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==}
+
+  '@types/istanbul-reports@3.0.1':
+    resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==}
+
+  '@types/jest@29.5.12':
+    resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==}
+
+  '@types/js-yaml@4.0.9':
+    resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
+
+  '@types/jsdom@21.1.6':
+    resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
+
+  '@types/json-schema@7.0.12':
+    resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
+
+  '@types/json-schema@7.0.15':
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+  '@types/json5@0.0.29':
+    resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+  '@types/jsonld@1.5.13':
+    resolution: {integrity: sha512-n7fUU6W4kSYK8VQlf/LsE9kddBHPKhODoVOjsZswmve+2qLwBy6naWxs/EiuSZN9NU0N06Ra01FR+j87C62T0A==}
+
+  '@types/jsrsasign@10.5.14':
+    resolution: {integrity: sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==}
+
+  '@types/keyv@3.1.4':
+    resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+
+  '@types/lodash@4.14.191':
+    resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
+
+  '@types/long@4.0.2':
+    resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==}
+
+  '@types/matter-js@0.19.6':
+    resolution: {integrity: sha512-ffk6tqJM5scla+ThXmnox+mdfCo3qYk6yMjQsNcrbo6eQ5DqorVdtnaL+1agCoYzxUjmHeiNB7poBMAmhuLY7w==}
+
+  '@types/mdast@4.0.3':
+    resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
+
+  '@types/mdx@2.0.3':
+    resolution: {integrity: sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==}
+
+  '@types/micromatch@4.0.7':
+    resolution: {integrity: sha512-C/FMQ8HJAZhTsDpl4wDKZdMeeW5USjgzOczUwTGbRc1ZopPgOhIEnxY2ZgUrsuyy4DwK1JVOJZKFakv3TbCKiA==}
+
+  '@types/mime-types@2.1.4':
+    resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==}
+
+  '@types/mime@3.0.1':
+    resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
+
+  '@types/minimatch@5.1.2':
+    resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
+
+  '@types/minimist@1.2.2':
+    resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==}
+
+  '@types/ms@0.7.34':
+    resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
+
+  '@types/mute-stream@0.0.4':
+    resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==}
+
+  '@types/node-fetch@2.6.4':
+    resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
+
+  '@types/node-fetch@3.0.3':
+    resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==}
+    deprecated: This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.
+
+  '@types/node@18.17.15':
+    resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
+
+  '@types/node@20.11.5':
+    resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==}
+
+  '@types/node@20.12.7':
+    resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==}
+
+  '@types/node@20.9.1':
+    resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==}
+
+  '@types/nodemailer@6.4.15':
+    resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==}
+
+  '@types/normalize-package-data@2.4.1':
+    resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
+
+  '@types/oauth2orize-pkce@0.1.2':
+    resolution: {integrity: sha512-g5rDzqQTTUIJJpY7UWxb0EU1WyURIwOj3TndKC2krEEEmaKrnZXgoEBkR72QY2kp4cJ6N9cF2AqTPJ0Qyg+caA==}
+
+  '@types/oauth2orize@1.11.5':
+    resolution: {integrity: sha512-C6hrRoh9hCnqis39OpeUZSwgw+TIzcV0CsxwJMGfQjTx4I1r+CLmuEPzoDJr5NRTfc7OMwHNLkQwrGFLKrJjMQ==}
+
+  '@types/oauth@0.9.4':
+    resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
+
+  '@types/offscreencanvas@2019.3.0':
+    resolution: {integrity: sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==}
+
+  '@types/offscreencanvas@2019.7.0':
+    resolution: {integrity: sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==}
+
+  '@types/pg@8.11.5':
+    resolution: {integrity: sha512-2xMjVviMxneZHDHX5p5S6tsRRs7TpDHeeK7kTTMe/kAC/mRRNjWHjZg0rkiY+e17jXSZV3zJYDxXV8Cy72/Vuw==}
+
+  '@types/pretty-hrtime@1.0.1':
+    resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==}
+
+  '@types/prop-types@15.7.5':
+    resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
+
+  '@types/pug@2.0.10':
+    resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
+
+  '@types/punycode@2.1.4':
+    resolution: {integrity: sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ==}
+
+  '@types/qrcode@1.5.5':
+    resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
+
+  '@types/qs@6.9.7':
+    resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
+
+  '@types/random-seed@0.3.5':
+    resolution: {integrity: sha512-CftxcDPAHgs0SLHU2dt+ZlDPJfGqLW3sZlC/ATr5vJDSe5tRLeOne7HMvCOJnFyF8e1U41wqzs3h6AMC613xtA==}
+
+  '@types/range-parser@1.2.4':
+    resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
+
+  '@types/ratelimiter@3.4.6':
+    resolution: {integrity: sha512-Bv6WLSXPGLVsBjkizXtn+ef78R92e36/DFQo2wXPTHtp1cYXF6rCULMqf9WcZPAtyMZMvQAtIPeYMA1xAyxghw==}
+
+  '@types/react@18.0.28':
+    resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==}
+
+  '@types/readdir-glob@1.1.1':
+    resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
+
+  '@types/rename@1.0.7':
+    resolution: {integrity: sha512-E9qapfghUGfBMi3jNhsmCKPIp3f2zvNKpaX1BDGLGJNjzpgsZ/RTx7NaNksFjGoJ+r9NvWF1NSM5vVecnNjVmw==}
+
+  '@types/resolve@1.20.3':
+    resolution: {integrity: sha512-NH5oErHOtHZYcjCtg69t26aXEk4BN2zLWqf7wnDZ+dpe0iR7Rds1SPGEItl3fca21oOe0n3OCnZ4W7jBxu7FOw==}
+
+  '@types/responselike@1.0.0':
+    resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
+
+  '@types/sanitize-html@2.11.0':
+    resolution: {integrity: sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==}
+
+  '@types/scheduler@0.16.2':
+    resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
+
+  '@types/seedrandom@2.4.30':
+    resolution: {integrity: sha512-AnxLHewubLVzoF/A4qdxBGHCKifw8cY32iro3DQX9TPcetE95zBeVt3jnsvtvAUf1vwzMfwzp4t/L2yqPlnjkQ==}
+
+  '@types/seedrandom@3.0.8':
+    resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==}
+
+  '@types/semver@7.5.8':
+    resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
+
+  '@types/serve-static@1.15.1':
+    resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
+
+  '@types/serviceworker@0.0.67':
+    resolution: {integrity: sha512-7TCH7iNsCSNb+aUD9M/36TekrWFSLCjNK8zw/3n5kOtRjbLtDfGYMXTrDnGhSfqXNwpqmt9Vd90w5C/ad1tX6Q==}
+
+  '@types/simple-oauth2@5.0.7':
+    resolution: {integrity: sha512-8JbWVJbiTSBQP/7eiyGKyXWAqp3dKQZpaA+pdW16FCi32ujkzRMG8JfjoAzdWt6W8U591ZNdHcPtP2D7ILTKuA==}
+
+  '@types/sinon@10.0.13':
+    resolution: {integrity: sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==}
+
+  '@types/sinonjs__fake-timers@8.1.1':
+    resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
+
+  '@types/sinonjs__fake-timers@8.1.5':
+    resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==}
+
+  '@types/sizzle@2.3.3':
+    resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
+
+  '@types/stack-utils@2.0.1':
+    resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
+
+  '@types/statuses@2.0.4':
+    resolution: {integrity: sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==}
+
+  '@types/throttle-debounce@5.0.2':
+    resolution: {integrity: sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==}
+
+  '@types/tinycolor2@1.4.6':
+    resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==}
+
+  '@types/tmp@0.2.6':
+    resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==}
+
+  '@types/tough-cookie@4.0.2':
+    resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==}
+
+  '@types/unist@3.0.2':
+    resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
+
+  '@types/uuid@9.0.8':
+    resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
+
+  '@types/vary@1.1.3':
+    resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==}
+
+  '@types/web-push@3.6.3':
+    resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
+
+  '@types/webgl-ext@0.0.30':
+    resolution: {integrity: sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==}
+
+  '@types/wrap-ansi@3.0.0':
+    resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==}
+
+  '@types/ws@8.5.10':
+    resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
+
+  '@types/yargs-parser@21.0.0':
+    resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
+
+  '@types/yargs@17.0.19':
+    resolution: {integrity: sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==}
+
+  '@types/yauzl@2.10.0':
+    resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
+
+  '@typescript-eslint/eslint-plugin@6.11.0':
+    resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
+      eslint: ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/eslint-plugin@7.1.0':
+    resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^7.0.0
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/eslint-plugin@7.7.1':
+    resolution: {integrity: sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^7.0.0
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/parser@6.11.0':
+    resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/parser@7.1.0':
+    resolution: {integrity: sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/parser@7.7.1':
+    resolution: {integrity: sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/scope-manager@6.11.0':
+    resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+
+  '@typescript-eslint/scope-manager@7.1.0':
+    resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+
+  '@typescript-eslint/scope-manager@7.7.1':
+    resolution: {integrity: sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+
+  '@typescript-eslint/type-utils@6.11.0':
+    resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/type-utils@7.1.0':
+    resolution: {integrity: sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/type-utils@7.7.1':
+    resolution: {integrity: sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/types@6.11.0':
+    resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+
+  '@typescript-eslint/types@7.1.0':
+    resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+
+  '@typescript-eslint/types@7.7.1':
+    resolution: {integrity: sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+
+  '@typescript-eslint/typescript-estree@6.11.0':
+    resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/typescript-estree@7.1.0':
+    resolution: {integrity: sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/typescript-estree@7.7.1':
+    resolution: {integrity: sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@typescript-eslint/utils@6.11.0':
+    resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0
+
+  '@typescript-eslint/utils@7.1.0':
+    resolution: {integrity: sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+
+  '@typescript-eslint/utils@7.7.1':
+    resolution: {integrity: sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+    peerDependencies:
+      eslint: ^8.56.0
+
+  '@typescript-eslint/visitor-keys@6.11.0':
+    resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+
+  '@typescript-eslint/visitor-keys@7.1.0':
+    resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+
+  '@typescript-eslint/visitor-keys@7.7.1':
+    resolution: {integrity: sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+
+  '@ungap/structured-clone@1.2.0':
+    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+
+  '@vitejs/plugin-vue@5.0.4':
+    resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      vite: ^5.0.0
+      vue: ^3.2.25
+
+  '@vitest/coverage-v8@0.34.6':
+    resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
+    peerDependencies:
+      vitest: '>=0.32.0 <1'
+
+  '@vitest/expect@0.34.6':
+    resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==}
+
+  '@vitest/expect@1.3.1':
+    resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
+
+  '@vitest/runner@0.34.6':
+    resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==}
+
+  '@vitest/snapshot@0.34.6':
+    resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==}
+
+  '@vitest/spy@0.34.6':
+    resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==}
+
+  '@vitest/spy@1.3.1':
+    resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
+
+  '@vitest/spy@1.6.0':
+    resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==}
+
+  '@vitest/utils@0.34.6':
+    resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==}
+
+  '@vitest/utils@1.3.1':
+    resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
+
+  '@vitest/utils@1.6.0':
+    resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==}
+
+  '@volar/language-core@2.2.0':
+    resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==}
+
+  '@volar/source-map@2.2.0':
+    resolution: {integrity: sha512-HQlPRlHOVqCCHK8wI76ZldHkEwKsjp7E6idUc36Ekni+KJDNrqgSqPvyHQixybXPHNU7CI9Uxd9/IkxO7LuNBw==}
+
+  '@volar/typescript@2.2.0':
+    resolution: {integrity: sha512-wC6l4zLiiCLxF+FGaHCbWlQYf4vMsnRxYhcI6WgvaNppOD6r1g+Ef1RKRJUApALWU46Yy/JDU/TbdV6w/X6Liw==}
+
+  '@vue/compiler-core@3.4.21':
+    resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
+
+  '@vue/compiler-core@3.4.25':
+    resolution: {integrity: sha512-Y2pLLopaElgWnMNolgG8w3C5nNUVev80L7hdQ5iIKPtMJvhVpG0zhnBG/g3UajJmZdvW0fktyZTotEHD1Srhbg==}
+
+  '@vue/compiler-core@3.4.26':
+    resolution: {integrity: sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==}
+
+  '@vue/compiler-dom@3.4.21':
+    resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
+
+  '@vue/compiler-dom@3.4.25':
+    resolution: {integrity: sha512-Ugz5DusW57+HjllAugLci19NsDK+VyjGvmbB2TXaTcSlQxwL++2PETHx/+Qv6qFwNLzSt7HKepPe4DcTE3pBWg==}
+
+  '@vue/compiler-dom@3.4.26':
+    resolution: {integrity: sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==}
+
+  '@vue/compiler-sfc@3.4.26':
+    resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==}
+
+  '@vue/compiler-ssr@3.4.25':
+    resolution: {integrity: sha512-H2ohvM/Pf6LelGxDBnfbbXFPyM4NE3hrw0e/EpwuSiYu8c819wx+SVGdJ65p/sFrYDd6OnSDxN1MB2mN07hRSQ==}
+
+  '@vue/compiler-ssr@3.4.26':
+    resolution: {integrity: sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==}
+
+  '@vue/devtools-api@6.6.1':
+    resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
+
+  '@vue/language-core@2.0.16':
+    resolution: {integrity: sha512-Bc2sexRH99pznOph8mLw2BlRZ9edm7tW51kcBXgx8adAoOcZUWJj3UNSsdQ6H9Y8meGz7BoazVrVo/jUukIsPw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  '@vue/reactivity@3.4.26':
+    resolution: {integrity: sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ==}
+
+  '@vue/runtime-core@3.4.26':
+    resolution: {integrity: sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw==}
+
+  '@vue/runtime-dom@3.4.26':
+    resolution: {integrity: sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==}
+
+  '@vue/server-renderer@3.4.25':
+    resolution: {integrity: sha512-8VTwq0Zcu3K4dWV0jOwIVINESE/gha3ifYCOKEhxOj6MEl5K5y8J8clQncTcDhKF+9U765nRw4UdUEXvrGhyVQ==}
+    peerDependencies:
+      vue: 3.4.25
+
+  '@vue/server-renderer@3.4.26':
+    resolution: {integrity: sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==}
+    peerDependencies:
+      vue: 3.4.26
+
+  '@vue/shared@3.4.21':
+    resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
+
+  '@vue/shared@3.4.25':
+    resolution: {integrity: sha512-k0yappJ77g2+KNrIaF0FFnzwLvUBLUYr8VOwz+/6vLsmItFp51AcxLL7Ey3iPd7BIRyWPOcqUjMnm7OkahXllA==}
+
+  '@vue/shared@3.4.26':
+    resolution: {integrity: sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==}
+
+  '@vue/test-utils@2.4.1':
+    resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
+    peerDependencies:
+      '@vue/server-renderer': ^3.0.1
+      vue: ^3.0.1
+    peerDependenciesMeta:
+      '@vue/server-renderer':
+        optional: true
+
+  '@webgpu/types@0.1.30':
+    resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==}
+
+  '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15':
+    resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==}
+    engines: {node: '>=14.15.0'}
+    peerDependencies:
+      esbuild: '>=0.10.0'
+
+  '@yarnpkg/fslib@2.10.3':
+    resolution: {integrity: sha512-41H+Ga78xT9sHvWLlFOZLIhtU6mTGZ20pZ29EiZa97vnxdohJD2AF42rCoAoWfqUz486xY6fhjMH+DYEM9r14A==}
+    engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'}
+
+  '@yarnpkg/libzip@2.3.0':
+    resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==}
+    engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'}
+
+  abbrev@1.1.1:
+    resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+
+  abbrev@2.0.0:
+    resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  abort-controller@3.0.0:
+    resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+    engines: {node: '>=6.5'}
+
+  abstract-logging@2.0.1:
+    resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
+
+  accepts@1.3.8:
+    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+    engines: {node: '>= 0.6'}
+
+  acorn-jsx@5.3.2:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+  acorn-walk@7.2.0:
+    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
+    engines: {node: '>=0.4.0'}
+
+  acorn-walk@8.3.2:
+    resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
+    engines: {node: '>=0.4.0'}
+
+  acorn@7.4.1:
+    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  acorn@8.11.3:
+    resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  address@1.2.2:
+    resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==}
+    engines: {node: '>= 10.0.0'}
+
+  adm-zip@0.5.10:
+    resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==}
+    engines: {node: '>=6.0'}
+
+  agent-base@4.3.0:
+    resolution: {integrity: sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==}
+    engines: {node: '>= 4.0.0'}
+
+  agent-base@6.0.2:
+    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+    engines: {node: '>= 6.0.0'}
+
+  agent-base@7.1.0:
+    resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
+    engines: {node: '>= 14'}
+
+  aggregate-error@3.1.0:
+    resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
+    engines: {node: '>=8'}
+
+  aggregate-error@5.0.0:
+    resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==}
+    engines: {node: '>=18'}
+
+  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424:
+    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424}
+    version: 0.1.4
+    engines: {vscode: ^1.83.0}
+
+  ajv-draft-04@1.0.0:
+    resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
+    peerDependencies:
+      ajv: ^8.5.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+
+  ajv-formats@2.1.1:
+    resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
+    peerDependencies:
+      ajv: ^8.0.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+
+  ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+  ajv@8.13.0:
+    resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
+
+  ansi-colors@4.1.3:
+    resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
+    engines: {node: '>=6'}
+
+  ansi-escapes@4.3.2:
+    resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+    engines: {node: '>=8'}
+
+  ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+
+  ansi-regex@6.0.1:
+    resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
+    engines: {node: '>=12'}
+
+  ansi-styles@3.2.1:
+    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+    engines: {node: '>=4'}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  ansi-styles@5.2.0:
+    resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+    engines: {node: '>=10'}
+
+  ansi-styles@6.2.1:
+    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+    engines: {node: '>=12'}
+
+  any-promise@1.3.0:
+    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+  anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+
+  app-root-dir@1.0.2:
+    resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==}
+
+  app-root-path@3.1.0:
+    resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==}
+    engines: {node: '>= 6.0.0'}
+
+  append-field@1.0.0:
+    resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
+
+  aproba@2.0.0:
+    resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
+
+  arch@2.2.0:
+    resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
+
+  archiver-utils@5.0.2:
+    resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==}
+    engines: {node: '>= 14'}
+
+  archiver@7.0.1:
+    resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==}
+    engines: {node: '>= 14'}
+
+  archy@1.0.0:
+    resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
+
+  are-we-there-yet@2.0.0:
+    resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
+    engines: {node: '>=10'}
+
+  arg@5.0.2:
+    resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+  argparse@1.0.10:
+    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+  argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+  aria-query@5.1.3:
+    resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+
+  array-buffer-byte-length@1.0.0:
+    resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
+
+  array-flatten@1.1.1:
+    resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
+
+  array-includes@3.1.7:
+    resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
+    engines: {node: '>= 0.4'}
+
+  array-union@2.1.0:
+    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+    engines: {node: '>=8'}
+
+  array.prototype.findlastindex@1.2.3:
+    resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==}
+    engines: {node: '>= 0.4'}
+
+  array.prototype.flat@1.3.2:
+    resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
+    engines: {node: '>= 0.4'}
+
+  array.prototype.flatmap@1.3.2:
+    resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
+    engines: {node: '>= 0.4'}
+
+  arraybuffer.prototype.slice@1.0.1:
+    resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==}
+    engines: {node: '>= 0.4'}
+
+  arrify@1.0.1:
+    resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
+    engines: {node: '>=0.10.0'}
+
+  asap@2.0.6:
+    resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+
+  asn1.js@5.4.1:
+    resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
+
+  asn1@0.2.6:
+    resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
+
+  asn1js@3.0.5:
+    resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==}
+    engines: {node: '>=12.0.0'}
+
+  assert-never@1.2.1:
+    resolution: {integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==}
+
+  assert-plus@1.0.0:
+    resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
+    engines: {node: '>=0.8'}
+
+  assert@2.1.0:
+    resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==}
+
+  assertion-error@1.1.0:
+    resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
+
+  ast-types@0.16.1:
+    resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
+    engines: {node: '>=4'}
+
+  astral-regex@2.0.0:
+    resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+    engines: {node: '>=8'}
+
+  astring@1.8.6:
+    resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
+    hasBin: true
+
+  async-mutex@0.5.0:
+    resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
+
+  async@3.2.4:
+    resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
+
+  asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+  at-least-node@1.0.0:
+    resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
+    engines: {node: '>= 4.0.0'}
+
+  atomic-sleep@1.0.0:
+    resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
+    engines: {node: '>=8.0.0'}
+
+  available-typed-arrays@1.0.5:
+    resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
+    engines: {node: '>= 0.4'}
+
+  avvio@8.3.0:
+    resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==}
+
+  aws-sdk-client-mock@3.0.1:
+    resolution: {integrity: sha512-9VAzJLl8mz99KP9HjOm/93d8vznRRUTpJooPBOunRdUAnVYopCe9xmMuu7eVemu8fQ+w6rP7o5bBK1kAFkB2KQ==}
+
+  aws-sign2@0.7.0:
+    resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
+
+  aws4@1.12.0:
+    resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
+
+  axios@0.24.0:
+    resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
+
+  axios@1.6.2:
+    resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==}
+
+  b4a@1.6.4:
+    resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
+
+  babel-core@7.0.0-bridge.0:
+    resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  babel-jest@29.7.0:
+    resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    peerDependencies:
+      '@babel/core': ^7.8.0
+
+  babel-plugin-istanbul@6.1.1:
+    resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
+    engines: {node: '>=8'}
+
+  babel-plugin-jest-hoist@29.6.3:
+    resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  babel-plugin-polyfill-corejs2@0.4.6:
+    resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  babel-plugin-polyfill-corejs3@0.8.6:
+    resolution: {integrity: sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  babel-plugin-polyfill-regenerator@0.5.3:
+    resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+  babel-preset-current-node-syntax@1.0.1:
+    resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  babel-preset-jest@29.6.3:
+    resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  babel-walk@3.0.0-canary-5:
+    resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==}
+    engines: {node: '>= 10.0.0'}
+
+  bail@2.0.2:
+    resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  base64-js@1.5.1:
+    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+  bcrypt-pbkdf@1.0.2:
+    resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
+
+  bcryptjs@2.4.3:
+    resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
+
+  better-opn@3.0.2:
+    resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==}
+    engines: {node: '>=12.0.0'}
+
+  big-integer@1.6.51:
+    resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==}
+    engines: {node: '>=0.6'}
+
+  bin-check@4.1.0:
+    resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==}
+    engines: {node: '>=4'}
+
+  bin-version-check@5.0.0:
+    resolution: {integrity: sha512-Q3FMQnS5eZmrBGqmDXLs4dbAn/f+52voP6ykJYmweSA60t6DyH4UTSwZhtbK5UH+LBoWvDljILUQMLRUtsynsA==}
+    engines: {node: '>=12'}
+
+  bin-version@6.0.0:
+    resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==}
+    engines: {node: '>=12'}
+
+  binary-extensions@2.2.0:
+    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+    engines: {node: '>=8'}
+
+  bl@4.1.0:
+    resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
+  blob-util@2.0.2:
+    resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==}
+
+  bluebird@3.7.2:
+    resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
+
+  blurhash@2.0.5:
+    resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==}
+
+  bn.js@4.12.0:
+    resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
+
+  body-parser@1.20.1:
+    resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+  body-parser@1.20.2:
+    resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+  boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+  bowser@2.11.0:
+    resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
+
+  bplist-parser@0.2.0:
+    resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
+    engines: {node: '>= 5.10.0'}
+
+  brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+  brace-expansion@2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+  braces@3.0.2:
+    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+    engines: {node: '>=8'}
+
+  broadcast-channel@7.0.0:
+    resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
+
+  browser-assert@1.2.1:
+    resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==}
+
+  browserify-zlib@0.1.4:
+    resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==}
+
+  browserslist@4.22.2:
+    resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  browserslist@4.23.0:
+    resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  bser@2.1.1:
+    resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+
+  buffer-crc32@0.2.13:
+    resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
+  buffer-crc32@1.0.0:
+    resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
+    engines: {node: '>=8.0.0'}
+
+  buffer-equal-constant-time@1.0.1:
+    resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
+
+  buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  buffer@5.6.0:
+    resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==}
+
+  buffer@5.7.1:
+    resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+  buffer@6.0.3:
+    resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+
+  bufferutil@4.0.7:
+    resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
+    engines: {node: '>=6.14.2'}
+
+  bullmq@5.7.8:
+    resolution: {integrity: sha512-F/Haeu6AVHkFrfeaU/kLOjhfrH6x3CaKAZlQQ+76fa8l3kfI9oaUHeFMW+1mYVz0NtYPF7PNTWFq4ylAHYcCgA==}
+
+  buraha@0.0.1:
+    resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==}
+
+  busboy@1.6.0:
+    resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+    engines: {node: '>=10.16.0'}
+
+  bytes@3.0.0:
+    resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
+    engines: {node: '>= 0.8'}
+
+  bytes@3.1.2:
+    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+    engines: {node: '>= 0.8'}
+
+  cac@6.7.14:
+    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+    engines: {node: '>=8'}
+
+  cacache@18.0.0:
+    resolution: {integrity: sha512-I7mVOPl3PUCeRub1U8YoGz2Lqv9WOBpobZ8RyWFXmReuILz+3OAyTa5oH3QPdtKZD7N0Yk00aLfzn0qvp8dZ1w==}
+    engines: {node: ^16.14.0 || >=18.0.0}
+
+  cacheable-lookup@5.0.4:
+    resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
+    engines: {node: '>=10.6.0'}
+
+  cacheable-lookup@7.0.0:
+    resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
+    engines: {node: '>=14.16'}
+
+  cacheable-request@10.2.14:
+    resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
+    engines: {node: '>=14.16'}
+
+  cacheable-request@7.0.2:
+    resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==}
+    engines: {node: '>=8'}
+
+  cachedir@2.3.0:
+    resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==}
+    engines: {node: '>=6'}
+
+  call-bind@1.0.2:
+    resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
+
+  call-me-maybe@1.0.2:
+    resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
+
+  callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+
+  camelcase-keys@6.2.2:
+    resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==}
+    engines: {node: '>=8'}
+
+  camelcase@5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+
+  camelcase@6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+
+  caniuse-api@3.0.0:
+    resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
+
+  caniuse-lite@1.0.30001566:
+    resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==}
+
+  caniuse-lite@1.0.30001591:
+    resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==}
+
+  canonicalize@1.0.8:
+    resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
+
+  canvas-confetti@1.9.3:
+    resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==}
+
+  caseless@0.12.0:
+    resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
+
+  cbor@9.0.2:
+    resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==}
+    engines: {node: '>=16'}
+
+  ccount@2.0.1:
+    resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+
+  chai@4.3.10:
+    resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
+    engines: {node: '>=4'}
+
+  chalk-template@1.1.0:
+    resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==}
+    engines: {node: '>=14.16'}
+
+  chalk@2.4.2:
+    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+    engines: {node: '>=4'}
+
+  chalk@3.0.0:
+    resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
+    engines: {node: '>=8'}
+
+  chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+
+  chalk@5.3.0:
+    resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
+    engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
+  char-regex@1.0.2:
+    resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
+    engines: {node: '>=10'}
+
+  character-entities@2.0.2:
+    resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
+  character-parser@2.2.0:
+    resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==}
+
+  chart.js@4.4.2:
+    resolution: {integrity: sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==}
+    engines: {pnpm: '>=8'}
+
+  chartjs-adapter-date-fns@3.0.0:
+    resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
+    peerDependencies:
+      chart.js: '>=2.8.0'
+      date-fns: '>=2.0.0'
+
+  chartjs-chart-matrix@2.0.1:
+    resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==}
+    peerDependencies:
+      chart.js: '>=3.0.0'
+
+  chartjs-plugin-gradient@0.6.1:
+    resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==}
+    peerDependencies:
+      chart.js: '>=2.6.0'
+
+  chartjs-plugin-zoom@2.0.1:
+    resolution: {integrity: sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==}
+    peerDependencies:
+      chart.js: '>=3.2.0'
+
+  check-error@1.0.3:
+    resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+
+  check-more-types@2.24.0:
+    resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==}
+    engines: {node: '>= 0.8.0'}
+
+  cheerio-select@2.1.0:
+    resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
+
+  cheerio@1.0.0-rc.12:
+    resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==}
+    engines: {node: '>= 6'}
+
+  chokidar@3.5.3:
+    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+    engines: {node: '>= 8.10.0'}
+
+  chownr@1.1.4:
+    resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+
+  chownr@2.0.0:
+    resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+    engines: {node: '>=10'}
+
+  chromatic@11.3.0:
+    resolution: {integrity: sha512-q1ZtJDJrjLGnz60ivpC16gmd7KFzcaA4eTb7gcytCqbaKqlHhCFr1xQmcUDsm14CK7JsqdkFU6S+JQdOd2ZNJg==}
+    hasBin: true
+    peerDependencies:
+      '@chromatic-com/cypress': ^0.*.* || ^1.0.0
+      '@chromatic-com/playwright': ^0.*.* || ^1.0.0
+    peerDependenciesMeta:
+      '@chromatic-com/cypress':
+        optional: true
+      '@chromatic-com/playwright':
+        optional: true
+
+  ci-info@3.7.1:
+    resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
+    engines: {node: '>=8'}
+
+  cjs-module-lexer@1.2.2:
+    resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
+
+  clean-stack@2.2.0:
+    resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
+    engines: {node: '>=6'}
+
+  clean-stack@5.2.0:
+    resolution: {integrity: sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==}
+    engines: {node: '>=14.16'}
+
+  cli-cursor@3.1.0:
+    resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+    engines: {node: '>=8'}
+
+  cli-highlight@2.1.11:
+    resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
+    engines: {node: '>=8.0.0', npm: '>=5.0.0'}
+    hasBin: true
+
+  cli-spinners@2.7.0:
+    resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==}
+    engines: {node: '>=6'}
+
+  cli-spinners@2.9.2:
+    resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
+    engines: {node: '>=6'}
+
+  cli-table3@0.6.3:
+    resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==}
+    engines: {node: 10.* || >= 12.*}
+
+  cli-truncate@2.1.0:
+    resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
+    engines: {node: '>=8'}
+
+  cli-width@4.1.0:
+    resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
+    engines: {node: '>= 12'}
+
+  cliui@6.0.0:
+    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+
+  cliui@7.0.4:
+    resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+
+  cliui@8.0.1:
+    resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+    engines: {node: '>=12'}
+
+  clone-deep@4.0.1:
+    resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
+    engines: {node: '>=6'}
+
+  clone-response@1.0.3:
+    resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
+
+  clone@1.0.4:
+    resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
+    engines: {node: '>=0.8'}
+
+  cluster-key-slot@1.1.2:
+    resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
+    engines: {node: '>=0.10.0'}
+
+  co@4.6.0:
+    resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
+    engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
+
+  code-error-fragment@0.0.230:
+    resolution: {integrity: sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==}
+    engines: {node: '>= 4'}
+
+  collect-v8-coverage@1.0.1:
+    resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
+
+  color-convert@1.9.3:
+    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+  color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+
+  color-name@1.1.3:
+    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+  color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+  color-string@1.9.1:
+    resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+  color-support@1.1.3:
+    resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
+    hasBin: true
+
+  color@4.2.3:
+    resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+    engines: {node: '>=12.5.0'}
+
+  colord@2.9.3:
+    resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
+
+  colorette@2.0.19:
+    resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
+
+  combined-stream@1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+
+  commander@10.0.1:
+    resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+    engines: {node: '>=14'}
+
+  commander@2.20.3:
+    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+  commander@6.2.1:
+    resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
+    engines: {node: '>= 6'}
+
+  commander@7.2.0:
+    resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+    engines: {node: '>= 10'}
+
+  commander@8.3.0:
+    resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+    engines: {node: '>= 12'}
+
+  commander@9.5.0:
+    resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
+    engines: {node: ^12.20.0 || >=14}
+
+  common-tags@1.8.2:
+    resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
+    engines: {node: '>=4.0.0'}
+
+  commondir@1.0.1:
+    resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
+
+  compare-versions@6.1.0:
+    resolution: {integrity: sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==}
+
+  compress-commons@6.0.2:
+    resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
+    engines: {node: '>= 14'}
+
+  compressible@2.0.18:
+    resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
+    engines: {node: '>= 0.6'}
+
+  compression@1.7.4:
+    resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==}
+    engines: {node: '>= 0.8.0'}
+
+  computeds@0.0.1:
+    resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  concat-stream@1.6.2:
+    resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
+    engines: {'0': node >= 0.8}
+
+  config-chain@1.1.13:
+    resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+
+  consola@2.15.3:
+    resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
+
+  console-control-strings@1.1.0:
+    resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
+
+  constantinople@4.0.1:
+    resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
+
+  content-disposition@0.5.4:
+    resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
+    engines: {node: '>= 0.6'}
+
+  content-type@1.0.5:
+    resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+    engines: {node: '>= 0.6'}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  cookie-signature@1.0.6:
+    resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
+
+  cookie-signature@1.2.1:
+    resolution: {integrity: sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==}
+    engines: {node: '>=6.6.0'}
+
+  cookie@0.5.0:
+    resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+    engines: {node: '>= 0.6'}
+
+  cookie@0.6.0:
+    resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
+    engines: {node: '>= 0.6'}
+
+  core-js-compat@3.33.3:
+    resolution: {integrity: sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==}
+
+  core-js@3.29.1:
+    resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==}
+
+  core-util-is@1.0.2:
+    resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
+
+  core-util-is@1.0.3:
+    resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
+  cors@2.8.5:
+    resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+    engines: {node: '>= 0.10'}
+
+  crc-32@1.2.2:
+    resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+
+  crc32-stream@6.0.0:
+    resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==}
+    engines: {node: '>= 14'}
+
+  create-jest@29.7.0:
+    resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    hasBin: true
+
+  cron-parser@4.8.1:
+    resolution: {integrity: sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==}
+    engines: {node: '>=12.0.0'}
+
+  cropperjs@2.0.0-beta.5:
+    resolution: {integrity: sha512-8RIynsyHV7KyCxbjV4fCQubGiM6sHMgYvRPKkzuUQSTYHK6shoUNvdvbBekwAwS8QRLsxEBcJ5lvl0W3dvkDQA==}
+
+  cross-env@7.0.3:
+    resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
+    engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
+    hasBin: true
+
+  cross-fetch@3.1.6:
+    resolution: {integrity: sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==}
+
+  cross-fetch@4.0.0:
+    resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
+
+  cross-spawn@5.1.0:
+    resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
+
+  cross-spawn@7.0.3:
+    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+    engines: {node: '>= 8'}
+
+  crypto-random-string@2.0.0:
+    resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
+    engines: {node: '>=8'}
+
+  css-declaration-sorter@7.2.0:
+    resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==}
+    engines: {node: ^14 || ^16 || >=18}
+    peerDependencies:
+      postcss: ^8.0.9
+
+  css-select@5.1.0:
+    resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
+
+  css-tree@2.2.1:
+    resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+  css-tree@2.3.1:
+    resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+  css-what@6.1.0:
+    resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
+    engines: {node: '>= 6'}
+
+  css.escape@1.5.1:
+    resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
+  cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  cssnano-preset-default@6.1.2:
+    resolution: {integrity: sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  cssnano-utils@4.0.2:
+    resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  cssnano@6.1.2:
+    resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  csso@5.0.5:
+    resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+  cssstyle@4.0.1:
+    resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==}
+    engines: {node: '>=18'}
+
+  csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  cwise-compiler@1.1.3:
+    resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==}
+
+  cypress@13.7.3:
+    resolution: {integrity: sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==}
+    engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
+    hasBin: true
+
+  cypress@13.8.1:
+    resolution: {integrity: sha512-Uk6ovhRbTg6FmXjeZW/TkbRM07KPtvM5gah1BIMp4Y2s+i/NMxgaLw0+PbYTOdw1+egE0FP3mWRiGcRkjjmhzA==}
+    engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
+    hasBin: true
+
+  dashdash@1.14.1:
+    resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
+    engines: {node: '>=0.10'}
+
+  data-uri-to-buffer@0.0.3:
+    resolution: {integrity: sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==}
+
+  data-uri-to-buffer@4.0.0:
+    resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==}
+    engines: {node: '>= 12'}
+
+  data-urls@5.0.0:
+    resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+    engines: {node: '>=18'}
+
+  date-fns@2.30.0:
+    resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
+    engines: {node: '>=0.11'}
+
+  dayjs@1.11.10:
+    resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
+
+  de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+  debug@2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  debug@3.2.7:
+    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  debug@4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  decamelize-keys@1.1.1:
+    resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
+    engines: {node: '>=0.10.0'}
+
+  decamelize@1.2.0:
+    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
+    engines: {node: '>=0.10.0'}
+
+  decimal.js@10.4.3:
+    resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+
+  decode-bmp@0.2.1:
+    resolution: {integrity: sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==}
+    engines: {node: '>=8.6.0'}
+
+  decode-ico@0.4.1:
+    resolution: {integrity: sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA==}
+    engines: {node: '>=8.6'}
+
+  decode-named-character-reference@1.0.2:
+    resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
+
+  decompress-response@6.0.0:
+    resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+    engines: {node: '>=10'}
+
+  dedent@1.3.0:
+    resolution: {integrity: sha512-7glNLfvdsMzZm3FpRY1CHuI2lbYDR+71YmrhmTZjYFD5pfT0ACgnGRdrrC9Mk2uICnzkcdelCx5at787UDGOvg==}
+    peerDependencies:
+      babel-plugin-macros: ^3.1.0
+    peerDependenciesMeta:
+      babel-plugin-macros:
+        optional: true
+
+  deep-email-validator@0.1.21:
+    resolution: {integrity: sha512-DBAmMzbr+MAubXQ+TS9tZuPwLcdKscb8YzKZiwoLqF3NmaeEgXvSSHhZ0EXOFeKFE2FNWC4mNXCyiQ/JdFXUwg==}
+
+  deep-eql@4.1.3:
+    resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
+    engines: {node: '>=6'}
+
+  deep-equal@2.2.0:
+    resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==}
+
+  deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+  deepmerge@4.2.2:
+    resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
+    engines: {node: '>=0.10.0'}
+
+  default-browser-id@3.0.0:
+    resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
+    engines: {node: '>=12'}
+
+  defaults@1.0.4:
+    resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
+
+  defer-to-connect@2.0.1:
+    resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
+    engines: {node: '>=10'}
+
+  define-lazy-prop@2.0.0:
+    resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
+    engines: {node: '>=8'}
+
+  define-properties@1.2.0:
+    resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
+    engines: {node: '>= 0.4'}
+
+  defu@6.1.4:
+    resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+  del@6.1.1:
+    resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==}
+    engines: {node: '>=10'}
+
+  delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+
+  delegates@1.0.0:
+    resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
+
+  denque@2.1.0:
+    resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
+    engines: {node: '>=0.10'}
+
+  depd@2.0.0:
+    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+    engines: {node: '>= 0.8'}
+
+  dequal@2.0.3:
+    resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+    engines: {node: '>=6'}
+
+  destroy@1.2.0:
+    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+  detect-indent@6.1.0:
+    resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+    engines: {node: '>=8'}
+
+  detect-libc@2.0.2:
+    resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
+    engines: {node: '>=8'}
+
+  detect-libc@2.0.3:
+    resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
+    engines: {node: '>=8'}
+
+  detect-newline@3.1.0:
+    resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+    engines: {node: '>=8'}
+
+  detect-package-manager@2.0.1:
+    resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==}
+    engines: {node: '>=12'}
+
+  detect-port@1.5.1:
+    resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==}
+    hasBin: true
+
+  devlop@1.1.0:
+    resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
+  diff-match-patch@1.0.5:
+    resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
+
+  diff-sequences@29.6.3:
+    resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  diff@5.1.0:
+    resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
+    engines: {node: '>=0.3.1'}
+
+  dijkstrajs@1.0.2:
+    resolution: {integrity: sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==}
+
+  dir-glob@3.0.1:
+    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+    engines: {node: '>=8'}
+
+  disposable-email-domains@1.0.62:
+    resolution: {integrity: sha512-LBQvhRw7mznQTPoyZbsmYeNOZt1pN5aCsx4BAU/3siVFuiM9f2oyKzUaB8v1jbxFjE3aYqYiMo63kAL4pHgfWQ==}
+
+  doctrine@2.1.0:
+    resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+    engines: {node: '>=0.10.0'}
+
+  doctrine@3.0.0:
+    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+    engines: {node: '>=6.0.0'}
+
+  doctypes@1.1.0:
+    resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==}
+
+  dom-accessibility-api@0.5.16:
+    resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+  dom-accessibility-api@0.6.3:
+    resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
+  dom-serializer@2.0.0:
+    resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+  domelementtype@2.3.0:
+    resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+  domhandler@5.0.3:
+    resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+    engines: {node: '>= 4'}
+
+  domutils@3.0.1:
+    resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==}
+
+  dotenv-expand@10.0.0:
+    resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==}
+    engines: {node: '>=12'}
+
+  dotenv@16.0.3:
+    resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
+    engines: {node: '>=12'}
+
+  duplexer@0.1.2:
+    resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
+
+  duplexify@3.7.1:
+    resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==}
+
+  eastasianwidth@0.2.0:
+    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+  ecc-jsbn@0.1.2:
+    resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
+
+  ecdsa-sig-formatter@1.0.11:
+    resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
+
+  editorconfig@1.0.4:
+    resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  ee-first@1.1.1:
+    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+  ejs@3.1.9:
+    resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
+    engines: {node: '>=0.10.0'}
+    hasBin: true
+
+  electron-to-chromium@1.4.601:
+    resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==}
+
+  electron-to-chromium@1.4.686:
+    resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==}
+
+  emittery@0.13.1:
+    resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
+    engines: {node: '>=12'}
+
+  emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+  emoji-regex@9.2.2:
+    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+  encode-utf8@1.0.3:
+    resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
+
+  encodeurl@1.0.2:
+    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+    engines: {node: '>= 0.8'}
+
+  encoding@0.1.13:
+    resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
+
+  end-of-stream@1.4.4:
+    resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+
+  enquirer@2.3.6:
+    resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
+    engines: {node: '>=8.6'}
+
+  entities@2.2.0:
+    resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
+
+  entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  env-paths@2.2.1:
+    resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+    engines: {node: '>=6'}
+
+  envinfo@7.8.1:
+    resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  err-code@2.0.3:
+    resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
+
+  error-ex@1.3.2:
+    resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+
+  es-abstract@1.22.1:
+    resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==}
+    engines: {node: '>= 0.4'}
+
+  es-get-iterator@1.1.3:
+    resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
+
+  es-module-lexer@0.9.3:
+    resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
+
+  es-set-tostringtag@2.0.1:
+    resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==}
+    engines: {node: '>= 0.4'}
+
+  es-shim-unscopables@1.0.0:
+    resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==}
+
+  es-to-primitive@1.2.1:
+    resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
+    engines: {node: '>= 0.4'}
+
+  es6-promise@4.2.8:
+    resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
+
+  es6-promisify@5.0.0:
+    resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==}
+
+  esbuild-plugin-alias@0.2.1:
+    resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==}
+
+  esbuild-register@3.5.0:
+    resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==}
+    peerDependencies:
+      esbuild: '>=0.12 <1'
+
+  esbuild@0.18.20:
+    resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  esbuild@0.19.11:
+    resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  esbuild@0.20.2:
+    resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  escalade@3.1.1:
+    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+    engines: {node: '>=6'}
+
+  escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+  escape-regexp@0.0.1:
+    resolution: {integrity: sha512-jVgdsYRa7RKxTT6MKNC3gdT+BF0Gfhpel19+HMRZJC2L0PufB0XOBuXBoXj29NKHwuktnAXd1Z1lyiH/8vOTpw==}
+
+  escape-string-regexp@1.0.5:
+    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+    engines: {node: '>=0.8.0'}
+
+  escape-string-regexp@2.0.0:
+    resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+    engines: {node: '>=8'}
+
+  escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+
+  escape-string-regexp@5.0.0:
+    resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+    engines: {node: '>=12'}
+
+  escodegen@2.1.0:
+    resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
+    engines: {node: '>=6.0'}
+    hasBin: true
+
+  eslint-formatter-pretty@4.1.0:
+    resolution: {integrity: sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==}
+    engines: {node: '>=10'}
+
+  eslint-import-resolver-node@0.3.9:
+    resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+  eslint-module-utils@2.8.0:
+    resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: '*'
+      eslint-import-resolver-node: '*'
+      eslint-import-resolver-typescript: '*'
+      eslint-import-resolver-webpack: '*'
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+      eslint:
+        optional: true
+      eslint-import-resolver-node:
+        optional: true
+      eslint-import-resolver-typescript:
+        optional: true
+      eslint-import-resolver-webpack:
+        optional: true
+
+  eslint-plugin-import@2.29.1:
+    resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@typescript-eslint/parser': '*'
+      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
+    peerDependenciesMeta:
+      '@typescript-eslint/parser':
+        optional: true
+
+  eslint-plugin-vue@9.25.0:
+    resolution: {integrity: sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+  eslint-rule-docs@1.1.235:
+    resolution: {integrity: sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==}
+
+  eslint-scope@7.2.2:
+    resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint-visitor-keys@3.4.3:
+    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint@8.53.0:
+    resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    hasBin: true
+
+  eslint@8.57.0:
+    resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    hasBin: true
+
+  espree@9.6.1:
+    resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  esprima@4.0.1:
+    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  esquery@1.4.2:
+    resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==}
+    engines: {node: '>=0.10'}
+
+  esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+
+  estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+
+  estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  estree-walker@3.0.3:
+    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+  esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+
+  etag@1.8.1:
+    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+    engines: {node: '>= 0.6'}
+
+  event-stream@3.3.4:
+    resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==}
+
+  event-target-shim@5.0.1:
+    resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+    engines: {node: '>=6'}
+
+  eventemitter2@6.4.7:
+    resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==}
+
+  eventemitter3@4.0.7:
+    resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+
+  eventemitter3@5.0.1:
+    resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+
+  events@3.3.0:
+    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+    engines: {node: '>=0.8.x'}
+
+  execa@0.7.0:
+    resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==}
+    engines: {node: '>=4'}
+
+  execa@4.1.0:
+    resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
+    engines: {node: '>=10'}
+
+  execa@5.1.1:
+    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+    engines: {node: '>=10'}
+
+  execa@6.1.0:
+    resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  execa@8.0.1:
+    resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
+    engines: {node: '>=16.17'}
+
+  executable@4.1.1:
+    resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
+    engines: {node: '>=4'}
+
+  exit@0.1.2:
+    resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
+    engines: {node: '>= 0.8.0'}
+
+  expect@29.7.0:
+    resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  exponential-backoff@3.1.1:
+    resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
+
+  express@4.18.2:
+    resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
+    engines: {node: '>= 0.10.0'}
+
+  express@4.19.2:
+    resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
+    engines: {node: '>= 0.10.0'}
+
+  ext-list@2.2.2:
+    resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==}
+    engines: {node: '>=0.10.0'}
+
+  ext-name@5.0.0:
+    resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==}
+    engines: {node: '>=4'}
+
+  extend@3.0.2:
+    resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+
+  extract-zip@2.0.1:
+    resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
+    engines: {node: '>= 10.17.0'}
+    hasBin: true
+
+  extsprintf@1.3.0:
+    resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
+    engines: {'0': node >=0.6.0}
+
+  fast-content-type-parse@1.1.0:
+    resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
+
+  fast-decode-uri-component@1.0.1:
+    resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
+
+  fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+  fast-fifo@1.3.0:
+    resolution: {integrity: sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw==}
+
+  fast-glob@3.3.2:
+    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+    engines: {node: '>=8.6.0'}
+
+  fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+  fast-json-stringify@5.8.0:
+    resolution: {integrity: sha512-VVwK8CFMSALIvt14U8AvrSzQAwN/0vaVRiFFUVlpnXSnDGrSkOAO5MtzyN8oQNjLd5AqTW5OZRgyjoNuAuR3jQ==}
+
+  fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+  fast-querystring@1.1.2:
+    resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+
+  fast-redact@3.1.2:
+    resolution: {integrity: sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==}
+    engines: {node: '>=6'}
+
+  fast-safe-stringify@2.1.1:
+    resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+
+  fast-uri@2.2.0:
+    resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==}
+
+  fast-xml-parser@4.2.5:
+    resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==}
+    hasBin: true
+
+  fastify-plugin@4.5.0:
+    resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==}
+
+  fastify-raw-body@4.3.0:
+    resolution: {integrity: sha512-F4o8ZIMVx4YoxGfwrZys6wyjl40gF3Yv6AWWRy62ozFAyZBSS831/uyyCAqKYw3tR73g180ryG98yih6To1PUQ==}
+    engines: {node: '>= 10'}
+
+  fastify@4.26.2:
+    resolution: {integrity: sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==}
+
+  fastq@1.15.0:
+    resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
+
+  fastq@1.17.1:
+    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
+  fb-watchman@2.0.2:
+    resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+
+  fd-slicer@1.1.0:
+    resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+
+  feed@4.2.2:
+    resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==}
+    engines: {node: '>=0.4.0'}
+
+  fetch-blob@3.2.0:
+    resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
+    engines: {node: ^12.20 || >= 14.13}
+
+  fetch-retry@5.0.4:
+    resolution: {integrity: sha512-LXcdgpdcVedccGg0AZqg+S8lX/FCdwXD92WNZ5k5qsb0irRhSFsBOpcJt7oevyqT2/C2nEE0zSFNdBEpj3YOSw==}
+
+  figures@3.2.0:
+    resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
+    engines: {node: '>=8'}
+
+  file-entry-cache@6.0.1:
+    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+
+  file-system-cache@2.3.0:
+    resolution: {integrity: sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==}
+
+  file-type@17.1.6:
+    resolution: {integrity: sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  file-type@19.0.0:
+    resolution: {integrity: sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==}
+    engines: {node: '>=18'}
+
+  filelist@1.0.4:
+    resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
+
+  filename-reserved-regex@3.0.0:
+    resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  filenamify@5.1.1:
+    resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==}
+    engines: {node: '>=12.20'}
+
+  fill-range@7.0.1:
+    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+    engines: {node: '>=8'}
+
+  finalhandler@1.2.0:
+    resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
+    engines: {node: '>= 0.8'}
+
+  find-cache-dir@2.1.0:
+    resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
+    engines: {node: '>=6'}
+
+  find-cache-dir@3.3.2:
+    resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
+    engines: {node: '>=8'}
+
+  find-my-way@8.2.0:
+    resolution: {integrity: sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA==}
+    engines: {node: '>=14'}
+
+  find-package-json@1.2.0:
+    resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==}
+
+  find-up@3.0.0:
+    resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
+    engines: {node: '>=6'}
+
+  find-up@4.1.0:
+    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+    engines: {node: '>=8'}
+
+  find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+
+  find-versions@5.1.0:
+    resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==}
+    engines: {node: '>=12'}
+
+  fkill@9.0.0:
+    resolution: {integrity: sha512-MdYSsbdCaIRjzo5edthZtWmEZVMfr1qrtYZUHIdO3swCE+CoZA8S5l0s4jDsYlTa9ZiXv0pTgpzE7s4N8NeUOA==}
+    engines: {node: '>=18'}
+
+  flat-cache@3.0.4:
+    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+
+  flatted@3.2.7:
+    resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+
+  flow-parser@0.202.0:
+    resolution: {integrity: sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw==}
+    engines: {node: '>=0.4.0'}
+
+  fluent-ffmpeg@2.1.2:
+    resolution: {integrity: sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==}
+    engines: {node: '>=0.8.0'}
+
+  follow-redirects@1.15.2:
+    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+
+  for-each@0.3.3:
+    resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+
+  foreground-child@3.1.1:
+    resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
+    engines: {node: '>=14'}
+
+  forever-agent@0.6.1:
+    resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
+
+  form-data-encoder@2.1.4:
+    resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
+    engines: {node: '>= 14.17'}
+
+  form-data-encoder@4.0.2:
+    resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==}
+    engines: {node: '>= 18'}
+
+  form-data@2.3.3:
+    resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
+    engines: {node: '>= 0.12'}
+
+  form-data@3.0.1:
+    resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
+    engines: {node: '>= 6'}
+
+  form-data@4.0.0:
+    resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
+    engines: {node: '>= 6'}
+
+  formdata-polyfill@4.0.10:
+    resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
+    engines: {node: '>=12.20.0'}
+
+  forwarded@0.2.0:
+    resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+    engines: {node: '>= 0.6'}
+
+  fresh@0.5.2:
+    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+    engines: {node: '>= 0.6'}
+
+  from@0.1.7:
+    resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
+
+  fs-constants@1.0.0:
+    resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+
+  fs-extra@11.1.1:
+    resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
+    engines: {node: '>=14.14'}
+
+  fs-extra@7.0.1:
+    resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
+    engines: {node: '>=6 <7 || >=8'}
+
+  fs-extra@8.1.0:
+    resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
+    engines: {node: '>=6 <7 || >=8'}
+
+  fs-extra@9.1.0:
+    resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
+    engines: {node: '>=10'}
+
+  fs-minipass@1.2.7:
+    resolution: {integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==}
+
+  fs-minipass@2.1.0:
+    resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+    engines: {node: '>= 8'}
+
+  fs-minipass@3.0.2:
+    resolution: {integrity: sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  fs.realpath@1.0.0:
+    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+  fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
+  function-bind@1.1.2:
+    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+  function.prototype.name@1.1.5:
+    resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
+    engines: {node: '>= 0.4'}
+
+  functions-have-names@1.2.3:
+    resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+  gauge@3.0.2:
+    resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
+    engines: {node: '>=10'}
+
+  gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  get-caller-file@2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+
+  get-func-name@2.0.2:
+    resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
+
+  get-intrinsic@1.2.1:
+    resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
+
+  get-npm-tarball-url@2.0.3:
+    resolution: {integrity: sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw==}
+    engines: {node: '>=12.17'}
+
+  get-package-type@0.1.0:
+    resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+    engines: {node: '>=8.0.0'}
+
+  get-pixels-frame-info-update@3.3.2:
+    resolution: {integrity: sha512-LzVij57X/gK4Y6LpcDdqj+R9WCpD6Sv3ZH85GMA+S3xgPGCz81mHql4GiSnF4GijRjk7TE0ja2sDr8FFYKLe2g==}
+
+  get-stream@3.0.0:
+    resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
+    engines: {node: '>=4'}
+
+  get-stream@5.2.0:
+    resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
+    engines: {node: '>=8'}
+
+  get-stream@6.0.1:
+    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+    engines: {node: '>=10'}
+
+  get-stream@8.0.1:
+    resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
+    engines: {node: '>=16'}
+
+  get-symbol-description@1.0.0:
+    resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
+    engines: {node: '>= 0.4'}
+
+  get-tsconfig@4.7.2:
+    resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
+
+  getos@3.2.1:
+    resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
+
+  getpass@0.1.7:
+    resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
+
+  gif-encoder@0.4.1:
+    resolution: {integrity: sha512-++rNGpDBgWQ9eXj9JfTBLHMUEd7lDOdzIvFyHQM9yL8ffxkcg4G6jWmsgu/r59Uq6nHc3wcVwtgy3geLnIWunQ==}
+    engines: {node: '>= 0.8.0'}
+
+  giget@1.1.2:
+    resolution: {integrity: sha512-HsLoS07HiQ5oqvObOI+Qb2tyZH4Gj5nYGfF9qQcZNrPw+uEFhdXtgJr01aO2pWadGHucajYDLxxbtQkm97ON2A==}
+    hasBin: true
+
+  github-slugger@2.0.0:
+    resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
+
+  glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+
+  glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+
+  glob-promise@4.2.2:
+    resolution: {integrity: sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw==}
+    engines: {node: '>=12'}
+    peerDependencies:
+      glob: ^7.1.6
+
+  glob-to-regexp@0.4.1:
+    resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+
+  glob@10.3.10:
+    resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+
+  glob@10.3.12:
+    resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+
+  glob@7.2.3:
+    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+
+  glob@8.1.0:
+    resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
+    engines: {node: '>=12'}
+
+  global-dirs@3.0.1:
+    resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
+    engines: {node: '>=10'}
+
+  globals@11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+
+  globals@13.19.0:
+    resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==}
+    engines: {node: '>=8'}
+
+  globals@13.24.0:
+    resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
+    engines: {node: '>=8'}
+
+  globalthis@1.0.3:
+    resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
+    engines: {node: '>= 0.4'}
+
+  globby@11.1.0:
+    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+    engines: {node: '>=10'}
+
+  google-protobuf@3.21.2:
+    resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==}
+
+  gopd@1.0.1:
+    resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+
+  got@11.8.5:
+    resolution: {integrity: sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==}
+    engines: {node: '>=10.19.0'}
+
+  got@12.6.1:
+    resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==}
+    engines: {node: '>=14.16'}
+
+  got@14.2.1:
+    resolution: {integrity: sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==}
+    engines: {node: '>=20'}
+
+  graceful-fs@4.2.11:
+    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+  grapheme-splitter@1.0.4:
+    resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
+
+  graphemer@1.4.0:
+    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+  graphql@16.8.1:
+    resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==}
+    engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
+
+  gunzip-maybe@1.4.2:
+    resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==}
+    hasBin: true
+
+  hammerjs@2.0.8:
+    resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
+    engines: {node: '>=0.8.0'}
+
+  handlebars@4.7.7:
+    resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==}
+    engines: {node: '>=0.4.7'}
+    hasBin: true
+
+  happy-dom@14.7.1:
+    resolution: {integrity: sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==}
+    engines: {node: '>=16.0.0'}
+
+  har-schema@2.0.0:
+    resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
+    engines: {node: '>=4'}
+
+  har-validator@5.1.5:
+    resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==}
+    engines: {node: '>=6'}
+    deprecated: this library is no longer supported
+
+  hard-rejection@2.1.0:
+    resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
+    engines: {node: '>=6'}
+
+  has-bigints@1.0.2:
+    resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
+
+  has-flag@3.0.0:
+    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+    engines: {node: '>=4'}
+
+  has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+
+  has-property-descriptors@1.0.0:
+    resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
+
+  has-proto@1.0.1:
+    resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
+    engines: {node: '>= 0.4'}
+
+  has-symbols@1.0.3:
+    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+    engines: {node: '>= 0.4'}
+
+  has-tostringtag@1.0.0:
+    resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
+    engines: {node: '>= 0.4'}
+
+  has-unicode@2.0.1:
+    resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
+
+  has@1.0.3:
+    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+    engines: {node: '>= 0.4.0'}
+
+  hash-sum@2.0.0:
+    resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
+
+  hashlru@2.3.0:
+    resolution: {integrity: sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==}
+
+  hasown@2.0.0:
+    resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
+    engines: {node: '>= 0.4'}
+
+  hast-util-heading-rank@3.0.0:
+    resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
+
+  hast-util-is-element@3.0.0:
+    resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
+
+  hast-util-to-string@3.0.0:
+    resolution: {integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==}
+
+  he@1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+    hasBin: true
+
+  headers-polyfill@4.0.2:
+    resolution: {integrity: sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==}
+
+  highlight.js@10.7.3:
+    resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
+
+  highlight.js@11.9.0:
+    resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==}
+    engines: {node: '>=12.0.0'}
+
+  hosted-git-info@2.8.9:
+    resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+
+  hosted-git-info@4.1.0:
+    resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
+    engines: {node: '>=10'}
+
+  hpagent@1.2.0:
+    resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==}
+    engines: {node: '>=14'}
+
+  html-encoding-sniffer@4.0.0:
+    resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+    engines: {node: '>=18'}
+
+  html-entities@2.3.2:
+    resolution: {integrity: sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==}
+
+  html-escaper@2.0.2:
+    resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+  html-tags@3.2.0:
+    resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==}
+    engines: {node: '>=8'}
+
+  htmlescape@1.1.1:
+    resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==}
+    engines: {node: '>=0.10'}
+
+  htmlparser2@8.0.1:
+    resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
+
+  http-cache-semantics@4.1.1:
+    resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
+
+  http-errors@2.0.0:
+    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+    engines: {node: '>= 0.8'}
+
+  http-link-header@1.1.3:
+    resolution: {integrity: sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==}
+    engines: {node: '>=6.0.0'}
+
+  http-proxy-agent@7.0.0:
+    resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
+    engines: {node: '>= 14'}
+
+  http-signature@1.2.0:
+    resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
+    engines: {node: '>=0.8', npm: '>=1.3.7'}
+
+  http-signature@1.3.6:
+    resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==}
+    engines: {node: '>=0.10'}
+
+  http2-wrapper@1.0.3:
+    resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==}
+    engines: {node: '>=10.19.0'}
+
+  http2-wrapper@2.2.1:
+    resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
+    engines: {node: '>=10.19.0'}
+
+  http_ece@1.2.0:
+    resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==}
+    engines: {node: '>=16'}
+
+  https-proxy-agent@2.2.4:
+    resolution: {integrity: sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==}
+    engines: {node: '>= 4.5.0'}
+
+  https-proxy-agent@5.0.1:
+    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+    engines: {node: '>= 6'}
+
+  https-proxy-agent@7.0.2:
+    resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==}
+    engines: {node: '>= 14'}
+
+  human-signals@1.1.1:
+    resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
+    engines: {node: '>=8.12.0'}
+
+  human-signals@2.1.0:
+    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+    engines: {node: '>=10.17.0'}
+
+  human-signals@3.0.1:
+    resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
+    engines: {node: '>=12.20.0'}
+
+  human-signals@5.0.0:
+    resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
+    engines: {node: '>=16.17.0'}
+
+  iconv-lite@0.4.24:
+    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+    engines: {node: '>=0.10.0'}
+
+  iconv-lite@0.6.3:
+    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+    engines: {node: '>=0.10.0'}
+
+  idb-keyval@6.2.1:
+    resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==}
+
+  ieee754@1.2.1:
+    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+  ignore-by-default@1.0.1:
+    resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
+
+  ignore-walk@6.0.4:
+    resolution: {integrity: sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  ignore@5.2.4:
+    resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
+    engines: {node: '>= 4'}
+
+  ignore@5.3.1:
+    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
+    engines: {node: '>= 4'}
+
+  immutable@4.2.2:
+    resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==}
+
+  import-fresh@3.3.0:
+    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+    engines: {node: '>=6'}
+
+  import-lazy@4.0.0:
+    resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
+    engines: {node: '>=8'}
+
+  import-local@3.1.0:
+    resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
+    engines: {node: '>=8'}
+    hasBin: true
+
+  imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+
+  indent-string@4.0.0:
+    resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+    engines: {node: '>=8'}
+
+  indent-string@5.0.0:
+    resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
+    engines: {node: '>=12'}
+
+  inflight@1.0.6:
+    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+
+  inherits@2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+  ini@1.3.8:
+    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+  ini@2.0.0:
+    resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==}
+    engines: {node: '>=10'}
+
+  insert-text-at-cursor@0.3.0:
+    resolution: {integrity: sha512-/nPtyeX9xPUvxZf+r0518B7uqNKlP+LqNJqSiXFEaa2T71rWIwTVXGH7hB9xO/EVdwa5/pWlFCPwShOW81XIxQ==}
+
+  install-artifact-from-github@1.3.5:
+    resolution: {integrity: sha512-gZHC7f/cJgXz7MXlHFBxPVMsvIbev1OQN1uKQYKVJDydGNm9oYf9JstbU4Atnh/eSvk41WtEovoRm+8IF686xg==}
+    hasBin: true
+
+  internal-slot@1.0.5:
+    resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
+    engines: {node: '>= 0.4'}
+
+  intersection-observer@0.12.2:
+    resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
+
+  ioredis@5.4.1:
+    resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==}
+    engines: {node: '>=12.22.0'}
+
+  iota-array@1.0.0:
+    resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==}
+
+  ip-address@7.1.0:
+    resolution: {integrity: sha512-V9pWC/VJf2lsXqP7IWJ+pe3P1/HCYGBMZrrnT62niLGjAfCbeiwXMUxaeHvnVlz19O27pvXP4azs+Pj/A0x+SQ==}
+    engines: {node: '>= 10'}
+
+  ip-cidr@3.1.0:
+    resolution: {integrity: sha512-HUCn4snshEX1P8cja/IyU3qk8FVDW8T5zZcegDFbu4w7NojmAhk5NcOgj3M8+0fmumo1afJTPDtJlzsxLdOjtg==}
+    engines: {node: '>=10.0.0'}
+
+  ip-regex@4.3.0:
+    resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==}
+    engines: {node: '>=8'}
+
+  ip@2.0.1:
+    resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==}
+
+  ipaddr.js@1.9.1:
+    resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+    engines: {node: '>= 0.10'}
+
+  ipaddr.js@2.2.0:
+    resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
+    engines: {node: '>= 10'}
+
+  irregular-plurals@3.5.0:
+    resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==}
+    engines: {node: '>=8'}
+
+  is-absolute-url@4.0.1:
+    resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  is-arguments@1.1.1:
+    resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
+    engines: {node: '>= 0.4'}
+
+  is-array-buffer@3.0.2:
+    resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
+
+  is-arrayish@0.2.1:
+    resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+  is-arrayish@0.3.2:
+    resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+
+  is-bigint@1.0.4:
+    resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+
+  is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+
+  is-boolean-object@1.1.2:
+    resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
+    engines: {node: '>= 0.4'}
+
+  is-buffer@1.1.6:
+    resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
+
+  is-callable@1.2.7:
+    resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+    engines: {node: '>= 0.4'}
+
+  is-ci@3.0.1:
+    resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
+    hasBin: true
+
+  is-core-module@2.13.1:
+    resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
+
+  is-date-object@1.0.5:
+    resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
+    engines: {node: '>= 0.4'}
+
+  is-deflate@1.0.0:
+    resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==}
+
+  is-docker@2.2.1:
+    resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+    engines: {node: '>=8'}
+    hasBin: true
+
+  is-expression@4.0.0:
+    resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==}
+
+  is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-file-animated@1.0.2:
+    resolution: {integrity: sha512-TAYDUkvyBmxqneRU26zzpeHLAgtzEOIsRQWrtDidPT/tFK3Yc0WKgtF3u4oOEAiN0kAuVfl7MTgbD0vXdFDztA==}
+
+  is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+
+  is-generator-fn@2.1.0:
+    resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
+    engines: {node: '>=6'}
+
+  is-generator-function@1.0.10:
+    resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
+    engines: {node: '>= 0.4'}
+
+  is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+
+  is-gzip@1.0.0:
+    resolution: {integrity: sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-installed-globally@0.4.0:
+    resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
+    engines: {node: '>=10'}
+
+  is-interactive@1.0.0:
+    resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
+    engines: {node: '>=8'}
+
+  is-ip@3.1.0:
+    resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==}
+    engines: {node: '>=8'}
+
+  is-lambda@1.0.1:
+    resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
+
+  is-map@2.0.2:
+    resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
+
+  is-nan@1.3.2:
+    resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
+    engines: {node: '>= 0.4'}
+
+  is-negative-zero@2.0.2:
+    resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
+    engines: {node: '>= 0.4'}
+
+  is-node-process@1.2.0:
+    resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
+
+  is-number-object@1.0.7:
+    resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
+    engines: {node: '>= 0.4'}
+
+  is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  is-path-cwd@2.2.0:
+    resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
+    engines: {node: '>=6'}
+
+  is-path-inside@3.0.3:
+    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+    engines: {node: '>=8'}
+
+  is-plain-obj@1.1.0:
+    resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
+    engines: {node: '>=0.10.0'}
+
+  is-plain-obj@4.1.0:
+    resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+    engines: {node: '>=12'}
+
+  is-plain-object@2.0.4:
+    resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
+    engines: {node: '>=0.10.0'}
+
+  is-plain-object@5.0.0:
+    resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
+    engines: {node: '>=0.10.0'}
+
+  is-potential-custom-element-name@1.0.1:
+    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+  is-promise@2.2.2:
+    resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
+
+  is-regex@1.1.4:
+    resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
+    engines: {node: '>= 0.4'}
+
+  is-set@2.0.2:
+    resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
+
+  is-shared-array-buffer@1.0.2:
+    resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
+
+  is-stream@1.1.0:
+    resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-stream@2.0.1:
+    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+    engines: {node: '>=8'}
+
+  is-stream@3.0.0:
+    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  is-string@1.0.7:
+    resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
+    engines: {node: '>= 0.4'}
+
+  is-svg@5.0.0:
+    resolution: {integrity: sha512-sRl7J0oX9yUNamSdc8cwgzh9KBLnQXNzGmW0RVHwg/jEYjGNYHC6UvnYD8+hAeut9WwxRvhG9biK7g/wDGxcMw==}
+    engines: {node: '>=14.16'}
+
+  is-symbol@1.0.4:
+    resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
+    engines: {node: '>= 0.4'}
+
+  is-typed-array@1.1.10:
+    resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==}
+    engines: {node: '>= 0.4'}
+
+  is-typedarray@1.0.0:
+    resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
+
+  is-unicode-supported@0.1.0:
+    resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
+    engines: {node: '>=10'}
+
+  is-weakmap@2.0.1:
+    resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
+
+  is-weakref@1.0.2:
+    resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+
+  is-weakset@2.0.2:
+    resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
+
+  is-wsl@2.2.0:
+    resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+    engines: {node: '>=8'}
+
+  isarray@0.0.1:
+    resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==}
+
+  isarray@1.0.0:
+    resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
+  isarray@2.0.5:
+    resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+  isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+  isexe@3.1.1:
+    resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
+    engines: {node: '>=16'}
+
+  isobject@3.0.1:
+    resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
+    engines: {node: '>=0.10.0'}
+
+  isstream@0.1.2:
+    resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
+
+  istanbul-lib-coverage@3.2.2:
+    resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+    engines: {node: '>=8'}
+
+  istanbul-lib-instrument@5.2.1:
+    resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
+    engines: {node: '>=8'}
+
+  istanbul-lib-instrument@6.0.0:
+    resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==}
+    engines: {node: '>=10'}
+
+  istanbul-lib-report@3.0.1:
+    resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+    engines: {node: '>=10'}
+
+  istanbul-lib-source-maps@4.0.1:
+    resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
+    engines: {node: '>=10'}
+
+  istanbul-reports@3.1.6:
+    resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==}
+    engines: {node: '>=8'}
+
+  iterare@1.2.1:
+    resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
+    engines: {node: '>=6'}
+
+  jackspeak@2.3.6:
+    resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
+    engines: {node: '>=14'}
+
+  jake@10.8.5:
+    resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  jest-changed-files@29.7.0:
+    resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-circus@29.7.0:
+    resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-cli@29.7.0:
+    resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    hasBin: true
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  jest-config@29.7.0:
+    resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    peerDependencies:
+      '@types/node': '*'
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      ts-node:
+        optional: true
+
+  jest-diff@29.7.0:
+    resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-docblock@29.7.0:
+    resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-each@29.7.0:
+    resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-environment-node@29.7.0:
+    resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-fetch-mock@3.0.3:
+    resolution: {integrity: sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==}
+
+  jest-get-type@29.6.3:
+    resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-haste-map@29.7.0:
+    resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-leak-detector@29.7.0:
+    resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-matcher-utils@29.7.0:
+    resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-message-util@29.7.0:
+    resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-mock@29.7.0:
+    resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-pnp-resolver@1.2.3:
+    resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
+    engines: {node: '>=6'}
+    peerDependencies:
+      jest-resolve: '*'
+    peerDependenciesMeta:
+      jest-resolve:
+        optional: true
+
+  jest-regex-util@29.6.3:
+    resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-resolve-dependencies@29.7.0:
+    resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-resolve@29.7.0:
+    resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-runner@29.7.0:
+    resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-runtime@29.7.0:
+    resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-snapshot@29.7.0:
+    resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-util@29.7.0:
+    resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-validate@29.7.0:
+    resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-watcher@29.7.0:
+    resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest-websocket-mock@2.5.0:
+    resolution: {integrity: sha512-a+UJGfowNIWvtIKIQBHoEWIUqRxxQHFx4CXT+R5KxxKBtEQ5rS3pPOV/5299sHzqbmeCzxxY5qE4+yfXePePig==}
+
+  jest-worker@29.7.0:
+    resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  jest@29.7.0:
+    resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+    hasBin: true
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+
+  jju@1.4.0:
+    resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
+
+  joi@17.11.0:
+    resolution: {integrity: sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==}
+
+  jpeg-js@0.3.7:
+    resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==}
+
+  js-beautify@1.14.9:
+    resolution: {integrity: sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==}
+    engines: {node: '>=12'}
+    hasBin: true
+
+  js-stringify@1.0.2:
+    resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==}
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  js-yaml@3.14.1:
+    resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+    hasBin: true
+
+  js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+
+  jsbn@0.1.1:
+    resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
+
+  jsbn@1.1.0:
+    resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
+
+  jschardet@3.0.0:
+    resolution: {integrity: sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==}
+    engines: {node: '>=0.1.90'}
+
+  jscodeshift@0.15.1:
+    resolution: {integrity: sha512-hIJfxUy8Rt4HkJn/zZPU9ChKfKZM1342waJ1QC2e2YsPcWhM+3BJ4dcfQCzArTrk1jJeNLB341H+qOcEHRxJZg==}
+    hasBin: true
+    peerDependencies:
+      '@babel/preset-env': ^7.1.6
+    peerDependenciesMeta:
+      '@babel/preset-env':
+        optional: true
+
+  jsdom@24.0.0:
+    resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==}
+    engines: {node: '>=18'}
+    peerDependencies:
+      canvas: ^2.11.2
+    peerDependenciesMeta:
+      canvas:
+        optional: true
+
+  jsesc@0.5.0:
+    resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
+    hasBin: true
+
+  jsesc@2.5.2:
+    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  json-buffer@3.0.1:
+    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+  json-parse-even-better-errors@2.3.1:
+    resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+  json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+  json-schema-traverse@1.0.0:
+    resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+  json-schema@0.4.0:
+    resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
+  json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+  json-stringify-safe@5.0.1:
+    resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
+
+  json-to-ast@2.1.0:
+    resolution: {integrity: sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==}
+    engines: {node: '>= 4'}
+
+  json5@1.0.2:
+    resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+    hasBin: true
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  jsonc-parser@3.2.0:
+    resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
+
+  jsonfile@4.0.0:
+    resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+
+  jsonfile@5.0.0:
+    resolution: {integrity: sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==}
+
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
+  jsonld@8.3.2:
+    resolution: {integrity: sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==}
+    engines: {node: '>=14'}
+
+  jsonpointer@5.0.1:
+    resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
+    engines: {node: '>=0.10.0'}
+
+  jsprim@1.4.2:
+    resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
+    engines: {node: '>=0.6.0'}
+
+  jsprim@2.0.2:
+    resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
+    engines: {'0': node >=0.6.0}
+
+  jsrsasign@11.1.0:
+    resolution: {integrity: sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==}
+
+  jssha@3.3.1:
+    resolution: {integrity: sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==}
+
+  jstransformer@1.0.0:
+    resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==}
+
+  just-extend@4.2.1:
+    resolution: {integrity: sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==}
+
+  jwa@2.0.0:
+    resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==}
+
+  jws@4.0.0:
+    resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
+
+  keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+  kind-of@6.0.3:
+    resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+    engines: {node: '>=0.10.0'}
+
+  kleur@3.0.3:
+    resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+    engines: {node: '>=6'}
+
+  ky-universal@0.11.0:
+    resolution: {integrity: sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==}
+    engines: {node: '>=14.16'}
+    peerDependencies:
+      ky: '>=0.31.4'
+      web-streams-polyfill: '>=3.2.1'
+    peerDependenciesMeta:
+      web-streams-polyfill:
+        optional: true
+
+  ky@0.33.3:
+    resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==}
+    engines: {node: '>=14.16'}
+
+  lazy-ass@1.6.0:
+    resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==}
+    engines: {node: '> 0.8'}
+
+  lazy-universal-dotenv@4.0.0:
+    resolution: {integrity: sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg==}
+    engines: {node: '>=14.0.0'}
+
+  lazystream@1.0.1:
+    resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
+    engines: {node: '>= 0.6.3'}
+
+  leven@3.1.0:
+    resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+    engines: {node: '>=6'}
+
+  levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+
+  light-my-request@5.11.0:
+    resolution: {integrity: sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==}
+
+  lilconfig@3.1.1:
+    resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
+    engines: {node: '>=14'}
+
+  lines-and-columns@1.2.4:
+    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+  listr2@3.14.0:
+    resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      enquirer: '>= 2.3.0 < 3'
+    peerDependenciesMeta:
+      enquirer:
+        optional: true
+
+  local-pkg@0.4.3:
+    resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
+    engines: {node: '>=14'}
+
+  locate-path@3.0.0:
+    resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
+    engines: {node: '>=6'}
+
+  locate-path@5.0.0:
+    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+    engines: {node: '>=8'}
+
+  locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+
+  lodash.debounce@4.0.8:
+    resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+  lodash.defaults@4.2.0:
+    resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
+
+  lodash.get@4.4.2:
+    resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
+
+  lodash.isarguments@3.1.0:
+    resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
+
+  lodash.isequal@4.5.0:
+    resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+
+  lodash.memoize@4.1.2:
+    resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
+
+  lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+  lodash.once@4.1.1:
+    resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
+
+  lodash.uniq@4.5.0:
+    resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
+
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  log-symbols@4.1.0:
+    resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
+    engines: {node: '>=10'}
+
+  log-update@4.0.0:
+    resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
+    engines: {node: '>=10'}
+
+  long@4.0.0:
+    resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==}
+
+  longest-streak@3.1.0:
+    resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+
+  loose-envify@1.4.0:
+    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+    hasBin: true
+
+  loupe@2.3.7:
+    resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+
+  lowercase-keys@2.0.0:
+    resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
+    engines: {node: '>=8'}
+
+  lowercase-keys@3.0.0:
+    resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  lru-cache@10.0.2:
+    resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==}
+    engines: {node: 14 || >=16.14}
+
+  lru-cache@10.2.2:
+    resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
+    engines: {node: 14 || >=16.14}
+
+  lru-cache@4.1.5:
+    resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  lru-cache@6.0.0:
+    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+    engines: {node: '>=10'}
+
+  lru-cache@8.0.4:
+    resolution: {integrity: sha512-E9FF6+Oc/uFLqZCuZwRKUzgFt5Raih6LfxknOSAVTjNkrCZkBf7DQCwJxZQgd9l4eHjIJDGR+E+1QKD1RhThPw==}
+    engines: {node: '>=16.14'}
+
+  luxon@3.3.0:
+    resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
+    engines: {node: '>=12'}
+
+  lz-string@1.5.0:
+    resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+    hasBin: true
+
+  magic-string@0.27.0:
+    resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
+    engines: {node: '>=12'}
+
+  magic-string@0.30.10:
+    resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
+
+  magic-string@0.30.7:
+    resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
+    engines: {node: '>=12'}
+
+  mailcheck@1.1.1:
+    resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
+
+  make-dir@2.1.0:
+    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
+    engines: {node: '>=6'}
+
+  make-dir@3.1.0:
+    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+    engines: {node: '>=8'}
+
+  make-dir@4.0.0:
+    resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+    engines: {node: '>=10'}
+
+  make-fetch-happen@13.0.0:
+    resolution: {integrity: sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==}
+    engines: {node: ^16.14.0 || >=18.0.0}
+
+  makeerror@1.0.12:
+    resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+
+  map-obj@1.0.1:
+    resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==}
+    engines: {node: '>=0.10.0'}
+
+  map-obj@4.3.0:
+    resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
+    engines: {node: '>=8'}
+
+  map-or-similar@1.5.0:
+    resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==}
+
+  map-stream@0.1.0:
+    resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
+
+  markdown-table@3.0.3:
+    resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
+
+  markdown-to-jsx@7.3.2:
+    resolution: {integrity: sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==}
+    engines: {node: '>= 10'}
+    peerDependencies:
+      react: '>= 0.14.0'
+
+  matter-js@0.19.0:
+    resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==}
+
+  mdast-util-find-and-replace@3.0.1:
+    resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
+
+  mdast-util-from-markdown@2.0.0:
+    resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==}
+
+  mdast-util-gfm-autolink-literal@2.0.0:
+    resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==}
+
+  mdast-util-gfm-footnote@2.0.0:
+    resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==}
+
+  mdast-util-gfm-strikethrough@2.0.0:
+    resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+
+  mdast-util-gfm-table@2.0.0:
+    resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+
+  mdast-util-gfm-task-list-item@2.0.0:
+    resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+
+  mdast-util-gfm@3.0.0:
+    resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==}
+
+  mdast-util-phrasing@4.1.0:
+    resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+
+  mdast-util-to-markdown@2.1.0:
+    resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==}
+
+  mdast-util-to-string@4.0.0:
+    resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+
+  mdn-data@2.0.28:
+    resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
+
+  mdn-data@2.0.30:
+    resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
+
+  media-typer@0.3.0:
+    resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+    engines: {node: '>= 0.6'}
+
+  meilisearch@0.38.0:
+    resolution: {integrity: sha512-bHaq8nYxSKw9/Qslq1Zes5g9tHgFkxy/I9o8942wv2PqlNOT0CzptIkh/x98N52GikoSZOXSQkgt6oMjtf5uZw==}
+
+  memoizerific@1.11.3:
+    resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==}
+
+  meow@9.0.0:
+    resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==}
+    engines: {node: '>=10'}
+
+  merge-descriptors@1.0.1:
+    resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
+
+  merge-stream@2.0.0:
+    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+  merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  methods@1.1.2:
+    resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
+    engines: {node: '>= 0.6'}
+
+  mfm-js@0.24.0:
+    resolution: {integrity: sha512-6m8N0ElH9/4CA1izhVqmxTfLj5Z9RspdqM/lMew4xU/UTgm4Pf//VpDunpasxbRFjeJSVW+zoVwL4ZPfPtfiQg==}
+
+  microformats-parser@2.0.2:
+    resolution: {integrity: sha512-tUf9DmN4Jq/tGyp1YH2V6D/Cud+9Uc0WhjjUFirqVeHTRkkfLDacv6BQFT7h7HFsD0Z8wja5eKkRgzZU8bv0Fw==}
+    engines: {node: '>=18'}
+
+  micromark-core-commonmark@2.0.0:
+    resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==}
+
+  micromark-extension-gfm-autolink-literal@2.0.0:
+    resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==}
+
+  micromark-extension-gfm-footnote@2.0.0:
+    resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==}
+
+  micromark-extension-gfm-strikethrough@2.0.0:
+    resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==}
+
+  micromark-extension-gfm-table@2.0.0:
+    resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==}
+
+  micromark-extension-gfm-tagfilter@2.0.0:
+    resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+
+  micromark-extension-gfm-task-list-item@2.0.1:
+    resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==}
+
+  micromark-extension-gfm@3.0.0:
+    resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+
+  micromark-factory-destination@2.0.0:
+    resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
+
+  micromark-factory-label@2.0.0:
+    resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
+
+  micromark-factory-space@2.0.0:
+    resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
+
+  micromark-factory-title@2.0.0:
+    resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==}
+
+  micromark-factory-whitespace@2.0.0:
+    resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==}
+
+  micromark-util-character@2.1.0:
+    resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
+
+  micromark-util-chunked@2.0.0:
+    resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==}
+
+  micromark-util-classify-character@2.0.0:
+    resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==}
+
+  micromark-util-combine-extensions@2.0.0:
+    resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==}
+
+  micromark-util-decode-numeric-character-reference@2.0.1:
+    resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==}
+
+  micromark-util-decode-string@2.0.0:
+    resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==}
+
+  micromark-util-encode@2.0.0:
+    resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
+
+  micromark-util-html-tag-name@2.0.0:
+    resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
+
+  micromark-util-normalize-identifier@2.0.0:
+    resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==}
+
+  micromark-util-resolve-all@2.0.0:
+    resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==}
+
+  micromark-util-sanitize-uri@2.0.0:
+    resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
+
+  micromark-util-subtokenize@2.0.0:
+    resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==}
+
+  micromark-util-symbol@2.0.0:
+    resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
+
+  micromark-util-types@2.0.0:
+    resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
+
+  micromark@4.0.0:
+    resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
+
+  micromatch@4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+
+  mime-db@1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+
+  mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+
+  mime@1.6.0:
+    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  mime@3.0.0:
+    resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+
+  mimic-fn@2.1.0:
+    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+    engines: {node: '>=6'}
+
+  mimic-fn@4.0.0:
+    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+    engines: {node: '>=12'}
+
+  mimic-response@1.0.1:
+    resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
+    engines: {node: '>=4'}
+
+  mimic-response@3.1.0:
+    resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+    engines: {node: '>=10'}
+
+  mimic-response@4.0.0:
+    resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  min-indent@1.0.1:
+    resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+    engines: {node: '>=4'}
+
+  minimalistic-assert@1.0.1:
+    resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
+
+  minimatch@3.0.8:
+    resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==}
+
+  minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+  minimatch@5.1.2:
+    resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==}
+    engines: {node: '>=10'}
+
+  minimatch@9.0.1:
+    resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minimatch@9.0.3:
+    resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minimatch@9.0.4:
+    resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minimist-options@4.1.0:
+    resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
+    engines: {node: '>= 6'}
+
+  minimist@1.2.8:
+    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+  minipass-collect@1.0.2:
+    resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
+    engines: {node: '>= 8'}
+
+  minipass-fetch@3.0.3:
+    resolution: {integrity: sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  minipass-flush@1.0.5:
+    resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
+    engines: {node: '>= 8'}
+
+  minipass-pipeline@1.2.4:
+    resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
+    engines: {node: '>=8'}
+
+  minipass-sized@1.0.3:
+    resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==}
+    engines: {node: '>=8'}
+
+  minipass@2.9.0:
+    resolution: {integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==}
+
+  minipass@3.3.6:
+    resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+    engines: {node: '>=8'}
+
+  minipass@5.0.0:
+    resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+    engines: {node: '>=8'}
+
+  minipass@7.0.4:
+    resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  minizlib@1.3.3:
+    resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==}
+
+  minizlib@2.1.2:
+    resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+    engines: {node: '>= 8'}
+
+  mkdirp-classic@0.5.3:
+    resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+
+  mkdirp@0.5.6:
+    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+    hasBin: true
+
+  mkdirp@1.0.4:
+    resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  mkdirp@2.1.6:
+    resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  mlly@1.5.0:
+    resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==}
+
+  mnemonist@0.39.6:
+    resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==}
+
+  mock-socket@9.3.1:
+    resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==}
+    engines: {node: '>= 8'}
+
+  mri@1.2.0:
+    resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+    engines: {node: '>=4'}
+
+  ms@2.0.0:
+    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
+  ms@2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  ms@3.0.0-canary.1:
+    resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==}
+    engines: {node: '>=12.13'}
+
+  msgpackr-extract@3.0.2:
+    resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==}
+    hasBin: true
+
+  msgpackr@1.10.1:
+    resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==}
+
+  msw-storybook-addon@2.0.1:
+    resolution: {integrity: sha512-pZ3JDQ9HkGQ3XDMIHvMcDSI4Vbp/LHmwHwiZu+pHLzimtI1vhAo1swjFEDAEJuBcozljYvREEC4sS7rQHPNtWg==}
+    peerDependencies:
+      msw: ^2.0.0
+
+  msw@2.2.14:
+    resolution: {integrity: sha512-64i8rNCa1xzDK8ZYsTrVMli05D687jty8+Th+PU5VTbJ2/4P7fkQFVyDQ6ZFT5FrNR8z2BHhbY47fKNvfHrumA==}
+    engines: {node: '>=18'}
+    hasBin: true
+    peerDependencies:
+      typescript: '>= 4.7.x'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  muggle-string@0.4.1:
+    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+  multer@1.4.4-lts.1:
+    resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==}
+    engines: {node: '>= 6.0.0'}
+
+  multi-integer-range@3.0.0:
+    resolution: {integrity: sha512-uQzynjVJ8F7x5wjaK0g4Ybhy2TvO/pk96+YHyS5g1W4GuUEV6HMebZ8HcRwWgKIRCUT2MLbM5uCKwYcAqkS+8Q==}
+
+  mute-stream@1.0.0:
+    resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  mylas@2.1.13:
+    resolution: {integrity: sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==}
+    engines: {node: '>=12.0.0'}
+
+  mz@2.7.0:
+    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+  nan@2.18.0:
+    resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==}
+
+  nanoid@3.3.7:
+    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  nanoid@5.0.7:
+    resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==}
+    engines: {node: ^18 || >=20}
+    hasBin: true
+
+  natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+  ncp@2.0.0:
+    resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==}
+    hasBin: true
+
+  ndarray-ops@1.2.2:
+    resolution: {integrity: sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==}
+
+  ndarray-pack@1.2.1:
+    resolution: {integrity: sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==}
+
+  ndarray@1.0.18:
+    resolution: {integrity: sha512-jUz6G+CIsEsqs2VlB1EvaQSAA0Jkf8YKm7eFBleKyhiQjYWzTxXqHzWEOm3jFoGCpxGh4DnPUYHB4ECWE+n9SQ==}
+
+  ndarray@1.0.19:
+    resolution: {integrity: sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==}
+
+  needle@2.9.1:
+    resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==}
+    engines: {node: '>= 4.4.x'}
+    hasBin: true
+
+  negotiator@0.6.3:
+    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+    engines: {node: '>= 0.6'}
+
+  neo-async@2.6.2:
+    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+
+  nested-property@4.0.0:
+    resolution: {integrity: sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==}
+
+  netmask@2.0.2:
+    resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
+    engines: {node: '>= 0.4.0'}
+
+  nice-napi@1.0.2:
+    resolution: {integrity: sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==}
+    os: ['!win32']
+
+  nise@5.1.4:
+    resolution: {integrity: sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==}
+
+  node-abort-controller@3.1.1:
+    resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
+
+  node-addon-api@3.2.1:
+    resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==}
+
+  node-bitmap@0.0.1:
+    resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==}
+    engines: {node: '>=v0.6.5'}
+
+  node-dir@0.1.17:
+    resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==}
+    engines: {node: '>= 0.10.5'}
+
+  node-domexception@1.0.0:
+    resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
+    engines: {node: '>=10.5.0'}
+
+  node-fetch-native@1.0.2:
+    resolution: {integrity: sha512-KIkvH1jl6b3O7es/0ShyCgWLcfXxlBrLBbP3rOr23WArC66IMcU4DeZEeYEOwnopYhawLTn7/y+YtmASe8DFVQ==}
+
+  node-fetch@2.6.11:
+    resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+
+  node-fetch@2.7.0:
+    resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+
+  node-fetch@3.3.2:
+    resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  node-gyp-build-optional-packages@5.0.7:
+    resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==}
+    hasBin: true
+
+  node-gyp-build@4.6.0:
+    resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
+    hasBin: true
+
+  node-gyp@10.0.1:
+    resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==}
+    engines: {node: ^16.14.0 || >=18.0.0}
+    hasBin: true
+
+  node-int64@0.4.0:
+    resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+
+  node-releases@2.0.14:
+    resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+
+  nodemailer@6.9.13:
+    resolution: {integrity: sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==}
+    engines: {node: '>=6.0.0'}
+
+  nodemon@3.0.2:
+    resolution: {integrity: sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  nodemon@3.1.0:
+    resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  nofilter@3.1.0:
+    resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==}
+    engines: {node: '>=12.19'}
+
+  nopt@1.0.10:
+    resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==}
+    hasBin: true
+
+  nopt@5.0.0:
+    resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  nopt@6.0.0:
+    resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==}
+    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+    hasBin: true
+
+  nopt@7.2.0:
+    resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    hasBin: true
+
+  normalize-package-data@2.5.0:
+    resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
+
+  normalize-package-data@3.0.3:
+    resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==}
+    engines: {node: '>=10'}
+
+  normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  normalize-url@6.1.0:
+    resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
+    engines: {node: '>=10'}
+
+  normalize-url@8.0.0:
+    resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
+    engines: {node: '>=14.16'}
+
+  npm-run-path@2.0.2:
+    resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
+    engines: {node: '>=4'}
+
+  npm-run-path@4.0.1:
+    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+    engines: {node: '>=8'}
+
+  npm-run-path@5.1.0:
+    resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  npmlog@5.0.1:
+    resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
+
+  nsfwjs@2.4.2:
+    resolution: {integrity: sha512-i4Pp2yt59qPQgeZFyg3wXFBX52uSeu/hkDoqdZfe+sILRxNBUu0VDogj7Lmqak0GlrXviS/wLiVeIx40IDUu7A==}
+    peerDependencies:
+      '@tensorflow/tfjs': ^3.18.0
+
+  nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+  nwsapi@2.2.9:
+    resolution: {integrity: sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==}
+
+  oauth-sign@0.9.0:
+    resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
+
+  oauth2orize-pkce@0.1.2:
+    resolution: {integrity: sha512-grto2UYhXHi9GLE3IBgBBbV87xci55+bCyjpVuxKyzol6I5Rg0K1MiTuXE+JZk54R86SG2wqXODMiZYHraPpxw==}
+
+  oauth2orize@1.12.0:
+    resolution: {integrity: sha512-j4XtFDQUBsvUHPjUmvmNDUDMYed2MphMIJBhyxVVe8hGCjkuYnjIsW+D9qk8c5ciXRdnk6x6tEbiO6PLeOZdCQ==}
+    engines: {node: '>= 0.4.0'}
+
+  oauth@0.10.0:
+    resolution: {integrity: sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==}
+
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
+  object-inspect@1.12.3:
+    resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
+
+  object-is@1.1.5:
+    resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==}
+    engines: {node: '>= 0.4'}
+
+  object-keys@1.1.1:
+    resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+    engines: {node: '>= 0.4'}
+
+  object.assign@4.1.4:
+    resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==}
+    engines: {node: '>= 0.4'}
+
+  object.fromentries@2.0.7:
+    resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
+    engines: {node: '>= 0.4'}
+
+  object.groupby@1.0.1:
+    resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==}
+
+  object.values@1.1.7:
+    resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
+    engines: {node: '>= 0.4'}
+
+  obliterator@2.0.4:
+    resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==}
+
+  oblivious-set@1.4.0:
+    resolution: {integrity: sha512-szyd0ou0T8nsAqHtprRcP3WidfsN1TnAR5yWXf2mFCEr5ek3LEOkT6EZ/92Xfs74HIdyhG5WkGxIssMU0jBaeg==}
+    engines: {node: '>=16'}
+
+  obuf@1.1.2:
+    resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
+
+  omggif@1.0.10:
+    resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==}
+
+  on-exit-leak-free@2.1.0:
+    resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==}
+
+  on-finished@2.4.1:
+    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+    engines: {node: '>= 0.8'}
+
+  on-headers@1.0.2:
+    resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==}
+    engines: {node: '>= 0.8'}
+
+  once@1.4.0:
+    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+  onetime@5.1.2:
+    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+    engines: {node: '>=6'}
+
+  onetime@6.0.0:
+    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+    engines: {node: '>=12'}
+
+  open@8.4.2:
+    resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
+    engines: {node: '>=12'}
+
+  openapi-types@12.1.3:
+    resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
+
+  openapi-typescript@6.7.3:
+    resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==}
+    hasBin: true
+
+  optionator@0.9.3:
+    resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
+    engines: {node: '>= 0.8.0'}
+
+  ora@5.4.1:
+    resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
+    engines: {node: '>=10'}
+
+  os-filter-obj@2.0.0:
+    resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==}
+    engines: {node: '>=4'}
+
+  os-utils@0.0.14:
+    resolution: {integrity: sha512-ajB8csaHLBvJOYsHJkp8YdO2FvlBbf/ZxaYQwXXRDyQ84UoE+uTuLXxqd0shekXMX6Qr/pt/DDyLMRAMsgfWzg==}
+
+  ospath@1.2.2:
+    resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
+
+  otpauth@9.2.3:
+    resolution: {integrity: sha512-oAG55Ch4MBL5Jdg+RXfKiRCZ2lCwa/UIQKsmSfYbGGLSI4dErY1HPZv0JGPPESIYGyDO3s9iJqM4HU/1IppMoQ==}
+
+  outvariant@1.4.2:
+    resolution: {integrity: sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==}
+
+  p-cancelable@2.1.1:
+    resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
+    engines: {node: '>=8'}
+
+  p-cancelable@3.0.0:
+    resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
+    engines: {node: '>=12.20'}
+
+  p-cancelable@4.0.1:
+    resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
+    engines: {node: '>=14.16'}
+
+  p-finally@1.0.0:
+    resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
+    engines: {node: '>=4'}
+
+  p-limit@2.3.0:
+    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+    engines: {node: '>=6'}
+
+  p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+
+  p-limit@4.0.0:
+    resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  p-locate@3.0.0:
+    resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
+    engines: {node: '>=6'}
+
+  p-locate@4.1.0:
+    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+    engines: {node: '>=8'}
+
+  p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+
+  p-map@4.0.0:
+    resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
+    engines: {node: '>=10'}
+
+  p-queue@6.6.2:
+    resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==}
+    engines: {node: '>=8'}
+
+  p-timeout@3.2.0:
+    resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
+    engines: {node: '>=8'}
+
+  p-try@2.2.0:
+    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+    engines: {node: '>=6'}
+
+  pako@0.2.9:
+    resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
+
+  parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+
+  parse-data-uri@0.2.0:
+    resolution: {integrity: sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==}
+
+  parse-json@5.2.0:
+    resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+    engines: {node: '>=8'}
+
+  parse-srcset@1.0.2:
+    resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
+
+  parse5-htmlparser2-tree-adapter@6.0.1:
+    resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
+
+  parse5-htmlparser2-tree-adapter@7.0.0:
+    resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
+
+  parse5@5.1.1:
+    resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==}
+
+  parse5@6.0.1:
+    resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+
+  parse5@7.1.2:
+    resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+
+  parseurl@1.3.3:
+    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+    engines: {node: '>= 0.8'}
+
+  path-browserify@1.0.1:
+    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+  path-exists@3.0.0:
+    resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
+    engines: {node: '>=4'}
+
+  path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+
+  path-is-absolute@1.0.1:
+    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+    engines: {node: '>=0.10.0'}
+
+  path-key@2.0.1:
+    resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
+    engines: {node: '>=4'}
+
+  path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+
+  path-key@4.0.0:
+    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+    engines: {node: '>=12'}
+
+  path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  path-scurry@1.10.1:
+    resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  path-scurry@1.10.2:
+    resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
+  path-to-regexp@0.1.7:
+    resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
+
+  path-to-regexp@1.8.0:
+    resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==}
+
+  path-to-regexp@3.2.0:
+    resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==}
+
+  path-to-regexp@6.2.1:
+    resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
+
+  path-type@4.0.0:
+    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+    engines: {node: '>=8'}
+
+  pathe@1.1.2:
+    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
+  pathval@1.1.1:
+    resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
+
+  pause-stream@0.0.11:
+    resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
+
+  peek-readable@5.0.0:
+    resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==}
+    engines: {node: '>=14.16'}
+
+  peek-stream@1.1.3:
+    resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==}
+
+  pend@1.2.0:
+    resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
+
+  performance-now@2.1.0:
+    resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
+
+  pg-cloudflare@1.1.1:
+    resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
+
+  pg-connection-string@2.6.4:
+    resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==}
+
+  pg-int8@1.0.1:
+    resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
+    engines: {node: '>=4.0.0'}
+
+  pg-numeric@1.0.2:
+    resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
+    engines: {node: '>=4'}
+
+  pg-pool@3.6.2:
+    resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==}
+    peerDependencies:
+      pg: '>=8.0'
+
+  pg-protocol@1.6.0:
+    resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
+
+  pg-protocol@1.6.1:
+    resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==}
+
+  pg-types@2.2.0:
+    resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
+    engines: {node: '>=4'}
+
+  pg-types@4.0.1:
+    resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==}
+    engines: {node: '>=10'}
+
+  pg@8.11.5:
+    resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==}
+    engines: {node: '>= 8.0.0'}
+    peerDependencies:
+      pg-native: '>=3.0.1'
+    peerDependenciesMeta:
+      pg-native:
+        optional: true
+
+  pgpass@1.0.5:
+    resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
+
+  photoswipe@5.4.3:
+    resolution: {integrity: sha512-9UC6oJBK4oXFZ5HcdlcvGkfEHsVrmE4csUdCQhEjHYb3PvPLO3PG7UhnPuOgjxwmhq5s17Un5NUdum01LgBDng==}
+    engines: {node: '>= 0.12.0'}
+
+  picocolors@1.0.0:
+    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+
+  picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  pid-port@1.0.0:
+    resolution: {integrity: sha512-LSNBeKChRPA4Xlrs6+zV588G1hSrFvANtPV5rt/5MPfSPK3V9XPWxx1d29svsrOjngT9ifLisXWCLS7DvO9ZhQ==}
+    engines: {node: '>=18'}
+
+  pify@2.3.0:
+    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+    engines: {node: '>=0.10.0'}
+
+  pify@4.0.1:
+    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+    engines: {node: '>=6'}
+
+  pino-abstract-transport@1.1.0:
+    resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==}
+
+  pino-std-serializers@6.1.0:
+    resolution: {integrity: sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==}
+
+  pino@8.17.0:
+    resolution: {integrity: sha512-ey+Mku+PVPhvxglLXMg1l1zQMwSHuNrKC3MD40EDZbkckJmmuY7DYZLIOwwjZ8ix/Nvhe9dZt5H99cgkot9bAw==}
+    hasBin: true
+
+  pirates@4.0.5:
+    resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
+    engines: {node: '>= 6'}
+
+  piscina@4.4.0:
+    resolution: {integrity: sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==}
+
+  pkce-challenge@4.1.0:
+    resolution: {integrity: sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==}
+    engines: {node: '>=16.20.0'}
+
+  pkg-dir@3.0.0:
+    resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
+    engines: {node: '>=6'}
+
+  pkg-dir@4.2.0:
+    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+    engines: {node: '>=8'}
+
+  pkg-dir@5.0.0:
+    resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==}
+    engines: {node: '>=10'}
+
+  pkg-types@1.0.3:
+    resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
+
+  plimit-lit@1.5.0:
+    resolution: {integrity: sha512-Eb/MqCb1Iv/ok4m1FqIXqvUKPISufcjZ605hl3KM/n8GaX8zfhtgdLwZU3vKjuHGh2O9Rjog/bHTq8ofIShdng==}
+
+  plur@4.0.0:
+    resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==}
+    engines: {node: '>=10'}
+
+  pngjs-nozlib@1.0.0:
+    resolution: {integrity: sha512-N1PggqLp9xDqwAoKvGohmZ3m4/N9xpY0nDZivFqQLcpLHmliHnCp9BuNCsOeqHWMuEEgFjpEaq9dZq6RZyy0fA==}
+    engines: {iojs: '>= 1.0.0', node: '>=0.10.0'}
+
+  pngjs@3.4.0:
+    resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
+    engines: {node: '>=4.0.0'}
+
+  pngjs@5.0.0:
+    resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+    engines: {node: '>=10.13.0'}
+
+  polished@4.2.2:
+    resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==}
+    engines: {node: '>=10'}
+
+  postcss-calc@9.0.1:
+    resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.2.2
+
+  postcss-colormin@6.1.0:
+    resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-convert-values@6.1.0:
+    resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-discard-comments@6.0.2:
+    resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-discard-duplicates@6.0.3:
+    resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-discard-empty@6.0.3:
+    resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-discard-overridden@6.0.2:
+    resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-merge-longhand@6.0.5:
+    resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-merge-rules@6.1.1:
+    resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-minify-font-values@6.1.0:
+    resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-minify-gradients@6.0.3:
+    resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-minify-params@6.1.0:
+    resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-minify-selectors@6.0.4:
+    resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-charset@6.0.2:
+    resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-display-values@6.0.2:
+    resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-positions@6.0.2:
+    resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-repeat-style@6.0.2:
+    resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-string@6.0.2:
+    resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-timing-functions@6.0.2:
+    resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-unicode@6.1.0:
+    resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-url@6.0.2:
+    resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-normalize-whitespace@6.0.2:
+    resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-ordered-values@6.0.2:
+    resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-reduce-initial@6.1.0:
+    resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-reduce-transforms@6.0.2:
+    resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-selector-parser@6.0.15:
+    resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==}
+    engines: {node: '>=4'}
+
+  postcss-selector-parser@6.0.16:
+    resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==}
+    engines: {node: '>=4'}
+
+  postcss-svgo@6.0.3:
+    resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==}
+    engines: {node: ^14 || ^16 || >= 18}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-unique-selectors@6.0.4:
+    resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  postcss-value-parser@4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+  postcss@8.4.38:
+    resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  postgres-array@2.0.0:
+    resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
+    engines: {node: '>=4'}
+
+  postgres-array@3.0.2:
+    resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
+    engines: {node: '>=12'}
+
+  postgres-bytea@1.0.0:
+    resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
+    engines: {node: '>=0.10.0'}
+
+  postgres-bytea@3.0.0:
+    resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
+    engines: {node: '>= 6'}
+
+  postgres-date@1.0.7:
+    resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
+    engines: {node: '>=0.10.0'}
+
+  postgres-date@2.0.1:
+    resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==}
+    engines: {node: '>=12'}
+
+  postgres-interval@1.2.0:
+    resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
+    engines: {node: '>=0.10.0'}
+
+  postgres-interval@3.0.0:
+    resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
+    engines: {node: '>=12'}
+
+  postgres-range@1.1.3:
+    resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==}
+
+  prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+
+  prettier@3.2.5:
+    resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
+    engines: {node: '>=14'}
+    hasBin: true
+
+  pretty-bytes@5.6.0:
+    resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
+    engines: {node: '>=6'}
+
+  pretty-format@27.5.1:
+    resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+  pretty-format@29.7.0:
+    resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
+    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+  pretty-hrtime@1.0.3:
+    resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==}
+    engines: {node: '>= 0.8'}
+
+  private-ip@2.3.3:
+    resolution: {integrity: sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw==}
+
+  probe-image-size@7.2.3:
+    resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==}
+
+  proc-log@3.0.0:
+    resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  process-exists@5.0.0:
+    resolution: {integrity: sha512-6QPRh5fyHD8MaXr4GYML8K/YY0Sq5dKHGIOrAKS3cYpHQdmygFCcijIu1dVoNKAZ0TWAMoeh8KDK9dF8auBkJA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  process-nextick-args@2.0.1:
+    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+  process-warning@2.2.0:
+    resolution: {integrity: sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==}
+
+  process-warning@3.0.0:
+    resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==}
+
+  process@0.11.10:
+    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+    engines: {node: '>= 0.6.0'}
+
+  progress@2.0.3:
+    resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
+    engines: {node: '>=0.4.0'}
+
+  promise-limit@2.7.0:
+    resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==}
+
+  promise-polyfill@8.3.0:
+    resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==}
+
+  promise-retry@2.0.1:
+    resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==}
+    engines: {node: '>=10'}
+
+  promise@7.3.1:
+    resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==}
+
+  prompts@2.4.2:
+    resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+    engines: {node: '>= 6'}
+
+  prop-types@15.8.1:
+    resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+  proto-list@1.2.4:
+    resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+
+  proxy-addr@2.0.7:
+    resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+    engines: {node: '>= 0.10'}
+
+  proxy-from-env@1.0.0:
+    resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==}
+
+  proxy-from-env@1.1.0:
+    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+  ps-list@8.1.1:
+    resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  ps-tree@1.2.0:
+    resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==}
+    engines: {node: '>= 0.10'}
+    hasBin: true
+
+  pseudomap@1.0.2:
+    resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
+
+  psl@1.9.0:
+    resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
+
+  pstree.remy@1.1.8:
+    resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
+
+  pug-attrs@3.0.0:
+    resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==}
+
+  pug-code-gen@3.0.2:
+    resolution: {integrity: sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==}
+
+  pug-error@2.0.0:
+    resolution: {integrity: sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==}
+
+  pug-filters@4.0.0:
+    resolution: {integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==}
+
+  pug-lexer@5.0.1:
+    resolution: {integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==}
+
+  pug-linker@4.0.0:
+    resolution: {integrity: sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==}
+
+  pug-load@3.0.0:
+    resolution: {integrity: sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==}
+
+  pug-parser@6.0.0:
+    resolution: {integrity: sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==}
+
+  pug-runtime@3.0.1:
+    resolution: {integrity: sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==}
+
+  pug-strip-comments@2.0.0:
+    resolution: {integrity: sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==}
+
+  pug-walk@2.0.0:
+    resolution: {integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==}
+
+  pug@3.0.2:
+    resolution: {integrity: sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==}
+
+  pump@2.0.1:
+    resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
+
+  pump@3.0.0:
+    resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
+
+  pumpify@1.5.1:
+    resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
+
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
+  pure-rand@6.0.0:
+    resolution: {integrity: sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==}
+
+  pvtsutils@1.3.5:
+    resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==}
+
+  pvutils@1.1.3:
+    resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
+    engines: {node: '>=6.0.0'}
+
+  qrcode@1.5.3:
+    resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+
+  qs@6.10.4:
+    resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==}
+    engines: {node: '>=0.6'}
+
+  qs@6.11.0:
+    resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
+    engines: {node: '>=0.6'}
+
+  qs@6.11.1:
+    resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==}
+    engines: {node: '>=0.6'}
+
+  qs@6.5.3:
+    resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
+    engines: {node: '>=0.6'}
+
+  querystringify@2.2.0:
+    resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+
+  queue-lit@1.5.0:
+    resolution: {integrity: sha512-IslToJ4eiCEE9xwMzq3viOO5nH8sUWUCwoElrhNMozzr9IIt2qqvB4I+uHu/zJTQVqc9R5DFwok4ijNK1pU3fA==}
+
+  queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+  queue-tick@1.0.1:
+    resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
+
+  quick-format-unescaped@4.0.4:
+    resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
+
+  quick-lru@4.0.1:
+    resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==}
+    engines: {node: '>=8'}
+
+  quick-lru@5.1.1:
+    resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
+    engines: {node: '>=10'}
+
+  ramda@0.29.0:
+    resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==}
+
+  random-seed@0.3.0:
+    resolution: {integrity: sha512-y13xtn3kcTlLub3HKWXxJNeC2qK4mB59evwZ5EkeRlolx+Bp2ztF7LbcZmyCnOqlHQrLnfuNbi1sVmm9lPDlDA==}
+    engines: {node: '>= 0.6.0'}
+
+  range-parser@1.2.1:
+    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+    engines: {node: '>= 0.6'}
+
+  ratelimiter@3.4.1:
+    resolution: {integrity: sha512-5FJbRW/Jkkdk29ksedAfWFkQkhbUrMx3QJGwMKAypeIiQf4yrLW+gtPKZiaWt4zPrtw1uGufOjGO7UGM6VllsQ==}
+
+  raw-body@2.5.1:
+    resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
+    engines: {node: '>= 0.8'}
+
+  raw-body@2.5.2:
+    resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
+    engines: {node: '>= 0.8'}
+
+  rdf-canonize@3.4.0:
+    resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==}
+    engines: {node: '>=12'}
+
+  re2@1.20.10:
+    resolution: {integrity: sha512-/5JjSPXobSDaKFL6rD5Gb4qD4CVBITQb7NAxfQ/NA7o0HER3SJAPV3lPO2kvzw0/PN1pVJNVATEUk4y9j7oIIA==}
+
+  react-colorful@5.6.1:
+    resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
+    peerDependencies:
+      react: '>=16.8.0'
+      react-dom: '>=16.8.0'
+
+  react-docgen-typescript@2.2.2:
+    resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
+    peerDependencies:
+      typescript: '>= 4.3.x'
+
+  react-docgen@7.0.1:
+    resolution: {integrity: sha512-rCz0HBIT0LWbIM+///LfRrJoTKftIzzwsYDf0ns5KwaEjejMHQRtphcns+IXFHDNY9pnz6G8l/JbbI6pD4EAIA==}
+    engines: {node: '>=16.14.0'}
+
+  react-dom@18.3.1:
+    resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
+    peerDependencies:
+      react: ^18.3.1
+
+  react-element-to-jsx-string@15.0.0:
+    resolution: {integrity: sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==}
+    peerDependencies:
+      react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0
+      react-dom: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0
+
+  react-is@16.13.1:
+    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+  react-is@17.0.2:
+    resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+  react-is@18.1.0:
+    resolution: {integrity: sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==}
+
+  react-is@18.2.0:
+    resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
+
+  react@18.3.1:
+    resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
+    engines: {node: '>=0.10.0'}
+
+  read-pkg-up@7.0.1:
+    resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
+    engines: {node: '>=8'}
+
+  read-pkg@5.2.0:
+    resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
+    engines: {node: '>=8'}
+
+  readable-stream@1.1.14:
+    resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==}
+
+  readable-stream@2.3.7:
+    resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
+
+  readable-stream@3.6.0:
+    resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
+    engines: {node: '>= 6'}
+
+  readable-stream@4.3.0:
+    resolution: {integrity: sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  readable-web-to-node-stream@3.0.2:
+    resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==}
+    engines: {node: '>=8'}
+
+  readdir-glob@1.1.2:
+    resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==}
+
+  readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+
+  real-require@0.2.0:
+    resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
+    engines: {node: '>= 12.13.0'}
+
+  recast@0.23.4:
+    resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==}
+    engines: {node: '>= 4'}
+
+  recast@0.23.6:
+    resolution: {integrity: sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==}
+    engines: {node: '>= 4'}
+
+  reconnecting-websocket@4.4.0:
+    resolution: {integrity: sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==}
+
+  redent@3.0.0:
+    resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+    engines: {node: '>=8'}
+
+  redis-errors@1.2.0:
+    resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
+    engines: {node: '>=4'}
+
+  redis-info@3.1.0:
+    resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==}
+
+  redis-lock@0.1.4:
+    resolution: {integrity: sha512-7/+zu86XVQfJVx1nHTzux5reglDiyUCDwmW7TSlvVezfhH2YLc/Rc8NE0ejQG+8/0lwKzm29/u/4+ogKeLosiA==}
+    engines: {node: '>=0.6'}
+
+  redis-parser@3.0.0:
+    resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
+    engines: {node: '>=4'}
+
+  reflect-metadata@0.2.2:
+    resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
+
+  regenerate-unicode-properties@10.1.0:
+    resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
+    engines: {node: '>=4'}
+
+  regenerate@1.4.2:
+    resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+
+  regenerator-runtime@0.13.11:
+    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+
+  regenerator-runtime@0.14.0:
+    resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
+
+  regenerator-transform@0.15.2:
+    resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
+
+  regexp.prototype.flags@1.5.0:
+    resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
+    engines: {node: '>= 0.4'}
+
+  regexpu-core@5.3.2:
+    resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
+    engines: {node: '>=4'}
+
+  regjsparser@0.9.1:
+    resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
+    hasBin: true
+
+  rehype-external-links@3.0.0:
+    resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==}
+
+  rehype-slug@6.0.0:
+    resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==}
+
+  remark-gfm@4.0.0:
+    resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==}
+
+  remark-parse@11.0.0:
+    resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+
+  remark-stringify@11.0.0:
+    resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+
+  rename@1.0.4:
+    resolution: {integrity: sha512-YMM6Fn3lrFOCjhORKjj+z/yizj8WSzv3F3YUlpJA20fteWCb0HbJU19nvuRBPUM5dWgxJcHP+kix3M+5NowJyA==}
+
+  request-progress@3.0.0:
+    resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==}
+
+  request@2.88.2:
+    resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
+    engines: {node: '>= 6'}
+    deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
+
+  require-directory@2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+
+  require-from-string@2.0.2:
+    resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+    engines: {node: '>=0.10.0'}
+
+  require-main-filename@2.0.0:
+    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+
+  requires-port@1.0.0:
+    resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+
+  resolve-alpn@1.2.1:
+    resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
+
+  resolve-cwd@3.0.0:
+    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+    engines: {node: '>=8'}
+
+  resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+
+  resolve-from@5.0.0:
+    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+    engines: {node: '>=8'}
+
+  resolve-pkg-maps@1.0.0:
+    resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+  resolve.exports@2.0.0:
+    resolution: {integrity: sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==}
+    engines: {node: '>=10'}
+
+  resolve@1.19.0:
+    resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==}
+
+  resolve@1.22.8:
+    resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
+    hasBin: true
+
+  responselike@2.0.1:
+    resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+
+  responselike@3.0.0:
+    resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==}
+    engines: {node: '>=14.16'}
+
+  restore-cursor@3.1.0:
+    resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+    engines: {node: '>=8'}
+
+  ret@0.4.3:
+    resolution: {integrity: sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==}
+    engines: {node: '>=10'}
+
+  retry@0.12.0:
+    resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
+    engines: {node: '>= 4'}
+
+  reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  rfdc@1.3.0:
+    resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
+
+  rimraf@2.6.3:
+    resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
+    hasBin: true
+
+  rimraf@2.7.1:
+    resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+    hasBin: true
+
+  rimraf@3.0.2:
+    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    hasBin: true
+
+  rollup@4.17.2:
+    resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  rrweb-cssom@0.6.0:
+    resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
+
+  rss-parser@3.13.0:
+    resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==}
+
+  run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+  rxjs@7.8.1:
+    resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
+
+  safe-array-concat@1.0.0:
+    resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==}
+    engines: {node: '>=0.4'}
+
+  safe-buffer@5.1.2:
+    resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
+  safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+  safe-regex-test@1.0.0:
+    resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
+
+  safe-regex2@3.1.0:
+    resolution: {integrity: sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==}
+
+  safe-stable-stringify@2.4.2:
+    resolution: {integrity: sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==}
+    engines: {node: '>=10'}
+
+  safer-buffer@2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+  sanitize-html@2.13.0:
+    resolution: {integrity: sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==}
+
+  sass@1.76.0:
+    resolution: {integrity: sha512-nc3LeqvF2FNW5xGF1zxZifdW3ffIz5aBb7I7tSvOoNu7z1RQ6pFt9MBuiPtjgaI62YWrM/txjWlOCFiGtf2xpw==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  sax@1.2.4:
+    resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
+
+  saxes@6.0.0:
+    resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+    engines: {node: '>=v12.22.7'}
+
+  scheduler@0.23.2:
+    resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
+
+  secure-json-parse@2.7.0:
+    resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
+
+  seedrandom@3.0.5:
+    resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==}
+
+  semver-regex@4.0.5:
+    resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==}
+    engines: {node: '>=12'}
+
+  semver-truncate@2.0.0:
+    resolution: {integrity: sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==}
+    engines: {node: '>=8'}
+
+  semver@5.7.1:
+    resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
+    hasBin: true
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  semver@7.5.4:
+    resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  semver@7.6.0:
+    resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  send@0.18.0:
+    resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
+    engines: {node: '>= 0.8.0'}
+
+  serve-static@1.15.0:
+    resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
+    engines: {node: '>= 0.8.0'}
+
+  set-blocking@2.0.0:
+    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+
+  set-cookie-parser@2.6.0:
+    resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
+
+  setimmediate@1.0.5:
+    resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
+
+  setprototypeof@1.2.0:
+    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+  sha.js@2.4.11:
+    resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
+    hasBin: true
+
+  shallow-clone@3.0.1:
+    resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
+    engines: {node: '>=8'}
+
+  sharp@0.33.3:
+    resolution: {integrity: sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==}
+    engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
+  shebang-command@1.2.0:
+    resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
+    engines: {node: '>=0.10.0'}
+
+  shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+
+  shebang-regex@1.0.0:
+    resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
+    engines: {node: '>=0.10.0'}
+
+  shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+
+  shiki@1.4.0:
+    resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==}
+
+  side-channel@1.0.4:
+    resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+
+  siginfo@2.0.0:
+    resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+  signal-exit@3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+  signal-exit@4.1.0:
+    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+    engines: {node: '>=14'}
+
+  simple-oauth2@5.0.0:
+    resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==}
+
+  simple-swizzle@0.2.2:
+    resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+
+  simple-update-notifier@2.0.0:
+    resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
+    engines: {node: '>=10'}
+
+  sinon@16.1.3:
+    resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==}
+
+  sisteransi@1.0.5:
+    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+  slacc-android-arm-eabi@0.0.10:
+    resolution: {integrity: sha512-U3dVBuM1m8rT1D/w6S4knJ/uscNwsCR+MKxSQFbgDJEh8Atv+ovuC+FMGuaBT4iOQjpMj5dWSsN3ZPjVeo3hgA==}
+    engines: {node: '>= 10'}
+    cpu: [arm]
+    os: [android]
+
+  slacc-android-arm64@0.0.10:
+    resolution: {integrity: sha512-guVp88sW+4j1clTSXMzyDJHG8ondVnd8/FMKXIOfzKCEwSwX3uBxsuyHqtGvXkEwyZAGsBUy13Ei/PZAwElwYA==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [android]
+
+  slacc-darwin-arm64@0.0.10:
+    resolution: {integrity: sha512-633qnOMTP7egvd5IeljAOku0tnxlBXSoCRu7HiT0yeXxN9y5Tbg2X2/FaRzstI36lClfIJ0Lavne4mOw/90z9A==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [darwin]
+
+  slacc-darwin-universal@0.0.10:
+    resolution: {integrity: sha512-x5kEqRMTEQTi3NCufPEukWvaWqcOL+7EkP18ZCCiajcWH83jWnT8DOSGOmmLYdrXd0B7ZZcbd8GyLp3i5zu8PA==}
+    engines: {node: '>= 10'}
+    os: [darwin]
+
+  slacc-darwin-x64@0.0.10:
+    resolution: {integrity: sha512-5gQYboy/4T6Bj3sVXiCpM3EvF1sK/Zx1Nq5YBMUuYb2GzrIwywghHbCD6bK4JYGvNsLN7r4PC45ZUB4gVkU8yA==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [darwin]
+
+  slacc-freebsd-x64@0.0.10:
+    resolution: {integrity: sha512-Jmi5YszELef/aCzYto+LwiNGhCk5mrlJfTJU/pOI91HBbrZlV+aRyIsPCcxAMg5yPsPQuyRljrDouVYrPzNmjw==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [freebsd]
+
+  slacc-linux-arm-gnueabihf@0.0.10:
+    resolution: {integrity: sha512-9lTM3DGtISQlZYSKrMuQyKCiUnHYRcy04mY6HF1ywYcQ2sqfv3bKEnrypVewepIFUtytlIGzkgpiUAk/ghYGoA==}
+    engines: {node: '>= 10'}
+    cpu: [arm]
+    os: [linux]
+
+  slacc-linux-arm64-gnu@0.0.10:
+    resolution: {integrity: sha512-qXrNWSINXOjHRO3c9idGm8DeOAjAjG1xHY8WiplCoHWgsZf3E7V+sPhWqRUaGQEvftsJg40+cFYREBaLQhpAVQ==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
+  slacc-linux-arm64-musl@0.0.10:
+    resolution: {integrity: sha512-3lUX7752f6Okn54aONioaA+9M5TvifqXBAart+u2lNXEdWmmh003cVSU2Vcwg7nJ9lLHtju2DkDmKKfJjFuShA==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
+  slacc-linux-x64-gnu@0.0.10:
+    resolution: {integrity: sha512-BxxvylF9zlOLRLCpiyMvKTIUpdLlpetNBJ+DSMDh5+Ggq+AmQz2NUGawmcBJw58F8nMCj9TpWLlGNWc2AuY+JQ==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
+  slacc-linux-x64-musl@0.0.10:
+    resolution: {integrity: sha512-TYJi8LOtJiTFcZvka4du7bMjF9Bz1RHRwyLnScr5E5yjjgoLRrsvgSu7bxp87xH+rgJ3CdEwE3w3Ux8EiewHpA==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
+  slacc-win32-arm64-msvc@0.0.10:
+    resolution: {integrity: sha512-1CHPLiDB4exzFyT5ndtJDsRRhBxNg8mGz6I6eJEMjelGkJR2KZPT9LZuby/1bS/bcVOr7zuJvGNfbEGBeHRwPQ==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [win32]
+
+  slacc-win32-x64-msvc@0.0.10:
+    resolution: {integrity: sha512-wAXBy5yKCAzfYWjVlyPpu6PscD+j4QhCQEy0wZaVuzNyx60HpXWcTZxxVnMR730Y7tfc7cBxSI8NtRb8RguSgg==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [win32]
+
+  slacc@0.0.10:
+    resolution: {integrity: sha512-2jgms2/4mLr1AMq4oloAwPdKQK9RQvgmoEpMIxvC+HeHMwCR0XxB7gr/rKo4iLOKJ6gx02mnBU0JHWcTIonpmA==}
+    engines: {node: '>= 10'}
+
+  slash@3.0.0:
+    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+    engines: {node: '>=8'}
+
+  slice-ansi@3.0.0:
+    resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
+    engines: {node: '>=8'}
+
+  slice-ansi@4.0.0:
+    resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
+    engines: {node: '>=10'}
+
+  smart-buffer@4.2.0:
+    resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
+    engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+
+  socks-proxy-agent@8.0.2:
+    resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==}
+    engines: {node: '>= 14'}
+
+  socks@2.7.1:
+    resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==}
+    engines: {node: '>= 10.13.0', npm: '>= 3.0.0'}
+
+  sonic-boom@3.7.0:
+    resolution: {integrity: sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==}
+
+  sort-keys-length@1.0.1:
+    resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==}
+    engines: {node: '>=0.10.0'}
+
+  sort-keys@1.1.2:
+    resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==}
+    engines: {node: '>=0.10.0'}
+
+  sortablejs@1.14.0:
+    resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
+
+  source-map-js@1.0.2:
+    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+    engines: {node: '>=0.10.0'}
+
+  source-map-js@1.2.0:
+    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
+    engines: {node: '>=0.10.0'}
+
+  source-map-support@0.5.13:
+    resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
+
+  source-map-support@0.5.21:
+    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+  source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  source-map@0.7.4:
+    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+    engines: {node: '>= 8'}
+
+  space-separated-tokens@2.0.2:
+    resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+
+  spdx-correct@3.1.1:
+    resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
+
+  spdx-exceptions@2.3.0:
+    resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
+
+  spdx-expression-parse@3.0.1:
+    resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
+
+  spdx-license-ids@3.0.12:
+    resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==}
+
+  split2@4.1.0:
+    resolution: {integrity: sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==}
+    engines: {node: '>= 10.x'}
+
+  split@0.3.3:
+    resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==}
+
+  sprintf-js@1.0.3:
+    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+  sprintf-js@1.1.2:
+    resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==}
+
+  sshpk@1.17.0:
+    resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==}
+    engines: {node: '>=0.10.0'}
+    hasBin: true
+
+  ssri@10.0.4:
+    resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  stack-utils@2.0.6:
+    resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+    engines: {node: '>=10'}
+
+  stackback@0.0.2:
+    resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+  standard-as-callback@2.1.0:
+    resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
+
+  start-server-and-test@2.0.3:
+    resolution: {integrity: sha512-QsVObjfjFZKJE6CS6bSKNwWZCKBG6975/jKRPPGFfFh+yOQglSeGXiNWjzgQNXdphcBI9nXbyso9tPfX4YAUhg==}
+    engines: {node: '>=16'}
+    hasBin: true
+
+  statuses@2.0.1:
+    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+    engines: {node: '>= 0.8'}
+
+  std-env@3.7.0:
+    resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
+
+  stop-iteration-iterator@1.0.0:
+    resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
+    engines: {node: '>= 0.4'}
+
+  store2@2.14.2:
+    resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
+
+  storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640:
+    resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
+    version: 0.0.0
+    peerDependencies:
+      '@storybook/blocks': ^7.0.0-rc.4
+      '@storybook/components': ^7.0.0-rc.4
+      '@storybook/core-events': ^7.0.0-rc.4
+      '@storybook/manager-api': ^7.0.0-rc.4
+      '@storybook/preview-api': ^7.0.0-rc.4
+      '@storybook/theming': ^7.0.0-rc.4
+      '@storybook/types': ^7.0.0-rc.4
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      react:
+        optional: true
+      react-dom:
+        optional: true
+
+  storybook@8.0.9:
+    resolution: {integrity: sha512-/Mvij0Br5bUwJpCvqAUZMEDIWmdRxEyllvVj8Ukw5lIWJePxfpSsz4px5jg9+R6B9tO8sQSqjg4HJvQ/pZk8Tg==}
+    hasBin: true
+
+  stream-browserify@3.0.0:
+    resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==}
+
+  stream-combiner@0.0.4:
+    resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==}
+
+  stream-parser@0.3.1:
+    resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==}
+
+  stream-shift@1.0.1:
+    resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
+
+  stream-wormhole@1.1.0:
+    resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
+    engines: {node: '>=4.0.0'}
+
+  streamsearch@1.1.0:
+    resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+    engines: {node: '>=10.0.0'}
+
+  streamx@2.15.0:
+    resolution: {integrity: sha512-HcxY6ncGjjklGs1xsP1aR71INYcsXFJet5CU1CHqihQ2J5nOsbd4OjgjHO42w/4QNv9gZb3BueV+Vxok5pLEXg==}
+
+  strict-event-emitter-types@2.0.0:
+    resolution: {integrity: sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==}
+
+  strict-event-emitter@0.5.1:
+    resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==}
+
+  string-argv@0.3.1:
+    resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
+    engines: {node: '>=0.6.19'}
+
+  string-length@4.0.2:
+    resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
+    engines: {node: '>=10'}
+
+  string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+
+  string-width@5.1.2:
+    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+    engines: {node: '>=12'}
+
+  string.prototype.trim@1.2.7:
+    resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==}
+    engines: {node: '>= 0.4'}
+
+  string.prototype.trimend@1.0.6:
+    resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==}
+
+  string.prototype.trimstart@1.0.6:
+    resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==}
+
+  string_decoder@0.10.31:
+    resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==}
+
+  string_decoder@1.1.1:
+    resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
+  string_decoder@1.3.0:
+    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+  stringz@2.1.0:
+    resolution: {integrity: sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==}
+
+  strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+
+  strip-ansi@7.1.0:
+    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+    engines: {node: '>=12'}
+
+  strip-bom@3.0.0:
+    resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+    engines: {node: '>=4'}
+
+  strip-bom@4.0.0:
+    resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+    engines: {node: '>=8'}
+
+  strip-eof@1.0.0:
+    resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
+    engines: {node: '>=0.10.0'}
+
+  strip-final-newline@2.0.0:
+    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+    engines: {node: '>=6'}
+
+  strip-final-newline@3.0.0:
+    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+    engines: {node: '>=12'}
+
+  strip-indent@3.0.0:
+    resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+    engines: {node: '>=8'}
+
+  strip-indent@4.0.0:
+    resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
+    engines: {node: '>=12'}
+
+  strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+
+  strip-literal@1.3.0:
+    resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==}
+
+  strip-outer@2.0.0:
+    resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  strnum@1.0.5:
+    resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
+
+  strtok3@7.0.0:
+    resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==}
+    engines: {node: '>=14.16'}
+
+  stylehacks@6.1.1:
+    resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==}
+    engines: {node: ^14 || ^16 || >=18.0}
+    peerDependencies:
+      postcss: ^8.4.31
+
+  supports-color@5.5.0:
+    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+    engines: {node: '>=4'}
+
+  supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+
+  supports-color@8.1.1:
+    resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+    engines: {node: '>=10'}
+
+  supports-color@9.4.0:
+    resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==}
+    engines: {node: '>=12'}
+
+  supports-hyperlinks@2.3.0:
+    resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
+    engines: {node: '>=8'}
+
+  supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+
+  svgo@3.2.0:
+    resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  symbol-tree@3.2.4:
+    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+  systeminformation@5.22.7:
+    resolution: {integrity: sha512-AWxlP05KeHbpGdgvZkcudJpsmChc2Y5Eo/GvxG/iUA/Aws5LZKHAMSeAo+V+nD+nxWZaxrwpWcnx4SH3oxNL3A==}
+    engines: {node: '>=8.0.0'}
+    os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
+    hasBin: true
+
+  tar-fs@2.1.1:
+    resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
+
+  tar-stream@2.2.0:
+    resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+    engines: {node: '>=6'}
+
+  tar-stream@3.1.6:
+    resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==}
+
+  tar@4.4.19:
+    resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==}
+    engines: {node: '>=4.5'}
+
+  tar@6.2.1:
+    resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
+    engines: {node: '>=10'}
+
+  taskkill@5.0.0:
+    resolution: {integrity: sha512-+HRtZ40Vc+6YfCDWCeAsixwxJgMbPY4HHuTgzPYH3JXvqHWUlsCfy+ylXlAKhFNcuLp4xVeWeFBUhDk+7KYUvQ==}
+    engines: {node: '>=14.16'}
+
+  telejson@7.2.0:
+    resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==}
+
+  temp-dir@2.0.0:
+    resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
+    engines: {node: '>=8'}
+
+  temp@0.8.4:
+    resolution: {integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==}
+    engines: {node: '>=6.0.0'}
+
+  tempy@1.0.1:
+    resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==}
+    engines: {node: '>=10'}
+
+  terser@5.30.3:
+    resolution: {integrity: sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  test-exclude@6.0.0:
+    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+    engines: {node: '>=8'}
+
+  text-table@0.2.0:
+    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+
+  textarea-caret@3.1.0:
+    resolution: {integrity: sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==}
+
+  thenify-all@1.6.0:
+    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+    engines: {node: '>=0.8'}
+
+  thenify@3.3.1:
+    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+  thread-stream@2.3.0:
+    resolution: {integrity: sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==}
+
+  three@0.164.1:
+    resolution: {integrity: sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w==}
+
+  throttle-debounce@5.0.0:
+    resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==}
+    engines: {node: '>=12.22'}
+
+  throttleit@1.0.0:
+    resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==}
+
+  through2@2.0.5:
+    resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
+
+  through@2.3.4:
+    resolution: {integrity: sha512-DwbmSAcABsMazNkLOJJSLRC3gfh4cPxUxJCn9npmvbcI6undhgoJ2ShvEOgZrW8BH62Gyr9jKboGbfFcmY5VsQ==}
+
+  through@2.3.8:
+    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
+  tiny-invariant@1.3.1:
+    resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
+
+  tiny-invariant@1.3.3:
+    resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
+  tiny-lru@10.0.1:
+    resolution: {integrity: sha512-Vst+6kEsWvb17Zpz14sRJV/f8bUWKhqm6Dc+v08iShmIJ/WxqWytHzCTd6m88pS33rE2zpX34TRmOpAJPloNCA==}
+    engines: {node: '>=6'}
+
+  tinybench@2.6.0:
+    resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
+
+  tinycolor2@1.6.0:
+    resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
+
+  tinypool@0.7.0:
+    resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
+    engines: {node: '>=14.0.0'}
+
+  tinyspy@2.2.0:
+    resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==}
+    engines: {node: '>=14.0.0'}
+
+  tmp@0.2.3:
+    resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
+    engines: {node: '>=14.14'}
+
+  tmpl@1.0.5:
+    resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+
+  to-data-view@1.1.0:
+    resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==}
+
+  to-fast-properties@2.0.0:
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+    engines: {node: '>=4'}
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  toad-cache@3.3.0:
+    resolution: {integrity: sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==}
+    engines: {node: '>=12'}
+
+  toad-cache@3.7.0:
+    resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
+    engines: {node: '>=12'}
+
+  tocbot@4.21.1:
+    resolution: {integrity: sha512-IfajhBTeg0HlMXu1f+VMbPef05QpDTsZ9X2Yn1+8npdaXsXg/+wrm9Ze1WG5OS1UDC3qJ5EQN/XOZ3gfXjPFCw==}
+
+  toidentifier@1.0.1:
+    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+    engines: {node: '>=0.6'}
+
+  token-stream@1.0.0:
+    resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==}
+
+  token-types@5.0.1:
+    resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==}
+    engines: {node: '>=14.16'}
+
+  touch@3.1.0:
+    resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==}
+    hasBin: true
+
+  tough-cookie@2.5.0:
+    resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
+    engines: {node: '>=0.8'}
+
+  tough-cookie@4.1.3:
+    resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
+    engines: {node: '>=6'}
+
+  tr46@0.0.3:
+    resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+  tr46@5.0.0:
+    resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
+    engines: {node: '>=18'}
+
+  trace-redirect@1.0.6:
+    resolution: {integrity: sha512-UUfa1DjjU5flcjMdaFIiIEGDTyu2y/IiMjOX4uGXa7meKBS4vD4f2Uy/tken9Qkd4Jsm4sRsfZcIIPqrRVF3Mg==}
+
+  trim-newlines@3.0.1:
+    resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
+    engines: {node: '>=8'}
+
+  trim-repeated@2.0.0:
+    resolution: {integrity: sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==}
+    engines: {node: '>=12'}
+
+  trough@2.2.0:
+    resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+
+  ts-api-utils@1.0.1:
+    resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
+    engines: {node: '>=16.13.0'}
+    peerDependencies:
+      typescript: '>=4.2.0'
+
+  ts-api-utils@1.3.0:
+    resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
+    engines: {node: '>=16'}
+    peerDependencies:
+      typescript: '>=4.2.0'
+
+  ts-case-convert@2.0.2:
+    resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
+    bundledDependencies: []
+
+  ts-dedent@2.2.0:
+    resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
+    engines: {node: '>=6.10'}
+
+  ts-map@1.0.3:
+    resolution: {integrity: sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==}
+
+  tsc-alias@1.8.8:
+    resolution: {integrity: sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==}
+    hasBin: true
+
+  tsconfig-paths@3.15.0:
+    resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+  tsconfig-paths@4.2.0:
+    resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
+    engines: {node: '>=6'}
+
+  tsd@0.30.7:
+    resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==}
+    engines: {node: '>=14.16'}
+    hasBin: true
+
+  tslib@1.14.1:
+    resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+
+  tslib@2.6.2:
+    resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
+
+  tsx@4.4.0:
+    resolution: {integrity: sha512-4fwcEjRUxW20ciSaMB8zkpGwCPxuRGnadDuj/pBk5S9uT29zvWz15PK36GrKJo45mSJomDxVejZ73c6lr3811Q==}
+    engines: {node: '>=18.0.0'}
+    hasBin: true
+
+  tunnel-agent@0.6.0:
+    resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+
+  tweetnacl@0.14.5:
+    resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
+
+  type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+
+  type-detect@4.0.8:
+    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+    engines: {node: '>=4'}
+
+  type-fest@0.16.0:
+    resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
+    engines: {node: '>=10'}
+
+  type-fest@0.18.1:
+    resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}
+    engines: {node: '>=10'}
+
+  type-fest@0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+
+  type-fest@0.21.3:
+    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+    engines: {node: '>=10'}
+
+  type-fest@0.6.0:
+    resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
+    engines: {node: '>=8'}
+
+  type-fest@0.8.1:
+    resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
+    engines: {node: '>=8'}
+
+  type-fest@2.19.0:
+    resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
+    engines: {node: '>=12.20'}
+
+  type-fest@4.9.0:
+    resolution: {integrity: sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==}
+    engines: {node: '>=16'}
+
+  type-is@1.6.18:
+    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+    engines: {node: '>= 0.6'}
+
+  typed-array-buffer@1.0.0:
+    resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-byte-length@1.0.0:
+    resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-byte-offset@1.0.0:
+    resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==}
+    engines: {node: '>= 0.4'}
+
+  typed-array-length@1.0.4:
+    resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
+
+  typedarray@0.0.6:
+    resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+
+  typeorm@0.3.20:
+    resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==}
+    engines: {node: '>=16.13.0'}
+    hasBin: true
+    peerDependencies:
+      '@google-cloud/spanner': ^5.18.0
+      '@sap/hana-client': ^2.12.25
+      better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0
+      hdb-pool: ^0.1.6
+      ioredis: ^5.0.4
+      mongodb: ^5.8.0
+      mssql: ^9.1.1 || ^10.0.1
+      mysql2: ^2.2.5 || ^3.0.1
+      oracledb: ^6.3.0
+      pg: ^8.5.1
+      pg-native: ^3.0.0
+      pg-query-stream: ^4.0.0
+      redis: ^3.1.1 || ^4.0.0
+      sql.js: ^1.4.0
+      sqlite3: ^5.0.3
+      ts-node: ^10.7.0
+      typeorm-aurora-data-api-driver: ^2.0.0
+    peerDependenciesMeta:
+      '@google-cloud/spanner':
+        optional: true
+      '@sap/hana-client':
+        optional: true
+      better-sqlite3:
+        optional: true
+      hdb-pool:
+        optional: true
+      ioredis:
+        optional: true
+      mongodb:
+        optional: true
+      mssql:
+        optional: true
+      mysql2:
+        optional: true
+      oracledb:
+        optional: true
+      pg:
+        optional: true
+      pg-native:
+        optional: true
+      pg-query-stream:
+        optional: true
+      redis:
+        optional: true
+      sql.js:
+        optional: true
+      sqlite3:
+        optional: true
+      ts-node:
+        optional: true
+      typeorm-aurora-data-api-driver:
+        optional: true
+
+  typescript@5.3.3:
+    resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  typescript@5.4.2:
+    resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  typescript@5.4.5:
+    resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  ufo@1.3.2:
+    resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==}
+
+  uglify-js@3.17.4:
+    resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}
+    engines: {node: '>=0.8.0'}
+    hasBin: true
+
+  uid2@0.0.4:
+    resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
+
+  uid@2.0.2:
+    resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
+    engines: {node: '>=8'}
+
+  ulid@2.3.0:
+    resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==}
+    hasBin: true
+
+  unbox-primitive@1.0.2:
+    resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+
+  undefsafe@2.0.5:
+    resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
+
+  undici-types@5.26.5:
+    resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+
+  undici@5.28.2:
+    resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==}
+    engines: {node: '>=14.0'}
+
+  unicode-canonical-property-names-ecmascript@2.0.0:
+    resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
+    engines: {node: '>=4'}
+
+  unicode-match-property-ecmascript@2.0.0:
+    resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
+    engines: {node: '>=4'}
+
+  unicode-match-property-value-ecmascript@2.1.0:
+    resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==}
+    engines: {node: '>=4'}
+
+  unicode-property-aliases-ecmascript@2.1.0:
+    resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
+    engines: {node: '>=4'}
+
+  unified@11.0.4:
+    resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==}
+
+  uniq@1.0.1:
+    resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==}
+
+  unique-filename@3.0.0:
+    resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  unique-slug@4.0.0:
+    resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+  unique-string@2.0.0:
+    resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
+    engines: {node: '>=8'}
+
+  unist-util-is@6.0.0:
+    resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+
+  unist-util-stringify-position@4.0.0:
+    resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+
+  unist-util-visit-parents@6.0.1:
+    resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+
+  unist-util-visit@5.0.0:
+    resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+
+  universalify@0.1.2:
+    resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+    engines: {node: '>= 4.0.0'}
+
+  universalify@0.2.0:
+    resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
+    engines: {node: '>= 4.0.0'}
+
+  universalify@2.0.0:
+    resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
+    engines: {node: '>= 10.0.0'}
+
+  unload@2.4.1:
+    resolution: {integrity: sha512-IViSAm8Z3sRBYA+9wc0fLQmU9Nrxb16rcDmIiR6Y9LJSZzI7QY5QsDhqPpKOjAn0O9/kfK1TfNEMMAGPTIraPw==}
+
+  unpipe@1.0.0:
+    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+    engines: {node: '>= 0.8'}
+
+  unplugin@1.4.0:
+    resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
+
+  untildify@4.0.0:
+    resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
+    engines: {node: '>=8'}
+
+  update-browserslist-db@1.0.13:
+    resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+  url-parse@1.5.10:
+    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+
+  utf-8-validate@6.0.3:
+    resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==}
+    engines: {node: '>=6.14.2'}
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  util@0.12.5:
+    resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
+
+  utils-merge@1.0.1:
+    resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+    engines: {node: '>= 0.4.0'}
+
+  uuid@3.4.0:
+    resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
+    deprecated: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
+    hasBin: true
+
+  uuid@8.3.2:
+    resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+    hasBin: true
+
+  uuid@9.0.1:
+    resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
+    hasBin: true
+
+  v-code-diff@1.11.0:
+    resolution: {integrity: sha512-lBlO+FXw3I3qFKbnlorXZ4sb5cFnrdxlc6lj3Y1CWrbn2LC7PoVbGlwH0W+nvAVX1rdJhhc15rKIQdHyMkXe/w==}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.9
+      vue: ^2.6.0 || >=3.0.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
+  v8-to-istanbul@9.2.0:
+    resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==}
+    engines: {node: '>=10.12.0'}
+
+  validate-npm-package-license@3.0.4:
+    resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+
+  validator@13.9.0:
+    resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==}
+    engines: {node: '>= 0.10'}
+
+  vary@1.1.2:
+    resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+    engines: {node: '>= 0.8'}
+
+  verror@1.10.0:
+    resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
+    engines: {'0': node >=0.6.0}
+
+  vfile-message@4.0.2:
+    resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
+
+  vfile@6.0.1:
+    resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
+
+  vite-node@0.34.6:
+    resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
+    engines: {node: '>=v14.18.0'}
+    hasBin: true
+
+  vite-plugin-turbosnap@1.0.3:
+    resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
+
+  vite@5.2.11:
+    resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || >=20.0.0
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+
+  vitest-fetch-mock@0.2.2:
+    resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==}
+    engines: {node: '>=14.14.0'}
+    peerDependencies:
+      vitest: '>=0.16.0'
+
+  vitest@0.34.6:
+    resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
+    engines: {node: '>=v14.18.0'}
+    hasBin: true
+    peerDependencies:
+      '@edge-runtime/vm': '*'
+      '@vitest/browser': '*'
+      '@vitest/ui': '*'
+      happy-dom: '*'
+      jsdom: '*'
+      playwright: '*'
+      safaridriver: '*'
+      webdriverio: '*'
+    peerDependenciesMeta:
+      '@edge-runtime/vm':
+        optional: true
+      '@vitest/browser':
+        optional: true
+      '@vitest/ui':
+        optional: true
+      happy-dom:
+        optional: true
+      jsdom:
+        optional: true
+      playwright:
+        optional: true
+      safaridriver:
+        optional: true
+      webdriverio:
+        optional: true
+
+  void-elements@3.1.0:
+    resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
+    engines: {node: '>=0.10.0'}
+
+  vscode-jsonrpc@8.2.0:
+    resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
+    engines: {node: '>=14.0.0'}
+
+  vscode-languageclient@9.0.1:
+    resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==}
+    engines: {vscode: ^1.82.0}
+
+  vscode-languageserver-protocol@3.17.5:
+    resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
+
+  vscode-languageserver-textdocument@1.0.11:
+    resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==}
+
+  vscode-languageserver-types@3.17.5:
+    resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
+
+  vscode-languageserver@9.0.1:
+    resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
+    hasBin: true
+
+  vue-component-meta@2.0.16:
+    resolution: {integrity: sha512-IyIMClUMYcKxAL34GqdPbR4V45MUeHXqQiZlHxeYMV5Qcqp4M+CEmtGpF//XBSS138heDkYkceHAtJQjLUB1Lw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  vue-component-type-helpers@1.8.4:
+    resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
+
+  vue-component-type-helpers@2.0.16:
+    resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
+
+  vue-demi@0.14.7:
+    resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
+    engines: {node: '>=12'}
+    hasBin: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+
+  vue-docgen-api@4.75.1:
+    resolution: {integrity: sha512-MECZ3uExz+ssmhD/2XrFoQQs93y17IVO1KDYTp8nr6i9GNrk67AAto6QAtilW1H/pTDPMkQxJ7w/25ZIqVtfAA==}
+    peerDependencies:
+      vue: '>=2'
+
+  vue-eslint-parser@9.4.2:
+    resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+
+  vue-i18n@9.13.1:
+    resolution: {integrity: sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==}
+    engines: {node: '>= 16'}
+    peerDependencies:
+      vue: ^3.0.0
+
+  vue-inbrowser-compiler-independent-utils@4.71.1:
+    resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==}
+    peerDependencies:
+      vue: '>=2'
+
+  vue-template-compiler@2.7.14:
+    resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
+
+  vue-tsc@2.0.16:
+    resolution: {integrity: sha512-/gHAWJa216PeEhfxtAToIbxdWgw01wuQzo48ZUqMYVEyNqDp+OYV9xMO5HaPS2P3Ls0+EsjguMZLY4cGobX4Ew==}
+    hasBin: true
+    peerDependencies:
+      typescript: '*'
+
+  vue@3.4.26:
+    resolution: {integrity: sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  vuedraggable@4.1.0:
+    resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
+    peerDependencies:
+      vue: ^3.0.1
+
+  w3c-xmlserializer@5.0.0:
+    resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+    engines: {node: '>=18'}
+
+  wait-on@7.2.0:
+    resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==}
+    engines: {node: '>=12.0.0'}
+    hasBin: true
+
+  walker@1.0.8:
+    resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+
+  watchpack@2.4.0:
+    resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
+    engines: {node: '>=10.13.0'}
+
+  wcwidth@1.0.1:
+    resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+
+  web-push@3.6.7:
+    resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==}
+    engines: {node: '>= 16'}
+    hasBin: true
+
+  web-streams-polyfill@3.2.1:
+    resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
+    engines: {node: '>= 8'}
+
+  webidl-conversions@3.0.1:
+    resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+  webidl-conversions@7.0.0:
+    resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+    engines: {node: '>=12'}
+
+  webpack-sources@3.2.3:
+    resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
+    engines: {node: '>=10.13.0'}
+
+  webpack-virtual-modules@0.5.0:
+    resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
+
+  whatwg-encoding@3.1.1:
+    resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+    engines: {node: '>=18'}
+
+  whatwg-mimetype@3.0.0:
+    resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
+    engines: {node: '>=12'}
+
+  whatwg-mimetype@4.0.0:
+    resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+    engines: {node: '>=18'}
+
+  whatwg-url@14.0.0:
+    resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
+    engines: {node: '>=18'}
+
+  whatwg-url@5.0.0:
+    resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+  which-boxed-primitive@1.0.2:
+    resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+
+  which-collection@1.0.1:
+    resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
+
+  which-module@2.0.0:
+    resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
+
+  which-typed-array@1.1.11:
+    resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==}
+    engines: {node: '>= 0.4'}
+
+  which@1.3.1:
+    resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+    hasBin: true
+
+  which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+
+  which@4.0.0:
+    resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==}
+    engines: {node: ^16.13.0 || >=18.0.0}
+    hasBin: true
+
+  why-is-node-running@2.2.2:
+    resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
+    engines: {node: '>=8'}
+    hasBin: true
+
+  wide-align@1.1.5:
+    resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
+
+  with@7.0.2:
+    resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
+    engines: {node: '>= 10.0.0'}
+
+  wordwrap@1.0.0:
+    resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
+
+  wrap-ansi@6.2.0:
+    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+    engines: {node: '>=8'}
+
+  wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+
+  wrap-ansi@8.1.0:
+    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+    engines: {node: '>=12'}
+
+  wrappy@1.0.2:
+    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+  write-file-atomic@2.4.3:
+    resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==}
+
+  write-file-atomic@4.0.2:
+    resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
+    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+  ws@8.17.0:
+    resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: '>=5.0.2'
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+
+  xev@3.0.2:
+    resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==}
+
+  xml-js@1.6.11:
+    resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==}
+    hasBin: true
+
+  xml-name-validator@4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+
+  xml-name-validator@5.0.0:
+    resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+    engines: {node: '>=18'}
+
+  xml2js@0.5.0:
+    resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
+    engines: {node: '>=4.0.0'}
+
+  xmlbuilder@11.0.1:
+    resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
+    engines: {node: '>=4.0'}
+
+  xmlchars@2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+  xtend@4.0.2:
+    resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+    engines: {node: '>=0.4'}
+
+  y18n@4.0.3:
+    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
+
+  y18n@5.0.8:
+    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+    engines: {node: '>=10'}
+
+  yallist@2.1.2:
+    resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yallist@4.0.0:
+    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+  yargs-parser@18.1.3:
+    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+    engines: {node: '>=6'}
+
+  yargs-parser@20.2.9:
+    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+    engines: {node: '>=10'}
+
+  yargs-parser@21.1.1:
+    resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+    engines: {node: '>=12'}
+
+  yargs@15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+
+  yargs@16.2.0:
+    resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+    engines: {node: '>=10'}
+
+  yargs@17.7.2:
+    resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+    engines: {node: '>=12'}
+
+  yauzl@2.10.0:
+    resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
+
+  yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+
+  yocto-queue@1.0.0:
+    resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
+    engines: {node: '>=12.20'}
+
+  z-schema@5.0.5:
+    resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==}
+    engines: {node: '>=8.0.0'}
+    hasBin: true
+
+  zip-stream@6.0.1:
+    resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
+    engines: {node: '>= 14'}
+
+  zwitch@2.0.4:
+    resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+
+snapshots:
+
+  '@aashutoshrathi/word-wrap@1.2.6': {}
+
+  '@adobe/css-tools@4.3.3': {}
+
+  '@aiscript-dev/aiscript-languageserver@https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz':
+    dependencies:
+      seedrandom: 3.0.5
+      stringz: 2.1.0
+      uuid: 9.0.1
+      vscode-languageserver: 9.0.1
+      vscode-languageserver-textdocument: 1.0.11
+
+  '@ampproject/remapping@2.2.1':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.2
+      '@jridgewell/trace-mapping': 0.3.18
+
+  '@apidevtools/openapi-schemas@2.1.0': {}
+
+  '@apidevtools/swagger-methods@3.0.2': {}
+
+  '@aw-web-design/x-default-browser@1.4.126':
+    dependencies:
+      default-browser-id: 3.0.0
+
+  '@aws-crypto/crc32@3.0.0':
+    dependencies:
+      '@aws-crypto/util': 3.0.0
+      '@aws-sdk/types': 3.413.0
+      tslib: 1.14.1
+
+  '@aws-crypto/crc32c@3.0.0':
+    dependencies:
+      '@aws-crypto/util': 3.0.0
+      '@aws-sdk/types': 3.413.0
+      tslib: 1.14.1
+
+  '@aws-crypto/ie11-detection@3.0.0':
+    dependencies:
+      tslib: 1.14.1
+
+  '@aws-crypto/sha1-browser@3.0.0':
+    dependencies:
+      '@aws-crypto/ie11-detection': 3.0.0
+      '@aws-crypto/supports-web-crypto': 3.0.0
+      '@aws-crypto/util': 3.0.0
+      '@aws-sdk/types': 3.413.0
+      '@aws-sdk/util-locate-window': 3.208.0
+      '@aws-sdk/util-utf8-browser': 3.259.0
+      tslib: 1.14.1
+
+  '@aws-crypto/sha256-browser@3.0.0':
+    dependencies:
+      '@aws-crypto/ie11-detection': 3.0.0
+      '@aws-crypto/sha256-js': 3.0.0
+      '@aws-crypto/supports-web-crypto': 3.0.0
+      '@aws-crypto/util': 3.0.0
+      '@aws-sdk/types': 3.413.0
+      '@aws-sdk/util-locate-window': 3.208.0
+      '@aws-sdk/util-utf8-browser': 3.259.0
+      tslib: 1.14.1
+
+  '@aws-crypto/sha256-js@3.0.0':
+    dependencies:
+      '@aws-crypto/util': 3.0.0
+      '@aws-sdk/types': 3.413.0
+      tslib: 1.14.1
+
+  '@aws-crypto/supports-web-crypto@3.0.0':
+    dependencies:
+      tslib: 1.14.1
+
+  '@aws-crypto/util@3.0.0':
+    dependencies:
+      '@aws-sdk/types': 3.413.0
+      '@aws-sdk/util-utf8-browser': 3.259.0
+      tslib: 1.14.1
+
+  '@aws-sdk/client-s3@3.412.0':
+    dependencies:
+      '@aws-crypto/sha1-browser': 3.0.0
+      '@aws-crypto/sha256-browser': 3.0.0
+      '@aws-crypto/sha256-js': 3.0.0
+      '@aws-sdk/client-sts': 3.410.0
+      '@aws-sdk/credential-provider-node': 3.410.0
+      '@aws-sdk/middleware-bucket-endpoint': 3.410.0
+      '@aws-sdk/middleware-expect-continue': 3.410.0
+      '@aws-sdk/middleware-flexible-checksums': 3.410.0
+      '@aws-sdk/middleware-host-header': 3.410.0
+      '@aws-sdk/middleware-location-constraint': 3.410.0
+      '@aws-sdk/middleware-logger': 3.410.0
+      '@aws-sdk/middleware-recursion-detection': 3.410.0
+      '@aws-sdk/middleware-sdk-s3': 3.410.0
+      '@aws-sdk/middleware-signing': 3.410.0
+      '@aws-sdk/middleware-ssec': 3.410.0
+      '@aws-sdk/middleware-user-agent': 3.410.0
+      '@aws-sdk/signature-v4-multi-region': 3.412.0
+      '@aws-sdk/types': 3.410.0
+      '@aws-sdk/util-endpoints': 3.410.0
+      '@aws-sdk/util-user-agent-browser': 3.410.0
+      '@aws-sdk/util-user-agent-node': 3.410.0
+      '@aws-sdk/xml-builder': 3.310.0
+      '@smithy/config-resolver': 2.0.9
+      '@smithy/eventstream-serde-browser': 2.0.8
+      '@smithy/eventstream-serde-config-resolver': 2.0.8
+      '@smithy/eventstream-serde-node': 2.0.8
+      '@smithy/fetch-http-handler': 2.1.4
+      '@smithy/hash-blob-browser': 2.0.8
+      '@smithy/hash-node': 2.0.8
+      '@smithy/hash-stream-node': 2.0.8
+      '@smithy/invalid-dependency': 2.0.8
+      '@smithy/md5-js': 2.0.8
+      '@smithy/middleware-content-length': 2.0.10
+      '@smithy/middleware-endpoint': 2.0.8
+      '@smithy/middleware-retry': 2.0.11
+      '@smithy/middleware-serde': 2.0.8
+      '@smithy/middleware-stack': 2.0.1
+      '@smithy/node-config-provider': 2.0.11
+      '@smithy/node-http-handler': 2.5.0
+      '@smithy/protocol-http': 3.0.10
+      '@smithy/smithy-client': 2.1.5
+      '@smithy/types': 2.6.0
+      '@smithy/url-parser': 2.0.8
+      '@smithy/util-base64': 2.0.0
+      '@smithy/util-body-length-browser': 2.0.0
+      '@smithy/util-body-length-node': 2.1.0
+      '@smithy/util-defaults-mode-browser': 2.0.9
+      '@smithy/util-defaults-mode-node': 2.0.11
+      '@smithy/util-retry': 2.0.1
+      '@smithy/util-stream': 2.0.11
+      '@smithy/util-utf8': 2.0.0
+      '@smithy/util-waiter': 2.0.8
+      fast-xml-parser: 4.2.5
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - aws-crt
+
+  '@aws-sdk/client-sso@3.410.0':
+    dependencies:
+      '@aws-crypto/sha256-browser': 3.0.0
+      '@aws-crypto/sha256-js': 3.0.0
+      '@aws-sdk/middleware-host-header': 3.410.0
+      '@aws-sdk/middleware-logger': 3.410.0
+      '@aws-sdk/middleware-recursion-detection': 3.410.0
+      '@aws-sdk/middleware-user-agent': 3.410.0
+      '@aws-sdk/types': 3.410.0
+      '@aws-sdk/util-endpoints': 3.410.0
+      '@aws-sdk/util-user-agent-browser': 3.410.0
+      '@aws-sdk/util-user-agent-node': 3.410.0
+      '@smithy/config-resolver': 2.0.9
+      '@smithy/fetch-http-handler': 2.1.4
+      '@smithy/hash-node': 2.0.8
+      '@smithy/invalid-dependency': 2.0.8
+      '@smithy/middleware-content-length': 2.0.10
+      '@smithy/middleware-endpoint': 2.0.8
+      '@smithy/middleware-retry': 2.0.11
+      '@smithy/middleware-serde': 2.0.8
+      '@smithy/middleware-stack': 2.0.1
+      '@smithy/node-config-provider': 2.0.11
+      '@smithy/node-http-handler': 2.5.0
+      '@smithy/protocol-http': 3.0.10
+      '@smithy/smithy-client': 2.1.5
+      '@smithy/types': 2.6.0
+      '@smithy/url-parser': 2.0.8
+      '@smithy/util-base64': 2.0.0
+      '@smithy/util-body-length-browser': 2.0.0
+      '@smithy/util-body-length-node': 2.1.0
+      '@smithy/util-defaults-mode-browser': 2.0.9
+      '@smithy/util-defaults-mode-node': 2.0.11
+      '@smithy/util-retry': 2.0.1
+      '@smithy/util-utf8': 2.0.0
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - aws-crt
+
+  '@aws-sdk/client-sts@3.410.0':
+    dependencies:
+      '@aws-crypto/sha256-browser': 3.0.0
+      '@aws-crypto/sha256-js': 3.0.0
+      '@aws-sdk/credential-provider-node': 3.410.0
+      '@aws-sdk/middleware-host-header': 3.410.0
+      '@aws-sdk/middleware-logger': 3.410.0
+      '@aws-sdk/middleware-recursion-detection': 3.410.0
+      '@aws-sdk/middleware-sdk-sts': 3.410.0
+      '@aws-sdk/middleware-signing': 3.410.0
+      '@aws-sdk/middleware-user-agent': 3.410.0
+      '@aws-sdk/types': 3.410.0
+      '@aws-sdk/util-endpoints': 3.410.0
+      '@aws-sdk/util-user-agent-browser': 3.410.0
+      '@aws-sdk/util-user-agent-node': 3.410.0
+      '@smithy/config-resolver': 2.0.9
+      '@smithy/fetch-http-handler': 2.1.4
+      '@smithy/hash-node': 2.0.8
+      '@smithy/invalid-dependency': 2.0.8
+      '@smithy/middleware-content-length': 2.0.10
+      '@smithy/middleware-endpoint': 2.0.8
+      '@smithy/middleware-retry': 2.0.11
+      '@smithy/middleware-serde': 2.0.8
+      '@smithy/middleware-stack': 2.0.1
+      '@smithy/node-config-provider': 2.0.11
+      '@smithy/node-http-handler': 2.5.0
+      '@smithy/protocol-http': 3.0.10
+      '@smithy/smithy-client': 2.1.5
+      '@smithy/types': 2.6.0
+      '@smithy/url-parser': 2.0.8
+      '@smithy/util-base64': 2.0.0
+      '@smithy/util-body-length-browser': 2.0.0
+      '@smithy/util-body-length-node': 2.1.0
+      '@smithy/util-defaults-mode-browser': 2.0.9
+      '@smithy/util-defaults-mode-node': 2.0.11
+      '@smithy/util-retry': 2.0.1
+      '@smithy/util-utf8': 2.0.0
+      fast-xml-parser: 4.2.5
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - aws-crt
+
+  '@aws-sdk/credential-provider-env@3.410.0':
+    dependencies:
+      '@aws-sdk/types': 3.410.0
       '@smithy/property-provider': 2.0.9
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/credential-provider-ini@3.410.0:
-    resolution: {integrity: sha512-D8rcr5bRCFD0f42MPQ7K6TWZq5d3pfqrKINL1/bpfkK5BJbvq1BGYmR88UC6CLpTRtZ1LHY2HgYG0fp/2zjjww==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-ini@3.410.0':
     dependencies:
       '@aws-sdk/credential-provider-env': 3.410.0
       '@aws-sdk/credential-provider-process': 3.410.0
@@ -1550,11 +11400,8 @@ packages:
       tslib: 2.6.2
     transitivePeerDependencies:
       - aws-crt
-    dev: false
 
-  /@aws-sdk/credential-provider-node@3.410.0:
-    resolution: {integrity: sha512-0wmVm33T/j1FS7MZ/j+WsPlgSc0YnCXnpbWSov1Mn6R86SHI2b2JhdIPRRE4XbGfyW2QGNUl2CwoZVaqhXeF5g==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-node@3.410.0':
     dependencies:
       '@aws-sdk/credential-provider-env': 3.410.0
       '@aws-sdk/credential-provider-ini': 3.410.0
@@ -1569,22 +11416,16 @@ packages:
       tslib: 2.6.2
     transitivePeerDependencies:
       - aws-crt
-    dev: false
 
-  /@aws-sdk/credential-provider-process@3.410.0:
-    resolution: {integrity: sha512-BMju1hlDCDNkkSZpKF5SQ8G0WCLRj6/Jvw9QmudLHJuVwYJXEW1r2AsVMg98OZ3hB9G+MAvHruHZIbMiNmUMXQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-process@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/property-provider': 2.0.9
       '@smithy/shared-ini-file-loader': 2.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/credential-provider-sso@3.410.0:
-    resolution: {integrity: sha512-zEaoY/sY+KYTlQUkp9dvveAHf175b8RIt0DsQkDrRPtrg/RBHR00r5rFvz9+nrwsR8546RaBU7h/zzTaQGhmcA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-sso@3.410.0':
     dependencies:
       '@aws-sdk/client-sso': 3.410.0
       '@aws-sdk/token-providers': 3.410.0
@@ -1595,23 +11436,15 @@ packages:
       tslib: 2.6.2
     transitivePeerDependencies:
       - aws-crt
-    dev: false
 
-  /@aws-sdk/credential-provider-web-identity@3.410.0:
-    resolution: {integrity: sha512-cE0l8LmEHdWbDkdPNgrfdYSgp4/cIVXrjUKI1QCATA729CrHZ/OQjB/maOBOrMHO9YTiggko887NkslVvwVB7w==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-web-identity@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/property-provider': 2.0.9
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/lib-storage@3.412.0(@aws-sdk/client-s3@3.412.0):
-    resolution: {integrity: sha512-uAdVtNuip06rJOs28zVrYXLNeHfKraxvJRTzTA+DW1dXkzh70GTKqDKHWH9IJkW/xMTE6wGSM+fDs8jsMOn/yA==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      '@aws-sdk/client-s3': ^3.0.0
+  '@aws-sdk/lib-storage@3.412.0(@aws-sdk/client-s3@3.412.0)':
     dependencies:
       '@aws-sdk/client-s3': 3.412.0
       '@smithy/abort-controller': 2.0.14
@@ -1621,11 +11454,8 @@ packages:
       events: 3.3.0
       stream-browserify: 3.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-bucket-endpoint@3.410.0:
-    resolution: {integrity: sha512-pUGrpFgCKf9fDHu01JJhhw+MUImheS0HFlZwNG37OMubkxUAbCdmYGewGxfTCUvWyZJtx9bVjrSu6gG7w+RARg==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-bucket-endpoint@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@aws-sdk/util-arn-parser': 3.310.0
@@ -1634,21 +11464,15 @@ packages:
       '@smithy/types': 2.6.0
       '@smithy/util-config-provider': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-expect-continue@3.410.0:
-    resolution: {integrity: sha512-e5YqGCNmW99GZjEPPujJ02RlEZql19U40oORysBhVF7mKz8BBvF3s8l37tvu37oxebDEkh1u/2cm2+ggOXxLjQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-expect-continue@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/protocol-http': 3.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-flexible-checksums@3.410.0:
-    resolution: {integrity: sha512-IK7KlvEKtrQVBfmAp/MmGd0wbWLuN2GZwwfAmsU0qFb0f5vOVUbKDsu6tudtDKCBG9uXyTEsx3/QGvoK2zDy+g==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-flexible-checksums@3.410.0':
     dependencies:
       '@aws-crypto/crc32': 3.0.0
       '@aws-crypto/crc32c': 3.0.0
@@ -1658,70 +11482,49 @@ packages:
       '@smithy/types': 2.6.0
       '@smithy/util-utf8': 2.0.0
       tslib: 2.6.2
-    dev: false
-
-  /@aws-sdk/middleware-host-header@3.410.0:
-    resolution: {integrity: sha512-ED/OVcyITln5rrxnajZP+V0PN1nug+gSDHJDqdDo/oLy7eiDr/ZWn3nlWW7WcMplQ1/Jnb+hK0UetBp/25XooA==}
-    engines: {node: '>=14.0.0'}
+
+  '@aws-sdk/middleware-host-header@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/protocol-http': 3.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-location-constraint@3.410.0:
-    resolution: {integrity: sha512-jAftSpOpw/5AdpOJ/cGiXCb+Vv22KXR5QZmxmllUDsnlm18672tpRaI2plmu/1d98CVvqhY61eSklFMrIf2c4w==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-location-constraint@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-logger@3.410.0:
-    resolution: {integrity: sha512-YtmKYCVtBfScq3/UFJk+aSZOktKJBNZL9DaSc2aPcy/goCVsYDOkGwtHk0jIkC1JRSNCkVTqL7ya60sSr8zaQQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-logger@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-recursion-detection@3.410.0:
-    resolution: {integrity: sha512-KWaes5FLzRqj28vaIEE4Bimpga2E596WdPF2HaH6zsVMJddoRDsc3ZX9ZhLOGrXzIO1RqBd0QxbLrM0S/B2aOQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-recursion-detection@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/protocol-http': 3.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-sdk-s3@3.410.0:
-    resolution: {integrity: sha512-K2sG2V1ZkezYMCIy3uMt0MwtflcfIwLptwm0iFLaYitiINZQ1tcslk9ggAjyTHg0rslDSI4/zjkhy8VHFOV7HA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-sdk-s3@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@aws-sdk/util-arn-parser': 3.310.0
       '@smithy/protocol-http': 3.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-sdk-sts@3.410.0:
-    resolution: {integrity: sha512-YfBpctDocRR4CcROoDueJA7D+aMLBV8nTFfmVNdLLLgyuLZ/AUR11VQSu1lf9gQZKl8IpKE/BLf2fRE/qV1ZuA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-sdk-sts@3.410.0':
     dependencies:
       '@aws-sdk/middleware-signing': 3.410.0
       '@aws-sdk/types': 3.410.0
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-signing@3.410.0:
-    resolution: {integrity: sha512-KBAZ/eoAJUSJv5us2HsKwK2OszG2s9FEyKpEhgnHLcbbKzW873zHBH5GcOGEQu4AWArTy2ndzJu3FF+9/J9hJQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-signing@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/property-provider': 2.0.9
@@ -1730,42 +11533,30 @@ packages:
       '@smithy/types': 2.6.0
       '@smithy/util-middleware': 2.0.1
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-ssec@3.410.0:
-    resolution: {integrity: sha512-DNsjVTXoxIh+PuW9o45CFaMiconbuZRm19MC3NA1yNCaCj3ZxD5OdXAutq6UjQdrx8UG4EjUlCJEEvBKmboITw==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-ssec@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/middleware-user-agent@3.410.0:
-    resolution: {integrity: sha512-ZayDtLfvCZUohSxQc/49BfoU/y6bDHLfLdyyUJbJ54Sv8zQcrmdyKvCBFUZwE6tHQgAmv9/ZT18xECMl+xiONA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-user-agent@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@aws-sdk/util-endpoints': 3.410.0
       '@smithy/protocol-http': 3.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/signature-v4-multi-region@3.412.0:
-    resolution: {integrity: sha512-ijxOeYpNDuk2T940S9HYcZ1C+wTP9vqp1Cw37zw9whVY2mKV3Vr7i+44D4FQ5HhWULgdwhjD7IctbNxPIPzUZQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/signature-v4-multi-region@3.412.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/protocol-http': 3.0.10
       '@smithy/signature-v4': 2.0.5
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/token-providers@3.410.0:
-    resolution: {integrity: sha512-d5Nc0xydkH/X0LA1HDyhGY5sEv4LuADFk+QpDtT8ogLilcre+b1jpdY8Sih/gd1KoGS1H+d1tz2hSGwUHAbUbw==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/token-providers@3.410.0':
     dependencies:
       '@aws-crypto/sha256-browser': 3.0.0
       '@aws-crypto/sha256-js': 3.0.0
@@ -1787,7 +11578,7 @@ packages:
       '@smithy/middleware-serde': 2.0.8
       '@smithy/middleware-stack': 2.0.1
       '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.10
+      '@smithy/node-http-handler': 2.5.0
       '@smithy/property-provider': 2.0.9
       '@smithy/protocol-http': 3.0.10
       '@smithy/shared-ini-file-loader': 2.0.10
@@ -1804,99 +11595,60 @@ packages:
       tslib: 2.6.2
     transitivePeerDependencies:
       - aws-crt
-    dev: false
 
-  /@aws-sdk/types@3.410.0:
-    resolution: {integrity: sha512-D7iaUCszv/v04NDaZUmCmekamy6VD/lKozm/3gS9+dkfU6cC2CsNoUfPV8BlV6dPdw0oWgF91am3I1stdvfVrQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/types@3.410.0':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/types@3.413.0:
-    resolution: {integrity: sha512-j1xib0f/TazIFc5ySIKOlT1ujntRbaoG4LJFeEezz4ji03/wSJMI8Vi4KjzpBp8J1tTu0oRDnsxRIGixsUBeYQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/types@3.413.0':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/util-arn-parser@3.310.0:
-    resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/util-arn-parser@3.310.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/util-endpoints@3.410.0:
-    resolution: {integrity: sha512-iNiqJyC7N3+8zFwnXUqcWSxrZecVZLToo1iTQQdeYL2af1IcOtRgb7n8jpAI/hmXhBSx2+3RI+Y7pxyFo1vu+w==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/util-endpoints@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/util-locate-window@3.208.0:
-    resolution: {integrity: sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/util-locate-window@3.208.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/util-user-agent-browser@3.410.0:
-    resolution: {integrity: sha512-i1G/XGpXGMRT2zEiAhi1xucJsfCWk8nNYjk/LbC0sA+7B9Huri96YAzVib12wkHPsJQvZxZC6CpQDIHWm4lXMA==}
+  '@aws-sdk/util-user-agent-browser@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/types': 2.6.0
       bowser: 2.11.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/util-user-agent-node@3.410.0:
-    resolution: {integrity: sha512-bK70t1jHRl8HrJXd4hEIwc5PBZ7U0w+81AKFnanIVKZwZedd6nLibUXDTK14z/Jp2GFcBqd4zkt2YLGkRt/U4A==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      aws-crt: '>=1.0.0'
-    peerDependenciesMeta:
-      aws-crt:
-        optional: true
+  '@aws-sdk/util-user-agent-node@3.410.0':
     dependencies:
       '@aws-sdk/types': 3.410.0
       '@smithy/node-config-provider': 2.0.11
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/util-utf8-browser@3.259.0:
-    resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==}
+  '@aws-sdk/util-utf8-browser@3.259.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@aws-sdk/xml-builder@3.310.0:
-    resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/xml-builder@3.310.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@babel/code-frame@7.23.5:
-    resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
-    engines: {node: '>=6.9.0'}
+  '@babel/code-frame@7.23.5':
     dependencies:
       '@babel/highlight': 7.23.4
       chalk: 2.4.2
-    dev: true
 
-  /@babel/compat-data@7.23.5:
-    resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
-    engines: {node: '>=6.9.0'}
-    dev: true
+  '@babel/compat-data@7.23.5': {}
 
-  /@babel/core@7.23.5:
-    resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==}
-    engines: {node: '>=6.9.0'}
+  '@babel/core@7.23.5':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@babel/code-frame': 7.23.5
@@ -1915,11 +11667,8 @@ packages:
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/core@7.24.0:
-    resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/core@7.24.0':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@babel/code-frame': 7.23.5
@@ -1938,69 +11687,46 @@ packages:
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/generator@7.23.5:
-    resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==}
-    engines: {node: '>=6.9.0'}
+  '@babel/generator@7.23.5':
     dependencies:
       '@babel/types': 7.23.5
       '@jridgewell/gen-mapping': 0.3.2
       '@jridgewell/trace-mapping': 0.3.18
       jsesc: 2.5.2
-    dev: true
 
-  /@babel/generator@7.23.6:
-    resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/generator@7.23.6':
     dependencies:
       '@babel/types': 7.24.0
       '@jridgewell/gen-mapping': 0.3.2
       '@jridgewell/trace-mapping': 0.3.18
       jsesc: 2.5.2
-    dev: true
 
-  /@babel/helper-annotate-as-pure@7.22.5:
-    resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-annotate-as-pure@7.22.5':
     dependencies:
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15:
-    resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-builder-binary-assignment-operator-visitor@7.22.15':
     dependencies:
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/helper-compilation-targets@7.22.15:
-    resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-compilation-targets@7.22.15':
     dependencies:
       '@babel/compat-data': 7.23.5
       '@babel/helper-validator-option': 7.23.5
       browserslist: 4.22.2
       lru-cache: 5.1.1
       semver: 6.3.1
-    dev: true
 
-  /@babel/helper-compilation-targets@7.23.6:
-    resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-compilation-targets@7.23.6':
     dependencies:
       '@babel/compat-data': 7.23.5
       '@babel/helper-validator-option': 7.23.5
       browserslist: 4.23.0
       lru-cache: 5.1.1
       semver: 6.3.1
-    dev: true
 
-  /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
@@ -2012,24 +11738,15 @@ packages:
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
       semver: 6.3.1
-    dev: true
 
-  /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.0):
-    resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
       regexpu-core: 5.3.2
       semver: 6.3.1
-    dev: true
 
-  /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+  '@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-compilation-targets': 7.23.6
@@ -2039,47 +11756,27 @@ packages:
       resolve: 1.22.8
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/helper-environment-visitor@7.22.20:
-    resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
-    engines: {node: '>=6.9.0'}
-    dev: true
+  '@babel/helper-environment-visitor@7.22.20': {}
 
-  /@babel/helper-function-name@7.23.0:
-    resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-function-name@7.23.0':
     dependencies:
-      '@babel/template': 7.22.15
-      '@babel/types': 7.23.5
-    dev: true
+      '@babel/template': 7.24.0
+      '@babel/types': 7.24.0
 
-  /@babel/helper-hoist-variables@7.22.5:
-    resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-hoist-variables@7.22.5':
     dependencies:
-      '@babel/types': 7.23.5
-    dev: true
+      '@babel/types': 7.24.0
 
-  /@babel/helper-member-expression-to-functions@7.23.0:
-    resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-member-expression-to-functions@7.23.0':
     dependencies:
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/helper-module-imports@7.22.15:
-    resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-module-imports@7.22.15':
     dependencies:
       '@babel/types': 7.23.5
-    dev: true
 
-  /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-environment-visitor': 7.22.20
@@ -2087,13 +11784,8 @@ packages:
       '@babel/helper-simple-access': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/helper-validator-identifier': 7.22.20
-    dev: true
 
-  /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-environment-visitor': 7.22.20
@@ -2101,586 +11793,327 @@ packages:
       '@babel/helper-simple-access': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/helper-validator-identifier': 7.22.20
-    dev: true
 
-  /@babel/helper-optimise-call-expression@7.22.5:
-    resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-optimise-call-expression@7.22.5':
     dependencies:
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/helper-plugin-utils@7.22.5:
-    resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
-    engines: {node: '>=6.9.0'}
-    dev: true
+  '@babel/helper-plugin-utils@7.22.5': {}
 
-  /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.0):
-    resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-wrap-function': 7.22.20
-    dev: true
 
-  /@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0):
-    resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-member-expression-to-functions': 7.23.0
       '@babel/helper-optimise-call-expression': 7.22.5
-    dev: true
 
-  /@babel/helper-simple-access@7.22.5:
-    resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-simple-access@7.22.5':
     dependencies:
       '@babel/types': 7.23.5
-    dev: true
 
-  /@babel/helper-skip-transparent-expression-wrappers@7.22.5:
-    resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-skip-transparent-expression-wrappers@7.22.5':
     dependencies:
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/helper-split-export-declaration@7.22.6:
-    resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-split-export-declaration@7.22.6':
     dependencies:
       '@babel/types': 7.23.5
-    dev: true
 
-  /@babel/helper-string-parser@7.23.4:
-    resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-string-parser@7.23.4': {}
 
-  /@babel/helper-validator-identifier@7.22.20:
-    resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-validator-identifier@7.22.20': {}
 
-  /@babel/helper-validator-option@7.23.5:
-    resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
-    engines: {node: '>=6.9.0'}
-    dev: true
+  '@babel/helper-validator-option@7.23.5': {}
 
-  /@babel/helper-wrap-function@7.22.20:
-    resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helper-wrap-function@7.22.20':
     dependencies:
       '@babel/helper-function-name': 7.23.0
       '@babel/template': 7.24.0
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/helpers@7.23.5:
-    resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helpers@7.23.5':
     dependencies:
       '@babel/template': 7.22.15
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/helpers@7.24.0:
-    resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==}
-    engines: {node: '>=6.9.0'}
+  '@babel/helpers@7.24.0':
     dependencies:
       '@babel/template': 7.24.0
       '@babel/traverse': 7.24.0
       '@babel/types': 7.24.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/highlight@7.23.4:
-    resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
-    engines: {node: '>=6.9.0'}
+  '@babel/highlight@7.23.4':
     dependencies:
       '@babel/helper-validator-identifier': 7.22.20
       chalk: 2.4.2
       js-tokens: 4.0.0
-    dev: true
 
-  /@babel/parser@7.23.9:
-    resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
+  '@babel/parser@7.23.9':
     dependencies:
       '@babel/types': 7.23.5
 
-  /@babel/parser@7.24.0:
-    resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
+  '@babel/parser@7.24.0':
     dependencies:
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/parser@7.24.5':
+    dependencies:
+      '@babel/types': 7.24.0
+
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.13.0
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
       '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0):
-    resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
-    dev: true
 
-  /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5):
-    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5):
-    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0):
-    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5):
-    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.5):
-    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5):
-    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
-
-  /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5):
-    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.0):
-    resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0)
       '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-module-imports': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.12.0
+  '@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-classes@7.23.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-classes@7.23.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
@@ -2692,253 +12125,138 @@ packages:
       '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
       '@babel/helper-split-export-declaration': 7.22.6
       globals: 11.12.0
-    dev: true
 
-  /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/template': 7.24.0
-    dev: true
 
-  /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-for-of@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-function-name@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-function-name': 7.23.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-literals@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-literals@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-simple-access': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-hoist-variables': 7.22.5
       '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-validator-identifier': 7.22.20
-    dev: true
 
-  /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-new-target@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/compat-data': 7.23.5
       '@babel/core': 7.24.0
@@ -2946,219 +12264,119 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0)
       '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-object-super@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
       '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-parameters@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.24.0):
-    resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
       '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       regenerator-transform: 0.15.2
-    dev: true
 
-  /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-spread@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-spread@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-typescript@7.23.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-typescript@7.23.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-annotate-as-pure': 7.22.5
       '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  '@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
       '@babel/helper-plugin-utils': 7.22.5
-    dev: true
 
-  /@babel/preset-env@7.23.5(@babel/core@7.24.0):
-    resolution: {integrity: sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/preset-env@7.23.5(@babel/core@7.24.0)':
     dependencies:
       '@babel/compat-data': 7.23.5
       '@babel/core': 7.24.0
@@ -3243,36 +12461,22 @@ packages:
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/preset-flow@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/preset-flow@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-validator-option': 7.23.5
       '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.0):
-    resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
+  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/types': 7.24.0
       esutils: 2.0.3
-    dev: true
 
-  /@babel/preset-typescript@7.23.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/preset-typescript@7.23.3(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-plugin-utils': 7.22.5
@@ -3280,13 +12484,8 @@ packages:
       '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0)
       '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0)
       '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.24.0)
-    dev: true
 
-  /@babel/register@7.22.15(@babel/core@7.24.0):
-    resolution: {integrity: sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  '@babel/register@7.22.15(@babel/core@7.24.0)':
     dependencies:
       '@babel/core': 7.24.0
       clone-deep: 4.0.1
@@ -3294,39 +12493,26 @@ packages:
       make-dir: 2.1.0
       pirates: 4.0.5
       source-map-support: 0.5.21
-    dev: true
 
-  /@babel/regjsgen@0.8.0:
-    resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
-    dev: true
+  '@babel/regjsgen@0.8.0': {}
 
-  /@babel/runtime@7.23.4:
-    resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==}
-    engines: {node: '>=6.9.0'}
+  '@babel/runtime@7.23.4':
     dependencies:
       regenerator-runtime: 0.14.0
 
-  /@babel/template@7.22.15:
-    resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
-    engines: {node: '>=6.9.0'}
+  '@babel/template@7.22.15':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/parser': 7.23.9
       '@babel/types': 7.23.5
-    dev: true
 
-  /@babel/template@7.24.0:
-    resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
-    engines: {node: '>=6.9.0'}
+  '@babel/template@7.24.0':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/parser': 7.24.0
       '@babel/types': 7.24.0
-    dev: true
 
-  /@babel/traverse@7.23.5:
-    resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
-    engines: {node: '>=6.9.0'}
+  '@babel/traverse@7.23.5':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/generator': 7.23.5
@@ -3340,11 +12526,8 @@ packages:
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/traverse@7.24.0:
-    resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==}
-    engines: {node: '>=6.9.0'}
+  '@babel/traverse@7.24.0':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/generator': 7.23.6
@@ -3358,172 +12541,120 @@ packages:
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@babel/types@7.23.5:
-    resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
-    engines: {node: '>=6.9.0'}
+  '@babel/types@7.23.5':
     dependencies:
       '@babel/helper-string-parser': 7.23.4
       '@babel/helper-validator-identifier': 7.22.20
       to-fast-properties: 2.0.0
 
-  /@babel/types@7.24.0:
-    resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
-    engines: {node: '>=6.9.0'}
+  '@babel/types@7.24.0':
     dependencies:
       '@babel/helper-string-parser': 7.23.4
       '@babel/helper-validator-identifier': 7.22.20
       to-fast-properties: 2.0.0
-    dev: true
 
-  /@base2/pretty-print-object@1.0.1:
-    resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==}
-    dev: true
+  '@base2/pretty-print-object@1.0.1': {}
 
-  /@bcoe/v8-coverage@0.2.3:
-    resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
-    dev: true
+  '@bcoe/v8-coverage@0.2.3': {}
 
-  /@bull-board/api@5.14.2(@bull-board/ui@5.14.2):
-    resolution: {integrity: sha512-0wppAGPU7ZMwWMpzkmtrlmm7ySI5immymyaRS1cVNJ54rUiGOZP5tnm+Sj7MwPdf63rxqIM843un8+PvQyARGg==}
-    peerDependencies:
-      '@bull-board/ui': 5.14.2
+  '@bull-board/api@5.17.0(@bull-board/ui@5.17.0)':
     dependencies:
-      '@bull-board/ui': 5.14.2
+      '@bull-board/ui': 5.17.0
       redis-info: 3.1.0
-    dev: false
 
-  /@bull-board/fastify@5.14.2:
-    resolution: {integrity: sha512-GQMK70tKOu2gjBi2pjWXMXcftzWRvQNSm+deLmGlJUgqUUbNlzIGRyvaTk7giT4CFzgKcP+hT+lphcAsGTKBQw==}
+  '@bull-board/fastify@5.17.0':
     dependencies:
-      '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
-      '@bull-board/ui': 5.14.2
+      '@bull-board/api': 5.17.0(@bull-board/ui@5.17.0)
+      '@bull-board/ui': 5.17.0
       '@fastify/static': 6.12.0
       '@fastify/view': 8.2.0
       ejs: 3.1.9
-    dev: false
 
-  /@bull-board/ui@5.14.2:
-    resolution: {integrity: sha512-NiyKWLjKjy29I6ySCnSYbzGX4ZJyPE4xlS5/Z5dVsF2bJLoAV+yD1obflxteJMt60FiEgLV7tfs6tMSVa+Htew==}
+  '@bull-board/ui@5.17.0':
     dependencies:
-      '@bull-board/api': 5.14.2(@bull-board/ui@5.14.2)
-    dev: false
+      '@bull-board/api': 5.17.0(@bull-board/ui@5.17.0)
 
-  /@bundled-es-modules/cookie@2.0.0:
-    resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==}
+  '@bundled-es-modules/cookie@2.0.0':
     dependencies:
       cookie: 0.5.0
-    dev: true
 
-  /@bundled-es-modules/statuses@1.0.1:
-    resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==}
+  '@bundled-es-modules/statuses@1.0.1':
     dependencies:
       statuses: 2.0.1
-    dev: true
 
-  /@canvas/image-data@1.0.0:
-    resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==}
-    dev: false
+  '@canvas/image-data@1.0.0': {}
 
-  /@colors/colors@1.5.0:
-    resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
-    engines: {node: '>=0.1.90'}
-    requiresBuild: true
-    dev: true
+  '@colors/colors@1.5.0':
     optional: true
 
-  /@cropper/element-canvas@2.0.0-beta.4:
-    resolution: {integrity: sha512-xL7k5YgtbCLdR/QEj81An4HpPcBTJXf1lq+2xisyHALGeUKQXjA9cJQL7bldYscHAKjmFgNZ5xOMrNaYM++qZw==}
+  '@cropper/element-canvas@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element-crosshair@2.0.0-beta.4:
-    resolution: {integrity: sha512-NiwIQZFh963i3E3QbXFiU9oNqs+P1cLJur3+e+DK0E3oLTa7rEfcigP/ZoMj/3DZ9Et0LPhKKRDY2SJ8ZszyPA==}
+  '@cropper/element-crosshair@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element-grid@2.0.0-beta.4:
-    resolution: {integrity: sha512-uMVVNk1SICwM2nA/7BHkyEojc0DAqsDFIUnC/sIGPtNf3fe5hYQyukby8BEPO7dlqzfIXYmnxacgLaPM9BZ7GQ==}
+  '@cropper/element-grid@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element-handle@2.0.0-beta.4:
-    resolution: {integrity: sha512-PHjC4ptBi0leQ82mPWvivNilNOpiBnV90ueqz99tli8f9bQobx+Os7dzKFwLIpj4WKCNRYhyEvxf1KuKhQisIg==}
+  '@cropper/element-handle@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element-image@2.0.0-beta.4:
-    resolution: {integrity: sha512-Nu5z5EFpyOEC2CAdhNZGfvpG9Xj6ZD46jvpJGKxsel7J7Kqf4qy+5m6nNdq2J+lK7YfTi16svkHeFwzNWZYLAA==}
+  '@cropper/element-image@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/element-canvas': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/element-canvas': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element-selection@2.0.0-beta.4:
-    resolution: {integrity: sha512-wHZhWI80cC5TfFHI/2HT1A+ZbHifnAO+/IAr4IqkbaxtDZ9duqEvM2hhC+ZXgB3BYqVidAJNwpSnZkVK+DlJ6A==}
+  '@cropper/element-selection@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/element-canvas': 2.0.0-beta.4
-      '@cropper/element-image': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/element-canvas': 2.0.0-beta.5
+      '@cropper/element-image': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element-shade@2.0.0-beta.4:
-    resolution: {integrity: sha512-sTFTzlmu+Z31Hp7RHgUAxfDsRIQ/uG8RueOBBHLeKVGFZbYhsIElQaLcVDwebgqXLHVr9imCEvvIX11JeTqiTQ==}
+  '@cropper/element-shade@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/element-canvas': 2.0.0-beta.4
-      '@cropper/element-selection': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/element-canvas': 2.0.0-beta.5
+      '@cropper/element-selection': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element-viewer@2.0.0-beta.4:
-    resolution: {integrity: sha512-bXW8OuezoHjyGFmQzX1QEj3OqvmSZwaLiQts+mVhcarYqAEVrK9s/bC/OqZKR2ZKkHeaiGWq+rTOBVAmhZja/A==}
+  '@cropper/element-viewer@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/element-canvas': 2.0.0-beta.4
-      '@cropper/element-image': 2.0.0-beta.4
-      '@cropper/element-selection': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/element-canvas': 2.0.0-beta.5
+      '@cropper/element-image': 2.0.0-beta.5
+      '@cropper/element-selection': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/element@2.0.0-beta.4:
-    resolution: {integrity: sha512-1P8Vm9+OqTQz4B/rEA0t8xmzKUkYyxzxTiOaDMPKjKbG2R3UZgJBWRzvTgTsDudld9vlR6FfXpDBU1ZWA1BWxQ==}
+  '@cropper/element@2.0.0-beta.5':
     dependencies:
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/utils': 2.0.0-beta.5
 
-  /@cropper/elements@2.0.0-beta.4:
-    resolution: {integrity: sha512-cXKNFwudKcFrxn75VU9nLWNpjUnHcY0rUvtLn+2YVOLAnCTFLlu+azjOW1XZJ01FAEcC62Itb4CvDae+qgDpcQ==}
+  '@cropper/elements@2.0.0-beta.5':
     dependencies:
-      '@cropper/element': 2.0.0-beta.4
-      '@cropper/element-canvas': 2.0.0-beta.4
-      '@cropper/element-crosshair': 2.0.0-beta.4
-      '@cropper/element-grid': 2.0.0-beta.4
-      '@cropper/element-handle': 2.0.0-beta.4
-      '@cropper/element-image': 2.0.0-beta.4
-      '@cropper/element-selection': 2.0.0-beta.4
-      '@cropper/element-shade': 2.0.0-beta.4
-      '@cropper/element-viewer': 2.0.0-beta.4
-    dev: false
+      '@cropper/element': 2.0.0-beta.5
+      '@cropper/element-canvas': 2.0.0-beta.5
+      '@cropper/element-crosshair': 2.0.0-beta.5
+      '@cropper/element-grid': 2.0.0-beta.5
+      '@cropper/element-handle': 2.0.0-beta.5
+      '@cropper/element-image': 2.0.0-beta.5
+      '@cropper/element-selection': 2.0.0-beta.5
+      '@cropper/element-shade': 2.0.0-beta.5
+      '@cropper/element-viewer': 2.0.0-beta.5
 
-  /@cropper/utils@2.0.0-beta.4:
-    resolution: {integrity: sha512-mrUTA3LbEq1Y3nPTC5X6koTd2Dk8P+6xTuhp4P8X3mg5Z7d8AVK+0OU5kbB49OLAaEfvGEqbZJ84rLwgMy9RHw==}
-    dev: false
+  '@cropper/utils@2.0.0-beta.5': {}
 
-  /@cypress/request@3.0.0:
-    resolution: {integrity: sha512-GKFCqwZwMYmL3IBoNeR2MM1SnxRIGERsQOTWeQKoYBt2JLqcqiy7JXqO894FLrpjZYqGxW92MNwRH2BN56obdQ==}
-    engines: {node: '>= 6'}
+  '@cypress/request@3.0.0':
     dependencies:
       aws-sign2: 0.7.0
       aws4: 1.12.0
@@ -3543,468 +12674,259 @@ packages:
       tough-cookie: 4.1.3
       tunnel-agent: 0.6.0
       uuid: 8.3.2
-    dev: true
 
-  /@cypress/xvfb@1.2.4(supports-color@8.1.1):
-    resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
+  '@cypress/xvfb@1.2.4(supports-color@8.1.1)':
     dependencies:
       debug: 3.2.7(supports-color@8.1.1)
       lodash.once: 4.1.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@digitalbazaar/http-client@3.4.1:
-    resolution: {integrity: sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==}
-    engines: {node: '>=14.0'}
+  '@digitalbazaar/http-client@3.4.1(web-streams-polyfill@3.2.1)':
     dependencies:
       ky: 0.33.3
-      ky-universal: 0.11.0(ky@0.33.3)
+      ky-universal: 0.11.0(ky@0.33.3)(web-streams-polyfill@3.2.1)
       undici: 5.28.2
     transitivePeerDependencies:
       - web-streams-polyfill
-    dev: false
 
-  /@discordapp/twemoji@15.0.2:
-    resolution: {integrity: sha512-SrWKcv3SrGfrLQ/vfUnA+bAG73Q6Yjys01UuoY5SzUlc9iS03amQ6DxLhzVsjW/aTdgiMQdUatLidD+YPfYMCw==}
+  '@discordapp/twemoji@15.0.3':
     dependencies:
       '@twemoji/parser': 15.0.0
       fs-extra: 8.1.0
       jsonfile: 5.0.0
       universalify: 0.1.2
-    dev: false
 
-  /@discoveryjs/json-ext@0.5.7:
-    resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
-    engines: {node: '>=10.0.0'}
-    dev: true
+  '@discoveryjs/json-ext@0.5.7': {}
 
-  /@emnapi/runtime@0.45.0:
-    resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==}
-    requiresBuild: true
+  '@emnapi/runtime@1.1.1':
     dependencies:
       tslib: 2.6.2
-    dev: false
     optional: true
 
-  /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0):
-    resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
-    peerDependencies:
-      react: '>=16.8.0'
+  '@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.3.1)':
     dependencies:
-      react: 18.2.0
-    dev: true
+      react: 18.3.1
 
-  /@esbuild/aix-ppc64@0.19.11:
-    resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==}
-    engines: {node: '>=12'}
-    cpu: [ppc64]
-    os: [aix]
-    requiresBuild: true
+  '@esbuild/aix-ppc64@0.19.11':
     optional: true
 
-  /@esbuild/android-arm64@0.18.20:
-    resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: true
+  '@esbuild/aix-ppc64@0.20.2':
     optional: true
 
-  /@esbuild/android-arm64@0.19.11:
-    resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
+  '@esbuild/android-arm64@0.18.20':
     optional: true
 
-  /@esbuild/android-arm@0.18.20:
-    resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: true
+  '@esbuild/android-arm64@0.19.11':
     optional: true
 
-  /@esbuild/android-arm@0.19.11:
-    resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
+  '@esbuild/android-arm64@0.20.2':
     optional: true
 
-  /@esbuild/android-x64@0.18.20:
-    resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [android]
-    requiresBuild: true
-    dev: true
+  '@esbuild/android-arm@0.18.20':
     optional: true
 
-  /@esbuild/android-x64@0.19.11:
-    resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [android]
-    requiresBuild: true
+  '@esbuild/android-arm@0.19.11':
     optional: true
 
-  /@esbuild/darwin-arm64@0.18.20:
-    resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
+  '@esbuild/android-arm@0.20.2':
     optional: true
 
-  /@esbuild/darwin-arm64@0.19.11:
-    resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
+  '@esbuild/android-x64@0.18.20':
     optional: true
 
-  /@esbuild/darwin-x64@0.18.20:
-    resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
+  '@esbuild/android-x64@0.19.11':
     optional: true
 
-  /@esbuild/darwin-x64@0.19.11:
-    resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
+  '@esbuild/android-x64@0.20.2':
     optional: true
 
-  /@esbuild/freebsd-arm64@0.18.20:
-    resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
+  '@esbuild/darwin-arm64@0.18.20':
     optional: true
 
-  /@esbuild/freebsd-arm64@0.19.11:
-    resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [freebsd]
-    requiresBuild: true
+  '@esbuild/darwin-arm64@0.19.11':
     optional: true
 
-  /@esbuild/freebsd-x64@0.18.20:
-    resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
+  '@esbuild/darwin-arm64@0.20.2':
     optional: true
 
-  /@esbuild/freebsd-x64@0.19.11:
-    resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
+  '@esbuild/darwin-x64@0.18.20':
     optional: true
 
-  /@esbuild/linux-arm64@0.18.20:
-    resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/darwin-x64@0.19.11':
     optional: true
 
-  /@esbuild/linux-arm64@0.19.11:
-    resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/darwin-x64@0.20.2':
     optional: true
 
-  /@esbuild/linux-arm@0.18.20:
-    resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/freebsd-arm64@0.18.20':
     optional: true
 
-  /@esbuild/linux-arm@0.19.11:
-    resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/freebsd-arm64@0.19.11':
     optional: true
 
-  /@esbuild/linux-ia32@0.18.20:
-    resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/freebsd-arm64@0.20.2':
     optional: true
 
-  /@esbuild/linux-ia32@0.19.11:
-    resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/freebsd-x64@0.18.20':
     optional: true
 
-  /@esbuild/linux-loong64@0.18.20:
-    resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
-    engines: {node: '>=12'}
-    cpu: [loong64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/freebsd-x64@0.19.11':
     optional: true
 
-  /@esbuild/linux-loong64@0.19.11:
-    resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==}
-    engines: {node: '>=12'}
-    cpu: [loong64]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/freebsd-x64@0.20.2':
     optional: true
 
-  /@esbuild/linux-mips64el@0.18.20:
-    resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
-    engines: {node: '>=12'}
-    cpu: [mips64el]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-arm64@0.18.20':
     optional: true
 
-  /@esbuild/linux-mips64el@0.19.11:
-    resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==}
-    engines: {node: '>=12'}
-    cpu: [mips64el]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/linux-arm64@0.19.11':
     optional: true
 
-  /@esbuild/linux-ppc64@0.18.20:
-    resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
-    engines: {node: '>=12'}
-    cpu: [ppc64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-arm64@0.20.2':
     optional: true
 
-  /@esbuild/linux-ppc64@0.19.11:
-    resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==}
-    engines: {node: '>=12'}
-    cpu: [ppc64]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/linux-arm@0.18.20':
     optional: true
 
-  /@esbuild/linux-riscv64@0.18.20:
-    resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
-    engines: {node: '>=12'}
-    cpu: [riscv64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-arm@0.19.11':
     optional: true
 
-  /@esbuild/linux-riscv64@0.19.11:
-    resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==}
-    engines: {node: '>=12'}
-    cpu: [riscv64]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/linux-arm@0.20.2':
     optional: true
 
-  /@esbuild/linux-s390x@0.18.20:
-    resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
-    engines: {node: '>=12'}
-    cpu: [s390x]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-ia32@0.18.20':
     optional: true
 
-  /@esbuild/linux-s390x@0.19.11:
-    resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==}
-    engines: {node: '>=12'}
-    cpu: [s390x]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/linux-ia32@0.19.11':
     optional: true
 
-  /@esbuild/linux-x64@0.18.20:
-    resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-ia32@0.20.2':
     optional: true
 
-  /@esbuild/linux-x64@0.19.11:
-    resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
+  '@esbuild/linux-loong64@0.18.20':
     optional: true
 
-  /@esbuild/netbsd-x64@0.18.20:
-    resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [netbsd]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-loong64@0.19.11':
     optional: true
 
-  /@esbuild/netbsd-x64@0.19.11:
-    resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [netbsd]
-    requiresBuild: true
+  '@esbuild/linux-loong64@0.20.2':
     optional: true
 
-  /@esbuild/openbsd-x64@0.18.20:
-    resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [openbsd]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-mips64el@0.18.20':
     optional: true
 
-  /@esbuild/openbsd-x64@0.19.11:
-    resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [openbsd]
-    requiresBuild: true
+  '@esbuild/linux-mips64el@0.19.11':
     optional: true
 
-  /@esbuild/sunos-x64@0.18.20:
-    resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [sunos]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-mips64el@0.20.2':
     optional: true
 
-  /@esbuild/sunos-x64@0.19.11:
-    resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [sunos]
-    requiresBuild: true
+  '@esbuild/linux-ppc64@0.18.20':
     optional: true
 
-  /@esbuild/win32-arm64@0.18.20:
-    resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-ppc64@0.19.11':
     optional: true
 
-  /@esbuild/win32-arm64@0.19.11:
-    resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
+  '@esbuild/linux-ppc64@0.20.2':
     optional: true
 
-  /@esbuild/win32-ia32@0.18.20:
-    resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-riscv64@0.18.20':
     optional: true
 
-  /@esbuild/win32-ia32@0.19.11:
-    resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
+  '@esbuild/linux-riscv64@0.19.11':
     optional: true
 
-  /@esbuild/win32-x64@0.18.20:
-    resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
+  '@esbuild/linux-riscv64@0.20.2':
     optional: true
 
-  /@esbuild/win32-x64@0.19.11:
-    resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
+  '@esbuild/linux-s390x@0.18.20':
     optional: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0):
-    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+  '@esbuild/linux-s390x@0.19.11':
+    optional: true
+
+  '@esbuild/linux-s390x@0.20.2':
+    optional: true
+
+  '@esbuild/linux-x64@0.18.20':
+    optional: true
+
+  '@esbuild/linux-x64@0.19.11':
+    optional: true
+
+  '@esbuild/linux-x64@0.20.2':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.18.20':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.19.11':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.20.2':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.18.20':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.19.11':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.20.2':
+    optional: true
+
+  '@esbuild/sunos-x64@0.18.20':
+    optional: true
+
+  '@esbuild/sunos-x64@0.19.11':
+    optional: true
+
+  '@esbuild/sunos-x64@0.20.2':
+    optional: true
+
+  '@esbuild/win32-arm64@0.18.20':
+    optional: true
+
+  '@esbuild/win32-arm64@0.19.11':
+    optional: true
+
+  '@esbuild/win32-arm64@0.20.2':
+    optional: true
+
+  '@esbuild/win32-ia32@0.18.20':
+    optional: true
+
+  '@esbuild/win32-ia32@0.19.11':
+    optional: true
+
+  '@esbuild/win32-ia32@0.20.2':
+    optional: true
+
+  '@esbuild/win32-x64@0.18.20':
+    optional: true
+
+  '@esbuild/win32-x64@0.19.11':
+    optional: true
+
+  '@esbuild/win32-x64@0.20.2':
+    optional: true
+
+  '@eslint-community/eslint-utils@4.4.0(eslint@8.53.0)':
     dependencies:
       eslint: 8.53.0
       eslint-visitor-keys: 3.4.3
-    dev: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0):
-    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+  '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
     dependencies:
       eslint: 8.57.0
       eslint-visitor-keys: 3.4.3
-    dev: true
 
-  /@eslint-community/regexpp@4.6.2:
-    resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==}
-    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
-    dev: true
+  '@eslint-community/regexpp@4.10.0': {}
 
-  /@eslint/eslintrc@2.1.4:
-    resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  '@eslint-community/regexpp@4.6.2': {}
+
+  '@eslint/eslintrc@2.1.4':
     dependencies:
       ajv: 6.12.6
       debug: 4.3.4(supports-color@8.1.1)
@@ -4017,115 +12939,73 @@ packages:
       strip-json-comments: 3.1.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@eslint/js@8.53.0:
-    resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dev: true
+  '@eslint/js@8.53.0': {}
 
-  /@eslint/js@8.57.0:
-    resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dev: true
+  '@eslint/js@8.57.0': {}
 
-  /@fal-works/esbuild-plugin-global-externals@2.1.2:
-    resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==}
-    dev: true
+  '@fal-works/esbuild-plugin-global-externals@2.1.2': {}
 
-  /@fastify/accept-negotiator@1.0.0:
-    resolution: {integrity: sha512-4R/N2KfYeld7A5LGkai+iUFMahXcxxYbDp+XS2B1yuL3cdmZLJ9TlCnNzT3q5xFTqsYm0GPpinLUwfSwjcVjyA==}
-    engines: {node: '>=14'}
-    dev: false
+  '@fastify/accept-negotiator@1.0.0': {}
 
-  /@fastify/accepts@4.3.0:
-    resolution: {integrity: sha512-QK4FoqXdwwPmaPOLL6NrxsyaXVvdviYVoS6ltHyOLdFlUyREIaMykHQIp+x0aJz9hB3B3n/Ht6QRdvBeGkptGQ==}
+  '@fastify/accepts@4.3.0':
     dependencies:
       accepts: 1.3.8
       fastify-plugin: 4.5.0
-    dev: false
 
-  /@fastify/ajv-compiler@3.5.0:
-    resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==}
+  '@fastify/ajv-compiler@3.5.0':
     dependencies:
-      ajv: 8.12.0
-      ajv-formats: 2.1.1(ajv@8.12.0)
+      ajv: 8.13.0
+      ajv-formats: 2.1.1(ajv@8.13.0)
       fast-uri: 2.2.0
-    dev: false
-
-  /@fastify/busboy@1.1.0:
-    resolution: {integrity: sha512-Fv854f94v0CzIDllbY3i/0NJPNBRNLDawf3BTYVGCe9VrIIs3Wi7AFx24F9NzCxdf0wyx/x0Q9kEVnvDOPnlxA==}
-    engines: {node: '>=10.17.0'}
-    dependencies:
-      text-decoding: 1.0.0
-    dev: false
 
-  /@fastify/busboy@2.1.0:
-    resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==}
-    engines: {node: '>=14'}
+  '@fastify/busboy@2.1.0': {}
 
-  /@fastify/cookie@9.3.1:
-    resolution: {integrity: sha512-h1NAEhB266+ZbZ0e9qUE6NnNR07i7DnNXWG9VbbZ8uC6O/hxHpl+Zoe5sw1yfdZ2U6XhToUGDnzQtWJdCaPwfg==}
+  '@fastify/cookie@9.3.1':
     dependencies:
       cookie-signature: 1.2.1
       fastify-plugin: 4.5.0
-    dev: false
 
-  /@fastify/cors@8.5.0:
-    resolution: {integrity: sha512-/oZ1QSb02XjP0IK1U0IXktEsw/dUBTxJOW7IpIeO8c/tNalw/KjoNSJv1Sf6eqoBPO+TDGkifq6ynFK3v68HFQ==}
+  '@fastify/cors@9.0.1':
     dependencies:
       fastify-plugin: 4.5.0
       mnemonist: 0.39.6
-    dev: false
 
-  /@fastify/deepmerge@1.3.0:
-    resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==}
-    dev: false
+  '@fastify/deepmerge@1.3.0': {}
 
-  /@fastify/error@3.4.0:
-    resolution: {integrity: sha512-e/mafFwbK3MNqxUcFBLgHhgxsF8UT1m8aj0dAlqEa2nJEgPsRtpHTZ3ObgrgkZ2M1eJHPTwgyUl/tXkvabsZdQ==}
-    dev: false
+  '@fastify/error@3.4.0': {}
 
-  /@fastify/express@2.3.0:
-    resolution: {integrity: sha512-jvvjlPPCfJsSHfF6tQDyARJ3+c3xXiqcxVZu6bi3xMWCWB3fl07vrjFDeaqnwqKhLZ9+m6cog5dw7gIMKEsTnQ==}
+  '@fastify/express@3.0.0':
     dependencies:
-      express: 4.18.2
+      express: 4.19.2
       fastify-plugin: 4.5.0
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /@fastify/fast-json-stringify-compiler@4.3.0:
-    resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==}
+  '@fastify/fast-json-stringify-compiler@4.3.0':
     dependencies:
       fast-json-stringify: 5.8.0
-    dev: false
 
-  /@fastify/http-proxy@9.3.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
-    resolution: {integrity: sha512-fQkdgwco8q7eI2PQA8lH++y3Q+hNlIByBYsphl+r4FKRbmrU7ey4WOA/CA9tBhe4oEojGpa3eTU4jXvqf2DBuQ==}
+  '@fastify/http-proxy@9.5.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)':
     dependencies:
       '@fastify/reply-from': 9.0.1
       fast-querystring: 1.1.2
       fastify-plugin: 4.5.0
-      ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - bufferutil
       - utf-8-validate
-    dev: false
 
-  /@fastify/multipart@8.1.0:
-    resolution: {integrity: sha512-sRX9X4ZhAqRbe2kDvXY2NK7i6Wf1Rm2g/CjpGYYM7+Np8E6uWQXcj761j08qPfPO8PJXM+vJ7yrKbK1GPB+OeQ==}
+  '@fastify/multipart@8.2.0':
     dependencies:
-      '@fastify/busboy': 1.1.0
+      '@fastify/busboy': 2.1.0
       '@fastify/deepmerge': 1.3.0
       '@fastify/error': 3.4.0
       fastify-plugin: 4.5.0
       secure-json-parse: 2.7.0
       stream-wormhole: 1.1.0
-    dev: false
 
-  /@fastify/reply-from@9.0.1:
-    resolution: {integrity: sha512-q9vFNUiXZTY1x8omDPe59os2MYq+3y7KgO/kZoXpZlnud+45Nd8Ot/svEvrUATzjkizIggfS4K8LR9zXDyZZKg==}
+  '@fastify/reply-from@9.0.1':
     dependencies:
       '@fastify/error': 3.4.0
       end-of-stream: 1.4.4
@@ -4134,20 +13014,16 @@ packages:
       pump: 3.0.0
       tiny-lru: 10.0.1
       undici: 5.28.2
-    dev: false
 
-  /@fastify/send@2.0.1:
-    resolution: {integrity: sha512-8jdouu0o5d0FMq1+zCKeKXc1tmOQ5tTGYdQP3MpyF9+WWrZT1KCBdh6hvoEYxOm3oJG/akdE9BpehLiJgYRvGw==}
+  '@fastify/send@2.0.1':
     dependencies:
       '@lukeed/ms': 2.0.1
       escape-html: 1.0.3
       fast-decode-uri-component: 1.0.1
       http-errors: 2.0.0
       mime: 3.0.0
-    dev: false
 
-  /@fastify/static@6.12.0:
-    resolution: {integrity: sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==}
+  '@fastify/static@6.12.0':
     dependencies:
       '@fastify/accept-negotiator': 1.0.0
       '@fastify/send': 2.0.1
@@ -4155,353 +13031,233 @@ packages:
       fastify-plugin: 4.5.0
       glob: 8.1.0
       p-limit: 3.1.0
-    dev: false
 
-  /@fastify/view@8.2.0:
-    resolution: {integrity: sha512-hBSiBofCnJNlPHEMZWpO1SL84eqOaqujJ1hR3jntFyZZCkweH5jMs12DKYyGesjVll7SJFRRxPUBB8kmUmneRQ==}
+  '@fastify/static@7.0.3':
+    dependencies:
+      '@fastify/accept-negotiator': 1.0.0
+      '@fastify/send': 2.0.1
+      content-disposition: 0.5.4
+      fastify-plugin: 4.5.0
+      fastq: 1.17.1
+      glob: 10.3.12
+
+  '@fastify/view@8.2.0':
     dependencies:
       fastify-plugin: 4.5.0
       hashlru: 2.3.0
-    dev: false
 
-  /@github/webauthn-json@2.1.1:
-    resolution: {integrity: sha512-XrftRn4z75SnaJOmZQbt7Mk+IIjqVHw+glDGOxuHwXkZBZh/MBoRS7MHjSZMDaLhT4RjN2VqiEU7EOYleuJWSQ==}
-    hasBin: true
-    dev: false
+  '@fastify/view@9.1.0':
+    dependencies:
+      fastify-plugin: 4.5.0
+      toad-cache: 3.7.0
 
-  /@hapi/boom@10.0.1:
-    resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==}
+  '@github/webauthn-json@2.1.1': {}
+
+  '@hapi/boom@10.0.1':
     dependencies:
       '@hapi/hoek': 11.0.2
-    dev: true
 
-  /@hapi/bourne@3.0.0:
-    resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
-    dev: true
+  '@hapi/bourne@3.0.0': {}
 
-  /@hapi/hoek@10.0.1:
-    resolution: {integrity: sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw==}
-    dev: true
+  '@hapi/hoek@10.0.1': {}
 
-  /@hapi/hoek@11.0.2:
-    resolution: {integrity: sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==}
-    dev: true
+  '@hapi/hoek@11.0.2': {}
 
-  /@hapi/hoek@9.3.0:
-    resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
-    dev: true
+  '@hapi/hoek@9.3.0': {}
 
-  /@hapi/topo@5.1.0:
-    resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
+  '@hapi/topo@5.1.0':
     dependencies:
       '@hapi/hoek': 9.3.0
-    dev: true
 
-  /@hapi/wreck@18.0.1:
-    resolution: {integrity: sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==}
+  '@hapi/wreck@18.0.1':
     dependencies:
       '@hapi/boom': 10.0.1
       '@hapi/bourne': 3.0.0
       '@hapi/hoek': 11.0.2
-    dev: true
 
-  /@hexagon/base64@1.1.27:
-    resolution: {integrity: sha512-PdUmzpvcUM3Rh39kvz9RdbPVYhMjBjdV7Suw7ZduP7urRLsZR8l5tzgSWKm7TExwBYDFwTnYrZbnE0rQ3N5NLQ==}
-    dev: false
+  '@hexagon/base64@1.1.27': {}
 
-  /@humanwhocodes/config-array@0.11.13:
-    resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
-    engines: {node: '>=10.10.0'}
+  '@humanwhocodes/config-array@0.11.13':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.1
       debug: 4.3.4(supports-color@8.1.1)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@humanwhocodes/config-array@0.11.14:
-    resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
-    engines: {node: '>=10.10.0'}
+  '@humanwhocodes/config-array@0.11.14':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.2
       debug: 4.3.4(supports-color@8.1.1)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@humanwhocodes/module-importer@1.0.1:
-    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
-    engines: {node: '>=12.22'}
-    dev: true
+  '@humanwhocodes/module-importer@1.0.1': {}
 
-  /@humanwhocodes/momoa@2.0.4:
-    resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==}
-    engines: {node: '>=10.10.0'}
-    dev: true
+  '@humanwhocodes/momoa@2.0.4': {}
 
-  /@humanwhocodes/object-schema@2.0.1:
-    resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
-    dev: true
+  '@humanwhocodes/object-schema@2.0.1': {}
 
-  /@humanwhocodes/object-schema@2.0.2:
-    resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
-    dev: true
+  '@humanwhocodes/object-schema@2.0.2': {}
 
-  /@img/sharp-darwin-arm64@0.33.2:
-    resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==}
-    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
+  '@img/sharp-darwin-arm64@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-darwin-arm64': 1.0.1
-    dev: false
+      '@img/sharp-libvips-darwin-arm64': 1.0.2
     optional: true
 
-  /@img/sharp-darwin-x64@0.33.2:
-    resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==}
-    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
+  '@img/sharp-darwin-x64@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-darwin-x64': 1.0.1
-    dev: false
+      '@img/sharp-libvips-darwin-x64': 1.0.2
     optional: true
 
-  /@img/sharp-libvips-darwin-arm64@1.0.1:
-    resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==}
-    engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-darwin-arm64@1.0.2':
     optional: true
 
-  /@img/sharp-libvips-darwin-x64@1.0.1:
-    resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==}
-    engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-darwin-x64@1.0.2':
     optional: true
 
-  /@img/sharp-libvips-linux-arm64@1.0.1:
-    resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==}
-    engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-linux-arm64@1.0.2':
     optional: true
 
-  /@img/sharp-libvips-linux-arm@1.0.1:
-    resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==}
-    engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-linux-arm@1.0.2':
     optional: true
 
-  /@img/sharp-libvips-linux-s390x@1.0.1:
-    resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==}
-    engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [s390x]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-linux-s390x@1.0.2':
     optional: true
 
-  /@img/sharp-libvips-linux-x64@1.0.1:
-    resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==}
-    engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-linux-x64@1.0.2':
     optional: true
 
-  /@img/sharp-libvips-linuxmusl-arm64@1.0.1:
-    resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==}
-    engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-linuxmusl-arm64@1.0.2':
     optional: true
 
-  /@img/sharp-libvips-linuxmusl-x64@1.0.1:
-    resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==}
-    engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-libvips-linuxmusl-x64@1.0.2':
     optional: true
 
-  /@img/sharp-linux-arm64@0.33.2:
-    resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==}
-    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
+  '@img/sharp-linux-arm64@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-linux-arm64': 1.0.1
-    dev: false
+      '@img/sharp-libvips-linux-arm64': 1.0.2
     optional: true
 
-  /@img/sharp-linux-arm@0.33.2:
-    resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==}
-    engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
+  '@img/sharp-linux-arm@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-linux-arm': 1.0.1
-    dev: false
+      '@img/sharp-libvips-linux-arm': 1.0.2
     optional: true
 
-  /@img/sharp-linux-s390x@0.33.2:
-    resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==}
-    engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [s390x]
-    os: [linux]
-    requiresBuild: true
+  '@img/sharp-linux-s390x@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-linux-s390x': 1.0.1
-    dev: false
+      '@img/sharp-libvips-linux-s390x': 1.0.2
     optional: true
 
-  /@img/sharp-linux-x64@0.33.2:
-    resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==}
-    engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
+  '@img/sharp-linux-x64@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-linux-x64': 1.0.1
-    dev: false
+      '@img/sharp-libvips-linux-x64': 1.0.2
     optional: true
 
-  /@img/sharp-linuxmusl-arm64@0.33.2:
-    resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==}
-    engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
+  '@img/sharp-linuxmusl-arm64@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-linuxmusl-arm64': 1.0.1
-    dev: false
+      '@img/sharp-libvips-linuxmusl-arm64': 1.0.2
     optional: true
 
-  /@img/sharp-linuxmusl-x64@0.33.2:
-    resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==}
-    engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
+  '@img/sharp-linuxmusl-x64@0.33.3':
     optionalDependencies:
-      '@img/sharp-libvips-linuxmusl-x64': 1.0.1
-    dev: false
+      '@img/sharp-libvips-linuxmusl-x64': 1.0.2
     optional: true
 
-  /@img/sharp-wasm32@0.33.2:
-    resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==}
-    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [wasm32]
-    requiresBuild: true
+  '@img/sharp-wasm32@0.33.3':
     dependencies:
-      '@emnapi/runtime': 0.45.0
-    dev: false
+      '@emnapi/runtime': 1.1.1
     optional: true
 
-  /@img/sharp-win32-ia32@0.33.2:
-    resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==}
-    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-win32-ia32@0.33.3':
     optional: true
 
-  /@img/sharp-win32-x64@0.33.2:
-    resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==}
-    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+  '@img/sharp-win32-x64@0.33.3':
     optional: true
 
-  /@ioredis/commands@1.2.0:
-    resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
-    dev: false
+  '@inquirer/confirm@3.1.6':
+    dependencies:
+      '@inquirer/core': 8.1.0
+      '@inquirer/type': 1.3.1
 
-  /@isaacs/cliui@8.0.2:
-    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
-    engines: {node: '>=12'}
+  '@inquirer/core@8.1.0':
+    dependencies:
+      '@inquirer/figures': 1.0.1
+      '@inquirer/type': 1.3.1
+      '@types/mute-stream': 0.0.4
+      '@types/node': 20.12.7
+      '@types/wrap-ansi': 3.0.0
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      cli-spinners: 2.9.2
+      cli-width: 4.1.0
+      mute-stream: 1.0.0
+      signal-exit: 4.1.0
+      strip-ansi: 6.0.1
+      wrap-ansi: 6.2.0
+
+  '@inquirer/figures@1.0.1': {}
+
+  '@inquirer/type@1.3.1': {}
+
+  '@intlify/core-base@9.13.1':
+    dependencies:
+      '@intlify/message-compiler': 9.13.1
+      '@intlify/shared': 9.13.1
+
+  '@intlify/message-compiler@9.13.1':
+    dependencies:
+      '@intlify/shared': 9.13.1
+      source-map-js: 1.0.2
+
+  '@intlify/shared@9.13.1': {}
+
+  '@ioredis/commands@1.2.0': {}
+
+  '@isaacs/cliui@8.0.2':
     dependencies:
       string-width: 5.1.2
-      string-width-cjs: /string-width@4.2.3
+      string-width-cjs: string-width@4.2.3
       strip-ansi: 7.1.0
-      strip-ansi-cjs: /strip-ansi@6.0.1
+      strip-ansi-cjs: strip-ansi@6.0.1
       wrap-ansi: 8.1.0
-      wrap-ansi-cjs: /wrap-ansi@7.0.0
+      wrap-ansi-cjs: wrap-ansi@7.0.0
 
-  /@istanbuljs/load-nyc-config@1.1.0:
-    resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
-    engines: {node: '>=8'}
+  '@istanbuljs/load-nyc-config@1.1.0':
     dependencies:
       camelcase: 5.3.1
       find-up: 4.1.0
       get-package-type: 0.1.0
       js-yaml: 3.14.1
       resolve-from: 5.0.0
-    dev: true
 
-  /@istanbuljs/schema@0.1.3:
-    resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
-    engines: {node: '>=8'}
-    dev: true
+  '@istanbuljs/schema@0.1.3': {}
 
-  /@jest/console@29.7.0:
-    resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/console@29.7.0':
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
       slash: 3.0.0
-    dev: true
 
-  /@jest/core@29.7.0:
-    resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    peerDependencies:
-      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
-    peerDependenciesMeta:
-      node-notifier:
-        optional: true
+  '@jest/core@29.7.0':
     dependencies:
       '@jest/console': 29.7.0
       '@jest/reporters': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.7.1
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.11.22)
+      jest-config: 29.7.0(@types/node@20.12.7)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -4521,57 +13277,39 @@ packages:
       - babel-plugin-macros
       - supports-color
       - ts-node
-    dev: true
 
-  /@jest/create-cache-key-function@29.7.0:
-    resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/create-cache-key-function@29.7.0':
     dependencies:
       '@jest/types': 29.6.3
-    dev: true
 
-  /@jest/environment@29.7.0:
-    resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/environment@29.7.0':
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       jest-mock: 29.7.0
-    dev: true
 
-  /@jest/expect-utils@29.7.0:
-    resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/expect-utils@29.7.0':
     dependencies:
       jest-get-type: 29.6.3
-    dev: true
 
-  /@jest/expect@29.7.0:
-    resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/expect@29.7.0':
     dependencies:
       expect: 29.7.0
       jest-snapshot: 29.7.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@jest/fake-timers@29.7.0:
-    resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/fake-timers@29.7.0':
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
-    dev: true
 
-  /@jest/globals@29.7.0:
-    resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/globals@29.7.0':
     dependencies:
       '@jest/environment': 29.7.0
       '@jest/expect': 29.7.0
@@ -4579,16 +13317,8 @@ packages:
       jest-mock: 29.7.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@jest/reporters@29.7.0:
-    resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    peerDependencies:
-      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
-    peerDependenciesMeta:
-      node-notifier:
-        optional: true
+  '@jest/reporters@29.7.0':
     dependencies:
       '@bcoe/v8-coverage': 0.2.3
       '@jest/console': 29.7.0
@@ -4596,7 +13326,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -4616,47 +13346,32 @@ packages:
       v8-to-istanbul: 9.2.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@jest/schemas@29.6.3:
-    resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/schemas@29.6.3':
     dependencies:
       '@sinclair/typebox': 0.27.8
-    dev: true
 
-  /@jest/source-map@29.6.3:
-    resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/source-map@29.6.3':
     dependencies:
       '@jridgewell/trace-mapping': 0.3.18
       callsites: 3.1.0
       graceful-fs: 4.2.11
-    dev: true
 
-  /@jest/test-result@29.7.0:
-    resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/test-result@29.7.0':
     dependencies:
       '@jest/console': 29.7.0
       '@jest/types': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       collect-v8-coverage: 1.0.1
-    dev: true
 
-  /@jest/test-sequencer@29.7.0:
-    resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/test-sequencer@29.7.0':
     dependencies:
       '@jest/test-result': 29.7.0
       graceful-fs: 4.2.11
       jest-haste-map: 29.7.0
       slash: 3.0.0
-    dev: true
 
-  /@jest/transform@29.7.0:
-    resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/transform@29.7.0':
     dependencies:
       '@babel/core': 7.23.5
       '@jest/types': 29.6.3
@@ -4675,231 +13390,153 @@ packages:
       write-file-atomic: 4.0.2
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@jest/types@29.6.3:
-    resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  '@jest/types@29.6.3':
     dependencies:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       '@types/yargs': 17.0.19
       chalk: 4.1.2
-    dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.1.4):
-    resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
-    peerDependencies:
-      typescript: '>= 4.3.x'
-      vite: ^3.0.0 || ^4.0.0 || ^5.0.0
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       glob: 7.2.3
       glob-promise: 4.2.2(glob@7.2.3)
       magic-string: 0.27.0
-      react-docgen-typescript: 2.2.2(typescript@5.3.3)
-      typescript: 5.3.3
-      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
-    dev: true
+      react-docgen-typescript: 2.2.2(typescript@5.4.5)
+      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+    optionalDependencies:
+      typescript: 5.4.5
 
-  /@jridgewell/gen-mapping@0.3.2:
-    resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
-    engines: {node: '>=6.0.0'}
+  '@jridgewell/gen-mapping@0.3.2':
     dependencies:
       '@jridgewell/set-array': 1.1.2
       '@jridgewell/sourcemap-codec': 1.4.15
       '@jridgewell/trace-mapping': 0.3.18
 
-  /@jridgewell/resolve-uri@3.1.0:
-    resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
-    engines: {node: '>=6.0.0'}
+  '@jridgewell/resolve-uri@3.1.0': {}
 
-  /@jridgewell/set-array@1.1.2:
-    resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
-    engines: {node: '>=6.0.0'}
+  '@jridgewell/set-array@1.1.2': {}
 
-  /@jridgewell/source-map@0.3.5:
-    resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
+  '@jridgewell/source-map@0.3.5':
     dependencies:
       '@jridgewell/gen-mapping': 0.3.2
       '@jridgewell/trace-mapping': 0.3.18
 
-  /@jridgewell/sourcemap-codec@1.4.14:
-    resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
+  '@jridgewell/sourcemap-codec@1.4.14': {}
 
-  /@jridgewell/sourcemap-codec@1.4.15:
-    resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
+  '@jridgewell/sourcemap-codec@1.4.15': {}
 
-  /@jridgewell/trace-mapping@0.3.18:
-    resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
+  '@jridgewell/trace-mapping@0.3.18':
     dependencies:
       '@jridgewell/resolve-uri': 3.1.0
       '@jridgewell/sourcemap-codec': 1.4.14
 
-  /@jsdevtools/ono@7.1.3:
-    resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
-    dev: true
+  '@jsdevtools/ono@7.1.3': {}
 
-  /@kurkle/color@0.3.2:
-    resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
-    dev: false
+  '@kurkle/color@0.3.2': {}
 
-  /@levischuck/tiny-cbor@0.2.2:
-    resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==}
-    dev: false
+  '@levischuck/tiny-cbor@0.2.2': {}
 
-  /@lukeed/csprng@1.0.1:
-    resolution: {integrity: sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==}
-    engines: {node: '>=8'}
+  '@lukeed/csprng@1.0.1': {}
 
-  /@lukeed/ms@2.0.1:
-    resolution: {integrity: sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==}
-    engines: {node: '>=8'}
-    dev: false
+  '@lukeed/ms@2.0.1': {}
 
-  /@mapbox/node-pre-gyp@1.0.9:
-    resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
-    hasBin: true
-    requiresBuild: true
+  '@mapbox/node-pre-gyp@1.0.9(encoding@0.1.13)':
     dependencies:
       detect-libc: 2.0.2
       https-proxy-agent: 5.0.1
       make-dir: 3.1.0
-      node-fetch: 2.7.0
+      node-fetch: 2.7.0(encoding@0.1.13)
       nopt: 5.0.0
       npmlog: 5.0.1
       rimraf: 3.0.2
       semver: 7.5.4
-      tar: 6.2.0
+      tar: 6.2.1
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: false
     optional: true
 
-  /@mcaptcha/core-glue@0.1.0-alpha-5:
-    resolution: {integrity: sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA==}
-    dev: false
+  '@mcaptcha/core-glue@0.1.0-alpha-5': {}
 
-  /@mcaptcha/vanilla-glue@0.1.0-alpha-3:
-    resolution: {integrity: sha512-GT6TJBgmViGXcXiT5VOr+h/6iOnThSlZuCoOWncubyTZU9R3cgU5vWPkF7G6Ob6ee2CBe3yqBxxk24CFVGTVXw==}
+  '@mcaptcha/vanilla-glue@0.1.0-alpha-3':
     dependencies:
       '@mcaptcha/core-glue': 0.1.0-alpha-5
-    dev: false
 
-  /@mdx-js/react@3.0.1(@types/react@18.0.28)(react@18.2.0):
-    resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==}
-    peerDependencies:
-      '@types/react': '>=16'
-      react: '>=16'
+  '@mdx-js/react@3.0.1(@types/react@18.0.28)(react@18.3.1)':
     dependencies:
       '@types/mdx': 2.0.3
       '@types/react': 18.0.28
-      react: 18.2.0
-    dev: true
+      react: 18.3.1
 
-  /@microsoft/api-extractor-model@7.28.4(@types/node@20.11.22):
-    resolution: {integrity: sha512-vucgyPmgHrJ/D4/xQywAmjTmSfxAx2/aDmD6TkIoLu51FdsAfuWRbijWA48AePy60OO+l+mmy9p2P/CEeBZqig==}
+  '@microsoft/api-extractor-model@7.28.14(@types/node@20.12.7)':
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22)
+      '@rushstack/node-core-library': 4.1.0(@types/node@20.12.7)
     transitivePeerDependencies:
       - '@types/node'
-    dev: true
 
-  /@microsoft/api-extractor@7.39.1(@types/node@20.11.22):
-    resolution: {integrity: sha512-V0HtCufWa8hZZvSmlEzQZfINcJkHAU/bmpyJQj6w+zpI87EkR8DuBOW6RWrO9c7mUYFZoDaNgUTyKo83ytv+QQ==}
-    hasBin: true
+  '@microsoft/api-extractor@7.43.1(@types/node@20.12.7)':
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.4(@types/node@20.11.22)
+      '@microsoft/api-extractor-model': 7.28.14(@types/node@20.12.7)
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.63.0(@types/node@20.11.22)
-      '@rushstack/rig-package': 0.5.1
-      '@rushstack/ts-command-line': 4.17.1
-      colors: 1.2.5
+      '@rushstack/node-core-library': 4.1.0(@types/node@20.12.7)
+      '@rushstack/rig-package': 0.5.2
+      '@rushstack/terminal': 0.10.1(@types/node@20.12.7)
+      '@rushstack/ts-command-line': 4.19.2(@types/node@20.12.7)
       lodash: 4.17.21
+      minimatch: 3.0.8
       resolve: 1.22.8
       semver: 7.5.4
       source-map: 0.6.1
-      typescript: 5.3.3
+      typescript: 5.4.2
     transitivePeerDependencies:
       - '@types/node'
-    dev: true
 
-  /@microsoft/tsdoc-config@0.16.2:
-    resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==}
+  '@microsoft/tsdoc-config@0.16.2':
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       ajv: 6.12.6
       jju: 1.4.0
       resolve: 1.19.0
-    dev: true
 
-  /@microsoft/tsdoc@0.14.2:
-    resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
-    dev: true
+  '@microsoft/tsdoc@0.14.2': {}
 
-  /@misskey-dev/browser-image-resizer@2024.1.0:
-    resolution: {integrity: sha512-4EnO0zLW5NDtng3Gaz5MuT761uiuoOuplwX18wBqgj8w56LTU5BjLn/vbHwDIIe0j2gwqDYhMb7bDjmr1/Fomg==}
-    dev: false
+  '@misskey-dev/browser-image-resizer@2024.1.0': {}
 
-  /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.11.0)(@typescript-eslint/parser@6.11.0)(eslint-plugin-import@2.29.1)(eslint@8.53.0):
-    resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==}
-    peerDependencies:
-      '@typescript-eslint/eslint-plugin': '>= 6'
-      '@typescript-eslint/parser': '>= 6'
-      eslint: '>= 3'
-      eslint-plugin-import: '>= 2'
+  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3))(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0))(eslint@8.53.0)':
     dependencies:
-      '@typescript-eslint/eslint-plugin': 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.3)
+      '@typescript-eslint/eslint-plugin': 6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       eslint: 8.53.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)
-    dev: true
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)
 
-  /@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.1.0)(@typescript-eslint/parser@7.1.0)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
-    resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==}
-    peerDependencies:
-      '@typescript-eslint/eslint-plugin': '>= 6'
-      '@typescript-eslint/parser': '>= 6'
-      eslint: '>= 3'
-      eslint-plugin-import: '>= 2'
+  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0))(eslint@8.57.0)':
     dependencies:
-      '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       eslint: 8.57.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)
-    dev: true
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)
 
-  /@misskey-dev/sharp-read-bmp@1.2.0:
-    resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
+  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)':
     dependencies:
-      decode-bmp: 0.2.1
-      decode-ico: 0.4.1
-      sharp: 0.33.2
-    dev: false
+      '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      eslint: 8.57.0
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
 
-  /@misskey-dev/summaly@5.0.3:
-    resolution: {integrity: sha512-jVkuLEDrq2FaeHL8VY51LTqB6j0Jv5L7s0nmKGKMnE0jPBpSj6flswnZgntGmz5mbdCj47utEqu8FY43kH7PVg==}
+  '@misskey-dev/sharp-read-bmp@1.2.0':
     dependencies:
-      cheerio: 1.0.0-rc.12
-      escape-regexp: 0.0.1
-      got: 12.6.1
-      html-entities: 2.3.2
-      iconv-lite: 0.6.3
-      jschardet: 3.0.0
-      private-ip: 2.3.3
-      trace-redirect: 1.0.6
-    dev: true
+      decode-bmp: 0.2.1
+      decode-ico: 0.4.1
+      sharp: 0.33.3
 
-  /@misskey-dev/summaly@5.1.0:
-    resolution: {integrity: sha512-WAUrgX3/z4h4aI8Y/WVwmJcJ6Fa1Zf2LJCSS651t9MHoWVGABLsQ2KCXRGmlpk4i+cMDNIwweObUroosE7j8rg==}
+  '@misskey-dev/summaly@5.1.0':
     dependencies:
       cheerio: 1.0.0-rc.12
       escape-regexp: 0.0.1
@@ -4909,11 +13546,8 @@ packages:
       jschardet: 3.0.0
       private-ip: 2.3.3
       trace-redirect: 1.0.6
-    dev: false
 
-  /@mole-inc/bin-wrapper@8.0.1:
-    resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  '@mole-inc/bin-wrapper@8.0.1':
     dependencies:
       bin-check: 4.1.0
       bin-version-check: 5.0.0
@@ -4923,64 +13557,28 @@ packages:
       filenamify: 5.1.1
       got: 11.8.5
       os-filter-obj: 2.0.0
-    dev: false
 
-  /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2:
-    resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2':
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2:
-    resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2':
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2:
-    resolution: {integrity: sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2':
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2:
-    resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2':
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2:
-    resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2':
     optional: true
-
-  /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2:
-    resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+
+  '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2':
     optional: true
 
-  /@mswjs/cookies@1.1.0:
-    resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==}
-    engines: {node: '>=18'}
-    dev: true
+  '@mswjs/cookies@1.1.0': {}
 
-  /@mswjs/interceptors@0.25.16:
-    resolution: {integrity: sha512-8QC8JyKztvoGAdPgyZy49c9vSHHAZjHagwl4RY9E8carULk8ym3iTaiawrT1YoLF/qb449h48f71XDPgkUSOUg==}
-    engines: {node: '>=18'}
+  '@mswjs/interceptors@0.26.15':
     dependencies:
       '@open-draft/deferred-promise': 2.2.0
       '@open-draft/logger': 0.3.0
@@ -4988,122 +13586,109 @@ packages:
       is-node-process: 1.2.0
       outvariant: 1.4.2
       strict-event-emitter: 0.5.1
-    dev: true
 
-  /@ndelangen/get-tarball@3.0.7:
-    resolution: {integrity: sha512-NqGfTZIZpRFef1GoVaShSSRwDC3vde3ThtTeqFdcYd6ipKqnfEVhjK2hUeHjCQUcptyZr2TONqcloFXM+5QBrQ==}
+  '@napi-rs/canvas-android-arm64@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-darwin-arm64@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-darwin-x64@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-linux-arm-gnueabihf@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-linux-arm64-gnu@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-linux-arm64-musl@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-linux-x64-gnu@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-linux-x64-musl@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas-win32-x64-msvc@0.1.52':
+    optional: true
+
+  '@napi-rs/canvas@0.1.52':
+    optionalDependencies:
+      '@napi-rs/canvas-android-arm64': 0.1.52
+      '@napi-rs/canvas-darwin-arm64': 0.1.52
+      '@napi-rs/canvas-darwin-x64': 0.1.52
+      '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.52
+      '@napi-rs/canvas-linux-arm64-gnu': 0.1.52
+      '@napi-rs/canvas-linux-arm64-musl': 0.1.52
+      '@napi-rs/canvas-linux-x64-gnu': 0.1.52
+      '@napi-rs/canvas-linux-x64-musl': 0.1.52
+      '@napi-rs/canvas-win32-x64-msvc': 0.1.52
+
+  '@ndelangen/get-tarball@3.0.7':
     dependencies:
       gunzip-maybe: 1.4.2
       pump: 3.0.0
       tar-fs: 2.1.1
-    dev: true
 
-  /@nestjs/common@10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1):
-    resolution: {integrity: sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==}
-    peerDependencies:
-      class-transformer: '*'
-      class-validator: '*'
-      reflect-metadata: ^0.1.12 || ^0.2.0
-      rxjs: ^7.1.0
-    peerDependenciesMeta:
-      class-transformer:
-        optional: true
-      class-validator:
-        optional: true
+  '@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)':
     dependencies:
       iterare: 1.2.1
-      reflect-metadata: 0.2.1
+      reflect-metadata: 0.2.2
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
 
-  /@nestjs/core@10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1):
-    resolution: {integrity: sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==}
-    requiresBuild: true
-    peerDependencies:
-      '@nestjs/common': ^10.0.0
-      '@nestjs/microservices': ^10.0.0
-      '@nestjs/platform-express': ^10.0.0
-      '@nestjs/websockets': ^10.0.0
-      reflect-metadata: ^0.1.12 || ^0.2.0
-      rxjs: ^7.1.0
-    peerDependenciesMeta:
-      '@nestjs/microservices':
-        optional: true
-      '@nestjs/platform-express':
-        optional: true
-      '@nestjs/websockets':
-        optional: true
+  '@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)':
     dependencies:
-      '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
-      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
-      '@nuxtjs/opencollective': 0.3.2
+      '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nuxtjs/opencollective': 0.3.2(encoding@0.1.13)
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
       path-to-regexp: 3.2.0
-      reflect-metadata: 0.2.1
+      reflect-metadata: 0.2.2
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
+    optionalDependencies:
+      '@nestjs/platform-express': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)
     transitivePeerDependencies:
       - encoding
 
-  /@nestjs/platform-express@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3):
-    resolution: {integrity: sha512-GGKSEU48Os7nYFIsUM0nutuFUGn5AbeP8gzFBiBCAtiuJWrXZXpZ58pMBYxAbMf7IrcOZFInHEukjHGAQU0OZw==}
-    peerDependencies:
-      '@nestjs/common': ^10.0.0
-      '@nestjs/core': ^10.0.0
+  '@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)':
     dependencies:
-      '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
-      '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
+      '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
       body-parser: 1.20.2
       cors: 2.8.5
-      express: 4.18.2
+      express: 4.19.2
       multer: 1.4.4-lts.1
       tslib: 2.6.2
     transitivePeerDependencies:
       - supports-color
 
-  /@nestjs/testing@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)(@nestjs/platform-express@10.3.3):
-    resolution: {integrity: sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==}
-    peerDependencies:
-      '@nestjs/common': ^10.0.0
-      '@nestjs/core': ^10.0.0
-      '@nestjs/microservices': ^10.0.0
-      '@nestjs/platform-express': ^10.0.0
-    peerDependenciesMeta:
-      '@nestjs/microservices':
-        optional: true
-      '@nestjs/platform-express':
-        optional: true
+  '@nestjs/testing@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8))':
     dependencies:
-      '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1)
-      '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/platform-express@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1)
-      '@nestjs/platform-express': 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3)
+      '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
       tslib: 2.6.2
-    dev: false
+    optionalDependencies:
+      '@nestjs/platform-express': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)
 
-  /@nodelib/fs.scandir@2.1.5:
-    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
-    engines: {node: '>= 8'}
+  '@nodelib/fs.scandir@2.1.5':
     dependencies:
       '@nodelib/fs.stat': 2.0.5
       run-parallel: 1.2.0
 
-  /@nodelib/fs.stat@2.0.5:
-    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
-    engines: {node: '>= 8'}
+  '@nodelib/fs.stat@2.0.5': {}
 
-  /@nodelib/fs.walk@1.2.8:
-    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
-    engines: {node: '>= 8'}
+  '@nodelib/fs.walk@1.2.8':
     dependencies:
       '@nodelib/fs.scandir': 2.1.5
       fastq: 1.15.0
 
-  /@npmcli/agent@2.2.0:
-    resolution: {integrity: sha512-2yThA1Es98orMkpSLVqlDZAMPK3jHJhifP2gnNUdk1754uZ8yI5c+ulCoVG+WlntQA6MzhrURMXjSd9Z7dJ2/Q==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  '@npmcli/agent@2.2.0':
     dependencies:
       agent-base: 7.1.0
       http-proxy-agent: 7.0.0
@@ -5112,380 +13697,244 @@ packages:
       socks-proxy-agent: 8.0.2
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /@npmcli/fs@3.1.0:
-    resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  '@npmcli/fs@3.1.0':
     dependencies:
       semver: 7.6.0
-    dev: false
 
-  /@nsfw-filter/gif-frames@1.0.2:
-    resolution: {integrity: sha512-XZrbJWEN8YfVla5i+PD4Wj51rRlJ8OgnXiPjjOt/OsrbsCR9GZRD4jr953oNWcwiRaoIcOCFWQNMQukO7Yb1dA==}
+  '@nsfw-filter/gif-frames@1.0.2':
     dependencies:
       '@nsfw-filter/save-pixels': 2.3.4
       get-pixels-frame-info-update: 3.3.2
       multi-integer-range: 3.0.0
-    dev: false
 
-  /@nsfw-filter/save-pixels@2.3.4:
-    resolution: {integrity: sha512-dRZXwrXadMvxwJYKChrDBqC6GNvxVqlmdkyvZJO5DV65qyBsHZw8bPg9CnX7EgpxGl6+4ba/MAdHDLxs2XoD0Q==}
+  '@nsfw-filter/save-pixels@2.3.4':
     dependencies:
       gif-encoder: 0.4.1
       ndarray: 1.0.18
       ndarray-ops: 1.2.2
       pngjs-nozlib: 1.0.0
       through: 2.3.4
-    dev: false
 
-  /@nuxtjs/opencollective@0.3.2:
-    resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==}
-    engines: {node: '>=8.0.0', npm: '>=5.0.0'}
-    hasBin: true
+  '@nuxtjs/opencollective@0.3.2(encoding@0.1.13)':
     dependencies:
       chalk: 4.1.2
       consola: 2.15.3
-      node-fetch: 2.7.0
+      node-fetch: 2.7.0(encoding@0.1.13)
     transitivePeerDependencies:
       - encoding
 
-  /@one-ini/wasm@0.1.1:
-    resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
-    dev: true
+  '@one-ini/wasm@0.1.1': {}
 
-  /@open-draft/deferred-promise@2.2.0:
-    resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
-    dev: true
+  '@open-draft/deferred-promise@2.2.0': {}
 
-  /@open-draft/logger@0.3.0:
-    resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==}
+  '@open-draft/logger@0.3.0':
     dependencies:
       is-node-process: 1.2.0
       outvariant: 1.4.2
-    dev: true
 
-  /@open-draft/until@2.1.0:
-    resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
-    dev: true
+  '@open-draft/until@2.1.0': {}
 
-  /@peculiar/asn1-android@2.3.10:
-    resolution: {integrity: sha512-z9Rx9cFJv7UUablZISe7uksNbFJCq13hO0yEAOoIpAymALTLlvUOSLnGiQS7okPaM5dP42oTLhezH6XDXRXjGw==}
+  '@peculiar/asn1-android@2.3.10':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       asn1js: 3.0.5
       tslib: 2.6.2
-    dev: false
 
-  /@peculiar/asn1-ecc@2.3.8:
-    resolution: {integrity: sha512-Ah/Q15y3A/CtxbPibiLM/LKcMbnLTdUdLHUgdpB5f60sSvGkXzxJCu5ezGTFHogZXWNX3KSmYqilCrfdmBc6pQ==}
+  '@peculiar/asn1-ecc@2.3.8':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       '@peculiar/asn1-x509': 2.3.8
       asn1js: 3.0.5
       tslib: 2.6.2
-    dev: false
 
-  /@peculiar/asn1-rsa@2.3.8:
-    resolution: {integrity: sha512-ES/RVEHu8VMYXgrg3gjb1m/XG0KJWnV4qyZZ7mAg7rrF3VTmRbLxO8mk+uy0Hme7geSMebp+Wvi2U6RLLEs12Q==}
+  '@peculiar/asn1-rsa@2.3.8':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       '@peculiar/asn1-x509': 2.3.8
       asn1js: 3.0.5
       tslib: 2.6.2
-    dev: false
 
-  /@peculiar/asn1-schema@2.3.8:
-    resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==}
+  '@peculiar/asn1-schema@2.3.8':
     dependencies:
       asn1js: 3.0.5
       pvtsutils: 1.3.5
       tslib: 2.6.2
-    dev: false
 
-  /@peculiar/asn1-x509@2.3.8:
-    resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==}
+  '@peculiar/asn1-x509@2.3.8':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       asn1js: 3.0.5
-      ipaddr.js: 2.1.0
+      ipaddr.js: 2.2.0
       pvtsutils: 1.3.5
       tslib: 2.6.2
-    dev: false
 
-  /@peertube/http-signature@1.7.0:
-    resolution: {integrity: sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==}
-    engines: {node: '>=0.10'}
+  '@peertube/http-signature@1.7.0':
     dependencies:
       assert-plus: 1.0.0
       jsprim: 1.4.2
       sshpk: 1.17.0
-    dev: false
 
-  /@pkgjs/parseargs@0.11.0:
-    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
-    engines: {node: '>=14'}
-    requiresBuild: true
+  '@pkgjs/parseargs@0.11.0':
     optional: true
 
-  /@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.28)(react@18.2.0):
-    resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
+  '@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.28)(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.23.4
+      react: 18.3.1
+    optionalDependencies:
       '@types/react': 18.0.28
-      react: 18.2.0
-    dev: true
 
-  /@radix-ui/react-slot@1.0.2(@types/react@18.0.28)(react@18.2.0):
-    resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
+  '@radix-ui/react-slot@1.0.2(@types/react@18.0.28)(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.23.4
-      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.28)(react@18.2.0)
+      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+    optionalDependencies:
       '@types/react': 18.0.28
-      react: 18.2.0
-    dev: true
 
-  /@readme/better-ajv-errors@1.6.0(ajv@8.12.0):
-    resolution: {integrity: sha512-9gO9rld84Jgu13kcbKRU+WHseNhaVt76wYMeRDGsUGYxwJtI3RmEJ9LY9dZCYQGI8eUZLuxb5qDja0nqklpFjQ==}
-    engines: {node: '>=14'}
-    peerDependencies:
-      ajv: 4.11.8 - 8
+  '@readme/better-ajv-errors@1.6.0(ajv@8.13.0)':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/runtime': 7.23.4
       '@humanwhocodes/momoa': 2.0.4
-      ajv: 8.12.0
+      ajv: 8.13.0
       chalk: 4.1.2
       json-to-ast: 2.1.0
       jsonpointer: 5.0.1
       leven: 3.1.0
-    dev: true
 
-  /@readme/json-schema-ref-parser@1.2.0:
-    resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==}
+  '@readme/json-schema-ref-parser@1.2.0':
     dependencies:
       '@jsdevtools/ono': 7.1.3
       '@types/json-schema': 7.0.12
       call-me-maybe: 1.0.2
       js-yaml: 4.1.0
-    dev: true
 
-  /@readme/openapi-parser@2.5.0(openapi-types@12.1.3):
-    resolution: {integrity: sha512-IbymbOqRuUzoIgxfAAR7XJt2FWl6n2yqN09fF5adacGm7W03siA3bj1Emql0X9D2T+RpBYz3x9zDsMhuoMP62A==}
-    engines: {node: '>=14'}
-    peerDependencies:
-      openapi-types: '>=7'
+  '@readme/openapi-parser@2.5.0(openapi-types@12.1.3)':
     dependencies:
       '@apidevtools/openapi-schemas': 2.1.0
       '@apidevtools/swagger-methods': 3.0.2
       '@jsdevtools/ono': 7.1.3
-      '@readme/better-ajv-errors': 1.6.0(ajv@8.12.0)
+      '@readme/better-ajv-errors': 1.6.0(ajv@8.13.0)
       '@readme/json-schema-ref-parser': 1.2.0
-      ajv: 8.12.0
-      ajv-draft-04: 1.0.0(ajv@8.12.0)
+      ajv: 8.13.0
+      ajv-draft-04: 1.0.0(ajv@8.13.0)
       call-me-maybe: 1.0.2
       openapi-types: 12.1.3
-    dev: true
 
-  /@rollup/plugin-json@6.1.0(rollup@4.12.0):
-    resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
-    peerDependenciesMeta:
-      rollup:
-        optional: true
+  '@rollup/plugin-json@6.1.0(rollup@4.17.2)':
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
-      rollup: 4.12.0
-    dev: false
+      '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
+    optionalDependencies:
+      rollup: 4.17.2
 
-  /@rollup/plugin-replace@5.0.5(rollup@4.12.0):
-    resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
-    peerDependenciesMeta:
-      rollup:
-        optional: true
+  '@rollup/plugin-replace@5.0.5(rollup@4.17.2)':
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
+      '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
       magic-string: 0.30.7
-      rollup: 4.12.0
-    dev: false
+    optionalDependencies:
+      rollup: 4.17.2
 
-  /@rollup/pluginutils@5.1.0(rollup@4.12.0):
-    resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
-    peerDependenciesMeta:
-      rollup:
-        optional: true
+  '@rollup/pluginutils@5.1.0(rollup@4.17.2)':
     dependencies:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.12.0
+    optionalDependencies:
+      rollup: 4.17.2
 
-  /@rollup/rollup-android-arm-eabi@4.12.0:
-    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
+  '@rollup/rollup-android-arm-eabi@4.17.2':
     optional: true
 
-  /@rollup/rollup-android-arm64@4.12.0:
-    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
+  '@rollup/rollup-android-arm64@4.17.2':
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.12.0:
-    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
+  '@rollup/rollup-darwin-arm64@4.17.2':
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.12.0:
-    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
+  '@rollup/rollup-darwin-x64@4.17.2':
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
-    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
+  '@rollup/rollup-linux-arm-gnueabihf@4.17.2':
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.12.0:
-    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
+  '@rollup/rollup-linux-arm-musleabihf@4.17.2':
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.12.0:
-    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
+  '@rollup/rollup-linux-arm64-gnu@4.17.2':
     optional: true
 
-  /@rollup/rollup-linux-riscv64-gnu@4.12.0:
-    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
-    cpu: [riscv64]
-    os: [linux]
-    requiresBuild: true
+  '@rollup/rollup-linux-arm64-musl@4.17.2':
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.12.0:
-    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
+  '@rollup/rollup-linux-powerpc64le-gnu@4.17.2':
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.12.0:
-    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
+  '@rollup/rollup-linux-riscv64-gnu@4.17.2':
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.12.0:
-    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
+  '@rollup/rollup-linux-s390x-gnu@4.17.2':
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.12.0:
-    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
+  '@rollup/rollup-linux-x64-gnu@4.17.2':
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.12.0:
-    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
+  '@rollup/rollup-linux-x64-musl@4.17.2':
     optional: true
 
-  /@rushstack/node-core-library@3.63.0(@types/node@20.11.22):
-    resolution: {integrity: sha512-Q7B3dVpBQF1v+mUfxNcNZh5uHVR8ntcnkN5GYjbBLrxUYHBGKbnCM+OdcN+hzCpFlLBH6Ob0dEHhZ0spQwf24A==}
-    peerDependencies:
-      '@types/node': '*'
-    peerDependenciesMeta:
-      '@types/node':
-        optional: true
+  '@rollup/rollup-win32-arm64-msvc@4.17.2':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.17.2':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.17.2':
+    optional: true
+
+  '@rushstack/node-core-library@4.1.0(@types/node@20.12.7)':
     dependencies:
-      '@types/node': 20.11.22
-      colors: 1.2.5
       fs-extra: 7.0.1
       import-lazy: 4.0.0
       jju: 1.4.0
       resolve: 1.22.8
       semver: 7.5.4
       z-schema: 5.0.5
-    dev: true
+    optionalDependencies:
+      '@types/node': 20.12.7
 
-  /@rushstack/rig-package@0.5.1:
-    resolution: {integrity: sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==}
+  '@rushstack/rig-package@0.5.2':
     dependencies:
       resolve: 1.22.8
       strip-json-comments: 3.1.1
-    dev: true
 
-  /@rushstack/ts-command-line@4.17.1:
-    resolution: {integrity: sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==}
+  '@rushstack/terminal@0.10.1(@types/node@20.12.7)':
+    dependencies:
+      '@rushstack/node-core-library': 4.1.0(@types/node@20.12.7)
+      supports-color: 8.1.1
+    optionalDependencies:
+      '@types/node': 20.12.7
+
+  '@rushstack/ts-command-line@4.19.2(@types/node@20.12.7)':
     dependencies:
+      '@rushstack/terminal': 0.10.1(@types/node@20.12.7)
       '@types/argparse': 1.0.38
       argparse: 1.0.10
-      colors: 1.2.5
       string-argv: 0.3.1
-    dev: true
+    transitivePeerDependencies:
+      - '@types/node'
 
-  /@shikijs/core@1.2.0:
-    resolution: {integrity: sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==}
-    dev: false
+  '@shikijs/core@1.4.0': {}
 
-  /@sideway/address@4.1.4:
-    resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
+  '@sideway/address@4.1.4':
     dependencies:
       '@hapi/hoek': 9.3.0
-    dev: true
 
-  /@sideway/formula@3.0.1:
-    resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==}
-    dev: true
+  '@sideway/formula@3.0.1': {}
 
-  /@sideway/pinpoint@2.0.0:
-    resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
-    dev: true
+  '@sideway/pinpoint@2.0.0': {}
 
-  /@simplewebauthn/server@9.0.3:
-    resolution: {integrity: sha512-FMZieoBosrVLFxCnxPFD9Enhd1U7D8nidVDT4MsHc6l4fdVcjoeHjDueeXCloO1k5O/fZg1fsSXXPKbY2XTzDA==}
-    engines: {node: '>=16.0.0'}
+  '@simplewebauthn/server@10.0.0(encoding@0.1.13)':
     dependencies:
       '@hexagon/base64': 1.1.27
       '@levischuck/tiny-cbor': 0.2.2
@@ -5494,238 +13943,168 @@ packages:
       '@peculiar/asn1-rsa': 2.3.8
       '@peculiar/asn1-schema': 2.3.8
       '@peculiar/asn1-x509': 2.3.8
-      '@simplewebauthn/types': 9.0.1
-      cross-fetch: 4.0.0
+      '@simplewebauthn/types': 10.0.0
+      cross-fetch: 4.0.0(encoding@0.1.13)
     transitivePeerDependencies:
       - encoding
-    dev: false
 
-  /@simplewebauthn/types@9.0.1:
-    resolution: {integrity: sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==}
+  '@simplewebauthn/types@10.0.0': {}
 
-  /@sinclair/typebox@0.27.8:
-    resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
-    dev: true
+  '@sinclair/typebox@0.27.8': {}
 
-  /@sindresorhus/is@4.6.0:
-    resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
-    engines: {node: '>=10'}
-    dev: false
+  '@sindresorhus/is@4.6.0': {}
 
-  /@sindresorhus/is@5.3.0:
-    resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
-    engines: {node: '>=14.16'}
+  '@sindresorhus/is@5.3.0': {}
 
-  /@sindresorhus/is@6.1.0:
-    resolution: {integrity: sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==}
-    engines: {node: '>=16'}
-    dev: false
+  '@sindresorhus/is@6.1.0': {}
 
-  /@sinonjs/commons@2.0.0:
-    resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==}
+  '@sinonjs/commons@2.0.0':
     dependencies:
       type-detect: 4.0.8
-    dev: true
 
-  /@sinonjs/commons@3.0.0:
-    resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==}
+  '@sinonjs/commons@3.0.0':
     dependencies:
       type-detect: 4.0.8
 
-  /@sinonjs/fake-timers@10.3.0:
-    resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+  '@sinonjs/fake-timers@10.3.0':
     dependencies:
       '@sinonjs/commons': 3.0.0
-    dev: true
 
-  /@sinonjs/fake-timers@11.2.2:
-    resolution: {integrity: sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==}
+  '@sinonjs/fake-timers@11.2.2':
     dependencies:
       '@sinonjs/commons': 3.0.0
-    dev: false
 
-  /@sinonjs/samsam@8.0.0:
-    resolution: {integrity: sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==}
+  '@sinonjs/samsam@8.0.0':
     dependencies:
       '@sinonjs/commons': 2.0.0
       lodash.get: 4.4.2
       type-detect: 4.0.8
-    dev: true
 
-  /@sinonjs/text-encoding@0.7.2:
-    resolution: {integrity: sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==}
-    dev: true
+  '@sinonjs/text-encoding@0.7.2': {}
 
-  /@smithy/abort-controller@2.0.14:
-    resolution: {integrity: sha512-zXtteuYLWbSXnzI3O6xq3FYvigYZFW8mdytGibfarLL2lxHto9L3ILtGVnVGmFZa7SDh62l39EnU5hesLN87Fw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/abort-controller@2.0.14':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/chunked-blob-reader-native@2.0.0:
-    resolution: {integrity: sha512-HM8V2Rp1y8+1343tkZUKZllFhEQPNmpNdgFAncbTsxkZ18/gqjk23XXv3qGyXWp412f3o43ZZ1UZHVcHrpRnCQ==}
+  '@smithy/abort-controller@2.2.0':
+    dependencies:
+      '@smithy/types': 2.12.0
+      tslib: 2.6.2
+
+  '@smithy/chunked-blob-reader-native@2.0.0':
     dependencies:
       '@smithy/util-base64': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/chunked-blob-reader@2.0.0:
-    resolution: {integrity: sha512-k+J4GHJsMSAIQPChGBrjEmGS+WbPonCXesoqP9fynIqjn7rdOThdH8FAeCmokP9mxTYKQAKoHCLPzNlm6gh7Wg==}
+  '@smithy/chunked-blob-reader@2.0.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/config-resolver@2.0.9:
-    resolution: {integrity: sha512-QBkGPLUqyPmis9Erz8v4q5lo/ErnF7+GD5WZHa6JZiXopUPfaaM+B21n8gzS5xCkIXZmnwzNQhObP9xQPu8oqQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/config-resolver@2.0.9':
     dependencies:
       '@smithy/node-config-provider': 2.0.11
       '@smithy/types': 2.6.0
       '@smithy/util-config-provider': 2.0.0
       '@smithy/util-middleware': 2.0.1
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/credential-provider-imds@2.0.11:
-    resolution: {integrity: sha512-uJJs8dnM5iXkn8a2GaKvlKMhcOJ+oJPYqY9gY3CM/EieCVObIDjxUtR/g8lU/k/A+OauA78GzScAfulmFjPOYA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/credential-provider-imds@2.0.11':
     dependencies:
       '@smithy/node-config-provider': 2.0.11
       '@smithy/property-provider': 2.0.9
       '@smithy/types': 2.6.0
       '@smithy/url-parser': 2.0.8
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/eventstream-codec@2.0.8:
-    resolution: {integrity: sha512-onO4to8ujCKn4m5XagReT9Nc6FlNG5vveuvjp1H7AtaG7njdet1LOl6/jmUOkskF2C/w+9jNw3r9Ak+ghOvN0A==}
+  '@smithy/eventstream-codec@2.0.8':
     dependencies:
       '@aws-crypto/crc32': 3.0.0
       '@smithy/types': 2.6.0
       '@smithy/util-hex-encoding': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/eventstream-serde-browser@2.0.8:
-    resolution: {integrity: sha512-/RGlkKUnC0sd+xKBKH/2APSBRmVMZTeLOKZMhrZmrO+ONoU+DwyMr/RLJ6WnmBKN+2ebjffM4pcIJTKLNNDD8g==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/eventstream-serde-browser@2.0.8':
     dependencies:
       '@smithy/eventstream-serde-universal': 2.0.8
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/eventstream-serde-config-resolver@2.0.8:
-    resolution: {integrity: sha512-EyAEj258eMUv9zcMvBbqrInh2eHRYuiwQAjXDMxZFCyP+JePzQB6O++3wFwjQeRKMFFgZipNgnEXfReII4+NAw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/eventstream-serde-config-resolver@2.0.8':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/eventstream-serde-node@2.0.8:
-    resolution: {integrity: sha512-FMBatSUSKwh6aguKVJokXfJaV8nqsuCkCZHb9MP9zah0ZF+ohbTLeeed7DQGeTVBueVIVWEzIsShPxtxBv7MMQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/eventstream-serde-node@2.0.8':
     dependencies:
       '@smithy/eventstream-serde-universal': 2.0.8
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/eventstream-serde-universal@2.0.8:
-    resolution: {integrity: sha512-6InMXH8BUKoEDa6CAuxR4Gn8Gf2vBfVtjA9A6zDKZClYHT+ANUJS+2EtOBc5wECJJGk4KLn5ajQyrt9MBv5lcw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/eventstream-serde-universal@2.0.8':
     dependencies:
       '@smithy/eventstream-codec': 2.0.8
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/fetch-http-handler@2.1.4:
-    resolution: {integrity: sha512-SL24M9W5ERByoXaVicRx+bj9GJVujDnPn+QO7GY7adhY0mPGa6DSF58pVKsgIh4r5Tx/k3SWCPlH4BxxSxA/fQ==}
+  '@smithy/fetch-http-handler@2.1.4':
     dependencies:
       '@smithy/protocol-http': 3.0.10
       '@smithy/querystring-builder': 2.0.14
       '@smithy/types': 2.6.0
       '@smithy/util-base64': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/hash-blob-browser@2.0.8:
-    resolution: {integrity: sha512-IgvRlBMfg/qLg321a59T1yTdEEbaizLrEVsU3DHj65DAO4lFRMF5f+l7vuV+je6m1G9wSD5GQXLturX8qlGb4g==}
+  '@smithy/hash-blob-browser@2.0.8':
     dependencies:
       '@smithy/chunked-blob-reader': 2.0.0
       '@smithy/chunked-blob-reader-native': 2.0.0
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/hash-node@2.0.8:
-    resolution: {integrity: sha512-yZL/nmxZzjZV5/QX5JWSgXlt0HxuMTwFO89CS++jOMMPiCMZngf6VYmtNdccs8IIIAMmfQeTzwu07XgUE/Zd3Q==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/hash-node@2.0.8':
     dependencies:
       '@smithy/types': 2.6.0
       '@smithy/util-buffer-from': 2.0.0
       '@smithy/util-utf8': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/hash-stream-node@2.0.8:
-    resolution: {integrity: sha512-82zC6I9ZJycbEZH8TVyXyBx9c2ZIPQDgBvM0x5AFPUl/i1AxwKKX+lwYRnzgkF//cYhIIoJaCfJ9mjSMPRGvCQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/hash-stream-node@2.0.8':
     dependencies:
       '@smithy/types': 2.6.0
       '@smithy/util-utf8': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/invalid-dependency@2.0.8:
-    resolution: {integrity: sha512-88VOS7W3KzUz/bNRc+Sl/F/CDIasFspEE4G39YZRHIh9YmsXF7GUyVaAKURfMNulTie62ayk6BHC9O0nOBAVgQ==}
+  '@smithy/invalid-dependency@2.0.8':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/is-array-buffer@2.0.0:
-    resolution: {integrity: sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/is-array-buffer@2.0.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/md5-js@2.0.8:
-    resolution: {integrity: sha512-1VVECXEiuJvjXv+mudiaUFKYwgDLOWz5MTTy8RzbrPiU3GiOb3/o5/urdkYpqmgoMfxdvxxOw/Adjv2dV2q2Yg==}
+  '@smithy/md5-js@2.0.8':
     dependencies:
       '@smithy/types': 2.6.0
       '@smithy/util-utf8': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/middleware-content-length@2.0.10:
-    resolution: {integrity: sha512-EGSbysyA4jH0p3xI6G0jdXoj9Iz9GUnAta6aEaHtXm3wVWtenRf80y2TeVvNkVSr5jwKOdSCjKIRI2l1A/oZLA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-content-length@2.0.10':
     dependencies:
       '@smithy/protocol-http': 3.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/middleware-endpoint@2.0.8:
-    resolution: {integrity: sha512-yOpogfG2d2V0cbJdAJ6GLAWkNOc9pVsL5hZUfXcxJu408N3CUCsXzIAFF6+70ZKSE+lCfG3GFErcSXv/UfUbjw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-endpoint@2.0.8':
     dependencies:
       '@smithy/middleware-serde': 2.0.8
       '@smithy/types': 2.6.0
       '@smithy/url-parser': 2.0.8
       '@smithy/util-middleware': 2.0.1
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/middleware-retry@2.0.11:
-    resolution: {integrity: sha512-pknfokumZ+wvBERSuKAI2vVr+aK3ZgPiWRg6+0ZG4kKJogBRpPmDGWw+Jht0izS9ZaEbIobNzueIb4wD33JJVg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-retry@2.0.11':
     dependencies:
       '@smithy/node-config-provider': 2.0.11
       '@smithy/protocol-http': 3.0.10
@@ -5735,96 +14114,74 @@ packages:
       '@smithy/util-retry': 2.0.1
       tslib: 2.6.2
       uuid: 8.3.2
-    dev: false
 
-  /@smithy/middleware-serde@2.0.8:
-    resolution: {integrity: sha512-Is0sm+LiNlgsc0QpstDzifugzL9ehno1wXp109GgBgpnKTK3j+KphiparBDI4hWTtH9/7OUsxuspNqai2yyhcg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-serde@2.0.8':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/middleware-stack@2.0.1:
-    resolution: {integrity: sha512-UexsfY6/oQZRjTQL56s9AKtMcR60tBNibSgNYX1I2WXaUaXg97W9JCkFyth85TzBWKDBTyhLfenrukS/kyu54A==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-stack@2.0.1':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/node-config-provider@2.0.11:
-    resolution: {integrity: sha512-CaR1dciSSGKttjhcefpytYjsfI/Yd5mqL8am4wfmyFCDxSiPsvnEWHl8UjM/RbcAjX0klt+CeIKPSHEc0wGvJA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/node-config-provider@2.0.11':
     dependencies:
       '@smithy/property-provider': 2.0.9
       '@smithy/shared-ini-file-loader': 2.0.10
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/node-http-handler@2.1.10:
-    resolution: {integrity: sha512-lkALAwtN6odygIM4nB8aHDahINM6WXXjNrZmWQAh0RSossySRT2qa31cFv0ZBuAYVWeprskRk13AFvvLmf1WLw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/node-http-handler@2.5.0':
     dependencies:
-      '@smithy/abort-controller': 2.0.14
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/querystring-builder': 2.0.14
-      '@smithy/types': 2.6.0
+      '@smithy/abort-controller': 2.2.0
+      '@smithy/protocol-http': 3.3.0
+      '@smithy/querystring-builder': 2.2.0
+      '@smithy/types': 2.12.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/property-provider@2.0.9:
-    resolution: {integrity: sha512-25pPZ8f8DeRwYI5wbPRZaoMoR+3vrw8DwbA0TjP+GsdiB2KxScndr4HQehiJ5+WJ0giOTWhLz0bd+7Djv1qpUQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/property-provider@2.0.9':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/protocol-http@3.0.10:
-    resolution: {integrity: sha512-6+tjNk7rXW7YTeGo9qwxXj/2BFpJTe37kTj3EnZCoX/nH+NP/WLA7O83fz8XhkGqsaAhLUPo/bB12vvd47nsmg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/protocol-http@3.0.10':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/querystring-builder@2.0.14:
-    resolution: {integrity: sha512-lQ4pm9vTv9nIhl5jt6uVMPludr6syE2FyJmHsIJJuOD7QPIJnrf9HhUGf1iHh9KJ4CUv21tpOU3X6s0rB6uJ0g==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/protocol-http@3.3.0':
+    dependencies:
+      '@smithy/types': 2.12.0
+      tslib: 2.6.2
+
+  '@smithy/querystring-builder@2.0.14':
     dependencies:
       '@smithy/types': 2.6.0
       '@smithy/util-uri-escape': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/querystring-parser@2.0.8:
-    resolution: {integrity: sha512-ArbanNuR7O/MmTd90ZqhDqGOPPDYmxx3huHxD+R3cuCnazcK/1tGQA+SnnR5307T7ZRb5WTpB6qBggERuibVSA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/querystring-builder@2.2.0':
+    dependencies:
+      '@smithy/types': 2.12.0
+      '@smithy/util-uri-escape': 2.2.0
+      tslib: 2.6.2
+
+  '@smithy/querystring-parser@2.0.8':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/service-error-classification@2.0.1:
-    resolution: {integrity: sha512-QHa9+t+v4s0cMuDCcbjIJN67mNZ42/+fc3jKe8P6ZMPXZl5ksKk6a8vhZ/m494GZng5eFTc3OePv+NF9cG83yg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/service-error-classification@2.0.1':
     dependencies:
       '@smithy/types': 2.6.0
-    dev: false
 
-  /@smithy/shared-ini-file-loader@2.0.10:
-    resolution: {integrity: sha512-jWASteSezRKohJ7GdA7pHDvmr7Q7tw3b5mu3xLHIkZy/ICftJ+O7aqNaF8wklhI7UNFoQ7flFRM3Rd0KA+1BbQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/shared-ini-file-loader@2.0.10':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/signature-v4@2.0.5:
-    resolution: {integrity: sha512-ABIzXmUDXK4n2c9cXjQLELgH2RdtABpYKT+U131e2I6RbCypFZmxIHmIBufJzU2kdMCQ3+thBGDWorAITFW04A==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/signature-v4@2.0.5':
     dependencies:
       '@smithy/eventstream-codec': 2.0.8
       '@smithy/is-array-buffer': 2.0.0
@@ -5834,83 +14191,59 @@ packages:
       '@smithy/util-uri-escape': 2.0.0
       '@smithy/util-utf8': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/smithy-client@2.1.5:
-    resolution: {integrity: sha512-7S865uKzsxApM8W8Q6zkij7tcUFgaG8PuADMFdMt1yL/ku3d0+s6Zwrg3N7iXCPM08Gu/mf0BIfTXIu/9i450Q==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/smithy-client@2.1.5':
     dependencies:
       '@smithy/middleware-stack': 2.0.1
       '@smithy/types': 2.6.0
       '@smithy/util-stream': 2.0.11
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/types@2.6.0:
-    resolution: {integrity: sha512-PgqxJq2IcdMF9iAasxcqZqqoOXBHufEfmbEUdN1pmJrJltT42b0Sc8UiYSWWzKkciIp9/mZDpzYi4qYG1qqg6g==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/types@2.12.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/url-parser@2.0.8:
-    resolution: {integrity: sha512-wQw7j004ScCrBRJ+oNPXlLE9mtofxyadSZ9D8ov/rHkyurS7z1HTNuyaGRj6OvKsEk0SVQsuY0C9+EfM75XTkw==}
+  '@smithy/types@2.6.0':
+    dependencies:
+      tslib: 2.6.2
+
+  '@smithy/url-parser@2.0.8':
     dependencies:
       '@smithy/querystring-parser': 2.0.8
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-base64@2.0.0:
-    resolution: {integrity: sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-base64@2.0.0':
     dependencies:
       '@smithy/util-buffer-from': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-body-length-browser@2.0.0:
-    resolution: {integrity: sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==}
+  '@smithy/util-body-length-browser@2.0.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-body-length-node@2.1.0:
-    resolution: {integrity: sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-body-length-node@2.1.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-buffer-from@2.0.0:
-    resolution: {integrity: sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-buffer-from@2.0.0':
     dependencies:
       '@smithy/is-array-buffer': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-config-provider@2.0.0:
-    resolution: {integrity: sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-config-provider@2.0.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-defaults-mode-browser@2.0.9:
-    resolution: {integrity: sha512-JONLJVQWT8165XoSV36ERn3SVlZLJJ4D6IeGsCSePv65Uxa93pzSLE0UMSR9Jwm4zix7rst9AS8W5QIypZWP8Q==}
-    engines: {node: '>= 10.0.0'}
+  '@smithy/util-defaults-mode-browser@2.0.9':
     dependencies:
       '@smithy/property-provider': 2.0.9
       '@smithy/smithy-client': 2.1.5
       '@smithy/types': 2.6.0
       bowser: 2.11.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-defaults-mode-node@2.0.11:
-    resolution: {integrity: sha512-tmqjNsfj+bgZN6jXBe6efZnukzILA7BUytHkzqikuRLNtR+0VVchQHvawD0w6vManh76rO81ydhioe7i4oBzuA==}
-    engines: {node: '>= 10.0.0'}
+  '@smithy/util-defaults-mode-node@2.0.11':
     dependencies:
       '@smithy/config-resolver': 2.0.9
       '@smithy/credential-provider-imds': 2.0.11
@@ -5919,76 +14252,55 @@ packages:
       '@smithy/smithy-client': 2.1.5
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-hex-encoding@2.0.0:
-    resolution: {integrity: sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-hex-encoding@2.0.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-middleware@2.0.1:
-    resolution: {integrity: sha512-LnsBMi0Mg3gfz/TpNGLv2Jjcz2ra1OX5HR/4IaCepIYmtPQzqMWDdhX/XTW1LS8OZ0xbQuyQPcHkQ+2XkhWOVQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-middleware@2.0.1':
     dependencies:
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-retry@2.0.1:
-    resolution: {integrity: sha512-naj4X0IafJ9yJnVJ58QgSMkCNLjyQOnyrnKh/T0f+0UOUxJiT8vuFn/hS7B/pNqbo2STY7PyJ4J4f+5YqxwNtA==}
-    engines: {node: '>= 14.0.0'}
+  '@smithy/util-retry@2.0.1':
     dependencies:
       '@smithy/service-error-classification': 2.0.1
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-stream@2.0.11:
-    resolution: {integrity: sha512-2MeWfqSpZKdmEJ+tH8CJQSgzLWhH5cmdE24X7JB0hiamXrOmswWGGuPvyj/9sQCTclo57pNxLR2p7KrP8Ahiyg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-stream@2.0.11':
     dependencies:
       '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/node-http-handler': 2.1.10
+      '@smithy/node-http-handler': 2.5.0
       '@smithy/types': 2.6.0
       '@smithy/util-base64': 2.0.0
       '@smithy/util-buffer-from': 2.0.0
       '@smithy/util-hex-encoding': 2.0.0
       '@smithy/util-utf8': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-uri-escape@2.0.0:
-    resolution: {integrity: sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-uri-escape@2.0.0':
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-utf8@2.0.0:
-    resolution: {integrity: sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-uri-escape@2.2.0':
+    dependencies:
+      tslib: 2.6.2
+
+  '@smithy/util-utf8@2.0.0':
     dependencies:
       '@smithy/util-buffer-from': 2.0.0
       tslib: 2.6.2
-    dev: false
 
-  /@smithy/util-waiter@2.0.8:
-    resolution: {integrity: sha512-t9yaoofNhdEhNlyDeV5al/JJEFJ62HIQBGktgCUE63MvKn6imnbkh1qISsYMyMYVLwhWCpZ3Xa3R1LA+SnWcng==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-waiter@2.0.8':
     dependencies:
       '@smithy/abort-controller': 2.0.14
       '@smithy/types': 2.6.0
       tslib: 2.6.2
-    dev: false
 
-  /@sqltools/formatter@1.2.5:
-    resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
-    dev: false
+  '@sqltools/formatter@1.2.5': {}
 
-  /@storybook/addon-actions@8.0.9:
-    resolution: {integrity: sha512-+I3VTvlKdj8puHeS2tyaOVv9syDiNLneVZbTfqN+UDOK2i42NwvZr8PVwjTzMlEj9eePJdCZgiipz55xwts5bw==}
+  '@storybook/addon-actions@8.0.9':
     dependencies:
       '@storybook/core-events': 8.0.9
       '@storybook/global': 5.0.0
@@ -5996,20 +14308,16 @@ packages:
       dequal: 2.0.3
       polished: 4.2.2
       uuid: 9.0.1
-    dev: true
 
-  /@storybook/addon-backgrounds@8.0.9:
-    resolution: {integrity: sha512-pCDecACrVyxPaJKEWS0sHsRb8xw+IPCSxDM1TkjaAQ6zZ468A/dcUnqW+LVK8bSXgQwWzn23wqnqPFSy5yptuQ==}
+  '@storybook/addon-backgrounds@8.0.9':
     dependencies:
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       ts-dedent: 2.2.0
-    dev: true
 
-  /@storybook/addon-controls@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-wWdmd62UP/sfPm8M7aJjEA+kEXTUIR/QsYi9PoYBhBZcXiikZ4kNan7oD7GfsnzGGKHrBVfwQhO+TqaENGYytA==}
+  '@storybook/addon-controls@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       lodash: 4.17.21
       ts-dedent: 2.2.0
     transitivePeerDependencies:
@@ -6018,50 +14326,46 @@ packages:
       - react
       - react-dom
       - supports-color
-    dev: true
 
-  /@storybook/addon-docs@8.0.9:
-    resolution: {integrity: sha512-x7hX7UuzJtClu6XwU3SfpyFhuckVcgqgD6BU6Ihxl0zs+i4xp6iKVXYSnHFMRM1sgoeT8TjPxab35Ke8w8BVRw==}
+  '@storybook/addon-docs@8.0.9(encoding@0.1.13)':
     dependencies:
       '@babel/core': 7.24.0
-      '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.2.0)
-      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.3.1)
+      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/client-logger': 8.0.9
-      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/csf-plugin': 8.0.9
       '@storybook/csf-tools': 8.0.9
       '@storybook/global': 5.0.0
       '@storybook/node-logger': 8.0.9
       '@storybook/preview-api': 8.0.9
-      '@storybook/react-dom-shim': 8.0.9(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/react-dom-shim': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/types': 8.0.9
       '@types/react': 18.0.28
       fs-extra: 11.1.1
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
       rehype-external-links: 3.0.0
       rehype-slug: 6.0.0
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/addon-essentials@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-mwAgdfrOsTuTDcagvM7veBh+iayZIWmKOazzkhrIWbhYcrXOsweigD2UOVeHgAiAzJK49znr4FXTCKcE1hOWcw==}
+  '@storybook/addon-essentials@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@storybook/addon-actions': 8.0.9
       '@storybook/addon-backgrounds': 8.0.9
-      '@storybook/addon-controls': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-docs': 8.0.9
+      '@storybook/addon-controls': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/addon-docs': 8.0.9(encoding@0.1.13)
       '@storybook/addon-highlight': 8.0.9
       '@storybook/addon-measure': 8.0.9
       '@storybook/addon-outline': 8.0.9
       '@storybook/addon-toolbars': 8.0.9
       '@storybook/addon-viewport': 8.0.9
-      '@storybook/core-common': 8.0.9
-      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-common': 8.0.9(encoding@0.1.13)
+      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/node-logger': 8.0.9
       '@storybook/preview-api': 8.0.9
       ts-dedent: 2.2.0
@@ -6071,20 +14375,16 @@ packages:
       - react
       - react-dom
       - supports-color
-    dev: true
 
-  /@storybook/addon-highlight@8.0.9:
-    resolution: {integrity: sha512-vaRHGDbx7dpNpQECAHk5wczlZO3ntstprGlqnZt0o7ylz6xB5+pTQwTuIFty0hwKv+3TPcskzzifATUyEOEmyg==}
+  '@storybook/addon-highlight@8.0.9':
     dependencies:
       '@storybook/global': 5.0.0
-    dev: true
 
-  /@storybook/addon-interactions@8.0.9(vitest@0.34.6):
-    resolution: {integrity: sha512-AMIdNcyM6DDAWvMitBJMqp1iPZND8AXB4QT4VZHGMKG2ngHNKktriEKpTfcRkfKPGTJs9T+71dWfm6/R4tticw==}
+  '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@storybook/global': 5.0.0
       '@storybook/instrumenter': 8.0.9
-      '@storybook/test': 8.0.9(vitest@0.34.6)
+      '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@storybook/types': 8.0.9
       polished: 4.2.2
       ts-dedent: 2.2.0
@@ -6094,147 +14394,104 @@ packages:
       - '@types/jest'
       - jest
       - vitest
-    dev: true
 
-  /@storybook/addon-links@8.0.9(react@18.2.0):
-    resolution: {integrity: sha512-FVt+AdW3JFSqbJzkKiqKsMRWqHXqEvCBqFs7lNfk3OW0w0jfv1iREtrxE0dVdJoUFQC9V/2Im/EpJ7UB3C2bNQ==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
+  '@storybook/addon-links@8.0.9(react@18.3.1)':
     dependencies:
       '@storybook/csf': 0.1.6
       '@storybook/global': 5.0.0
-      react: 18.2.0
       ts-dedent: 2.2.0
-    dev: true
+    optionalDependencies:
+      react: 18.3.1
 
-  /@storybook/addon-mdx-gfm@8.0.9:
-    resolution: {integrity: sha512-AoEx+OGKANtVZgKyWKrQhGpMpDuc2S7PnOlNLUiDYzmj8ABAGPmEJmqeb/VHVgqLQSjhOW1fMsQ4fYsecvMxTQ==}
+  '@storybook/addon-mdx-gfm@8.0.9':
     dependencies:
       '@storybook/node-logger': 8.0.9
       remark-gfm: 4.0.0
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@storybook/addon-measure@8.0.9:
-    resolution: {integrity: sha512-91svOOGEXmGG4USglwXLE3wtlUVgtbKJVxTKX7xRI+AC5JEEaKByVzP17/X8Qn/8HilUL7AfSQ0kCoqtPSJ5cA==}
+  '@storybook/addon-measure@8.0.9':
     dependencies:
       '@storybook/global': 5.0.0
       tiny-invariant: 1.3.1
-    dev: true
 
-  /@storybook/addon-outline@8.0.9:
-    resolution: {integrity: sha512-fQ+jm356TgUnz81IxsC99/aOesbLw3N5OQRJpo/A6kqbLMzlq3ybVzuXYCKC3f0ArgQRNh4NoMeJBMRFMtaWRw==}
+  '@storybook/addon-outline@8.0.9':
     dependencies:
       '@storybook/global': 5.0.0
       ts-dedent: 2.2.0
-    dev: true
 
-  /@storybook/addon-storysource@8.0.9:
-    resolution: {integrity: sha512-5m3K2Rs4fQtKtqwrq4CDS1jK2wzWOlnxhE2ArX5XTWytb1am65CEPxfYTEQkvZH9oPGwX3cXytPCziynqysFMQ==}
+  '@storybook/addon-storysource@8.0.9':
     dependencies:
       '@storybook/source-loader': 8.0.9
       estraverse: 5.3.0
       tiny-invariant: 1.3.1
-    dev: true
 
-  /@storybook/addon-toolbars@8.0.9:
-    resolution: {integrity: sha512-nNSBnnBOhQ+EJwkrIkK4ZBYPcozNmEH770CZ/6NK85SUJ6WEBZapE6ru33jIUokFGEvlOlNCeai0GUc++cQP8w==}
-    dev: true
+  '@storybook/addon-toolbars@8.0.9': {}
 
-  /@storybook/addon-viewport@8.0.9:
-    resolution: {integrity: sha512-Ao4+D56cO7biaw+iTlMU1FBec1idX0cmdosDeCFZin06MSawcPkeBlRBeruaSQYdLes8TBMdZPFgfuqI5yIk6g==}
+  '@storybook/addon-viewport@8.0.9':
     dependencies:
       memoizerific: 1.11.3
-    dev: true
 
-  /@storybook/blocks@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-F2zSrfSwzTFN7qW3zB80tG+EXtmfmCDC6Ird0F7tolszb6tOqJcAcBOwQbE2O0wI63sLu21qxzXgaKBMkiWvJg==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  '@storybook/blocks@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@storybook/channels': 8.0.9
       '@storybook/client-logger': 8.0.9
-      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/core-events': 8.0.9
       '@storybook/csf': 0.1.6
-      '@storybook/docs-tools': 8.0.9
+      '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
       '@storybook/global': 5.0.0
-      '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/preview-api': 8.0.9
-      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/types': 8.0.9
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
       lodash: 4.17.21
-      markdown-to-jsx: 7.3.2(react@18.2.0)
+      markdown-to-jsx: 7.3.2(react@18.3.1)
       memoizerific: 1.11.3
       polished: 4.2.2
-      react: 18.2.0
-      react-colorful: 5.6.1(react-dom@18.2.0)(react@18.2.0)
-      react-dom: 18.2.0(react@18.2.0)
+      react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       telejson: 7.2.0
       tocbot: 4.21.1
       ts-dedent: 2.2.0
       util-deprecate: 1.0.2
+    optionalDependencies:
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
     transitivePeerDependencies:
       - '@types/react'
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/builder-manager@8.0.9:
-    resolution: {integrity: sha512-/PxDwZIfMc/PSRZcasb6SIdGr3azIlenzx7dBF7Imt8i4jLHiAf1t00GvghlfJsvsrn4DNp95rbRbXTDyTj7tQ==}
+  '@storybook/builder-manager@8.0.9(encoding@0.1.13)':
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 8.0.9
+      '@storybook/core-common': 8.0.9(encoding@0.1.13)
       '@storybook/manager': 8.0.9
       '@storybook/node-logger': 8.0.9
       '@types/ejs': 3.1.2
-      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.19.11)
+      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.20.2)
       browser-assert: 1.2.1
       ejs: 3.1.9
-      esbuild: 0.19.11
+      esbuild: 0.20.2
       esbuild-plugin-alias: 0.2.1
-      express: 4.18.2
+      express: 4.19.2
       fs-extra: 11.1.1
       process: 0.11.10
       util: 0.12.5
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/builder-vite@8.0.9(typescript@5.3.3)(vite@5.1.4):
-    resolution: {integrity: sha512-7hEQFZIIz7VvxdySDpPE96iMvZxQvRZcRdhaNGeE+8Y2pyc3DgYE4WY3sjr+LUoB0a6TYLpAIKqbXwtLz0R+PQ==}
-    peerDependencies:
-      '@preact/preset-vite': '*'
-      typescript: '>= 4.3.x'
-      vite: ^4.0.0 || ^5.0.0
-      vite-plugin-glimmerx: '*'
-    peerDependenciesMeta:
-      '@preact/preset-vite':
-        optional: true
-      typescript:
-        optional: true
-      vite-plugin-glimmerx:
-        optional: true
+  '@storybook/builder-vite@8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@storybook/channels': 8.0.9
       '@storybook/client-logger': 8.0.9
-      '@storybook/core-common': 8.0.9
+      '@storybook/core-common': 8.0.9(encoding@0.1.13)
       '@storybook/core-events': 8.0.9
       '@storybook/csf-plugin': 8.0.9
       '@storybook/node-logger': 8.0.9
@@ -6249,37 +14506,33 @@ packages:
       fs-extra: 11.1.1
       magic-string: 0.30.7
       ts-dedent: 2.2.0
-      typescript: 5.3.3
-      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+    optionalDependencies:
+      typescript: 5.4.5
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/channels@8.0.9:
-    resolution: {integrity: sha512-7Lcfyy5CsLWWGhMPO9WG4jZ/Alzp0AjepFhEreYHRPtQrfttp6qMAjE/g1aHgun0qHCYWxwqIG4NLR/hqDNrXQ==}
+  '@storybook/channels@8.0.9':
     dependencies:
       '@storybook/client-logger': 8.0.9
       '@storybook/core-events': 8.0.9
       '@storybook/global': 5.0.0
       telejson: 7.2.0
       tiny-invariant: 1.3.1
-    dev: true
 
-  /@storybook/cli@8.0.9(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-lilYTKn8F5YOePijqfRYFa5v2mHVIJxPCIgTn+OXAmAFbcizZ6P8P6niU4J/NXulgx68Ln1M7hYhFtTP25hVTw==}
-    hasBin: true
+  '@storybook/cli@8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/types': 7.24.0
       '@ndelangen/get-tarball': 3.0.7
       '@storybook/codemod': 8.0.9
-      '@storybook/core-common': 8.0.9
+      '@storybook/core-common': 8.0.9(encoding@0.1.13)
       '@storybook/core-events': 8.0.9
-      '@storybook/core-server': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-server': 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
       '@storybook/csf-tools': 8.0.9
       '@storybook/node-logger': 8.0.9
-      '@storybook/telemetry': 8.0.9
+      '@storybook/telemetry': 8.0.9(encoding@0.1.13)
       '@storybook/types': 8.0.9
       '@types/semver': 7.5.8
       '@yarnpkg/fslib': 2.10.3
@@ -6295,7 +14548,7 @@ packages:
       get-npm-tarball-url: 2.0.3
       giget: 1.1.2
       globby: 11.1.0
-      jscodeshift: 0.15.1(@babel/preset-env@7.23.5)
+      jscodeshift: 0.15.1(@babel/preset-env@7.23.5(@babel/core@7.24.0))
       leven: 3.1.0
       ora: 5.4.1
       prettier: 3.2.5
@@ -6314,16 +14567,12 @@ packages:
       - react-dom
       - supports-color
       - utf-8-validate
-    dev: true
 
-  /@storybook/client-logger@8.0.9:
-    resolution: {integrity: sha512-LzV/RHkbf07sRc1Jc0ff36RlapKf9Ul7/+9VMvVbI3hshH1CpmrZK4t/tsIdpX/EVOdJ1Gg5cES06PnleOAIPA==}
+  '@storybook/client-logger@8.0.9':
     dependencies:
       '@storybook/global': 5.0.0
-    dev: true
 
-  /@storybook/codemod@8.0.9:
-    resolution: {integrity: sha512-VBeGpSZSQpL6iyLLqceJSNGhdCqcNwv+xC/aWdDFOkmuE1YfbmNNwpa9QYv4ZFJ2QjUsm4iTWG60qK+9NXeSKA==}
+  '@storybook/codemod@8.0.9':
     dependencies:
       '@babel/core': 7.24.0
       '@babel/preset-env': 7.23.5(@babel/core@7.24.0)
@@ -6335,38 +14584,31 @@ packages:
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
-      jscodeshift: 0.15.1(@babel/preset-env@7.23.5)
+      jscodeshift: 0.15.1(@babel/preset-env@7.23.5(@babel/core@7.24.0))
       lodash: 4.17.21
       prettier: 3.2.5
       recast: 0.23.6
       tiny-invariant: 1.3.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-JcwBGADzIJs0PSzqykrrD2KHzNG9wtexUOKuidt+FSv9szpUhe3qBAXIHpdfBRl7mOJ9TRZ5rt+mukEnfncdzA==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+  '@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@radix-ui/react-slot': 1.0.2(@types/react@18.0.28)(react@18.2.0)
+      '@radix-ui/react-slot': 1.0.2(@types/react@18.0.28)(react@18.3.1)
       '@storybook/client-logger': 8.0.9
       '@storybook/csf': 0.1.6
       '@storybook/global': 5.0.0
-      '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/types': 8.0.9
       memoizerific: 1.11.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
       util-deprecate: 1.0.2
     transitivePeerDependencies:
       - '@types/react'
-    dev: true
 
-  /@storybook/core-common@8.0.9:
-    resolution: {integrity: sha512-Jmue+sfHFb4GTYBzyWYw1MygoJiQSfISIrKmNIzAmZ+oR9EOr+jpu/i/bH+uetZ2Hqg1AGhj1VB7OtJp9HQyWw==}
+  '@storybook/core-common@8.0.9(encoding@0.1.13)':
     dependencies:
       '@storybook/core-events': 8.0.9
       '@storybook/csf-tools': 8.0.9
@@ -6376,17 +14618,17 @@ packages:
       '@yarnpkg/libzip': 2.3.0
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      esbuild: 0.19.11
-      esbuild-register: 3.5.0(esbuild@0.19.11)
+      esbuild: 0.20.2
+      esbuild-register: 3.5.0(esbuild@0.20.2)
       execa: 5.1.1
       file-system-cache: 2.3.0
       find-cache-dir: 3.3.2
       find-up: 5.0.0
       fs-extra: 11.1.1
-      glob: 10.3.10
+      glob: 10.3.12
       handlebars: 4.7.7
       lazy-universal-dotenv: 4.0.0
-      node-fetch: 2.7.0
+      node-fetch: 2.7.0(encoding@0.1.13)
       picomatch: 2.3.1
       pkg-dir: 5.0.0
       pretty-hrtime: 1.0.3
@@ -6399,33 +14641,29 @@ packages:
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/core-events@8.0.9:
-    resolution: {integrity: sha512-DxSUx7wG9Qe3OFUBnv3OrYq48J8UWNo2DUR5/JecJCtp3n++L4fAEW3J0IF5FfxpQDMQSp1yTNjZ2PaWCMd2ag==}
+  '@storybook/core-events@8.0.9':
     dependencies:
       ts-dedent: 2.2.0
-    dev: true
 
-  /@storybook/core-server@8.0.9(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-BIe1T5YUBl0GYxEjRoTQsvXD2pyuzL8rPTUD41zlzSQM0R8U6Iant9SzRms4u0+rKUm2mGxxKuODlUo5ewqaGA==}
+  '@storybook/core-server@8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@babel/core': 7.24.0
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 8.0.9
+      '@storybook/builder-manager': 8.0.9(encoding@0.1.13)
       '@storybook/channels': 8.0.9
-      '@storybook/core-common': 8.0.9
+      '@storybook/core-common': 8.0.9(encoding@0.1.13)
       '@storybook/core-events': 8.0.9
       '@storybook/csf': 0.1.6
       '@storybook/csf-tools': 8.0.9
       '@storybook/docs-mdx': 3.0.0
       '@storybook/global': 5.0.0
       '@storybook/manager': 8.0.9
-      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/node-logger': 8.0.9
       '@storybook/preview-api': 8.0.9
-      '@storybook/telemetry': 8.0.9
+      '@storybook/telemetry': 8.0.9(encoding@0.1.13)
       '@storybook/types': 8.0.9
       '@types/detect-port': 1.3.2
       '@types/node': 18.17.15
@@ -6452,7 +14690,7 @@ packages:
       util: 0.12.5
       util-deprecate: 1.0.2
       watchpack: 2.4.0
-      ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -6460,19 +14698,15 @@ packages:
       - react-dom
       - supports-color
       - utf-8-validate
-    dev: true
 
-  /@storybook/csf-plugin@8.0.9:
-    resolution: {integrity: sha512-pXaNCNi++kxKsqSWwvx215fPx8cNqvepLVxQ7B69qXLHj80DHn0Q3DFBO3sLXNiQMJ2JK4OYcTxMfuOiyzszKw==}
+  '@storybook/csf-plugin@8.0.9':
     dependencies:
       '@storybook/csf-tools': 8.0.9
       unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@storybook/csf-tools@8.0.9:
-    resolution: {integrity: sha512-PiNMhL97giLytTdQwuhsZ92buVk4gy9H/8DtrDhUc45/1OmF95gogm6T2Yap729SIFwgpOcuq/U3aVo6d6swVQ==}
+  '@storybook/csf-tools@8.0.9':
     dependencies:
       '@babel/generator': 7.23.6
       '@babel/parser': 7.24.0
@@ -6485,22 +14719,16 @@ packages:
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@storybook/csf@0.1.6:
-    resolution: {integrity: sha512-JjWnBptVhBYJ14yq+cHs66BXjykRUWQ5TlD1RhPxMOtavynYyV/Q+QR98/N+XB+mcPtFMm5I2DvNkpj0/Dk8Mw==}
+  '@storybook/csf@0.1.6':
     dependencies:
       type-fest: 2.19.0
-    dev: true
 
-  /@storybook/docs-mdx@3.0.0:
-    resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==}
-    dev: true
+  '@storybook/docs-mdx@3.0.0': {}
 
-  /@storybook/docs-tools@8.0.9:
-    resolution: {integrity: sha512-OzogAeOmeHea/MxSPKRBWtOQVNSpoq+OOpimO9YRA5h5GBRJ2TUOGT44Gny6QT4ll5AvQA8fIiq9KezKcLekAg==}
+  '@storybook/docs-tools@8.0.9(encoding@0.1.13)':
     dependencies:
-      '@storybook/core-common': 8.0.9
+      '@storybook/core-common': 8.0.9(encoding@0.1.13)
       '@storybook/core-events': 8.0.9
       '@storybook/preview-api': 8.0.9
       '@storybook/types': 8.0.9
@@ -6511,46 +14739,34 @@ packages:
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/global@5.0.0:
-    resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
-    dev: true
+  '@storybook/global@5.0.0': {}
 
-  /@storybook/icons@1.2.5(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-m3jnuE+zmkZy6K+cdUDzAoUuCJyl0fWCAXPCji7VZCH1TzFohyvnPqhc9JMkQpanej2TOW3wWXaplPzHghcBSg==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+  '@storybook/icons@1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
 
-  /@storybook/instrumenter@8.0.9:
-    resolution: {integrity: sha512-Gw74dgpTU/2p7FG0s7DuVdqCbJ2MEcSuRJjDo7HcXRYcvWp7I6Ly+C0v7N5VaoS+kbBVerAhLKIHZgG/LZf1og==}
+  '@storybook/instrumenter@8.0.9':
     dependencies:
       '@storybook/channels': 8.0.9
       '@storybook/client-logger': 8.0.9
       '@storybook/core-events': 8.0.9
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 8.0.9
-      '@vitest/utils': 1.5.3
+      '@vitest/utils': 1.6.0
       util: 0.12.5
-    dev: true
 
-  /@storybook/manager-api@8.0.9(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-99b3yKArDSvfabXL7QE3nA95e4DdW/5H/ZCcr6/E2qCQJayZ6G1v/WWamKXbiaTpkndulFmcb/+ZmnDXcweIIQ==}
+  '@storybook/manager-api@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@storybook/channels': 8.0.9
       '@storybook/client-logger': 8.0.9
       '@storybook/core-events': 8.0.9
       '@storybook/csf': 0.1.6
       '@storybook/global': 5.0.0
-      '@storybook/icons': 1.2.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/router': 8.0.9
-      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/types': 8.0.9
       dequal: 2.0.3
       lodash: 4.17.21
@@ -6561,18 +14777,12 @@ packages:
     transitivePeerDependencies:
       - react
       - react-dom
-    dev: true
 
-  /@storybook/manager@8.0.9:
-    resolution: {integrity: sha512-+NnRo+5JQFGNqveKrLtC0b+Z08Tae4m44iq292bPeZMpr9OkFsIkU0PBPsHTHPkrqC/zZXRNsCsTEgvu3p2OIA==}
-    dev: true
+  '@storybook/manager@8.0.9': {}
 
-  /@storybook/node-logger@8.0.9:
-    resolution: {integrity: sha512-5ajMdZFrYrjGLJOVDq7dlEQNFsgeLHymt4dCK9MulL/ciXykmXUZXE3Bye0wFy+I2qqDVvrvR8uzCvSFvm5MAQ==}
-    dev: true
+  '@storybook/node-logger@8.0.9': {}
 
-  /@storybook/preview-api@8.0.9:
-    resolution: {integrity: sha512-zHfX34bkAMzzmE7vbDzaqFwSW6ExiBD0HiO1L/IsHF55f0f7xV7IH8uJyFRrDTvAoW3ReSxZDMvvPpeydFPKGA==}
+  '@storybook/preview-api@8.0.9':
     dependencies:
       '@storybook/channels': 8.0.9
       '@storybook/client-logger': 8.0.9
@@ -6588,43 +14798,29 @@ packages:
       tiny-invariant: 1.3.1
       ts-dedent: 2.2.0
       util-deprecate: 1.0.2
-    dev: true
 
-  /@storybook/preview@8.0.9:
-    resolution: {integrity: sha512-tFsR8xc8AYBZZrZw8enklFbSQt7ZAV+rv20BoxwDhd3q7fjXyK7O4moGPqUwBZ7rukTG13nPoISxr+VXAk/HYA==}
-    dev: true
+  '@storybook/preview@8.0.9': {}
 
-  /@storybook/react-dom-shim@8.0.9(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-8011KlRuG3obr5pZZ7bcEyYYNWF3tR596YadoMd267NPoHKvwAbKL1L/DNgb6kiYjZDUf9QfaKSCWW31k0kcRQ==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+  '@storybook/react-dom-shim@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
 
-  /@storybook/react-vite@8.0.9(react-dom@18.2.0)(react@18.2.0)(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4):
-    resolution: {integrity: sha512-FT5KeulUH6grfzOJOxJCxpv9+81UVDrT9UPcgiFhQT9rKtsgmltezThwbHknByZNw3WWnf+ieidMLEis9hd73A==}
-    engines: {node: '>=18.0.0'}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-      vite: ^4.0.0 || ^5.0.0
+  '@storybook/react-vite@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.1.4)
-      '@rollup/pluginutils': 5.1.0(rollup@4.12.0)
-      '@storybook/builder-vite': 8.0.9(typescript@5.3.3)(vite@5.1.4)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
+      '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
+      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
       '@storybook/node-logger': 8.0.9
-      '@storybook/react': 8.0.9(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+      '@storybook/react': 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
       find-up: 5.0.0
       magic-string: 0.30.7
-      react: 18.2.0
+      react: 18.3.1
       react-docgen: 7.0.1
-      react-dom: 18.2.0(react@18.2.0)
+      react-dom: 18.3.1(react@18.3.1)
       resolve: 1.22.8
       tsconfig-paths: 4.2.0
-      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -6632,24 +14828,14 @@ packages:
       - supports-color
       - typescript
       - vite-plugin-glimmerx
-    dev: true
 
-  /@storybook/react@8.0.9(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-NeQ6suZG3HKikwe3Tx9cAIaRx7uP8FKCmlVvIiBg4LTTI5orCt94PPakvuZukZcbkqvcCnEBkebAzwUpn8PiJw==}
-    engines: {node: '>=18.0.0'}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-      typescript: '>= 4.2.x'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@storybook/react@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)':
     dependencies:
       '@storybook/client-logger': 8.0.9
-      '@storybook/docs-tools': 8.0.9
+      '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 8.0.9
-      '@storybook/react-dom-shim': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/react-dom-shim': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/types': 8.0.9
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
@@ -6661,42 +14847,37 @@ packages:
       html-tags: 3.2.0
       lodash: 4.17.21
       prop-types: 15.8.1
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+      react-element-to-jsx-string: 15.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       semver: 7.6.0
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      typescript: 5.3.3
       util-deprecate: 1.0.2
+    optionalDependencies:
+      typescript: 5.4.5
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/router@8.0.9:
-    resolution: {integrity: sha512-aAOWxbM9J4mt+cp4o88T2PB29mgBBTOzU37/pUsTHYnKnR9XI4npXEXdN8Gv+ryqM0kj0AbBpz/llFlnR2MNNA==}
+  '@storybook/router@8.0.9':
     dependencies:
       '@storybook/client-logger': 8.0.9
       memoizerific: 1.11.3
       qs: 6.11.1
-    dev: true
 
-  /@storybook/source-loader@8.0.9:
-    resolution: {integrity: sha512-FDnpxIGE5nIYT15pvYe6rz95TSBrdLcDll7lOHNyZisWt19MI3wZU3YkVsFNRBuFrebo+FjVU3wHyoV81ur1Qw==}
+  '@storybook/source-loader@8.0.9':
     dependencies:
       '@storybook/csf': 0.1.6
       '@storybook/types': 8.0.9
       estraverse: 5.3.0
       lodash: 4.17.21
       prettier: 3.2.5
-    dev: true
 
-  /@storybook/telemetry@8.0.9:
-    resolution: {integrity: sha512-AGGfcup06t+wxhBIkHd0iybieOh9PDVZQJ9oPct5JGB39+ni9wvs0WOD+MYlHbsjp8id7+aGkh6mYuYOvfck+Q==}
+  '@storybook/telemetry@8.0.9(encoding@0.1.13)':
     dependencies:
       '@storybook/client-logger': 8.0.9
-      '@storybook/core-common': 8.0.9
+      '@storybook/core-common': 8.0.9(encoding@0.1.13)
       '@storybook/csf-tools': 8.0.9
       chalk: 4.1.2
       detect-package-manager: 2.0.1
@@ -6706,20 +14887,18 @@ packages:
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@storybook/test@8.0.9(vitest@0.34.6):
-    resolution: {integrity: sha512-bRd5tBJnPzR6UKbDXONWnFWtdkNOY99HMLDUWe5fTRo50GwkrpFBVqPflhdkruEeof0kAbBUbnoN2CIYgtnAFw==}
+  '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@storybook/client-logger': 8.0.9
       '@storybook/core-events': 8.0.9
       '@storybook/instrumenter': 8.0.9
       '@storybook/preview-api': 8.0.9
       '@testing-library/dom': 9.3.4
-      '@testing-library/jest-dom': 6.4.2(vitest@0.34.6)
+      '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4)
       '@vitest/expect': 1.3.1
-      '@vitest/spy': 1.5.3
+      '@vitest/spy': 1.6.0
       util: 0.12.5
     transitivePeerDependencies:
       - '@jest/globals'
@@ -6727,50 +14906,34 @@ packages:
       - '@types/jest'
       - jest
       - vitest
-    dev: true
 
-  /@storybook/theming@8.0.9(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-jgfDuYoiNMMirQiASN3Eg0hGDXsEtpdAcMxyShqYGwu9elxgD9yUnYC2nSckYsM74a3ZQ3JaViZ9ZFSe2FHmeQ==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  '@storybook/theming@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
+      '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.3.1)
       '@storybook/client-logger': 8.0.9
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
+    optionalDependencies:
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
 
-  /@storybook/types@8.0.9:
-    resolution: {integrity: sha512-ew0EXzk9k4B557P1qIWYrnvUcgaE0WWA5qQS0AU8l+fRTp5nvl9O3SP/zNIB0SN1qDFO7dXr3idTNTyIikTcEQ==}
+  '@storybook/types@8.0.9':
     dependencies:
       '@storybook/channels': 8.0.9
       '@types/express': 4.17.17
       file-system-cache: 2.3.0
-    dev: true
 
-  /@storybook/vue3-vite@8.0.9(react-dom@18.2.0)(react@18.2.0)(vite@5.1.4)(vue@3.4.21):
-    resolution: {integrity: sha512-IkzYsEyCo5HIvLWbJeGrBu/VIN4u+LvdIAz7vcFqVVXBtTUhy+9/8caLx8fdnM0FWgKcBRQs8HnjBB2V0lOFcg==}
-    engines: {node: '>=18.0.0'}
-    peerDependencies:
-      vite: ^4.0.0 || ^5.0.0
+  '@storybook/vue3-vite@8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))':
     dependencies:
-      '@storybook/builder-vite': 8.0.9(typescript@5.3.3)(vite@5.1.4)
-      '@storybook/core-server': 8.0.9(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/vue3': 8.0.9(vue@3.4.21)
+      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
+      '@storybook/core-server': 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+      '@storybook/vue3': 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.4.5))
       find-package-json: 1.2.0
       magic-string: 0.30.7
-      typescript: 5.3.3
-      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
-      vue-component-meta: 2.0.16(typescript@5.3.3)
-      vue-docgen-api: 4.75.1(vue@3.4.21)
+      typescript: 5.4.5
+      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      vue-component-meta: 2.0.16(typescript@5.4.5)
+      vue-docgen-api: 4.75.1(vue@3.4.26(typescript@5.4.5))
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - bufferutil
@@ -6781,15 +14944,10 @@ packages:
       - utf-8-validate
       - vite-plugin-glimmerx
       - vue
-    dev: true
 
-  /@storybook/vue3@8.0.9(vue@3.4.21):
-    resolution: {integrity: sha512-EqVdS62YbOCAE0wJrQKW0sHpM90be8N8Mvmj+HzB0QYhJNtFqP9ehwbcTfwEKtaVGudisHgGBOzNoSKDlxFaag==}
-    engines: {node: '>=18.0.0'}
-    peerDependencies:
-      vue: ^3.0.0
+  '@storybook/vue3@8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.4.5))':
     dependencies:
-      '@storybook/docs-tools': 8.0.9
+      '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 8.0.9
       '@storybook/types': 8.0.9
@@ -6797,344 +14955,167 @@ packages:
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.4.21(typescript@5.3.3)
+      vue: 3.4.26(typescript@5.4.5)
       vue-component-type-helpers: 2.0.16
     transitivePeerDependencies:
       - encoding
       - supports-color
-    dev: true
 
-  /@swc/cli@0.1.63(@swc/core@1.3.107)(chokidar@3.5.3):
-    resolution: {integrity: sha512-EM9oxxHzmmsprYRbGqsS2M4M/Gr5Gkcl0ROYYIdlUyTkhOiX822EQiRCpPCwdutdnzH2GyaTN7wc6i0Y+CKd3A==}
-    engines: {node: '>= 12.13'}
-    hasBin: true
-    peerDependencies:
-      '@swc/core': ^1.2.66
-      chokidar: 3.5.3
-    peerDependenciesMeta:
-      chokidar:
-        optional: true
+  '@swc/cli@0.3.12(@swc/core@1.4.17)(chokidar@3.5.3)':
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
-      '@swc/core': 1.3.107
-      chokidar: 3.5.3
-      commander: 7.2.0
+      '@swc/core': 1.4.17
+      '@swc/counter': 0.1.3
+      commander: 8.3.0
       fast-glob: 3.3.2
-      semver: 7.5.4
+      minimatch: 9.0.3
+      piscina: 4.4.0
+      semver: 7.6.0
       slash: 3.0.0
       source-map: 0.7.4
-    dev: false
+    optionalDependencies:
+      chokidar: 3.5.3
 
-  /@swc/core-android-arm64@1.3.11:
-    resolution: {integrity: sha512-M7FamR3kFpVTyTw73FzKcOZmS7/TWHX75eqtwBTaU9fW4shf0KTLr/h9DnMxNKAnwUMeub/lqlINUe5EKFIKwQ==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
+  '@swc/core-android-arm64@1.3.11':
     dependencies:
       '@swc/wasm': 1.2.130
-    dev: false
     optional: true
 
-  /@swc/core-darwin-arm64@1.3.107:
-    resolution: {integrity: sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
+  '@swc/core-darwin-arm64@1.3.56':
     optional: true
 
-  /@swc/core-darwin-arm64@1.3.56:
-    resolution: {integrity: sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  '@swc/core-darwin-arm64@1.4.17':
     optional: true
 
-  /@swc/core-darwin-x64@1.3.107:
-    resolution: {integrity: sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
+  '@swc/core-darwin-x64@1.3.56':
     optional: true
 
-  /@swc/core-darwin-x64@1.3.56:
-    resolution: {integrity: sha512-VH5saqYFasdRXJy6RAT+MXm0+IjkMZvOkohJwUei+oA65cKJofQwrJ1jZro8yOJFYvUSI3jgNRGsdBkmo/4hMw==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  '@swc/core-darwin-x64@1.4.17':
     optional: true
 
-  /@swc/core-freebsd-x64@1.3.11:
-    resolution: {integrity: sha512-02uqYktPp6WmZfZ2Crc/yIVOcgANtjo8ciHcT7yLHvz7v+S7gx1I2tyNGUFtTX5hcR2IFNGrL8Yj4DvpTABFHg==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
+  '@swc/core-freebsd-x64@1.3.11':
     dependencies:
       '@swc/wasm': 1.2.130
-    dev: false
     optional: true
 
-  /@swc/core-linux-arm-gnueabihf@1.3.107:
-    resolution: {integrity: sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==}
-    engines: {node: '>=10'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
+  '@swc/core-linux-arm-gnueabihf@1.3.56':
     optional: true
 
-  /@swc/core-linux-arm-gnueabihf@1.3.56:
-    resolution: {integrity: sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==}
-    engines: {node: '>=10'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@swc/core-linux-arm-gnueabihf@1.4.17':
     optional: true
 
-  /@swc/core-linux-arm64-gnu@1.3.107:
-    resolution: {integrity: sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
+  '@swc/core-linux-arm64-gnu@1.3.56':
     optional: true
 
-  /@swc/core-linux-arm64-gnu@1.3.56:
-    resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@swc/core-linux-arm64-gnu@1.4.17':
     optional: true
 
-  /@swc/core-linux-arm64-musl@1.3.107:
-    resolution: {integrity: sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
+  '@swc/core-linux-arm64-musl@1.3.56':
     optional: true
 
-  /@swc/core-linux-arm64-musl@1.3.56:
-    resolution: {integrity: sha512-9gxL09BIiAv8zY0DjfnFf19bo8+P4T9tdhzPwcm+1yPJcY5yr1+YFWLNFzz01agtOj6VlZ2/wUJTaOfdjjtc+A==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@swc/core-linux-arm64-musl@1.4.17':
     optional: true
 
-  /@swc/core-linux-x64-gnu@1.3.107:
-    resolution: {integrity: sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
+  '@swc/core-linux-x64-gnu@1.3.56':
     optional: true
 
-  /@swc/core-linux-x64-gnu@1.3.56:
-    resolution: {integrity: sha512-n0ORNknl50vMRkll3BDO1E4WOqY6iISlPV1ZQCRLWQ6YQ2q8/WAryBxc2OAybcGHBUFkxyACpJukeU1QZ/9tNw==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@swc/core-linux-x64-gnu@1.4.17':
     optional: true
 
-  /@swc/core-linux-x64-musl@1.3.107:
-    resolution: {integrity: sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
+  '@swc/core-linux-x64-musl@1.3.56':
     optional: true
 
-  /@swc/core-linux-x64-musl@1.3.56:
-    resolution: {integrity: sha512-r+D34WLAOAlJtfw1gaVWpHRwCncU9nzW9i7w9kSw4HpWYnHJOz54jLGSEmNsrhdTCz1VK2ar+V2ktFUsrlGlDA==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  '@swc/core-linux-x64-musl@1.4.17':
     optional: true
 
-  /@swc/core-win32-arm64-msvc@1.3.107:
-    resolution: {integrity: sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
+  '@swc/core-win32-arm64-msvc@1.3.56':
     optional: true
 
-  /@swc/core-win32-arm64-msvc@1.3.56:
-    resolution: {integrity: sha512-29Yt75Is6X24z3x8h/xZC1HnDPkPpyLH9mDQiM6Cuc0I9mVr1XSriPEUB2N/awf5IE4SA8c+3IVq1DtKWbkJIw==}
-    engines: {node: '>=10'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+  '@swc/core-win32-arm64-msvc@1.4.17':
     optional: true
-
-  /@swc/core-win32-ia32-msvc@1.3.107:
-    resolution: {integrity: sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==}
-    engines: {node: '>=10'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
+
+  '@swc/core-win32-ia32-msvc@1.3.56':
     optional: true
 
-  /@swc/core-win32-ia32-msvc@1.3.56:
-    resolution: {integrity: sha512-mplp0zbYDrcHtfvkniXlXdB04e2qIjz2Gq/XHKr4Rnc6xVORJjjXF91IemXKpavx2oZYJws+LNJL7UFQ8jyCdQ==}
-    engines: {node: '>=10'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+  '@swc/core-win32-ia32-msvc@1.4.17':
     optional: true
 
-  /@swc/core-win32-x64-msvc@1.3.107:
-    resolution: {integrity: sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
+  '@swc/core-win32-x64-msvc@1.3.56':
     optional: true
 
-  /@swc/core-win32-x64-msvc@1.3.56:
-    resolution: {integrity: sha512-zp8MBnrw/bjdLenO/ifYzHrImSjKunqL0C2IF4LXYNRfcbYFh2NwobsVQMZ20IT0474lKRdlP8Oxdt+bHuXrzA==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+  '@swc/core-win32-x64-msvc@1.4.17':
     optional: true
 
-  /@swc/core@1.3.107:
-    resolution: {integrity: sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==}
-    engines: {node: '>=10'}
-    requiresBuild: true
-    peerDependencies:
-      '@swc/helpers': ^0.5.0
-    peerDependenciesMeta:
-      '@swc/helpers':
-        optional: true
+  '@swc/core@1.4.17':
     dependencies:
-      '@swc/counter': 0.1.1
+      '@swc/counter': 0.1.3
       '@swc/types': 0.1.5
     optionalDependencies:
-      '@swc/core-darwin-arm64': 1.3.107
-      '@swc/core-darwin-x64': 1.3.107
-      '@swc/core-linux-arm-gnueabihf': 1.3.107
-      '@swc/core-linux-arm64-gnu': 1.3.107
-      '@swc/core-linux-arm64-musl': 1.3.107
-      '@swc/core-linux-x64-gnu': 1.3.107
-      '@swc/core-linux-x64-musl': 1.3.107
-      '@swc/core-win32-arm64-msvc': 1.3.107
-      '@swc/core-win32-ia32-msvc': 1.3.107
-      '@swc/core-win32-x64-msvc': 1.3.107
-
-  /@swc/counter@0.1.1:
-    resolution: {integrity: sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==}
-
-  /@swc/jest@0.2.31(@swc/core@1.3.107):
-    resolution: {integrity: sha512-Gh0Ste380O8KUY1IqsKr+aOvqqs2Loa+WcWWVNwl+lhXqOWK1iTFAP1K0IDfLqAuFP68+D/PxcpBJn21e6Quvw==}
-    engines: {npm: '>= 7.0.0'}
-    peerDependencies:
-      '@swc/core': '*'
+      '@swc/core-darwin-arm64': 1.4.17
+      '@swc/core-darwin-x64': 1.4.17
+      '@swc/core-linux-arm-gnueabihf': 1.4.17
+      '@swc/core-linux-arm64-gnu': 1.4.17
+      '@swc/core-linux-arm64-musl': 1.4.17
+      '@swc/core-linux-x64-gnu': 1.4.17
+      '@swc/core-linux-x64-musl': 1.4.17
+      '@swc/core-win32-arm64-msvc': 1.4.17
+      '@swc/core-win32-ia32-msvc': 1.4.17
+      '@swc/core-win32-x64-msvc': 1.4.17
+
+  '@swc/counter@0.1.3': {}
+
+  '@swc/jest@0.2.36(@swc/core@1.4.17)':
     dependencies:
       '@jest/create-cache-key-function': 29.7.0
-      '@swc/core': 1.3.107
+      '@swc/core': 1.4.17
+      '@swc/counter': 0.1.3
       jsonc-parser: 3.2.0
-    dev: true
 
-  /@swc/types@0.1.5:
-    resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
+  '@swc/types@0.1.5': {}
 
-  /@swc/wasm@1.2.130:
-    resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
-    requiresBuild: true
-    dev: false
+  '@swc/wasm@1.2.130':
     optional: true
 
-  /@syuilo/aiscript@0.18.0:
-    resolution: {integrity: sha512-/iY9Vv4LLjtW/KUzId1QwXC4BlpIEPCMcoT7dyRhYdyxtwhS3Hx4b/4j1HYP+n3Pq9XKyW5zvkY72/+DNu4g6Q==}
+  '@syuilo/aiscript@0.18.0':
     dependencies:
       seedrandom: 3.0.5
       stringz: 2.1.0
       uuid: 9.0.1
-    dev: false
 
-  /@szmarczak/http-timer@4.0.6:
-    resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
-    engines: {node: '>=10'}
+  '@szmarczak/http-timer@4.0.6':
     dependencies:
       defer-to-connect: 2.0.1
-    dev: false
 
-  /@szmarczak/http-timer@5.0.1:
-    resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
-    engines: {node: '>=14.16'}
+  '@szmarczak/http-timer@5.0.1':
     dependencies:
       defer-to-connect: 2.0.1
 
-  /@tabler/icons-webfont@2.44.0:
-    resolution: {integrity: sha512-E5+CYnZXKTUGFhpb0KGiRR4s+Tylbsq6/CA88QKn44xaBfU3ui/zVwKc1vaTKBL9kxZIxNz7qLkne+lUd/Hvzw==}
+  '@tabler/icons-webfont@3.3.0':
     dependencies:
-      '@tabler/icons': 2.44.0
-    dev: false
+      '@tabler/icons': 3.3.0
 
-  /@tabler/icons@2.44.0:
-    resolution: {integrity: sha512-WPPtihDcAwEm1QZM9MXQw6+r/R2/qx7KMU1eegsi9DsqBLAb0W2kbt6e/syvd6j9c+6XNpRVBW1ziGqSWQAWOg==}
-    dev: false
+  '@tabler/icons@3.3.0': {}
 
-  /@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0):
-    resolution: {integrity: sha512-d4eln500/qNym78z9IrUUzF0ITBoJGLrxV8xd92kLVoXhg35Mm+zqUXShjFcrH8joOHOFuST0qZ0TbDDqcPzPA==}
-    engines: {yarn: '>= 1.3.2'}
-    requiresBuild: true
-    peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+  '@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
       '@types/seedrandom': 2.4.30
       seedrandom: 3.0.5
-    dev: false
 
-  /@tensorflow/tfjs-backend-webgl@4.4.0(@tensorflow/tfjs-core@4.4.0):
-    resolution: {integrity: sha512-TzQKvfAPgGt9cMG+5bVoTckoG1xr/PVJM/uODkPvzcMqi3j97kuWDXwkYJIgXldStmfiKkU7f5CmyD3Cq3E6BA==}
-    engines: {yarn: '>= 1.3.2'}
-    requiresBuild: true
-    peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+  '@tensorflow/tfjs-backend-webgl@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0)
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
       '@types/offscreencanvas': 2019.3.0
       '@types/seedrandom': 2.4.30
       '@types/webgl-ext': 0.0.30
       seedrandom: 3.0.5
-    dev: false
 
-  /@tensorflow/tfjs-converter@4.4.0(@tensorflow/tfjs-core@4.4.0):
-    resolution: {integrity: sha512-JUjpRStrAuw37tgPd5UENu0UjQVuJT09yF7KpOur4BriJ0uQqrbEZHMPHmvUtr5nYzkqlXJTuXIyxvEY/olNpg==}
-    requiresBuild: true
-    peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+  '@tensorflow/tfjs-converter@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0
-    dev: false
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
 
-  /@tensorflow/tfjs-core@4.4.0:
-    resolution: {integrity: sha512-Anxpc7cAOA0Q7EUXdTbQKMg3reFvrdkgDlaYzH9ZfkMq2CgLV4Au6E/s6HmbYn/VrAtWy9mLY5c/lLJqh4764g==}
-    engines: {yarn: '>= 1.3.2'}
-    requiresBuild: true
+  '@tensorflow/tfjs-core@4.4.0(encoding@0.1.13)':
     dependencies:
       '@types/long': 4.0.2
       '@types/offscreencanvas': 2019.7.0
@@ -7142,44 +15123,29 @@ packages:
       '@types/webgl-ext': 0.0.30
       '@webgpu/types': 0.1.30
       long: 4.0.0
-      node-fetch: 2.6.11
+      node-fetch: 2.6.11(encoding@0.1.13)
       seedrandom: 3.0.5
     transitivePeerDependencies:
       - encoding
-    dev: false
 
-  /@tensorflow/tfjs-data@4.4.0(@tensorflow/tfjs-core@4.4.0)(seedrandom@3.0.5):
-    resolution: {integrity: sha512-aY4eq4cgrsrXeBU6ABZAAN3tV0fG4YcHd0z+cYuNXnCo+VEQLJnPmhn+xymZ4VQZQH4GXbVS4dV9pXMclFNRFw==}
-    requiresBuild: true
-    peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
-      seedrandom: ^3.0.5
+  '@tensorflow/tfjs-data@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
       '@types/node-fetch': 2.6.4
-      node-fetch: 2.6.11
+      node-fetch: 2.6.11(encoding@0.1.13)
       seedrandom: 3.0.5
       string_decoder: 1.3.0
     transitivePeerDependencies:
       - encoding
-    dev: false
 
-  /@tensorflow/tfjs-layers@4.4.0(@tensorflow/tfjs-core@4.4.0):
-    resolution: {integrity: sha512-OGC7shfiD9Gc698hINHK4y9slOJvu5m54tVNm4xf+WSNrw/avvgpar6yyoL5bakYIZNQvFNK75Yr8VRPR7oPeQ==}
-    requiresBuild: true
-    peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+  '@tensorflow/tfjs-layers@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0
-    dev: false
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
 
-  /@tensorflow/tfjs-node@4.4.0(seedrandom@3.0.5):
-    resolution: {integrity: sha512-+JSAddsupjSQUDZeb7QGOFkL3Tty3kjPHx8ethiYFzwTZJHCMvM7wZJd0Fqnjxym6A0KpsmB7SPZgwRRXVIlPA==}
-    engines: {node: '>=8.11.0'}
-    requiresBuild: true
+  '@tensorflow/tfjs-node@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
-      '@mapbox/node-pre-gyp': 1.0.9
-      '@tensorflow/tfjs': 4.4.0(seedrandom@3.0.5)
+      '@mapbox/node-pre-gyp': 1.0.9(encoding@0.1.13)
+      '@tensorflow/tfjs': 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
       adm-zip: 0.5.10
       google-protobuf: 3.21.2
       https-proxy-agent: 2.2.4
@@ -7190,20 +15156,16 @@ packages:
       - encoding
       - seedrandom
       - supports-color
-    dev: false
     optional: true
 
-  /@tensorflow/tfjs@4.4.0(seedrandom@3.0.5):
-    resolution: {integrity: sha512-EmCsnzdvawyk4b+4JKaLLuicHcJQRZtL1zSy9AWJLiiHTbDDseYgLxfaCEfLk8v2bUe7SBXwl3n3B7OjgvH11Q==}
-    hasBin: true
-    requiresBuild: true
+  '@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
-      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0)
-      '@tensorflow/tfjs-backend-webgl': 4.4.0(@tensorflow/tfjs-core@4.4.0)
-      '@tensorflow/tfjs-converter': 4.4.0(@tensorflow/tfjs-core@4.4.0)
-      '@tensorflow/tfjs-core': 4.4.0
-      '@tensorflow/tfjs-data': 4.4.0(@tensorflow/tfjs-core@4.4.0)(seedrandom@3.0.5)
-      '@tensorflow/tfjs-layers': 4.4.0(@tensorflow/tfjs-core@4.4.0)
+      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-backend-webgl': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-converter': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
+      '@tensorflow/tfjs-data': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)
+      '@tensorflow/tfjs-layers': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
       argparse: 1.0.10
       chalk: 4.1.2
       core-js: 3.29.1
@@ -7212,11 +15174,8 @@ packages:
     transitivePeerDependencies:
       - encoding
       - seedrandom
-    dev: false
 
-  /@testing-library/dom@9.3.3:
-    resolution: {integrity: sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==}
-    engines: {node: '>=14'}
+  '@testing-library/dom@9.3.3':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/runtime': 7.23.4
@@ -7226,11 +15185,8 @@ packages:
       dom-accessibility-api: 0.5.16
       lz-string: 1.5.0
       pretty-format: 27.5.1
-    dev: true
 
-  /@testing-library/dom@9.3.4:
-    resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==}
-    engines: {node: '>=14'}
+  '@testing-library/dom@9.3.4':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/runtime': 7.23.4
@@ -7240,28 +15196,8 @@ packages:
       dom-accessibility-api: 0.5.16
       lz-string: 1.5.0
       pretty-format: 27.5.1
-    dev: true
 
-  /@testing-library/jest-dom@6.4.2(vitest@0.34.6):
-    resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==}
-    engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
-    peerDependencies:
-      '@jest/globals': '>= 28'
-      '@types/bun': latest
-      '@types/jest': '>= 28'
-      jest: '>= 28'
-      vitest: '>= 0.32'
-    peerDependenciesMeta:
-      '@jest/globals':
-        optional: true
-      '@types/bun':
-        optional: true
-      '@types/jest':
-        optional: true
-      jest:
-        optional: true
-      vitest:
-        optional: true
+  '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@adobe/css-tools': 4.3.3
       '@babel/runtime': 7.23.4
@@ -7271,686 +15207,411 @@ packages:
       dom-accessibility-api: 0.6.3
       lodash: 4.17.21
       redent: 3.0.0
-      vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
-    dev: true
+    optionalDependencies:
+      '@jest/globals': 29.7.0
+      '@types/jest': 29.5.12
+      jest: 29.7.0(@types/node@20.12.7)
+      vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
 
-  /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4):
-    resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==}
-    engines: {node: '>=12', npm: '>=6'}
-    peerDependencies:
-      '@testing-library/dom': '>=7.21.4'
+  '@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4)':
     dependencies:
       '@testing-library/dom': 9.3.4
-    dev: true
 
-  /@testing-library/vue@8.0.2(@vue/compiler-sfc@3.4.21)(vue@3.4.21):
-    resolution: {integrity: sha512-A8wWX+qQn0o0izpQWnGCpwQt8wAdpsVP8vPP2h5Q/jcGhZ5yKXz9PPUqhQv+45LTFaWlyRf8bArTVaB/KFFd5A==}
-    engines: {node: '>=14'}
-    peerDependencies:
-      '@vue/compiler-sfc': '>= 3'
-      vue: '>= 3'
-    peerDependenciesMeta:
-      '@vue/compiler-sfc':
-        optional: true
+  '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
     dependencies:
       '@babel/runtime': 7.23.4
       '@testing-library/dom': 9.3.3
-      '@vue/compiler-sfc': 3.4.21
-      '@vue/test-utils': 2.4.1(vue@3.4.21)
-      vue: 3.4.21(typescript@5.3.3)
+      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
+      vue: 3.4.26(typescript@5.4.5)
+    optionalDependencies:
+      '@vue/compiler-sfc': 3.4.26
     transitivePeerDependencies:
       - '@vue/server-renderer'
-    dev: true
 
-  /@tokenizer/token@0.3.0:
-    resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
-    dev: false
+  '@tokenizer/token@0.3.0': {}
 
-  /@trysound/sax@0.2.0:
-    resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
-    engines: {node: '>=10.13.0'}
-    dev: false
+  '@trysound/sax@0.2.0': {}
 
-  /@tsd/typescript@5.3.3:
-    resolution: {integrity: sha512-CQlfzol0ldaU+ftWuG52vH29uRoKboLinLy84wS8TQOu+m+tWoaUfk4svL4ij2V8M5284KymJBlHUusKj6k34w==}
-    engines: {node: '>=14.17'}
-    dev: true
+  '@tsd/typescript@5.3.3': {}
 
-  /@twemoji/parser@15.0.0:
-    resolution: {integrity: sha512-lh9515BNsvKSNvyUqbj5yFu83iIDQ77SwVcsN/SnEGawczhsKU6qWuogewN1GweTi5Imo5ToQ9s+nNTf97IXvg==}
-    dev: false
+  '@twemoji/parser@15.0.0': {}
 
-  /@types/accepts@1.3.7:
-    resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
+  '@twemoji/parser@15.1.1': {}
+
+  '@types/accepts@1.3.7':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/archiver@6.0.2:
-    resolution: {integrity: sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==}
+  '@types/archiver@6.0.2':
     dependencies:
       '@types/readdir-glob': 1.1.1
-    dev: true
 
-  /@types/argparse@1.0.38:
-    resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
-    dev: true
+  '@types/argparse@1.0.38': {}
 
-  /@types/aria-query@5.0.1:
-    resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==}
-    dev: true
+  '@types/aria-query@5.0.1': {}
 
-  /@types/babel__core@7.20.0:
-    resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
+  '@types/babel__core@7.20.0':
     dependencies:
-      '@babel/parser': 7.23.9
-      '@babel/types': 7.23.5
+      '@babel/parser': 7.24.0
+      '@babel/types': 7.24.0
       '@types/babel__generator': 7.6.4
       '@types/babel__template': 7.4.1
       '@types/babel__traverse': 7.20.0
-    dev: true
 
-  /@types/babel__generator@7.6.4:
-    resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
+  '@types/babel__generator@7.6.4':
     dependencies:
-      '@babel/types': 7.23.5
-    dev: true
+      '@babel/types': 7.24.0
 
-  /@types/babel__template@7.4.1:
-    resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
+  '@types/babel__template@7.4.1':
     dependencies:
-      '@babel/parser': 7.23.9
-      '@babel/types': 7.23.5
-    dev: true
+      '@babel/parser': 7.24.0
+      '@babel/types': 7.24.0
 
-  /@types/babel__traverse@7.20.0:
-    resolution: {integrity: sha512-TBOjqAGf0hmaqRwpii5LLkJLg7c6OMm4nHLmpsUxwk9bBHtoTC6dAHdVWdGv4TBxj2CZOZY8Xfq8WmfoVi7n4Q==}
+  '@types/babel__traverse@7.20.0':
     dependencies:
-      '@babel/types': 7.23.5
-    dev: true
+      '@babel/types': 7.24.0
 
-  /@types/bcryptjs@2.4.6:
-    resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==}
-    dev: true
+  '@types/bcryptjs@2.4.6': {}
 
-  /@types/body-parser@1.19.5:
-    resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
+  '@types/body-parser@1.19.5':
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/braces@3.0.1:
-    resolution: {integrity: sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==}
-    dev: true
+  '@types/braces@3.0.1': {}
 
-  /@types/cacheable-request@6.0.3:
-    resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
+  '@types/cacheable-request@6.0.3':
     dependencies:
       '@types/http-cache-semantics': 4.0.4
       '@types/keyv': 3.1.4
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       '@types/responselike': 1.0.0
-    dev: false
 
-  /@types/chai-subset@1.3.5:
-    resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==}
+  '@types/chai-subset@1.3.5':
     dependencies:
       '@types/chai': 4.3.11
-    dev: true
 
-  /@types/chai@4.3.11:
-    resolution: {integrity: sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==}
-    dev: true
+  '@types/chai@4.3.11': {}
 
-  /@types/color-convert@2.0.3:
-    resolution: {integrity: sha512-2Q6wzrNiuEvYxVQqhh7sXM2mhIhvZR/Paq4FdsQkOMgWsCIkKvSGj8Le1/XalulrmgOzPMqNa0ix+ePY4hTrfg==}
+  '@types/color-convert@2.0.3':
     dependencies:
       '@types/color-name': 1.1.1
-    dev: true
 
-  /@types/color-name@1.1.1:
-    resolution: {integrity: sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==}
-    dev: true
+  '@types/color-name@1.1.1': {}
 
-  /@types/connect@3.4.35:
-    resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
+  '@types/connect@3.4.35':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/content-disposition@0.5.8:
-    resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==}
-    dev: true
+  '@types/content-disposition@0.5.8': {}
 
-  /@types/cookie@0.6.0:
-    resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
-    dev: true
+  '@types/cookie@0.6.0': {}
 
-  /@types/cross-spawn@6.0.2:
-    resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
+  '@types/cross-spawn@6.0.2':
     dependencies:
-      '@types/node': 20.11.28
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/debug@4.1.12:
-    resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+  '@types/debug@4.1.12':
     dependencies:
       '@types/ms': 0.7.34
-    dev: true
 
-  /@types/detect-port@1.3.2:
-    resolution: {integrity: sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==}
-    dev: true
+  '@types/detect-port@1.3.2': {}
 
-  /@types/disposable-email-domains@1.0.2:
-    resolution: {integrity: sha512-SDKwyYTjk3y5aZBxxc38yRecpJPjsqn57STz1bNxYYlv4k11bBe7QB8w4llXDTmQXKT1mFvgGmJv+8Zdu3YmJw==}
-    dev: false
+  '@types/disposable-email-domains@1.0.2': {}
 
-  /@types/doctrine@0.0.3:
-    resolution: {integrity: sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==}
-    dev: true
+  '@types/doctrine@0.0.3': {}
 
-  /@types/doctrine@0.0.9:
-    resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==}
-    dev: true
+  '@types/doctrine@0.0.9': {}
 
-  /@types/ejs@3.1.2:
-    resolution: {integrity: sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==}
-    dev: true
+  '@types/ejs@3.1.2': {}
 
-  /@types/emscripten@1.39.7:
-    resolution: {integrity: sha512-tLqYV94vuqDrXh515F/FOGtBcRMTPGvVV1LzLbtYDcQmmhtpf/gLYf+hikBbQk8MzOHNz37wpFfJbYAuSn8HqA==}
-    dev: true
+  '@types/emscripten@1.39.7': {}
 
-  /@types/escape-regexp@0.0.3:
-    resolution: {integrity: sha512-FQMYUxaf1dVeWLUzJFSvfdDugfOpDyM13p67QfyMdagxSkBa689opkr/q9uR/VWyrWrl0jAyQaSPKxX9MpAXFw==}
-    dev: true
+  '@types/escape-regexp@0.0.3': {}
 
-  /@types/escodegen@0.0.6:
-    resolution: {integrity: sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig==}
-    dev: true
+  '@types/escodegen@0.0.6': {}
 
-  /@types/eslint@7.29.0:
-    resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==}
+  '@types/eslint@7.29.0':
     dependencies:
       '@types/estree': 1.0.5
       '@types/json-schema': 7.0.12
-    dev: true
 
-  /@types/estree@0.0.51:
-    resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
-    dev: true
+  '@types/estree@0.0.51': {}
 
-  /@types/estree@1.0.5:
-    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+  '@types/estree@1.0.5': {}
 
-  /@types/express-serve-static-core@4.17.33:
-    resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
+  '@types/express-serve-static-core@4.17.33':
     dependencies:
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
-    dev: true
 
-  /@types/express@4.17.17:
-    resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==}
+  '@types/express@4.17.17':
     dependencies:
       '@types/body-parser': 1.19.5
       '@types/express-serve-static-core': 4.17.33
       '@types/qs': 6.9.7
       '@types/serve-static': 1.15.1
-    dev: true
 
-  /@types/find-cache-dir@3.2.1:
-    resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==}
-    dev: true
+  '@types/find-cache-dir@3.2.1': {}
 
-  /@types/fluent-ffmpeg@2.1.24:
-    resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
+  '@types/fluent-ffmpeg@2.1.24':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/glob@7.2.0:
-    resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
+  '@types/glob@7.2.0':
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.11.28
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/graceful-fs@4.1.6:
-    resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
+  '@types/graceful-fs@4.1.6':
     dependencies:
-      '@types/node': 20.11.28
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/hast@3.0.4:
-    resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+  '@types/hast@3.0.4':
     dependencies:
       '@types/unist': 3.0.2
-    dev: true
 
-  /@types/htmlescape@1.1.3:
-    resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==}
-    dev: true
+  '@types/htmlescape@1.1.3': {}
 
-  /@types/http-cache-semantics@4.0.4:
-    resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+  '@types/http-cache-semantics@4.0.4': {}
 
-  /@types/http-link-header@1.0.5:
-    resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
+  '@types/http-link-header@1.0.5':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/istanbul-lib-coverage@2.0.4:
-    resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
-    dev: true
+  '@types/istanbul-lib-coverage@2.0.4': {}
 
-  /@types/istanbul-lib-report@3.0.0:
-    resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==}
+  '@types/istanbul-lib-report@3.0.0':
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
-    dev: true
 
-  /@types/istanbul-reports@3.0.1:
-    resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==}
+  '@types/istanbul-reports@3.0.1':
     dependencies:
       '@types/istanbul-lib-report': 3.0.0
-    dev: true
-
-  /@types/jest@29.5.11:
-    resolution: {integrity: sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==}
-    dependencies:
-      expect: 29.7.0
-      pretty-format: 29.7.0
-    dev: true
 
-  /@types/jest@29.5.12:
-    resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==}
+  '@types/jest@29.5.12':
     dependencies:
       expect: 29.7.0
       pretty-format: 29.7.0
-    dev: true
 
-  /@types/js-yaml@4.0.9:
-    resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
-    dev: true
+  '@types/js-yaml@4.0.9': {}
 
-  /@types/jsdom@21.1.6:
-    resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
+  '@types/jsdom@21.1.6':
     dependencies:
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
-    dev: true
 
-  /@types/json-schema@7.0.12:
-    resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
-    dev: true
+  '@types/json-schema@7.0.12': {}
 
-  /@types/json5@0.0.29:
-    resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
-    dev: true
+  '@types/json-schema@7.0.15': {}
 
-  /@types/jsonld@1.5.13:
-    resolution: {integrity: sha512-n7fUU6W4kSYK8VQlf/LsE9kddBHPKhODoVOjsZswmve+2qLwBy6naWxs/EiuSZN9NU0N06Ra01FR+j87C62T0A==}
-    dev: true
+  '@types/json5@0.0.29': {}
 
-  /@types/jsrsasign@10.5.12:
-    resolution: {integrity: sha512-sOA+eVnHU+FziThpMhuqs/tjFKe5gHVJKIS7g1BzhXP+e2FS8OvtzM0K3IzFxVksDOr98Gz5FJiZVxZ9uFoHhw==}
-    dev: true
+  '@types/jsonld@1.5.13': {}
 
-  /@types/keyv@3.1.4:
-    resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+  '@types/jsrsasign@10.5.14': {}
+
+  '@types/keyv@3.1.4':
     dependencies:
-      '@types/node': 20.11.22
-    dev: false
+      '@types/node': 20.12.7
 
-  /@types/lodash@4.14.191:
-    resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
-    dev: true
+  '@types/lodash@4.14.191': {}
 
-  /@types/long@4.0.2:
-    resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==}
-    requiresBuild: true
-    dev: false
+  '@types/long@4.0.2': {}
 
-  /@types/matter-js@0.19.6:
-    resolution: {integrity: sha512-ffk6tqJM5scla+ThXmnox+mdfCo3qYk6yMjQsNcrbo6eQ5DqorVdtnaL+1agCoYzxUjmHeiNB7poBMAmhuLY7w==}
-    dev: true
+  '@types/matter-js@0.19.6': {}
 
-  /@types/mdast@4.0.3:
-    resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
+  '@types/mdast@4.0.3':
     dependencies:
       '@types/unist': 3.0.2
-    dev: true
 
-  /@types/mdx@2.0.3:
-    resolution: {integrity: sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==}
-    dev: true
+  '@types/mdx@2.0.3': {}
 
-  /@types/micromatch@4.0.6:
-    resolution: {integrity: sha512-2eulCHWqjEpk9/vyic4tBhI8a9qQEl6DaK2n/sF7TweX9YESlypgKyhXMDGt4DAOy/jhLPvVrZc8pTDAMsplJA==}
+  '@types/micromatch@4.0.7':
     dependencies:
       '@types/braces': 3.0.1
-    dev: true
 
-  /@types/mime-types@2.1.4:
-    resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==}
-    dev: true
+  '@types/mime-types@2.1.4': {}
 
-  /@types/mime@3.0.1:
-    resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
-    dev: true
+  '@types/mime@3.0.1': {}
 
-  /@types/minimatch@5.1.2:
-    resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
-    dev: true
+  '@types/minimatch@5.1.2': {}
 
-  /@types/minimist@1.2.2:
-    resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==}
-    dev: true
+  '@types/minimist@1.2.2': {}
 
-  /@types/ms@0.7.34:
-    resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
-    dev: true
+  '@types/ms@0.7.34': {}
 
-  /@types/node-fetch@2.6.4:
-    resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
-    requiresBuild: true
+  '@types/mute-stream@0.0.4':
     dependencies:
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
+
+  '@types/node-fetch@2.6.4':
+    dependencies:
+      '@types/node': 20.12.7
       form-data: 3.0.1
-    dev: false
 
-  /@types/node-fetch@3.0.3:
-    resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==}
-    deprecated: This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.
+  '@types/node-fetch@3.0.3':
     dependencies:
       node-fetch: 3.3.2
-    dev: true
 
-  /@types/node@18.17.15:
-    resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
-    dev: true
-
-  /@types/node@20.11.22:
-    resolution: {integrity: sha512-/G+IxWxma6V3E+pqK1tSl2Fo1kl41pK1yeCyDsgkF9WlVAme4j5ISYM2zR11bgLFJGLN5sVK40T4RJNuiZbEjA==}
-    dependencies:
-      undici-types: 5.26.5
+  '@types/node@18.17.15': {}
 
-  /@types/node@20.11.28:
-    resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==}
+  '@types/node@20.11.5':
     dependencies:
       undici-types: 5.26.5
-    dev: true
 
-  /@types/node@20.11.5:
-    resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==}
+  '@types/node@20.12.7':
     dependencies:
       undici-types: 5.26.5
-    dev: true
 
-  /@types/node@20.9.1:
-    resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==}
+  '@types/node@20.9.1':
     dependencies:
       undici-types: 5.26.5
-    dev: true
 
-  /@types/nodemailer@6.4.14:
-    resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
+  '@types/nodemailer@6.4.15':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/normalize-package-data@2.4.1:
-    resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
-    dev: true
+  '@types/normalize-package-data@2.4.1': {}
 
-  /@types/oauth2orize-pkce@0.1.2:
-    resolution: {integrity: sha512-g5rDzqQTTUIJJpY7UWxb0EU1WyURIwOj3TndKC2krEEEmaKrnZXgoEBkR72QY2kp4cJ6N9cF2AqTPJ0Qyg+caA==}
+  '@types/oauth2orize-pkce@0.1.2':
     dependencies:
-      '@types/oauth2orize': 1.11.3
-    dev: true
+      '@types/oauth2orize': 1.11.5
 
-  /@types/oauth2orize@1.11.3:
-    resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==}
+  '@types/oauth2orize@1.11.5':
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/oauth@0.9.4:
-    resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
+  '@types/oauth@0.9.4':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/offscreencanvas@2019.3.0:
-    resolution: {integrity: sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==}
-    requiresBuild: true
-    dev: false
+  '@types/offscreencanvas@2019.3.0': {}
 
-  /@types/offscreencanvas@2019.7.0:
-    resolution: {integrity: sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==}
-    requiresBuild: true
-    dev: false
+  '@types/offscreencanvas@2019.7.0': {}
 
-  /@types/pg@8.11.2:
-    resolution: {integrity: sha512-G2Mjygf2jFMU/9hCaTYxJrwdObdcnuQde1gndooZSOHsNSaCehAuwc7EIuSA34Do8Jx2yZ19KtvW8P0j4EuUXw==}
+  '@types/pg@8.11.5':
     dependencies:
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       pg-protocol: 1.6.0
       pg-types: 4.0.1
-    dev: true
 
-  /@types/pretty-hrtime@1.0.1:
-    resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==}
-    dev: true
+  '@types/pretty-hrtime@1.0.1': {}
 
-  /@types/prop-types@15.7.5:
-    resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
-    dev: true
+  '@types/prop-types@15.7.5': {}
 
-  /@types/pug@2.0.10:
-    resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
-    dev: true
+  '@types/pug@2.0.10': {}
 
-  /@types/punycode@2.1.4:
-    resolution: {integrity: sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ==}
-    dev: true
+  '@types/punycode@2.1.4': {}
 
-  /@types/qrcode@1.5.5:
-    resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
+  '@types/qrcode@1.5.5':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/qs@6.9.7:
-    resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
-    dev: true
+  '@types/qs@6.9.7': {}
 
-  /@types/random-seed@0.3.5:
-    resolution: {integrity: sha512-CftxcDPAHgs0SLHU2dt+ZlDPJfGqLW3sZlC/ATr5vJDSe5tRLeOne7HMvCOJnFyF8e1U41wqzs3h6AMC613xtA==}
-    dev: true
+  '@types/random-seed@0.3.5': {}
 
-  /@types/range-parser@1.2.4:
-    resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
-    dev: true
+  '@types/range-parser@1.2.4': {}
 
-  /@types/ratelimiter@3.4.6:
-    resolution: {integrity: sha512-Bv6WLSXPGLVsBjkizXtn+ef78R92e36/DFQo2wXPTHtp1cYXF6rCULMqf9WcZPAtyMZMvQAtIPeYMA1xAyxghw==}
-    dev: true
+  '@types/ratelimiter@3.4.6': {}
 
-  /@types/react@18.0.28:
-    resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==}
+  '@types/react@18.0.28':
     dependencies:
       '@types/prop-types': 15.7.5
       '@types/scheduler': 0.16.2
       csstype: 3.1.3
-    dev: true
 
-  /@types/readdir-glob@1.1.1:
-    resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
+  '@types/readdir-glob@1.1.1':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/rename@1.0.7:
-    resolution: {integrity: sha512-E9qapfghUGfBMi3jNhsmCKPIp3f2zvNKpaX1BDGLGJNjzpgsZ/RTx7NaNksFjGoJ+r9NvWF1NSM5vVecnNjVmw==}
-    dev: true
+  '@types/rename@1.0.7': {}
 
-  /@types/resolve@1.20.3:
-    resolution: {integrity: sha512-NH5oErHOtHZYcjCtg69t26aXEk4BN2zLWqf7wnDZ+dpe0iR7Rds1SPGEItl3fca21oOe0n3OCnZ4W7jBxu7FOw==}
-    dev: true
+  '@types/resolve@1.20.3': {}
 
-  /@types/responselike@1.0.0:
-    resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
+  '@types/responselike@1.0.0':
     dependencies:
-      '@types/node': 20.11.22
-    dev: false
+      '@types/node': 20.12.7
 
-  /@types/sanitize-html@2.11.0:
-    resolution: {integrity: sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==}
+  '@types/sanitize-html@2.11.0':
     dependencies:
       htmlparser2: 8.0.1
-    dev: true
 
-  /@types/scheduler@0.16.2:
-    resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
-    dev: true
+  '@types/scheduler@0.16.2': {}
 
-  /@types/seedrandom@2.4.30:
-    resolution: {integrity: sha512-AnxLHewubLVzoF/A4qdxBGHCKifw8cY32iro3DQX9TPcetE95zBeVt3jnsvtvAUf1vwzMfwzp4t/L2yqPlnjkQ==}
-    requiresBuild: true
-    dev: false
+  '@types/seedrandom@2.4.30': {}
 
-  /@types/seedrandom@3.0.8:
-    resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==}
-    dev: true
+  '@types/seedrandom@3.0.8': {}
 
-  /@types/semver@7.5.8:
-    resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
-    dev: true
+  '@types/semver@7.5.8': {}
 
-  /@types/serve-static@1.15.1:
-    resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
+  '@types/serve-static@1.15.1':
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/serviceworker@0.0.67:
-    resolution: {integrity: sha512-7TCH7iNsCSNb+aUD9M/36TekrWFSLCjNK8zw/3n5kOtRjbLtDfGYMXTrDnGhSfqXNwpqmt9Vd90w5C/ad1tX6Q==}
-    dev: true
+  '@types/serviceworker@0.0.67': {}
 
-  /@types/simple-oauth2@5.0.7:
-    resolution: {integrity: sha512-8JbWVJbiTSBQP/7eiyGKyXWAqp3dKQZpaA+pdW16FCi32ujkzRMG8JfjoAzdWt6W8U591ZNdHcPtP2D7ILTKuA==}
-    dev: true
+  '@types/simple-oauth2@5.0.7': {}
 
-  /@types/sinon@10.0.13:
-    resolution: {integrity: sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==}
+  '@types/sinon@10.0.13':
     dependencies:
       '@types/sinonjs__fake-timers': 8.1.5
-    dev: true
-
-  /@types/sinonjs__fake-timers@8.1.1:
-    resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
-    dev: true
 
-  /@types/sinonjs__fake-timers@8.1.5:
-    resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==}
-    dev: true
+  '@types/sinonjs__fake-timers@8.1.1': {}
 
-  /@types/sizzle@2.3.3:
-    resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
-    dev: true
+  '@types/sinonjs__fake-timers@8.1.5': {}
 
-  /@types/stack-utils@2.0.1:
-    resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
-    dev: true
+  '@types/sizzle@2.3.3': {}
 
-  /@types/statuses@2.0.4:
-    resolution: {integrity: sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw==}
-    dev: true
+  '@types/stack-utils@2.0.1': {}
 
-  /@types/throttle-debounce@5.0.2:
-    resolution: {integrity: sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==}
-    dev: true
+  '@types/statuses@2.0.4': {}
 
-  /@types/tinycolor2@1.4.6:
-    resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==}
-    dev: true
+  '@types/throttle-debounce@5.0.2': {}
 
-  /@types/tmp@0.2.6:
-    resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==}
-    dev: true
+  '@types/tinycolor2@1.4.6': {}
 
-  /@types/tough-cookie@4.0.2:
-    resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==}
-    dev: true
+  '@types/tmp@0.2.6': {}
 
-  /@types/unist@3.0.2:
-    resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
-    dev: true
+  '@types/tough-cookie@4.0.2': {}
 
-  /@types/uuid@9.0.8:
-    resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
-    dev: true
+  '@types/unist@3.0.2': {}
 
-  /@types/vary@1.1.3:
-    resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==}
-    dependencies:
-      '@types/node': 20.11.22
-    dev: true
+  '@types/uuid@9.0.8': {}
 
-  /@types/web-push@3.6.3:
-    resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
+  '@types/vary@1.1.3':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
-
-  /@types/webgl-ext@0.0.30:
-    resolution: {integrity: sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==}
-    requiresBuild: true
-    dev: false
+      '@types/node': 20.12.7
 
-  /@types/ws@8.5.10:
-    resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
+  '@types/web-push@3.6.3':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
+      '@types/node': 20.12.7
 
-  /@types/yargs-parser@21.0.0:
-    resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
-    dev: true
+  '@types/webgl-ext@0.0.30': {}
 
-  /@types/yargs@17.0.19:
-    resolution: {integrity: sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==}
-    dependencies:
-      '@types/yargs-parser': 21.0.0
-    dev: true
+  '@types/wrap-ansi@3.0.0': {}
 
-  /@types/yauzl@2.10.0:
-    resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
-    requiresBuild: true
+  '@types/ws@8.5.10':
     dependencies:
-      '@types/node': 20.11.22
-    dev: true
-    optional: true
-
-  /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
-      eslint: ^7.0.0 || ^8.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+      '@types/node': 20.12.7
+
+  '@types/yargs-parser@21.0.0': {}
+
+  '@types/yargs@17.0.19':
+    dependencies:
+      '@types/yargs-parser': 21.0.0
+
+  '@types/yauzl@2.10.0':
+    dependencies:
+      '@types/node': 20.12.7
+    optional: true
+
+  '@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
       '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
@@ -7965,21 +15626,12 @@ packages:
       natural-compare: 1.4.0
       semver: 7.5.4
       ts-api-utils: 1.0.1(typescript@5.3.3)
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      '@typescript-eslint/parser': ^7.0.0
-      eslint: ^8.56.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
       '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
@@ -7994,20 +15646,32 @@ packages:
       natural-compare: 1.4.0
       semver: 7.6.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)':
+    dependencies:
+      '@eslint-community/regexpp': 4.10.0
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/scope-manager': 7.7.1
+      '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/visitor-keys': 7.7.1
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.57.0
+      graphemer: 1.4.0
+      ignore: 5.3.1
+      natural-compare: 1.4.0
+      semver: 7.6.0
+      ts-api-utils: 1.3.0(typescript@5.4.5)
+    optionalDependencies:
+      typescript: 5.4.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
@@ -8015,20 +15679,12 @@ packages:
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^8.56.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 7.1.0
       '@typescript-eslint/types': 7.1.0
@@ -8036,85 +15692,82 @@ packages:
       '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/scope-manager@6.11.0:
-    resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  '@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5)':
+    dependencies:
+      '@typescript-eslint/scope-manager': 7.7.1
+      '@typescript-eslint/types': 7.7.1
+      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
+      '@typescript-eslint/visitor-keys': 7.7.1
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.57.0
+    optionalDependencies:
+      typescript: 5.4.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/scope-manager@6.11.0':
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
-    dev: true
 
-  /@typescript-eslint/scope-manager@7.1.0:
-    resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  '@typescript-eslint/scope-manager@7.1.0':
     dependencies:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/visitor-keys': 7.1.0
-    dev: true
 
-  /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@typescript-eslint/scope-manager@7.7.1':
+    dependencies:
+      '@typescript-eslint/types': 7.7.1
+      '@typescript-eslint/visitor-keys': 7.7.1
+
+  '@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^8.56.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/types@6.11.0:
-    resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dev: true
+  '@typescript-eslint/type-utils@7.7.1(eslint@8.57.0)(typescript@5.4.5)':
+    dependencies:
+      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
+      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.57.0
+      ts-api-utils: 1.3.0(typescript@5.4.5)
+    optionalDependencies:
+      typescript: 5.4.5
+    transitivePeerDependencies:
+      - supports-color
 
-  /@typescript-eslint/types@7.1.0:
-    resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    dev: true
+  '@typescript-eslint/types@6.11.0': {}
 
-  /@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.3):
-    resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@typescript-eslint/types@7.1.0': {}
+
+  '@typescript-eslint/types@7.7.1': {}
+
+  '@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
@@ -8123,19 +15776,12 @@ packages:
       is-glob: 4.0.3
       semver: 7.5.4
       ts-api-utils: 1.0.1(typescript@5.3.3)
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3):
-    resolution: {integrity: sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/visitor-keys': 7.1.0
@@ -8145,16 +15791,27 @@ packages:
       minimatch: 9.0.3
       semver: 7.6.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
+    optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^7.0.0 || ^8.0.0
+  '@typescript-eslint/typescript-estree@7.7.1(typescript@5.4.5)':
+    dependencies:
+      '@typescript-eslint/types': 7.7.1
+      '@typescript-eslint/visitor-keys': 7.7.1
+      debug: 4.3.4(supports-color@8.1.1)
+      globby: 11.1.0
+      is-glob: 4.0.3
+      minimatch: 9.0.4
+      semver: 7.6.0
+      ts-api-utils: 1.3.0(typescript@5.4.5)
+    optionalDependencies:
+      typescript: 5.4.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
       '@types/json-schema': 7.0.12
@@ -8167,13 +15824,8 @@ packages:
     transitivePeerDependencies:
       - supports-color
       - typescript
-    dev: true
 
-  /@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==}
-    engines: {node: ^16.0.0 || >=18.0.0}
-    peerDependencies:
-      eslint: ^8.56.0
+  '@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       '@types/json-schema': 7.0.12
@@ -8186,43 +15838,44 @@ packages:
     transitivePeerDependencies:
       - supports-color
       - typescript
-    dev: true
 
-  /@typescript-eslint/visitor-keys@6.11.0:
-    resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  '@typescript-eslint/utils@7.7.1(eslint@8.57.0)(typescript@5.4.5)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+      '@types/json-schema': 7.0.15
+      '@types/semver': 7.5.8
+      '@typescript-eslint/scope-manager': 7.7.1
+      '@typescript-eslint/types': 7.7.1
+      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
+      eslint: 8.57.0
+      semver: 7.6.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+
+  '@typescript-eslint/visitor-keys@6.11.0':
     dependencies:
       '@typescript-eslint/types': 6.11.0
       eslint-visitor-keys: 3.4.3
-    dev: true
 
-  /@typescript-eslint/visitor-keys@7.1.0:
-    resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==}
-    engines: {node: ^16.0.0 || >=18.0.0}
+  '@typescript-eslint/visitor-keys@7.1.0':
     dependencies:
       '@typescript-eslint/types': 7.1.0
       eslint-visitor-keys: 3.4.3
-    dev: true
 
-  /@ungap/structured-clone@1.2.0:
-    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
-    dev: true
+  '@typescript-eslint/visitor-keys@7.7.1':
+    dependencies:
+      '@typescript-eslint/types': 7.7.1
+      eslint-visitor-keys: 3.4.3
 
-  /@vitejs/plugin-vue@5.0.4(vite@5.1.4)(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
+  '@ungap/structured-clone@1.2.0': {}
+
+  '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))':
     dependencies:
-      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
-      vue: 3.4.21(typescript@5.3.3)
-    dev: false
+      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      vue: 3.4.26(typescript@5.4.5)
 
-  /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
-    resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
-    peerDependencies:
-      vitest: '>=0.32.0 <1'
+  '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@bcoe/v8-coverage': 0.2.3
@@ -8235,641 +15888,424 @@ packages:
       std-env: 3.7.0
       test-exclude: 6.0.0
       v8-to-istanbul: 9.2.0
-      vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
+      vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /@vitest/expect@0.34.6:
-    resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==}
+  '@vitest/expect@0.34.6':
     dependencies:
       '@vitest/spy': 0.34.6
       '@vitest/utils': 0.34.6
       chai: 4.3.10
-    dev: true
 
-  /@vitest/expect@1.3.1:
-    resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
+  '@vitest/expect@1.3.1':
     dependencies:
       '@vitest/spy': 1.3.1
       '@vitest/utils': 1.3.1
       chai: 4.3.10
-    dev: true
 
-  /@vitest/runner@0.34.6:
-    resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==}
+  '@vitest/runner@0.34.6':
     dependencies:
       '@vitest/utils': 0.34.6
       p-limit: 4.0.0
       pathe: 1.1.2
-    dev: true
 
-  /@vitest/snapshot@0.34.6:
-    resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==}
+  '@vitest/snapshot@0.34.6':
     dependencies:
       magic-string: 0.30.7
       pathe: 1.1.2
       pretty-format: 29.7.0
-    dev: true
 
-  /@vitest/spy@0.34.6:
-    resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==}
+  '@vitest/spy@0.34.6':
     dependencies:
       tinyspy: 2.2.0
-    dev: true
 
-  /@vitest/spy@1.3.1:
-    resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
+  '@vitest/spy@1.3.1':
     dependencies:
       tinyspy: 2.2.0
-    dev: true
 
-  /@vitest/spy@1.5.3:
-    resolution: {integrity: sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==}
+  '@vitest/spy@1.6.0':
     dependencies:
       tinyspy: 2.2.0
-    dev: true
 
-  /@vitest/utils@0.34.6:
-    resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==}
+  '@vitest/utils@0.34.6':
     dependencies:
       diff-sequences: 29.6.3
       loupe: 2.3.7
       pretty-format: 29.7.0
-    dev: true
 
-  /@vitest/utils@1.3.1:
-    resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
+  '@vitest/utils@1.3.1':
     dependencies:
       diff-sequences: 29.6.3
       estree-walker: 3.0.3
       loupe: 2.3.7
       pretty-format: 29.7.0
-    dev: true
 
-  /@vitest/utils@1.5.3:
-    resolution: {integrity: sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==}
+  '@vitest/utils@1.6.0':
     dependencies:
       diff-sequences: 29.6.3
       estree-walker: 3.0.3
       loupe: 2.3.7
       pretty-format: 29.7.0
-    dev: true
 
-  /@volar/language-core@1.11.1:
-    resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==}
-    dependencies:
-      '@volar/source-map': 1.11.1
-    dev: true
-
-  /@volar/language-core@2.2.0:
-    resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==}
+  '@volar/language-core@2.2.0':
     dependencies:
       '@volar/source-map': 2.2.0
-    dev: true
-
-  /@volar/source-map@1.11.1:
-    resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==}
-    dependencies:
-      muggle-string: 0.3.1
-    dev: true
 
-  /@volar/source-map@2.2.0:
-    resolution: {integrity: sha512-HQlPRlHOVqCCHK8wI76ZldHkEwKsjp7E6idUc36Ekni+KJDNrqgSqPvyHQixybXPHNU7CI9Uxd9/IkxO7LuNBw==}
+  '@volar/source-map@2.2.0':
     dependencies:
       muggle-string: 0.4.1
-    dev: true
-
-  /@volar/typescript@1.11.1:
-    resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==}
-    dependencies:
-      '@volar/language-core': 1.11.1
-      path-browserify: 1.0.1
-    dev: true
 
-  /@volar/typescript@2.2.0:
-    resolution: {integrity: sha512-wC6l4zLiiCLxF+FGaHCbWlQYf4vMsnRxYhcI6WgvaNppOD6r1g+Ef1RKRJUApALWU46Yy/JDU/TbdV6w/X6Liw==}
+  '@volar/typescript@2.2.0':
     dependencies:
       '@volar/language-core': 2.2.0
       path-browserify: 1.0.1
-    dev: true
 
-  /@vue/compiler-core@3.4.18:
-    resolution: {integrity: sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==}
+  '@vue/compiler-core@3.4.21':
     dependencies:
-      '@babel/parser': 7.23.9
-      '@vue/shared': 3.4.18
+      '@babel/parser': 7.24.0
+      '@vue/shared': 3.4.21
       entities: 4.5.0
       estree-walker: 2.0.2
       source-map-js: 1.0.2
-    dev: true
 
-  /@vue/compiler-core@3.4.21:
-    resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
+  '@vue/compiler-core@3.4.25':
     dependencies:
-      '@babel/parser': 7.23.9
-      '@vue/shared': 3.4.21
+      '@babel/parser': 7.24.5
+      '@vue/shared': 3.4.25
       entities: 4.5.0
       estree-walker: 2.0.2
-      source-map-js: 1.0.2
+      source-map-js: 1.2.0
 
-  /@vue/compiler-dom@3.4.18:
-    resolution: {integrity: sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==}
+  '@vue/compiler-core@3.4.26':
     dependencies:
-      '@vue/compiler-core': 3.4.18
-      '@vue/shared': 3.4.18
-    dev: true
+      '@babel/parser': 7.24.5
+      '@vue/shared': 3.4.26
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.0
 
-  /@vue/compiler-dom@3.4.21:
-    resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
+  '@vue/compiler-dom@3.4.21':
     dependencies:
       '@vue/compiler-core': 3.4.21
       '@vue/shared': 3.4.21
 
-  /@vue/compiler-sfc@3.4.21:
-    resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
+  '@vue/compiler-dom@3.4.25':
     dependencies:
-      '@babel/parser': 7.23.9
-      '@vue/compiler-core': 3.4.21
-      '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-ssr': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/compiler-core': 3.4.25
+      '@vue/shared': 3.4.25
+
+  '@vue/compiler-dom@3.4.26':
+    dependencies:
+      '@vue/compiler-core': 3.4.26
+      '@vue/shared': 3.4.26
+
+  '@vue/compiler-sfc@3.4.26':
+    dependencies:
+      '@babel/parser': 7.24.5
+      '@vue/compiler-core': 3.4.26
+      '@vue/compiler-dom': 3.4.26
+      '@vue/compiler-ssr': 3.4.26
+      '@vue/shared': 3.4.26
       estree-walker: 2.0.2
-      magic-string: 0.30.7
-      postcss: 8.4.35
-      source-map-js: 1.0.2
+      magic-string: 0.30.10
+      postcss: 8.4.38
+      source-map-js: 1.2.0
 
-  /@vue/compiler-ssr@3.4.21:
-    resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==}
+  '@vue/compiler-ssr@3.4.25':
     dependencies:
-      '@vue/compiler-dom': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/compiler-dom': 3.4.25
+      '@vue/shared': 3.4.25
+    optional: true
 
-  /@vue/language-core@1.8.27(typescript@5.3.3):
-    resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@vue/compiler-ssr@3.4.26':
     dependencies:
-      '@volar/language-core': 1.11.1
-      '@volar/source-map': 1.11.1
-      '@vue/compiler-dom': 3.4.18
-      '@vue/shared': 3.3.12
-      computeds: 0.0.1
-      minimatch: 9.0.3
-      muggle-string: 0.3.1
-      path-browserify: 1.0.1
-      typescript: 5.3.3
-      vue-template-compiler: 2.7.14
-    dev: true
+      '@vue/compiler-dom': 3.4.26
+      '@vue/shared': 3.4.26
 
-  /@vue/language-core@2.0.16(typescript@5.3.3):
-    resolution: {integrity: sha512-Bc2sexRH99pznOph8mLw2BlRZ9edm7tW51kcBXgx8adAoOcZUWJj3UNSsdQ6H9Y8meGz7BoazVrVo/jUukIsPw==}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  '@vue/devtools-api@6.6.1': {}
+
+  '@vue/language-core@2.0.16(typescript@5.4.5)':
     dependencies:
       '@volar/language-core': 2.2.0
-      '@vue/compiler-dom': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/compiler-dom': 3.4.25
+      '@vue/shared': 3.4.25
       computeds: 0.0.1
-      minimatch: 9.0.3
+      minimatch: 9.0.4
       path-browserify: 1.0.1
-      typescript: 5.3.3
       vue-template-compiler: 2.7.14
-    dev: true
+    optionalDependencies:
+      typescript: 5.4.5
 
-  /@vue/reactivity@3.4.21:
-    resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
+  '@vue/reactivity@3.4.26':
     dependencies:
-      '@vue/shared': 3.4.21
+      '@vue/shared': 3.4.26
 
-  /@vue/runtime-core@3.4.21:
-    resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==}
+  '@vue/runtime-core@3.4.26':
     dependencies:
-      '@vue/reactivity': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/reactivity': 3.4.26
+      '@vue/shared': 3.4.26
 
-  /@vue/runtime-dom@3.4.21:
-    resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==}
+  '@vue/runtime-dom@3.4.26':
     dependencies:
-      '@vue/runtime-core': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/runtime-core': 3.4.26
+      '@vue/shared': 3.4.26
       csstype: 3.1.3
 
-  /@vue/server-renderer@3.4.21(vue@3.4.21):
-    resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==}
-    peerDependencies:
-      vue: 3.4.21
+  '@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5))':
     dependencies:
-      '@vue/compiler-ssr': 3.4.21
-      '@vue/shared': 3.4.21
-      vue: 3.4.21(typescript@5.3.3)
+      '@vue/compiler-ssr': 3.4.25
+      '@vue/shared': 3.4.25
+      vue: 3.4.26(typescript@5.4.5)
+    optional: true
 
-  /@vue/shared@3.3.12:
-    resolution: {integrity: sha512-6p0Yin0pclvnER7BLNOQuod9Z+cxSYh8pSh7CzHnWNjAIP6zrTlCdHRvSCb1aYEx6i3Q3kvfuWU7nG16CgG1ag==}
-    dev: true
+  '@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5))':
+    dependencies:
+      '@vue/compiler-ssr': 3.4.26
+      '@vue/shared': 3.4.26
+      vue: 3.4.26(typescript@5.4.5)
 
-  /@vue/shared@3.4.18:
-    resolution: {integrity: sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==}
-    dev: true
+  '@vue/shared@3.4.21': {}
 
-  /@vue/shared@3.4.21:
-    resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
+  '@vue/shared@3.4.25': {}
 
-  /@vue/test-utils@2.4.1(vue@3.4.21):
-    resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
-    peerDependencies:
-      '@vue/server-renderer': ^3.0.1
-      vue: ^3.0.1
-    peerDependenciesMeta:
-      '@vue/server-renderer':
-        optional: true
+  '@vue/shared@3.4.26': {}
+
+  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.4.21(typescript@5.3.3)
+      vue: 3.4.26(typescript@5.4.5)
       vue-component-type-helpers: 1.8.4
-    dev: true
+    optionalDependencies:
+      '@vue/server-renderer': 3.4.25(vue@3.4.26(typescript@5.4.5))
 
-  /@webgpu/types@0.1.30:
-    resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==}
-    requiresBuild: true
-    dev: false
+  '@webgpu/types@0.1.30': {}
 
-  /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.19.11):
-    resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==}
-    engines: {node: '>=14.15.0'}
-    peerDependencies:
-      esbuild: '>=0.10.0'
+  '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.20.2)':
     dependencies:
-      esbuild: 0.19.11
+      esbuild: 0.20.2
       tslib: 2.6.2
-    dev: true
 
-  /@yarnpkg/fslib@2.10.3:
-    resolution: {integrity: sha512-41H+Ga78xT9sHvWLlFOZLIhtU6mTGZ20pZ29EiZa97vnxdohJD2AF42rCoAoWfqUz486xY6fhjMH+DYEM9r14A==}
-    engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'}
+  '@yarnpkg/fslib@2.10.3':
     dependencies:
       '@yarnpkg/libzip': 2.3.0
       tslib: 1.14.1
-    dev: true
 
-  /@yarnpkg/libzip@2.3.0:
-    resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==}
-    engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'}
+  '@yarnpkg/libzip@2.3.0':
     dependencies:
       '@types/emscripten': 1.39.7
       tslib: 1.14.1
-    dev: true
 
-  /abbrev@1.1.1:
-    resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+  abbrev@1.1.1: {}
 
-  /abbrev@2.0.0:
-    resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
-    dev: false
+  abbrev@2.0.0: {}
 
-  /abort-controller@3.0.0:
-    resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
-    engines: {node: '>=6.5'}
+  abort-controller@3.0.0:
     dependencies:
       event-target-shim: 5.0.1
-    dev: false
 
-  /abstract-logging@2.0.1:
-    resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
-    dev: false
+  abstract-logging@2.0.1: {}
 
-  /accepts@1.3.8:
-    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
-    engines: {node: '>= 0.6'}
+  accepts@1.3.8:
     dependencies:
       mime-types: 2.1.35
       negotiator: 0.6.3
 
-  /acorn-jsx@5.3.2(acorn@7.4.1):
-    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
-    peerDependencies:
-      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+  acorn-jsx@5.3.2(acorn@7.4.1):
     dependencies:
       acorn: 7.4.1
-    dev: true
 
-  /acorn-jsx@5.3.2(acorn@8.11.3):
-    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
-    peerDependencies:
-      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+  acorn-jsx@5.3.2(acorn@8.11.3):
     dependencies:
       acorn: 8.11.3
-    dev: true
-
-  /acorn-walk@7.2.0:
-    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
-    engines: {node: '>=0.4.0'}
-    dev: true
 
-  /acorn-walk@8.3.2:
-    resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
-    engines: {node: '>=0.4.0'}
-    dev: true
+  acorn-walk@7.2.0: {}
 
-  /acorn@7.4.1:
-    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
+  acorn-walk@8.3.2: {}
 
-  /acorn@8.11.3:
-    resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
+  acorn@7.4.1: {}
 
-  /address@1.2.2:
-    resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==}
-    engines: {node: '>= 10.0.0'}
-    dev: true
+  acorn@8.11.3: {}
 
-  /adm-zip@0.5.10:
-    resolution: {integrity: sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==}
-    engines: {node: '>=6.0'}
-    requiresBuild: true
-    dev: false
+  address@1.2.2: {}
+
+  adm-zip@0.5.10:
     optional: true
 
-  /agent-base@4.3.0:
-    resolution: {integrity: sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==}
-    engines: {node: '>= 4.0.0'}
-    requiresBuild: true
+  agent-base@4.3.0:
     dependencies:
       es6-promisify: 5.0.0
-    dev: false
     optional: true
 
-  /agent-base@6.0.2:
-    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
-    engines: {node: '>= 6.0.0'}
-    requiresBuild: true
+  agent-base@6.0.2:
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
-  /agent-base@7.1.0:
-    resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
-    engines: {node: '>= 14'}
+  agent-base@7.1.0:
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /aggregate-error@3.1.0:
-    resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
-    engines: {node: '>=8'}
+  aggregate-error@3.1.0:
     dependencies:
       clean-stack: 2.2.0
       indent-string: 4.0.0
 
-  /aggregate-error@5.0.0:
-    resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==}
-    engines: {node: '>=18'}
+  aggregate-error@5.0.0:
     dependencies:
       clean-stack: 5.2.0
       indent-string: 5.0.0
-    dev: true
 
-  /ajv-draft-04@1.0.0(ajv@8.12.0):
-    resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
-    peerDependencies:
-      ajv: ^8.5.0
-    peerDependenciesMeta:
-      ajv:
-        optional: true
+  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424:
     dependencies:
-      ajv: 8.12.0
-    dev: true
+      '@aiscript-dev/aiscript-languageserver': https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz
+      vscode-languageclient: 9.0.1
 
-  /ajv-formats@2.1.1(ajv@8.12.0):
-    resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
-    peerDependencies:
-      ajv: ^8.0.0
-    peerDependenciesMeta:
-      ajv:
-        optional: true
-    dependencies:
-      ajv: 8.12.0
-    dev: false
+  ajv-draft-04@1.0.0(ajv@8.13.0):
+    optionalDependencies:
+      ajv: 8.13.0
 
-  /ajv@6.12.6:
-    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+  ajv-formats@2.1.1(ajv@8.13.0):
+    optionalDependencies:
+      ajv: 8.13.0
+
+  ajv@6.12.6:
     dependencies:
       fast-deep-equal: 3.1.3
       fast-json-stable-stringify: 2.1.0
       json-schema-traverse: 0.4.1
       uri-js: 4.4.1
 
-  /ajv@8.12.0:
-    resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
+  ajv@8.13.0:
     dependencies:
       fast-deep-equal: 3.1.3
       json-schema-traverse: 1.0.0
       require-from-string: 2.0.2
       uri-js: 4.4.1
 
-  /ansi-colors@4.1.3:
-    resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
-    engines: {node: '>=6'}
-    dev: true
+  ansi-colors@4.1.3: {}
 
-  /ansi-escapes@4.3.2:
-    resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
-    engines: {node: '>=8'}
+  ansi-escapes@4.3.2:
     dependencies:
       type-fest: 0.21.3
-    dev: true
 
-  /ansi-regex@5.0.1:
-    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
-    engines: {node: '>=8'}
+  ansi-regex@5.0.1: {}
 
-  /ansi-regex@6.0.1:
-    resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
-    engines: {node: '>=12'}
+  ansi-regex@6.0.1: {}
 
-  /ansi-styles@3.2.1:
-    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
-    engines: {node: '>=4'}
+  ansi-styles@3.2.1:
     dependencies:
       color-convert: 1.9.3
-    dev: true
 
-  /ansi-styles@4.3.0:
-    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
-    engines: {node: '>=8'}
+  ansi-styles@4.3.0:
     dependencies:
       color-convert: 2.0.1
 
-  /ansi-styles@5.2.0:
-    resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
-    engines: {node: '>=10'}
-    dev: true
+  ansi-styles@5.2.0: {}
 
-  /ansi-styles@6.2.1:
-    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
-    engines: {node: '>=12'}
+  ansi-styles@6.2.1: {}
 
-  /any-promise@1.3.0:
-    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
-    dev: false
+  any-promise@1.3.0: {}
 
-  /anymatch@3.1.3:
-    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
-    engines: {node: '>= 8'}
+  anymatch@3.1.3:
     dependencies:
       normalize-path: 3.0.0
       picomatch: 2.3.1
 
-  /app-root-dir@1.0.2:
-    resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==}
-    dev: true
+  app-root-dir@1.0.2: {}
 
-  /app-root-path@3.1.0:
-    resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==}
-    engines: {node: '>= 6.0.0'}
-    dev: false
+  app-root-path@3.1.0: {}
 
-  /append-field@1.0.0:
-    resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
+  append-field@1.0.0: {}
 
-  /aproba@2.0.0:
-    resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
-    requiresBuild: true
-    dev: false
+  aproba@2.0.0:
     optional: true
 
-  /arch@2.2.0:
-    resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
+  arch@2.2.0: {}
 
-  /archiver-utils@4.0.1:
-    resolution: {integrity: sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==}
-    engines: {node: '>= 12.0.0'}
+  archiver-utils@5.0.2:
     dependencies:
-      glob: 8.1.0
+      glob: 10.3.12
       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.0
-    dev: false
+      readable-stream: 4.3.0
 
-  /archiver@6.0.1:
-    resolution: {integrity: sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==}
-    engines: {node: '>= 12.0.0'}
+  archiver@7.0.1:
     dependencies:
-      archiver-utils: 4.0.1
+      archiver-utils: 5.0.2
       async: 3.2.4
-      buffer-crc32: 0.2.13
-      readable-stream: 3.6.0
+      buffer-crc32: 1.0.0
+      readable-stream: 4.3.0
       readdir-glob: 1.1.2
       tar-stream: 3.1.6
-      zip-stream: 5.0.1
-    dev: false
+      zip-stream: 6.0.1
 
-  /archy@1.0.0:
-    resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
-    dev: false
+  archy@1.0.0: {}
 
-  /are-we-there-yet@2.0.0:
-    resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
-    engines: {node: '>=10'}
-    requiresBuild: true
+  are-we-there-yet@2.0.0:
     dependencies:
       delegates: 1.0.0
       readable-stream: 3.6.0
-    dev: false
     optional: true
 
-  /arg@5.0.2:
-    resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
-    dev: true
+  arg@5.0.2: {}
 
-  /argparse@1.0.10:
-    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+  argparse@1.0.10:
     dependencies:
       sprintf-js: 1.0.3
 
-  /argparse@2.0.1:
-    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+  argparse@2.0.1: {}
 
-  /aria-query@5.1.3:
-    resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+  aria-query@5.1.3:
     dependencies:
       deep-equal: 2.2.0
-    dev: true
 
-  /array-buffer-byte-length@1.0.0:
-    resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
+  array-buffer-byte-length@1.0.0:
     dependencies:
       call-bind: 1.0.2
       is-array-buffer: 3.0.2
-    dev: true
 
-  /array-flatten@1.1.1:
-    resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
+  array-flatten@1.1.1: {}
 
-  /array-includes@3.1.7:
-    resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
-    engines: {node: '>= 0.4'}
+  array-includes@3.1.7:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
       get-intrinsic: 1.2.1
       is-string: 1.0.7
-    dev: true
 
-  /array-union@2.1.0:
-    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
-    engines: {node: '>=8'}
+  array-union@2.1.0: {}
 
-  /array.prototype.findlastindex@1.2.3:
-    resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==}
-    engines: {node: '>= 0.4'}
+  array.prototype.findlastindex@1.2.3:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
       es-shim-unscopables: 1.0.0
       get-intrinsic: 1.2.1
-    dev: true
 
-  /array.prototype.flat@1.3.2:
-    resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
-    engines: {node: '>= 0.4'}
+  array.prototype.flat@1.3.2:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
       es-shim-unscopables: 1.0.0
-    dev: true
 
-  /array.prototype.flatmap@1.3.2:
-    resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
-    engines: {node: '>= 0.4'}
+  array.prototype.flatmap@1.3.2:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
       es-shim-unscopables: 1.0.0
-    dev: true
 
-  /arraybuffer.prototype.slice@1.0.1:
-    resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==}
-    engines: {node: '>= 0.4'}
+  arraybuffer.prototype.slice@1.0.1:
     dependencies:
       array-buffer-byte-length: 1.0.0
       call-bind: 1.0.2
@@ -8877,163 +16313,104 @@ packages:
       get-intrinsic: 1.2.1
       is-array-buffer: 3.0.2
       is-shared-array-buffer: 1.0.2
-    dev: true
 
-  /arrify@1.0.1:
-    resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
+  arrify@1.0.1: {}
 
-  /asap@2.0.6:
-    resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+  asap@2.0.6: {}
 
-  /asn1.js@5.4.1:
-    resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
+  asn1.js@5.4.1:
     dependencies:
       bn.js: 4.12.0
       inherits: 2.0.4
       minimalistic-assert: 1.0.1
       safer-buffer: 2.1.2
-    dev: false
 
-  /asn1@0.2.6:
-    resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
+  asn1@0.2.6:
     dependencies:
       safer-buffer: 2.1.2
 
-  /asn1js@3.0.5:
-    resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==}
-    engines: {node: '>=12.0.0'}
+  asn1js@3.0.5:
     dependencies:
       pvtsutils: 1.3.5
       pvutils: 1.1.3
       tslib: 2.6.2
-    dev: false
 
-  /assert-never@1.2.1:
-    resolution: {integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==}
+  assert-never@1.2.1: {}
 
-  /assert-plus@1.0.0:
-    resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
-    engines: {node: '>=0.8'}
+  assert-plus@1.0.0: {}
 
-  /assert@2.1.0:
-    resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==}
+  assert@2.1.0:
     dependencies:
       call-bind: 1.0.2
       is-nan: 1.3.2
       object-is: 1.1.5
       object.assign: 4.1.4
       util: 0.12.5
-    dev: true
 
-  /assertion-error@1.1.0:
-    resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
-    dev: true
+  assertion-error@1.1.0: {}
 
-  /ast-types@0.16.1:
-    resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
-    engines: {node: '>=4'}
+  ast-types@0.16.1:
     dependencies:
       tslib: 2.6.2
-    dev: true
 
-  /astral-regex@2.0.0:
-    resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
-    engines: {node: '>=8'}
-    dev: true
+  astral-regex@2.0.0: {}
 
-  /astring@1.8.6:
-    resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
-    hasBin: true
-    dev: false
+  astring@1.8.6: {}
 
-  /async-mutex@0.4.1:
-    resolution: {integrity: sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==}
+  async-mutex@0.5.0:
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /async@3.2.4:
-    resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
+  async@3.2.4: {}
 
-  /asynckit@0.4.0:
-    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+  asynckit@0.4.0: {}
 
-  /at-least-node@1.0.0:
-    resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
-    engines: {node: '>= 4.0.0'}
-    dev: true
+  at-least-node@1.0.0: {}
 
-  /atomic-sleep@1.0.0:
-    resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
-    engines: {node: '>=8.0.0'}
-    dev: false
+  atomic-sleep@1.0.0: {}
 
-  /available-typed-arrays@1.0.5:
-    resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
-    engines: {node: '>= 0.4'}
-    dev: true
+  available-typed-arrays@1.0.5: {}
 
-  /avvio@8.2.1:
-    resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
+  avvio@8.3.0:
     dependencies:
+      '@fastify/error': 3.4.0
       archy: 1.0.0
       debug: 4.3.4(supports-color@8.1.1)
-      fastq: 1.15.0
+      fastq: 1.17.1
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /aws-sdk-client-mock@3.0.1:
-    resolution: {integrity: sha512-9VAzJLl8mz99KP9HjOm/93d8vznRRUTpJooPBOunRdUAnVYopCe9xmMuu7eVemu8fQ+w6rP7o5bBK1kAFkB2KQ==}
+  aws-sdk-client-mock@3.0.1:
     dependencies:
       '@types/sinon': 10.0.13
       sinon: 16.1.3
       tslib: 2.6.2
-    dev: true
 
-  /aws-sign2@0.7.0:
-    resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
+  aws-sign2@0.7.0: {}
 
-  /aws4@1.12.0:
-    resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
+  aws4@1.12.0: {}
 
-  /axios@0.24.0:
-    resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
+  axios@0.24.0:
     dependencies:
       follow-redirects: 1.15.2(debug@4.3.4)
     transitivePeerDependencies:
       - debug
-    dev: false
 
-  /axios@1.6.2(debug@4.3.4):
-    resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==}
+  axios@1.6.2(debug@4.3.4):
     dependencies:
       follow-redirects: 1.15.2(debug@4.3.4)
       form-data: 4.0.0
       proxy-from-env: 1.1.0
     transitivePeerDependencies:
       - debug
-    dev: true
 
-  /b4a@1.6.4:
-    resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
-    dev: false
+  b4a@1.6.4: {}
 
-  /babel-core@7.0.0-bridge.0(@babel/core@7.24.0):
-    resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
+  babel-core@7.0.0-bridge.0(@babel/core@7.24.0):
     dependencies:
       '@babel/core': 7.24.0
-    dev: true
 
-  /babel-jest@29.7.0(@babel/core@7.23.5):
-    resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    peerDependencies:
-      '@babel/core': ^7.8.0
+  babel-jest@29.7.0(@babel/core@7.23.5):
     dependencies:
       '@babel/core': 7.23.5
       '@jest/transform': 29.7.0
@@ -9045,11 +16422,8 @@ packages:
       slash: 3.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /babel-plugin-istanbul@6.1.1:
-    resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
-    engines: {node: '>=8'}
+  babel-plugin-istanbul@6.1.1:
     dependencies:
       '@babel/helper-plugin-utils': 7.22.5
       '@istanbuljs/load-nyc-config': 1.1.0
@@ -9058,22 +16432,15 @@ packages:
       test-exclude: 6.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /babel-plugin-jest-hoist@29.6.3:
-    resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  babel-plugin-jest-hoist@29.6.3:
     dependencies:
-      '@babel/template': 7.22.15
-      '@babel/types': 7.23.5
+      '@babel/template': 7.24.0
+      '@babel/types': 7.24.0
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
-    dev: true
 
-  /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.24.0):
-    resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+  babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.24.0):
     dependencies:
       '@babel/compat-data': 7.23.5
       '@babel/core': 7.24.0
@@ -9081,35 +16448,23 @@ packages:
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.24.0):
-    resolution: {integrity: sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+  babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.24.0):
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
       core-js-compat: 3.33.3
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.24.0):
-    resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==}
-    peerDependencies:
-      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+  babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.24.0):
     dependencies:
       '@babel/core': 7.24.0
       '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.5):
-    resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.5):
     dependencies:
       '@babel/core': 7.23.5
       '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.5)
@@ -9124,118 +16479,68 @@ packages:
       '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.5)
       '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5)
       '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5)
-    dev: true
 
-  /babel-preset-jest@29.6.3(@babel/core@7.23.5):
-    resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    peerDependencies:
-      '@babel/core': ^7.0.0
+  babel-preset-jest@29.6.3(@babel/core@7.23.5):
     dependencies:
       '@babel/core': 7.23.5
       babel-plugin-jest-hoist: 29.6.3
       babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.5)
-    dev: true
 
-  /babel-walk@3.0.0-canary-5:
-    resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==}
-    engines: {node: '>= 10.0.0'}
+  babel-walk@3.0.0-canary-5:
     dependencies:
       '@babel/types': 7.23.5
 
-  /bail@2.0.2:
-    resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
-    dev: true
+  bail@2.0.2: {}
 
-  /balanced-match@1.0.2:
-    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+  balanced-match@1.0.2: {}
 
-  /base64-js@1.5.1:
-    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+  base64-js@1.5.1: {}
 
-  /bcrypt-pbkdf@1.0.2:
-    resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
+  bcrypt-pbkdf@1.0.2:
     dependencies:
       tweetnacl: 0.14.5
 
-  /bcryptjs@2.4.3:
-    resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
-    dev: false
+  bcryptjs@2.4.3: {}
 
-  /better-opn@3.0.2:
-    resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==}
-    engines: {node: '>=12.0.0'}
+  better-opn@3.0.2:
     dependencies:
       open: 8.4.2
-    dev: true
-
-  /bidi-js@1.0.3:
-    resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
-    dependencies:
-      require-from-string: 2.0.2
-    dev: false
 
-  /big-integer@1.6.51:
-    resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==}
-    engines: {node: '>=0.6'}
-    dev: true
+  big-integer@1.6.51: {}
 
-  /bin-check@4.1.0:
-    resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==}
-    engines: {node: '>=4'}
+  bin-check@4.1.0:
     dependencies:
       execa: 0.7.0
       executable: 4.1.1
-    dev: false
 
-  /bin-version-check@5.0.0:
-    resolution: {integrity: sha512-Q3FMQnS5eZmrBGqmDXLs4dbAn/f+52voP6ykJYmweSA60t6DyH4UTSwZhtbK5UH+LBoWvDljILUQMLRUtsynsA==}
-    engines: {node: '>=12'}
+  bin-version-check@5.0.0:
     dependencies:
       bin-version: 6.0.0
-      semver: 7.5.4
+      semver: 7.6.0
       semver-truncate: 2.0.0
-    dev: false
 
-  /bin-version@6.0.0:
-    resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==}
-    engines: {node: '>=12'}
+  bin-version@6.0.0:
     dependencies:
       execa: 5.1.1
       find-versions: 5.1.0
-    dev: false
 
-  /binary-extensions@2.2.0:
-    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
-    engines: {node: '>=8'}
+  binary-extensions@2.2.0: {}
 
-  /bl@4.1.0:
-    resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+  bl@4.1.0:
     dependencies:
       buffer: 5.7.1
       inherits: 2.0.4
       readable-stream: 3.6.0
-    dev: true
 
-  /blob-util@2.0.2:
-    resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==}
-    dev: true
+  blob-util@2.0.2: {}
 
-  /bluebird@3.7.2:
-    resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
-    dev: true
+  bluebird@3.7.2: {}
 
-  /blurhash@2.0.5:
-    resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==}
-    dev: false
+  blurhash@2.0.5: {}
 
-  /bn.js@4.12.0:
-    resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
-    dev: false
+  bn.js@4.12.0: {}
 
-  /body-parser@1.20.1:
-    resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+  body-parser@1.20.1:
     dependencies:
       bytes: 3.1.2
       content-type: 1.0.5
@@ -9252,9 +16557,7 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /body-parser@1.20.2:
-    resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+  body-parser@1.20.2:
     dependencies:
       bytes: 3.1.2
       content-type: 1.0.5
@@ -9271,174 +16574,115 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /boolbase@1.0.0:
-    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+  boolbase@1.0.0: {}
 
-  /bowser@2.11.0:
-    resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
-    dev: false
+  bowser@2.11.0: {}
 
-  /bplist-parser@0.2.0:
-    resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
-    engines: {node: '>= 5.10.0'}
+  bplist-parser@0.2.0:
     dependencies:
       big-integer: 1.6.51
-    dev: true
 
-  /brace-expansion@1.1.11:
-    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+  brace-expansion@1.1.11:
     dependencies:
       balanced-match: 1.0.2
       concat-map: 0.0.1
 
-  /brace-expansion@2.0.1:
-    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+  brace-expansion@2.0.1:
     dependencies:
       balanced-match: 1.0.2
 
-  /braces@3.0.2:
-    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
-    engines: {node: '>=8'}
+  braces@3.0.2:
     dependencies:
       fill-range: 7.0.1
 
-  /broadcast-channel@7.0.0:
-    resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
+  broadcast-channel@7.0.0:
     dependencies:
       '@babel/runtime': 7.23.4
       oblivious-set: 1.4.0
       p-queue: 6.6.2
       unload: 2.4.1
-    dev: false
 
-  /browser-assert@1.2.1:
-    resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==}
-    dev: true
+  browser-assert@1.2.1: {}
 
-  /browserify-zlib@0.1.4:
-    resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==}
+  browserify-zlib@0.1.4:
     dependencies:
       pako: 0.2.9
-    dev: true
 
-  /browserslist@4.22.2:
-    resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
-    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
-    hasBin: true
+  browserslist@4.22.2:
     dependencies:
       caniuse-lite: 1.0.30001566
       electron-to-chromium: 1.4.601
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.22.2)
-    dev: true
 
-  /browserslist@4.23.0:
-    resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
-    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
-    hasBin: true
+  browserslist@4.23.0:
     dependencies:
       caniuse-lite: 1.0.30001591
       electron-to-chromium: 1.4.686
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.23.0)
 
-  /bser@2.1.1:
-    resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+  bser@2.1.1:
     dependencies:
       node-int64: 0.4.0
-    dev: true
 
-  /buffer-crc32@0.2.13:
-    resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+  buffer-crc32@0.2.13: {}
 
-  /buffer-equal-constant-time@1.0.1:
-    resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
-    dev: false
+  buffer-crc32@1.0.0: {}
 
-  /buffer-from@1.1.2:
-    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+  buffer-equal-constant-time@1.0.1: {}
 
-  /buffer-writer@2.0.0:
-    resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
-    engines: {node: '>=4'}
-    dev: false
+  buffer-from@1.1.2: {}
 
-  /buffer@5.6.0:
-    resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==}
+  buffer@5.6.0:
     dependencies:
       base64-js: 1.5.1
       ieee754: 1.2.1
-    dev: false
 
-  /buffer@5.7.1:
-    resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+  buffer@5.7.1:
     dependencies:
       base64-js: 1.5.1
       ieee754: 1.2.1
-    dev: true
 
-  /buffer@6.0.3:
-    resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+  buffer@6.0.3:
     dependencies:
       base64-js: 1.5.1
       ieee754: 1.2.1
-    dev: false
 
-  /bufferutil@4.0.7:
-    resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
-    engines: {node: '>=6.14.2'}
-    requiresBuild: true
+  bufferutil@4.0.7:
     dependencies:
       node-gyp-build: 4.6.0
+    optional: true
 
-  /bullmq@5.4.0:
-    resolution: {integrity: sha512-QNOT+Vp/OFctwKa1/LYvrfIMXqb6Hu8a1VwXxQpa/JXoFAQ9E4ZcqW4fyEjx9iYrXakpV6cAGPbmdgWaKTGXOQ==}
+  bullmq@5.7.8:
     dependencies:
       cron-parser: 4.8.1
-      fast-glob: 3.3.2
-      ioredis: 5.3.2
-      lodash: 4.17.21
-      minimatch: 9.0.3
+      ioredis: 5.4.1
       msgpackr: 1.10.1
       node-abort-controller: 3.1.1
-      semver: 7.5.4
+      semver: 7.6.0
       tslib: 2.6.2
       uuid: 9.0.1
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /buraha@0.0.1:
-    resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==}
-    dev: false
+  buraha@0.0.1: {}
 
-  /busboy@1.6.0:
-    resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
-    engines: {node: '>=10.16.0'}
+  busboy@1.6.0:
     dependencies:
       streamsearch: 1.1.0
 
-  /bytes@3.0.0:
-    resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
-    engines: {node: '>= 0.8'}
-    dev: true
+  bytes@3.0.0: {}
 
-  /bytes@3.1.2:
-    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
-    engines: {node: '>= 0.8'}
+  bytes@3.1.2: {}
 
-  /cac@6.7.14:
-    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
-    engines: {node: '>=8'}
-    dev: true
+  cac@6.7.14: {}
 
-  /cacache@18.0.0:
-    resolution: {integrity: sha512-I7mVOPl3PUCeRub1U8YoGz2Lqv9WOBpobZ8RyWFXmReuILz+3OAyTa5oH3QPdtKZD7N0Yk00aLfzn0qvp8dZ1w==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  cacache@18.0.0:
     dependencies:
       '@npmcli/fs': 3.1.0
       fs-minipass: 3.0.2
-      glob: 10.3.10
+      glob: 10.3.12
       lru-cache: 10.0.2
       minipass: 7.0.4
       minipass-collect: 1.0.2
@@ -9446,22 +16690,14 @@ packages:
       minipass-pipeline: 1.2.4
       p-map: 4.0.0
       ssri: 10.0.4
-      tar: 6.2.0
+      tar: 6.2.1
       unique-filename: 3.0.0
-    dev: false
 
-  /cacheable-lookup@5.0.4:
-    resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
-    engines: {node: '>=10.6.0'}
-    dev: false
+  cacheable-lookup@5.0.4: {}
 
-  /cacheable-lookup@7.0.0:
-    resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
-    engines: {node: '>=14.16'}
+  cacheable-lookup@7.0.0: {}
 
-  /cacheable-request@10.2.14:
-    resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
-    engines: {node: '>=14.16'}
+  cacheable-request@10.2.14:
     dependencies:
       '@types/http-cache-semantics': 4.0.4
       get-stream: 6.0.1
@@ -9471,9 +16707,7 @@ packages:
       normalize-url: 8.0.0
       responselike: 3.0.0
 
-  /cacheable-request@7.0.2:
-    resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==}
-    engines: {node: '>=8'}
+  cacheable-request@7.0.2:
     dependencies:
       clone-response: 1.0.3
       get-stream: 5.2.0
@@ -9482,86 +16716,52 @@ packages:
       lowercase-keys: 2.0.0
       normalize-url: 6.1.0
       responselike: 2.0.1
-    dev: false
 
-  /cachedir@2.3.0:
-    resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==}
-    engines: {node: '>=6'}
-    dev: true
+  cachedir@2.3.0: {}
 
-  /call-bind@1.0.2:
-    resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
+  call-bind@1.0.2:
     dependencies:
       function-bind: 1.1.2
       get-intrinsic: 1.2.1
 
-  /call-me-maybe@1.0.2:
-    resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
-    dev: true
+  call-me-maybe@1.0.2: {}
 
-  /callsites@3.1.0:
-    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
-    engines: {node: '>=6'}
-    dev: true
+  callsites@3.1.0: {}
 
-  /camelcase-keys@6.2.2:
-    resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==}
-    engines: {node: '>=8'}
+  camelcase-keys@6.2.2:
     dependencies:
       camelcase: 5.3.1
       map-obj: 4.3.0
       quick-lru: 4.0.1
-    dev: true
-
-  /camelcase@5.3.1:
-    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
-    engines: {node: '>=6'}
 
-  /camelcase@6.3.0:
-    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
-    engines: {node: '>=10'}
-    dev: true
+  camelcase@5.3.1: {}
 
-  /caniuse-api@3.0.0:
-    resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
+  camelcase@6.3.0: {}
+
+  caniuse-api@3.0.0:
     dependencies:
       browserslist: 4.23.0
-      caniuse-lite: 1.0.30001566
+      caniuse-lite: 1.0.30001591
       lodash.memoize: 4.1.2
       lodash.uniq: 4.5.0
-    dev: false
 
-  /caniuse-lite@1.0.30001566:
-    resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==}
+  caniuse-lite@1.0.30001566: {}
 
-  /caniuse-lite@1.0.30001591:
-    resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==}
+  caniuse-lite@1.0.30001591: {}
 
-  /canonicalize@1.0.8:
-    resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
-    dev: false
+  canonicalize@1.0.8: {}
 
-  /canvas-confetti@1.9.2:
-    resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==}
-    dev: false
+  canvas-confetti@1.9.3: {}
 
-  /caseless@0.12.0:
-    resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
+  caseless@0.12.0: {}
 
-  /cbor@9.0.2:
-    resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==}
-    engines: {node: '>=16'}
+  cbor@9.0.2:
     dependencies:
       nofilter: 3.1.0
-    dev: false
 
-  /ccount@2.0.1:
-    resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
-    dev: true
+  ccount@2.0.1: {}
 
-  /chai@4.3.10:
-    resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
-    engines: {node: '>=4'}
+  chai@4.3.10:
     dependencies:
       assertion-error: 1.1.0
       check-error: 1.0.3
@@ -9570,116 +16770,66 @@ packages:
       loupe: 2.3.7
       pathval: 1.1.1
       type-detect: 4.0.8
-    dev: true
 
-  /chalk-template@1.1.0:
-    resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==}
-    engines: {node: '>=14.16'}
+  chalk-template@1.1.0:
     dependencies:
       chalk: 5.3.0
-    dev: false
 
-  /chalk@2.4.2:
-    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
-    engines: {node: '>=4'}
+  chalk@2.4.2:
     dependencies:
       ansi-styles: 3.2.1
       escape-string-regexp: 1.0.5
       supports-color: 5.5.0
-    dev: true
 
-  /chalk@3.0.0:
-    resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
-    engines: {node: '>=8'}
+  chalk@3.0.0:
     dependencies:
       ansi-styles: 4.3.0
       supports-color: 7.2.0
-    dev: true
 
-  /chalk@4.1.2:
-    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
-    engines: {node: '>=10'}
+  chalk@4.1.2:
     dependencies:
       ansi-styles: 4.3.0
       supports-color: 7.2.0
 
-  /chalk@5.3.0:
-    resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
-    engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
-    dev: false
+  chalk@5.3.0: {}
 
-  /char-regex@1.0.2:
-    resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
-    engines: {node: '>=10'}
+  char-regex@1.0.2: {}
 
-  /character-entities@2.0.2:
-    resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
-    dev: true
+  character-entities@2.0.2: {}
 
-  /character-parser@2.2.0:
-    resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==}
+  character-parser@2.2.0:
     dependencies:
       is-regex: 1.1.4
 
-  /chardet@0.7.0:
-    resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
-    dev: true
-
-  /chart.js@4.4.2:
-    resolution: {integrity: sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==}
-    engines: {pnpm: '>=8'}
+  chart.js@4.4.2:
     dependencies:
       '@kurkle/color': 0.3.2
-    dev: false
 
-  /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@2.30.0):
-    resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
-    peerDependencies:
-      chart.js: '>=2.8.0'
-      date-fns: '>=2.0.0'
+  chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@2.30.0):
     dependencies:
       chart.js: 4.4.2
       date-fns: 2.30.0
-    dev: false
 
-  /chartjs-chart-matrix@2.0.1(chart.js@4.4.2):
-    resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==}
-    peerDependencies:
-      chart.js: '>=3.0.0'
+  chartjs-chart-matrix@2.0.1(chart.js@4.4.2):
     dependencies:
       chart.js: 4.4.2
-    dev: false
 
-  /chartjs-plugin-gradient@0.6.1(chart.js@4.4.2):
-    resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==}
-    peerDependencies:
-      chart.js: '>=2.6.0'
+  chartjs-plugin-gradient@0.6.1(chart.js@4.4.2):
     dependencies:
       chart.js: 4.4.2
-    dev: false
 
-  /chartjs-plugin-zoom@2.0.1(chart.js@4.4.2):
-    resolution: {integrity: sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==}
-    peerDependencies:
-      chart.js: '>=3.2.0'
+  chartjs-plugin-zoom@2.0.1(chart.js@4.4.2):
     dependencies:
       chart.js: 4.4.2
       hammerjs: 2.0.8
-    dev: false
 
-  /check-error@1.0.3:
-    resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+  check-error@1.0.3:
     dependencies:
       get-func-name: 2.0.2
-    dev: true
 
-  /check-more-types@2.24.0:
-    resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==}
-    engines: {node: '>= 0.8.0'}
-    dev: true
+  check-more-types@2.24.0: {}
 
-  /cheerio-select@2.1.0:
-    resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
+  cheerio-select@2.1.0:
     dependencies:
       boolbase: 1.0.0
       css-select: 5.1.0
@@ -9688,9 +16838,7 @@ packages:
       domhandler: 5.0.3
       domutils: 3.0.1
 
-  /cheerio@1.0.0-rc.12:
-    resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==}
-    engines: {node: '>= 6'}
+  cheerio@1.0.0-rc.12:
     dependencies:
       cheerio-select: 2.1.0
       dom-serializer: 2.0.0
@@ -9700,9 +16848,7 @@ packages:
       parse5: 7.1.2
       parse5-htmlparser2-tree-adapter: 7.0.0
 
-  /chokidar@3.5.3:
-    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
-    engines: {node: '>= 8.10.0'}
+  chokidar@3.5.3:
     dependencies:
       anymatch: 3.1.3
       braces: 3.0.2
@@ -9714,58 +16860,27 @@ packages:
     optionalDependencies:
       fsevents: 2.3.3
 
-  /chownr@1.1.4:
-    resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+  chownr@1.1.4: {}
 
-  /chownr@2.0.0:
-    resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
-    engines: {node: '>=10'}
-    requiresBuild: true
+  chownr@2.0.0: {}
 
-  /chromatic@11.0.0:
-    resolution: {integrity: sha512-utzRVqdMrpzYwZNf7dHWU0z0/rx6SH/FUVNozQxYHDtQfYUdEj+Ro4OSch5+Wsk2FoUmznJyLkaC2J839z1N7A==}
-    hasBin: true
-    peerDependencies:
-      '@chromatic-com/cypress': ^0.5.2 || ^1.0.0
-      '@chromatic-com/playwright': ^0.5.2 || ^1.0.0
-    peerDependenciesMeta:
-      '@chromatic-com/cypress':
-        optional: true
-      '@chromatic-com/playwright':
-        optional: true
-    dev: false
+  chromatic@11.3.0: {}
 
-  /ci-info@3.7.1:
-    resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
-    engines: {node: '>=8'}
-    dev: true
+  ci-info@3.7.1: {}
 
-  /cjs-module-lexer@1.2.2:
-    resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
-    dev: true
+  cjs-module-lexer@1.2.2: {}
 
-  /clean-stack@2.2.0:
-    resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
-    engines: {node: '>=6'}
+  clean-stack@2.2.0: {}
 
-  /clean-stack@5.2.0:
-    resolution: {integrity: sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==}
-    engines: {node: '>=14.16'}
+  clean-stack@5.2.0:
     dependencies:
       escape-string-regexp: 5.0.0
-    dev: true
 
-  /cli-cursor@3.1.0:
-    resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
-    engines: {node: '>=8'}
+  cli-cursor@3.1.0:
     dependencies:
       restore-cursor: 3.1.0
-    dev: true
 
-  /cli-highlight@2.1.11:
-    resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
-    engines: {node: '>=8.0.0', npm: '>=5.0.0'}
-    hasBin: true
+  cli-highlight@2.1.11:
     dependencies:
       chalk: 4.1.2
       highlight.js: 10.7.3
@@ -9773,213 +16888,126 @@ packages:
       parse5: 5.1.1
       parse5-htmlparser2-tree-adapter: 6.0.1
       yargs: 16.2.0
-    dev: false
 
-  /cli-spinners@2.7.0:
-    resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==}
-    engines: {node: '>=6'}
-    dev: true
+  cli-spinners@2.7.0: {}
 
-  /cli-table3@0.6.3:
-    resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==}
-    engines: {node: 10.* || >= 12.*}
+  cli-spinners@2.9.2: {}
+
+  cli-table3@0.6.3:
     dependencies:
       string-width: 4.2.3
     optionalDependencies:
       '@colors/colors': 1.5.0
-    dev: true
 
-  /cli-truncate@2.1.0:
-    resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
-    engines: {node: '>=8'}
+  cli-truncate@2.1.0:
     dependencies:
       slice-ansi: 3.0.0
       string-width: 4.2.3
-    dev: true
 
-  /cli-width@3.0.0:
-    resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
-    engines: {node: '>= 10'}
-    dev: true
+  cli-width@4.1.0: {}
 
-  /cliui@6.0.0:
-    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+  cliui@6.0.0:
     dependencies:
       string-width: 4.2.3
       strip-ansi: 6.0.1
       wrap-ansi: 6.2.0
-    dev: false
 
-  /cliui@7.0.4:
-    resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+  cliui@7.0.4:
     dependencies:
       string-width: 4.2.3
       strip-ansi: 6.0.1
       wrap-ansi: 7.0.0
-    dev: false
 
-  /cliui@8.0.1:
-    resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
-    engines: {node: '>=12'}
+  cliui@8.0.1:
     dependencies:
       string-width: 4.2.3
       strip-ansi: 6.0.1
       wrap-ansi: 7.0.0
 
-  /clone-deep@4.0.1:
-    resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
-    engines: {node: '>=6'}
+  clone-deep@4.0.1:
     dependencies:
       is-plain-object: 2.0.4
       kind-of: 6.0.3
       shallow-clone: 3.0.1
-    dev: true
 
-  /clone-response@1.0.3:
-    resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
+  clone-response@1.0.3:
     dependencies:
       mimic-response: 1.0.1
-    dev: false
 
-  /clone@1.0.4:
-    resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
-    engines: {node: '>=0.8'}
-    dev: true
+  clone@1.0.4: {}
 
-  /cluster-key-slot@1.1.2:
-    resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
-    engines: {node: '>=0.10.0'}
-    dev: false
+  cluster-key-slot@1.1.2: {}
 
-  /co@4.6.0:
-    resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
-    engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
-    dev: true
+  co@4.6.0: {}
 
-  /code-error-fragment@0.0.230:
-    resolution: {integrity: sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==}
-    engines: {node: '>= 4'}
-    dev: true
+  code-error-fragment@0.0.230: {}
 
-  /collect-v8-coverage@1.0.1:
-    resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
-    dev: true
+  collect-v8-coverage@1.0.1: {}
 
-  /color-convert@1.9.3:
-    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+  color-convert@1.9.3:
     dependencies:
       color-name: 1.1.3
-    dev: true
 
-  /color-convert@2.0.1:
-    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
-    engines: {node: '>=7.0.0'}
+  color-convert@2.0.1:
     dependencies:
       color-name: 1.1.4
 
-  /color-name@1.1.3:
-    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-    dev: true
+  color-name@1.1.3: {}
 
-  /color-name@1.1.4:
-    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+  color-name@1.1.4: {}
 
-  /color-string@1.9.1:
-    resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+  color-string@1.9.1:
     dependencies:
       color-name: 1.1.4
       simple-swizzle: 0.2.2
-    dev: false
 
-  /color-support@1.1.3:
-    resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
-    hasBin: true
-    requiresBuild: true
-    dev: false
+  color-support@1.1.3:
     optional: true
 
-  /color@4.2.3:
-    resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
-    engines: {node: '>=12.5.0'}
+  color@4.2.3:
     dependencies:
       color-convert: 2.0.1
       color-string: 1.9.1
-    dev: false
-
-  /colord@2.9.3:
-    resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
-    dev: false
 
-  /colorette@2.0.19:
-    resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
-    dev: true
+  colord@2.9.3: {}
 
-  /colors@1.2.5:
-    resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==}
-    engines: {node: '>=0.1.90'}
-    dev: true
+  colorette@2.0.19: {}
 
-  /combined-stream@1.0.8:
-    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
-    engines: {node: '>= 0.8'}
+  combined-stream@1.0.8:
     dependencies:
       delayed-stream: 1.0.0
 
-  /commander@10.0.1:
-    resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
-    engines: {node: '>=14'}
-    dev: true
+  commander@10.0.1: {}
 
-  /commander@2.20.3:
-    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+  commander@2.20.3: {}
 
-  /commander@6.2.1:
-    resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
-    engines: {node: '>= 6'}
-    dev: true
+  commander@6.2.1: {}
 
-  /commander@7.2.0:
-    resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
-    engines: {node: '>= 10'}
-    dev: false
+  commander@7.2.0: {}
 
-  /commander@9.5.0:
-    resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
-    engines: {node: ^12.20.0 || >=14}
+  commander@8.3.0: {}
 
-  /common-tags@1.8.2:
-    resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
-    engines: {node: '>=4.0.0'}
-    dev: true
+  commander@9.5.0: {}
 
-  /commondir@1.0.1:
-    resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
-    dev: true
+  common-tags@1.8.2: {}
 
-  /compare-versions@6.1.0:
-    resolution: {integrity: sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==}
-    dev: false
+  commondir@1.0.1: {}
 
-  /compress-commons@5.0.1:
-    resolution: {integrity: sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==}
-    engines: {node: '>= 12.0.0'}
+  compare-versions@6.1.0: {}
+
+  compress-commons@6.0.2:
     dependencies:
       crc-32: 1.2.2
-      crc32-stream: 5.0.0
+      crc32-stream: 6.0.0
+      is-stream: 2.0.1
       normalize-path: 3.0.0
-      readable-stream: 3.6.0
-    dev: false
+      readable-stream: 4.3.0
 
-  /compressible@2.0.18:
-    resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
-    engines: {node: '>= 0.6'}
+  compressible@2.0.18:
     dependencies:
       mime-db: 1.52.0
-    dev: true
 
-  /compression@1.7.4:
-    resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==}
-    engines: {node: '>= 0.8.0'}
+  compression@1.7.4:
     dependencies:
       accepts: 1.3.8
       bytes: 3.0.0
@@ -9990,120 +17018,78 @@ packages:
       vary: 1.1.2
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /computeds@0.0.1:
-    resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
-    dev: true
+  computeds@0.0.1: {}
 
-  /concat-map@0.0.1:
-    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+  concat-map@0.0.1: {}
 
-  /concat-stream@1.6.2:
-    resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
-    engines: {'0': node >= 0.8}
+  concat-stream@1.6.2:
     dependencies:
       buffer-from: 1.1.2
       inherits: 2.0.4
       readable-stream: 2.3.7
       typedarray: 0.0.6
 
-  /config-chain@1.1.13:
-    resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+  config-chain@1.1.13:
     dependencies:
       ini: 1.3.8
       proto-list: 1.2.4
-    dev: true
 
-  /consola@2.15.3:
-    resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==}
+  consola@2.15.3: {}
 
-  /console-control-strings@1.1.0:
-    resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
-    requiresBuild: true
-    dev: false
+  console-control-strings@1.1.0:
     optional: true
 
-  /constantinople@4.0.1:
-    resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
+  constantinople@4.0.1:
     dependencies:
       '@babel/parser': 7.23.9
       '@babel/types': 7.23.5
 
-  /content-disposition@0.5.4:
-    resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
-    engines: {node: '>= 0.6'}
+  content-disposition@0.5.4:
     dependencies:
       safe-buffer: 5.2.1
 
-  /content-type@1.0.5:
-    resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
-    engines: {node: '>= 0.6'}
+  content-type@1.0.5: {}
 
-  /convert-source-map@2.0.0:
-    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
-    dev: true
+  convert-source-map@2.0.0: {}
 
-  /cookie-signature@1.0.6:
-    resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
+  cookie-signature@1.0.6: {}
 
-  /cookie-signature@1.2.1:
-    resolution: {integrity: sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==}
-    engines: {node: '>=6.6.0'}
-    dev: false
+  cookie-signature@1.2.1: {}
 
-  /cookie@0.5.0:
-    resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
-    engines: {node: '>= 0.6'}
+  cookie@0.5.0: {}
 
-  /core-js-compat@3.33.3:
-    resolution: {integrity: sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==}
+  cookie@0.6.0: {}
+
+  core-js-compat@3.33.3:
     dependencies:
       browserslist: 4.23.0
-    dev: true
 
-  /core-js@3.29.1:
-    resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==}
-    requiresBuild: true
-    dev: false
+  core-js@3.29.1: {}
 
-  /core-util-is@1.0.2:
-    resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
+  core-util-is@1.0.2: {}
 
-  /core-util-is@1.0.3:
-    resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+  core-util-is@1.0.3: {}
 
-  /cors@2.8.5:
-    resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
-    engines: {node: '>= 0.10'}
+  cors@2.8.5:
     dependencies:
       object-assign: 4.1.1
       vary: 1.1.2
 
-  /crc-32@1.2.2:
-    resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
-    engines: {node: '>=0.8'}
-    hasBin: true
-    dev: false
+  crc-32@1.2.2: {}
 
-  /crc32-stream@5.0.0:
-    resolution: {integrity: sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==}
-    engines: {node: '>= 12.0.0'}
+  crc32-stream@6.0.0:
     dependencies:
       crc-32: 1.2.2
-      readable-stream: 3.6.0
-    dev: false
+      readable-stream: 4.3.0
 
-  /create-jest@29.7.0(@types/node@20.11.22):
-    resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    hasBin: true
+  create-jest@29.7.0(@types/node@20.12.7):
     dependencies:
       '@jest/types': 29.6.3
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.11.22)
+      jest-config: 29.7.0(@types/node@20.12.7)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -10111,77 +17097,51 @@ packages:
       - babel-plugin-macros
       - supports-color
       - ts-node
-    dev: true
 
-  /cron-parser@4.8.1:
-    resolution: {integrity: sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==}
-    engines: {node: '>=12.0.0'}
+  cron-parser@4.8.1:
     dependencies:
       luxon: 3.3.0
-    dev: false
 
-  /cropperjs@2.0.0-beta.4:
-    resolution: {integrity: sha512-tWIQnvbou6eJvQkajwhGLOOEw03dM/i23FmnUQtMKjuzbTDSMP61kcwM77Uit8MXEWcUb5PH8n4jawyrFvj5ag==}
+  cropperjs@2.0.0-beta.5:
     dependencies:
-      '@cropper/elements': 2.0.0-beta.4
-      '@cropper/utils': 2.0.0-beta.4
-    dev: false
+      '@cropper/elements': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-beta.5
 
-  /cross-env@7.0.3:
-    resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
-    engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
-    hasBin: true
+  cross-env@7.0.3:
     dependencies:
       cross-spawn: 7.0.3
-    dev: true
 
-  /cross-fetch@3.1.6:
-    resolution: {integrity: sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==}
+  cross-fetch@3.1.6(encoding@0.1.13):
     dependencies:
-      node-fetch: 2.7.0
+      node-fetch: 2.7.0(encoding@0.1.13)
     transitivePeerDependencies:
       - encoding
 
-  /cross-fetch@4.0.0:
-    resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
+  cross-fetch@4.0.0(encoding@0.1.13):
     dependencies:
-      node-fetch: 2.7.0
+      node-fetch: 2.7.0(encoding@0.1.13)
     transitivePeerDependencies:
       - encoding
-    dev: false
 
-  /cross-spawn@5.1.0:
-    resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
+  cross-spawn@5.1.0:
     dependencies:
       lru-cache: 4.1.5
       shebang-command: 1.2.0
       which: 1.3.1
-    dev: false
 
-  /cross-spawn@7.0.3:
-    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
-    engines: {node: '>= 8'}
+  cross-spawn@7.0.3:
     dependencies:
       path-key: 3.1.1
       shebang-command: 2.0.0
       which: 2.0.2
 
-  /crypto-random-string@2.0.0:
-    resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
-    engines: {node: '>=8'}
-    dev: true
+  crypto-random-string@2.0.0: {}
 
-  /css-declaration-sorter@7.1.1(postcss@8.4.35):
-    resolution: {integrity: sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==}
-    engines: {node: ^14 || ^16 || >=18}
-    peerDependencies:
-      postcss: ^8.0.9
+  css-declaration-sorter@7.2.0(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /css-select@5.1.0:
-    resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
+  css-select@5.1.0:
     dependencies:
       boolbase: 1.0.0
       css-what: 6.1.0
@@ -10189,120 +17149,126 @@ packages:
       domutils: 3.0.1
       nth-check: 2.1.1
 
-  /css-tree@2.2.1:
-    resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
-    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+  css-tree@2.2.1:
     dependencies:
       mdn-data: 2.0.28
       source-map-js: 1.0.2
-    dev: false
 
-  /css-tree@2.3.1:
-    resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
-    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+  css-tree@2.3.1:
     dependencies:
       mdn-data: 2.0.30
       source-map-js: 1.0.2
-    dev: false
-
-  /css-what@6.1.0:
-    resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
-    engines: {node: '>= 6'}
 
-  /css.escape@1.5.1:
-    resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+  css-what@6.1.0: {}
 
-  /cssesc@3.0.0:
-    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
-    engines: {node: '>=4'}
-    hasBin: true
+  css.escape@1.5.1: {}
 
-  /cssnano-preset-default@6.0.5(postcss@8.4.35):
-    resolution: {integrity: sha512-M+qRDEr5QZrfNl0B2ySdbTLGyNb8kBcSjuwR7WBamYBOEREH9t2efnB/nblekqhdGLZdkf4oZNetykG2JWRdZQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
-    dependencies:
-      css-declaration-sorter: 7.1.1(postcss@8.4.35)
-      cssnano-utils: 4.0.1(postcss@8.4.35)
-      postcss: 8.4.35
-      postcss-calc: 9.0.1(postcss@8.4.35)
-      postcss-colormin: 6.0.3(postcss@8.4.35)
-      postcss-convert-values: 6.0.4(postcss@8.4.35)
-      postcss-discard-comments: 6.0.1(postcss@8.4.35)
-      postcss-discard-duplicates: 6.0.2(postcss@8.4.35)
-      postcss-discard-empty: 6.0.2(postcss@8.4.35)
-      postcss-discard-overridden: 6.0.1(postcss@8.4.35)
-      postcss-merge-longhand: 6.0.3(postcss@8.4.35)
-      postcss-merge-rules: 6.0.4(postcss@8.4.35)
-      postcss-minify-font-values: 6.0.2(postcss@8.4.35)
-      postcss-minify-gradients: 6.0.2(postcss@8.4.35)
-      postcss-minify-params: 6.0.3(postcss@8.4.35)
-      postcss-minify-selectors: 6.0.2(postcss@8.4.35)
-      postcss-normalize-charset: 6.0.1(postcss@8.4.35)
-      postcss-normalize-display-values: 6.0.1(postcss@8.4.35)
-      postcss-normalize-positions: 6.0.1(postcss@8.4.35)
-      postcss-normalize-repeat-style: 6.0.1(postcss@8.4.35)
-      postcss-normalize-string: 6.0.1(postcss@8.4.35)
-      postcss-normalize-timing-functions: 6.0.1(postcss@8.4.35)
-      postcss-normalize-unicode: 6.0.3(postcss@8.4.35)
-      postcss-normalize-url: 6.0.1(postcss@8.4.35)
-      postcss-normalize-whitespace: 6.0.1(postcss@8.4.35)
-      postcss-ordered-values: 6.0.1(postcss@8.4.35)
-      postcss-reduce-initial: 6.0.3(postcss@8.4.35)
-      postcss-reduce-transforms: 6.0.1(postcss@8.4.35)
-      postcss-svgo: 6.0.2(postcss@8.4.35)
-      postcss-unique-selectors: 6.0.2(postcss@8.4.35)
-    dev: false
-
-  /cssnano-utils@4.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
-    dependencies:
-      postcss: 8.4.35
-    dev: false
+  cssesc@3.0.0: {}
 
-  /cssnano@6.0.5(postcss@8.4.35):
-    resolution: {integrity: sha512-tpTp/ukgrElwu3ESFY4IvWnGn8eTt8cJhC2aAbtA3lvUlxp6t6UPv8YCLjNnEGiFreT1O0LiOM1U3QyTBVFl2A==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  cssnano-preset-default@6.1.2(postcss@8.4.38):
     dependencies:
-      cssnano-preset-default: 6.0.5(postcss@8.4.35)
+      browserslist: 4.23.0
+      css-declaration-sorter: 7.2.0(postcss@8.4.38)
+      cssnano-utils: 4.0.2(postcss@8.4.38)
+      postcss: 8.4.38
+      postcss-calc: 9.0.1(postcss@8.4.38)
+      postcss-colormin: 6.1.0(postcss@8.4.38)
+      postcss-convert-values: 6.1.0(postcss@8.4.38)
+      postcss-discard-comments: 6.0.2(postcss@8.4.38)
+      postcss-discard-duplicates: 6.0.3(postcss@8.4.38)
+      postcss-discard-empty: 6.0.3(postcss@8.4.38)
+      postcss-discard-overridden: 6.0.2(postcss@8.4.38)
+      postcss-merge-longhand: 6.0.5(postcss@8.4.38)
+      postcss-merge-rules: 6.1.1(postcss@8.4.38)
+      postcss-minify-font-values: 6.1.0(postcss@8.4.38)
+      postcss-minify-gradients: 6.0.3(postcss@8.4.38)
+      postcss-minify-params: 6.1.0(postcss@8.4.38)
+      postcss-minify-selectors: 6.0.4(postcss@8.4.38)
+      postcss-normalize-charset: 6.0.2(postcss@8.4.38)
+      postcss-normalize-display-values: 6.0.2(postcss@8.4.38)
+      postcss-normalize-positions: 6.0.2(postcss@8.4.38)
+      postcss-normalize-repeat-style: 6.0.2(postcss@8.4.38)
+      postcss-normalize-string: 6.0.2(postcss@8.4.38)
+      postcss-normalize-timing-functions: 6.0.2(postcss@8.4.38)
+      postcss-normalize-unicode: 6.1.0(postcss@8.4.38)
+      postcss-normalize-url: 6.0.2(postcss@8.4.38)
+      postcss-normalize-whitespace: 6.0.2(postcss@8.4.38)
+      postcss-ordered-values: 6.0.2(postcss@8.4.38)
+      postcss-reduce-initial: 6.1.0(postcss@8.4.38)
+      postcss-reduce-transforms: 6.0.2(postcss@8.4.38)
+      postcss-svgo: 6.0.3(postcss@8.4.38)
+      postcss-unique-selectors: 6.0.4(postcss@8.4.38)
+
+  cssnano-utils@4.0.2(postcss@8.4.38):
+    dependencies:
+      postcss: 8.4.38
+
+  cssnano@6.1.2(postcss@8.4.38):
+    dependencies:
+      cssnano-preset-default: 6.1.2(postcss@8.4.38)
       lilconfig: 3.1.1
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /csso@5.0.5:
-    resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
-    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+  csso@5.0.5:
     dependencies:
       css-tree: 2.2.1
-    dev: false
 
-  /cssstyle@4.0.1:
-    resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==}
-    engines: {node: '>=18'}
+  cssstyle@4.0.1:
     dependencies:
       rrweb-cssom: 0.6.0
-    dev: false
 
-  /csstype@3.1.3:
-    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+  csstype@3.1.3: {}
 
-  /cwise-compiler@1.1.3:
-    resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==}
+  cwise-compiler@1.1.3:
     dependencies:
       uniq: 1.0.1
-    dev: false
 
-  /cypress@13.6.6:
-    resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==}
-    engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
-    hasBin: true
-    requiresBuild: true
+  cypress@13.7.3:
+    dependencies:
+      '@cypress/request': 3.0.0
+      '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
+      '@types/sinonjs__fake-timers': 8.1.1
+      '@types/sizzle': 2.3.3
+      arch: 2.2.0
+      blob-util: 2.0.2
+      bluebird: 3.7.2
+      buffer: 5.7.1
+      cachedir: 2.3.0
+      chalk: 4.1.2
+      check-more-types: 2.24.0
+      cli-cursor: 3.1.0
+      cli-table3: 0.6.3
+      commander: 6.2.1
+      common-tags: 1.8.2
+      dayjs: 1.11.10
+      debug: 4.3.4(supports-color@8.1.1)
+      enquirer: 2.3.6
+      eventemitter2: 6.4.7
+      execa: 4.1.0
+      executable: 4.1.1
+      extract-zip: 2.0.1(supports-color@8.1.1)
+      figures: 3.2.0
+      fs-extra: 9.1.0
+      getos: 3.2.1
+      is-ci: 3.0.1
+      is-installed-globally: 0.4.0
+      lazy-ass: 1.6.0
+      listr2: 3.14.0(enquirer@2.3.6)
+      lodash: 4.17.21
+      log-symbols: 4.1.0
+      minimist: 1.2.8
+      ospath: 1.2.2
+      pretty-bytes: 5.6.0
+      process: 0.11.10
+      proxy-from-env: 1.0.0
+      request-progress: 3.0.0
+      semver: 7.6.0
+      supports-color: 8.1.1
+      tmp: 0.2.3
+      untildify: 4.0.0
+      yauzl: 2.10.0
+
+  cypress@13.8.1:
     dependencies:
       '@cypress/request': 3.0.0
       '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
@@ -10341,151 +17307,86 @@ packages:
       process: 0.11.10
       proxy-from-env: 1.0.0
       request-progress: 3.0.0
-      semver: 7.5.4
+      semver: 7.6.0
       supports-color: 8.1.1
-      tmp: 0.2.2
+      tmp: 0.2.3
       untildify: 4.0.0
       yauzl: 2.10.0
-    dev: true
 
-  /dashdash@1.14.1:
-    resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
-    engines: {node: '>=0.10'}
+  dashdash@1.14.1:
     dependencies:
       assert-plus: 1.0.0
 
-  /data-uri-to-buffer@0.0.3:
-    resolution: {integrity: sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==}
-    dev: false
+  data-uri-to-buffer@0.0.3: {}
 
-  /data-uri-to-buffer@4.0.0:
-    resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==}
-    engines: {node: '>= 12'}
+  data-uri-to-buffer@4.0.0: {}
 
-  /data-urls@5.0.0:
-    resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
-    engines: {node: '>=18'}
+  data-urls@5.0.0:
     dependencies:
       whatwg-mimetype: 4.0.0
       whatwg-url: 14.0.0
-    dev: false
 
-  /date-fns@2.30.0:
-    resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
-    engines: {node: '>=0.11'}
+  date-fns@2.30.0:
     dependencies:
       '@babel/runtime': 7.23.4
-    dev: false
 
-  /dayjs@1.11.10:
-    resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
+  dayjs@1.11.10: {}
 
-  /de-indent@1.0.2:
-    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
-    dev: true
+  de-indent@1.0.2: {}
 
-  /debug@2.6.9:
-    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
+  debug@2.6.9:
     dependencies:
       ms: 2.0.0
 
-  /debug@3.2.7(supports-color@8.1.1):
-    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
+  debug@3.2.7(supports-color@8.1.1):
     dependencies:
       ms: 2.1.3
+    optionalDependencies:
       supports-color: 8.1.1
 
-  /debug@4.3.4(supports-color@5.5.0):
-    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
-    engines: {node: '>=6.0'}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
+  debug@4.3.4(supports-color@5.5.0):
     dependencies:
       ms: 2.1.2
+    optionalDependencies:
       supports-color: 5.5.0
-    dev: true
 
-  /debug@4.3.4(supports-color@8.1.1):
-    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
-    engines: {node: '>=6.0'}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
+  debug@4.3.4(supports-color@8.1.1):
     dependencies:
       ms: 2.1.2
+    optionalDependencies:
       supports-color: 8.1.1
 
-  /decamelize-keys@1.1.1:
-    resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
-    engines: {node: '>=0.10.0'}
+  decamelize-keys@1.1.1:
     dependencies:
       decamelize: 1.2.0
       map-obj: 1.0.1
-    dev: true
 
-  /decamelize@1.2.0:
-    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
-    engines: {node: '>=0.10.0'}
+  decamelize@1.2.0: {}
 
-  /decimal.js@10.4.3:
-    resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
-    dev: false
+  decimal.js@10.4.3: {}
 
-  /decode-bmp@0.2.1:
-    resolution: {integrity: sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA==}
-    engines: {node: '>=8.6.0'}
+  decode-bmp@0.2.1:
     dependencies:
       '@canvas/image-data': 1.0.0
       to-data-view: 1.1.0
-    dev: false
 
-  /decode-ico@0.4.1:
-    resolution: {integrity: sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA==}
-    engines: {node: '>=8.6'}
+  decode-ico@0.4.1:
     dependencies:
       '@canvas/image-data': 1.0.0
       decode-bmp: 0.2.1
       to-data-view: 1.1.0
-    dev: false
 
-  /decode-named-character-reference@1.0.2:
-    resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
+  decode-named-character-reference@1.0.2:
     dependencies:
       character-entities: 2.0.2
-    dev: true
 
-  /decompress-response@6.0.0:
-    resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
-    engines: {node: '>=10'}
+  decompress-response@6.0.0:
     dependencies:
       mimic-response: 3.1.0
 
-  /dedent@1.3.0:
-    resolution: {integrity: sha512-7glNLfvdsMzZm3FpRY1CHuI2lbYDR+71YmrhmTZjYFD5pfT0ACgnGRdrrC9Mk2uICnzkcdelCx5at787UDGOvg==}
-    peerDependencies:
-      babel-plugin-macros: ^3.1.0
-    peerDependenciesMeta:
-      babel-plugin-macros:
-        optional: true
-    dev: true
+  dedent@1.3.0: {}
 
-  /deep-email-validator@0.1.21:
-    resolution: {integrity: sha512-DBAmMzbr+MAubXQ+TS9tZuPwLcdKscb8YzKZiwoLqF3NmaeEgXvSSHhZ0EXOFeKFE2FNWC4mNXCyiQ/JdFXUwg==}
+  deep-email-validator@0.1.21:
     dependencies:
       '@types/disposable-email-domains': 1.0.2
       axios: 0.24.0
@@ -10493,17 +17394,12 @@ packages:
       mailcheck: 1.1.1
     transitivePeerDependencies:
       - debug
-    dev: false
 
-  /deep-eql@4.1.3:
-    resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
-    engines: {node: '>=6'}
+  deep-eql@4.1.3:
     dependencies:
       type-detect: 4.0.8
-    dev: true
 
-  /deep-equal@2.2.0:
-    resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==}
+  deep-equal@2.2.0:
     dependencies:
       call-bind: 1.0.2
       es-get-iterator: 1.1.3
@@ -10522,54 +17418,32 @@ packages:
       which-boxed-primitive: 1.0.2
       which-collection: 1.0.1
       which-typed-array: 1.1.11
-    dev: true
 
-  /deep-is@0.1.4:
-    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
-    dev: true
+  deep-is@0.1.4: {}
 
-  /deepmerge@4.2.2:
-    resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
-    engines: {node: '>=0.10.0'}
+  deepmerge@4.2.2: {}
 
-  /default-browser-id@3.0.0:
-    resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
-    engines: {node: '>=12'}
+  default-browser-id@3.0.0:
     dependencies:
       bplist-parser: 0.2.0
       untildify: 4.0.0
-    dev: true
 
-  /defaults@1.0.4:
-    resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
+  defaults@1.0.4:
     dependencies:
       clone: 1.0.4
-    dev: true
 
-  /defer-to-connect@2.0.1:
-    resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
-    engines: {node: '>=10'}
+  defer-to-connect@2.0.1: {}
 
-  /define-lazy-prop@2.0.0:
-    resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
-    engines: {node: '>=8'}
-    dev: true
+  define-lazy-prop@2.0.0: {}
 
-  /define-properties@1.2.0:
-    resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
-    engines: {node: '>= 0.4'}
+  define-properties@1.2.0:
     dependencies:
       has-property-descriptors: 1.0.0
       object-keys: 1.1.1
-    dev: true
 
-  /defu@6.1.4:
-    resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
-    dev: true
+  defu@6.1.4: {}
 
-  /del@6.1.1:
-    resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==}
-    engines: {node: '>=10'}
+  del@6.1.1:
     dependencies:
       globby: 11.1.0
       graceful-fs: 4.2.11
@@ -10579,285 +17453,169 @@ packages:
       p-map: 4.0.0
       rimraf: 3.0.2
       slash: 3.0.0
-    dev: true
 
-  /delayed-stream@1.0.0:
-    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
-    engines: {node: '>=0.4.0'}
+  delayed-stream@1.0.0: {}
 
-  /delegates@1.0.0:
-    resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
-    requiresBuild: true
-    dev: false
+  delegates@1.0.0:
     optional: true
 
-  /denque@2.1.0:
-    resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
-    engines: {node: '>=0.10'}
-    dev: false
+  denque@2.1.0: {}
 
-  /depd@2.0.0:
-    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
-    engines: {node: '>= 0.8'}
+  depd@2.0.0: {}
 
-  /dequal@2.0.3:
-    resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
-    engines: {node: '>=6'}
-    dev: true
+  dequal@2.0.3: {}
 
-  /destroy@1.2.0:
-    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+  destroy@1.2.0: {}
 
-  /detect-indent@6.1.0:
-    resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
-    engines: {node: '>=8'}
-    dev: true
+  detect-indent@6.1.0: {}
 
-  /detect-libc@2.0.2:
-    resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
-    engines: {node: '>=8'}
-    dev: false
+  detect-libc@2.0.2:
+    optional: true
 
-  /detect-newline@3.1.0:
-    resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
-    engines: {node: '>=8'}
-    dev: true
+  detect-libc@2.0.3: {}
 
-  /detect-package-manager@2.0.1:
-    resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==}
-    engines: {node: '>=12'}
+  detect-newline@3.1.0: {}
+
+  detect-package-manager@2.0.1:
     dependencies:
       execa: 5.1.1
-    dev: true
 
-  /detect-port@1.5.1:
-    resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==}
-    hasBin: true
+  detect-port@1.5.1:
     dependencies:
       address: 1.2.2
       debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /devlop@1.1.0:
-    resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+  devlop@1.1.0:
     dependencies:
       dequal: 2.0.3
-    dev: true
 
-  /diff-match-patch@1.0.5:
-    resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
-    dev: false
+  diff-match-patch@1.0.5: {}
 
-  /diff-sequences@29.6.3:
-    resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dev: true
+  diff-sequences@29.6.3: {}
 
-  /diff@5.1.0:
-    resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
-    engines: {node: '>=0.3.1'}
+  diff@5.1.0: {}
 
-  /dijkstrajs@1.0.2:
-    resolution: {integrity: sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==}
-    dev: false
+  dijkstrajs@1.0.2: {}
 
-  /dir-glob@3.0.1:
-    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
-    engines: {node: '>=8'}
+  dir-glob@3.0.1:
     dependencies:
       path-type: 4.0.0
 
-  /disposable-email-domains@1.0.62:
-    resolution: {integrity: sha512-LBQvhRw7mznQTPoyZbsmYeNOZt1pN5aCsx4BAU/3siVFuiM9f2oyKzUaB8v1jbxFjE3aYqYiMo63kAL4pHgfWQ==}
-    dev: false
+  disposable-email-domains@1.0.62: {}
 
-  /doctrine@2.1.0:
-    resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
-    engines: {node: '>=0.10.0'}
+  doctrine@2.1.0:
     dependencies:
       esutils: 2.0.3
-    dev: true
 
-  /doctrine@3.0.0:
-    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
-    engines: {node: '>=6.0.0'}
+  doctrine@3.0.0:
     dependencies:
       esutils: 2.0.3
-    dev: true
 
-  /doctypes@1.1.0:
-    resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==}
+  doctypes@1.1.0: {}
 
-  /dom-accessibility-api@0.5.16:
-    resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
-    dev: true
+  dom-accessibility-api@0.5.16: {}
 
-  /dom-accessibility-api@0.6.3:
-    resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
-    dev: true
+  dom-accessibility-api@0.6.3: {}
 
-  /dom-serializer@2.0.0:
-    resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+  dom-serializer@2.0.0:
     dependencies:
       domelementtype: 2.3.0
       domhandler: 5.0.3
       entities: 4.5.0
 
-  /domelementtype@2.3.0:
-    resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+  domelementtype@2.3.0: {}
 
-  /domhandler@5.0.3:
-    resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
-    engines: {node: '>= 4'}
+  domhandler@5.0.3:
     dependencies:
       domelementtype: 2.3.0
 
-  /domutils@3.0.1:
-    resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==}
+  domutils@3.0.1:
     dependencies:
       dom-serializer: 2.0.0
       domelementtype: 2.3.0
       domhandler: 5.0.3
 
-  /dotenv-expand@10.0.0:
-    resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==}
-    engines: {node: '>=12'}
-    dev: true
+  dotenv-expand@10.0.0: {}
 
-  /dotenv@16.0.3:
-    resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
-    engines: {node: '>=12'}
+  dotenv@16.0.3: {}
 
-  /duplexer@0.1.2:
-    resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
-    dev: true
+  duplexer@0.1.2: {}
 
-  /duplexify@3.7.1:
-    resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==}
+  duplexify@3.7.1:
     dependencies:
       end-of-stream: 1.4.4
       inherits: 2.0.4
       readable-stream: 2.3.7
       stream-shift: 1.0.1
-    dev: true
 
-  /eastasianwidth@0.2.0:
-    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+  eastasianwidth@0.2.0: {}
 
-  /ecc-jsbn@0.1.2:
-    resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
+  ecc-jsbn@0.1.2:
     dependencies:
       jsbn: 0.1.1
       safer-buffer: 2.1.2
 
-  /ecdsa-sig-formatter@1.0.11:
-    resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
+  ecdsa-sig-formatter@1.0.11:
     dependencies:
       safe-buffer: 5.2.1
-    dev: false
 
-  /editorconfig@1.0.4:
-    resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
-    engines: {node: '>=14'}
-    hasBin: true
+  editorconfig@1.0.4:
     dependencies:
       '@one-ini/wasm': 0.1.1
       commander: 10.0.1
       minimatch: 9.0.1
       semver: 7.6.0
-    dev: true
 
-  /ee-first@1.1.1:
-    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+  ee-first@1.1.1: {}
 
-  /ejs@3.1.9:
-    resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
-    engines: {node: '>=0.10.0'}
-    hasBin: true
+  ejs@3.1.9:
     dependencies:
       jake: 10.8.5
 
-  /electron-to-chromium@1.4.601:
-    resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==}
-    dev: true
+  electron-to-chromium@1.4.601: {}
 
-  /electron-to-chromium@1.4.686:
-    resolution: {integrity: sha512-3avY1B+vUzNxEgkBDpKOP8WarvUAEwpRaiCL0He5OKWEFxzaOFiq4WoZEZe7qh0ReS7DiWoHMnYoQCKxNZNzSg==}
+  electron-to-chromium@1.4.686: {}
 
-  /emittery@0.13.1:
-    resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
-    engines: {node: '>=12'}
-    dev: true
+  emittery@0.13.1: {}
 
-  /emoji-regex@8.0.0:
-    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+  emoji-regex@8.0.0: {}
 
-  /emoji-regex@9.2.2:
-    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+  emoji-regex@9.2.2: {}
 
-  /encode-utf8@1.0.3:
-    resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
-    dev: false
+  encode-utf8@1.0.3: {}
 
-  /encodeurl@1.0.2:
-    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
-    engines: {node: '>= 0.8'}
+  encodeurl@1.0.2: {}
 
-  /encoding@0.1.13:
-    resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
-    requiresBuild: true
+  encoding@0.1.13:
     dependencies:
       iconv-lite: 0.6.3
-    dev: false
     optional: true
 
-  /end-of-stream@1.4.4:
-    resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+  end-of-stream@1.4.4:
     dependencies:
       once: 1.4.0
 
-  /enquirer@2.3.6:
-    resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
-    engines: {node: '>=8.6'}
+  enquirer@2.3.6:
     dependencies:
       ansi-colors: 4.1.3
-    dev: true
 
-  /entities@2.2.0:
-    resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
-    dev: false
+  entities@2.2.0: {}
 
-  /entities@4.5.0:
-    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
-    engines: {node: '>=0.12'}
+  entities@4.5.0: {}
 
-  /env-paths@2.2.1:
-    resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
-    engines: {node: '>=6'}
-    dev: false
+  env-paths@2.2.1: {}
 
-  /envinfo@7.8.1:
-    resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
+  envinfo@7.8.1: {}
 
-  /err-code@2.0.3:
-    resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
-    dev: false
+  err-code@2.0.3: {}
 
-  /error-ex@1.3.2:
-    resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+  error-ex@1.3.2:
     dependencies:
       is-arrayish: 0.2.1
-    dev: true
 
-  /es-abstract@1.22.1:
-    resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==}
-    engines: {node: '>= 0.4'}
+  es-abstract@1.22.1:
     dependencies:
       array-buffer-byte-length: 1.0.0
       arraybuffer.prototype.slice: 1.0.1
@@ -10898,10 +17656,8 @@ packages:
       typed-array-length: 1.0.4
       unbox-primitive: 1.0.2
       which-typed-array: 1.1.11
-    dev: true
 
-  /es-get-iterator@1.1.3:
-    resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
+  es-get-iterator@1.1.3:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
@@ -10912,70 +17668,43 @@ packages:
       is-string: 1.0.7
       isarray: 2.0.5
       stop-iteration-iterator: 1.0.0
-    dev: true
 
-  /es-module-lexer@0.9.3:
-    resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
-    dev: true
+  es-module-lexer@0.9.3: {}
 
-  /es-set-tostringtag@2.0.1:
-    resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==}
-    engines: {node: '>= 0.4'}
+  es-set-tostringtag@2.0.1:
     dependencies:
       get-intrinsic: 1.2.1
       has: 1.0.3
       has-tostringtag: 1.0.0
-    dev: true
 
-  /es-shim-unscopables@1.0.0:
-    resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==}
+  es-shim-unscopables@1.0.0:
     dependencies:
       has: 1.0.3
-    dev: true
 
-  /es-to-primitive@1.2.1:
-    resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
-    engines: {node: '>= 0.4'}
+  es-to-primitive@1.2.1:
     dependencies:
       is-callable: 1.2.7
       is-date-object: 1.0.5
       is-symbol: 1.0.4
-    dev: true
 
-  /es6-promise@4.2.8:
-    resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
-    requiresBuild: true
-    dev: false
+  es6-promise@4.2.8:
     optional: true
 
-  /es6-promisify@5.0.0:
-    resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==}
-    requiresBuild: true
+  es6-promisify@5.0.0:
     dependencies:
       es6-promise: 4.2.8
-    dev: false
     optional: true
 
-  /esbuild-plugin-alias@0.2.1:
-    resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==}
-    dev: true
+  esbuild-plugin-alias@0.2.1: {}
 
-  /esbuild-register@3.5.0(esbuild@0.19.11):
-    resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==}
-    peerDependencies:
-      esbuild: '>=0.12 <1'
+  esbuild-register@3.5.0(esbuild@0.20.2):
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
-      esbuild: 0.19.11
+      esbuild: 0.20.2
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /esbuild@0.18.20:
-    resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
-    engines: {node: '>=12'}
-    hasBin: true
-    requiresBuild: true
+  esbuild@0.18.20:
     optionalDependencies:
       '@esbuild/android-arm': 0.18.20
       '@esbuild/android-arm64': 0.18.20
@@ -10999,13 +17728,8 @@ packages:
       '@esbuild/win32-arm64': 0.18.20
       '@esbuild/win32-ia32': 0.18.20
       '@esbuild/win32-x64': 0.18.20
-    dev: true
 
-  /esbuild@0.19.11:
-    resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==}
-    engines: {node: '>=12'}
-    hasBin: true
-    requiresBuild: true
+  esbuild@0.19.11:
     optionalDependencies:
       '@esbuild/aix-ppc64': 0.19.11
       '@esbuild/android-arm': 0.19.11
@@ -11031,49 +17755,55 @@ packages:
       '@esbuild/win32-ia32': 0.19.11
       '@esbuild/win32-x64': 0.19.11
 
-  /escalade@3.1.1:
-    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
-    engines: {node: '>=6'}
-
-  /escape-html@1.0.3:
-    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
-
-  /escape-regexp@0.0.1:
-    resolution: {integrity: sha512-jVgdsYRa7RKxTT6MKNC3gdT+BF0Gfhpel19+HMRZJC2L0PufB0XOBuXBoXj29NKHwuktnAXd1Z1lyiH/8vOTpw==}
-
-  /escape-string-regexp@1.0.5:
-    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
-    engines: {node: '>=0.8.0'}
-    dev: true
-
-  /escape-string-regexp@2.0.0:
-    resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /escape-string-regexp@4.0.0:
-    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
-    engines: {node: '>=10'}
-
-  /escape-string-regexp@5.0.0:
-    resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
-    engines: {node: '>=12'}
-
-  /escodegen@2.1.0:
-    resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
-    engines: {node: '>=6.0'}
-    hasBin: true
+  esbuild@0.20.2:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.20.2
+      '@esbuild/android-arm': 0.20.2
+      '@esbuild/android-arm64': 0.20.2
+      '@esbuild/android-x64': 0.20.2
+      '@esbuild/darwin-arm64': 0.20.2
+      '@esbuild/darwin-x64': 0.20.2
+      '@esbuild/freebsd-arm64': 0.20.2
+      '@esbuild/freebsd-x64': 0.20.2
+      '@esbuild/linux-arm': 0.20.2
+      '@esbuild/linux-arm64': 0.20.2
+      '@esbuild/linux-ia32': 0.20.2
+      '@esbuild/linux-loong64': 0.20.2
+      '@esbuild/linux-mips64el': 0.20.2
+      '@esbuild/linux-ppc64': 0.20.2
+      '@esbuild/linux-riscv64': 0.20.2
+      '@esbuild/linux-s390x': 0.20.2
+      '@esbuild/linux-x64': 0.20.2
+      '@esbuild/netbsd-x64': 0.20.2
+      '@esbuild/openbsd-x64': 0.20.2
+      '@esbuild/sunos-x64': 0.20.2
+      '@esbuild/win32-arm64': 0.20.2
+      '@esbuild/win32-ia32': 0.20.2
+      '@esbuild/win32-x64': 0.20.2
+
+  escalade@3.1.1: {}
+
+  escape-html@1.0.3: {}
+
+  escape-regexp@0.0.1: {}
+
+  escape-string-regexp@1.0.5: {}
+
+  escape-string-regexp@2.0.0: {}
+
+  escape-string-regexp@4.0.0: {}
+
+  escape-string-regexp@5.0.0: {}
+
+  escodegen@2.1.0:
     dependencies:
       esprima: 4.0.1
       estraverse: 5.3.0
       esutils: 2.0.3
     optionalDependencies:
       source-map: 0.6.1
-    dev: true
 
-  /eslint-formatter-pretty@4.1.0:
-    resolution: {integrity: sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==}
-    engines: {node: '>=10'}
+  eslint-formatter-pretty@4.1.0:
     dependencies:
       '@types/eslint': 7.29.0
       ansi-escapes: 4.3.2
@@ -11083,87 +17813,47 @@ packages:
       plur: 4.0.0
       string-width: 4.2.3
       supports-hyperlinks: 2.3.0
-    dev: true
 
-  /eslint-import-resolver-node@0.3.9:
-    resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+  eslint-import-resolver-node@0.3.9:
     dependencies:
       debug: 3.2.7(supports-color@8.1.1)
       is-core-module: 2.13.1
       resolve: 1.22.8
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.11.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0):
-    resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: '*'
-      eslint-import-resolver-node: '*'
-      eslint-import-resolver-typescript: '*'
-      eslint-import-resolver-webpack: '*'
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
-      eslint:
-        optional: true
-      eslint-import-resolver-node:
-        optional: true
-      eslint-import-resolver-typescript:
-        optional: true
-      eslint-import-resolver-webpack:
-        optional: true
+  eslint-module-utils@2.8.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.53.0):
     dependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      debug: 3.2.7(supports-color@8.1.1)
-      eslint: 8.53.0
-      eslint-import-resolver-node: 0.3.9
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
-    resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: '*'
-      eslint-import-resolver-node: '*'
-      eslint-import-resolver-typescript: '*'
-      eslint-import-resolver-webpack: '*'
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
-      eslint:
-        optional: true
-      eslint-import-resolver-node:
-        optional: true
-      eslint-import-resolver-typescript:
-        optional: true
-      eslint-import-resolver-webpack:
-        optional: true
+      debug: 3.2.7(supports-color@8.1.1)
+    optionalDependencies:
+      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
+      eslint: 8.53.0
+      eslint-import-resolver-node: 0.3.9
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
     dependencies:
+      debug: 3.2.7(supports-color@8.1.1)
+    optionalDependencies:
       '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      eslint: 8.57.0
+      eslint-import-resolver-node: 0.3.9
+    transitivePeerDependencies:
+      - supports-color
+
+  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
+    dependencies:
       debug: 3.2.7(supports-color@8.1.1)
+    optionalDependencies:
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0)(eslint@8.53.0):
-    resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0):
     dependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
@@ -11172,7 +17862,7 @@ packages:
       doctrine: 2.1.0
       eslint: 8.53.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.11.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.53.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -11182,23 +17872,42 @@ packages:
       object.values: 1.1.7
       semver: 6.3.1
       tsconfig-paths: 3.15.0
+    optionalDependencies:
+      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
-    dev: true
 
-  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0)(eslint@8.57.0):
-    resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
-    engines: {node: '>=4'}
-    peerDependencies:
-      '@typescript-eslint/parser': '*'
-      eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
-    peerDependenciesMeta:
-      '@typescript-eslint/parser':
-        optional: true
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0):
     dependencies:
+      array-includes: 3.1.7
+      array.prototype.findlastindex: 1.2.3
+      array.prototype.flat: 1.3.2
+      array.prototype.flatmap: 1.3.2
+      debug: 3.2.7(supports-color@8.1.1)
+      doctrine: 2.1.0
+      eslint: 8.57.0
+      eslint-import-resolver-node: 0.3.9
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
+      hasown: 2.0.0
+      is-core-module: 2.13.1
+      is-glob: 4.0.3
+      minimatch: 3.1.2
+      object.fromentries: 2.0.7
+      object.groupby: 1.0.1
+      object.values: 1.1.7
+      semver: 6.3.1
+      tsconfig-paths: 3.15.0
+    optionalDependencies:
       '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+    transitivePeerDependencies:
+      - eslint-import-resolver-typescript
+      - eslint-import-resolver-webpack
+      - supports-color
+
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
+    dependencies:
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
@@ -11207,7 +17916,7 @@ packages:
       doctrine: 2.1.0
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -11217,20 +17926,18 @@ packages:
       object.values: 1.1.7
       semver: 6.3.1
       tsconfig-paths: 3.15.0
+    optionalDependencies:
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
-    dev: true
 
-  /eslint-plugin-vue@9.22.0(eslint@8.57.0):
-    resolution: {integrity: sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==}
-    engines: {node: ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
+  eslint-plugin-vue@9.25.0(eslint@8.57.0):
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       eslint: 8.57.0
+      globals: 13.24.0
       natural-compare: 1.4.0
       nth-check: 2.1.1
       postcss-selector-parser: 6.0.15
@@ -11239,29 +17946,17 @@ packages:
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /eslint-rule-docs@1.1.235:
-    resolution: {integrity: sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==}
-    dev: true
+  eslint-rule-docs@1.1.235: {}
 
-  /eslint-scope@7.2.2:
-    resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  eslint-scope@7.2.2:
     dependencies:
       esrecurse: 4.3.0
       estraverse: 5.3.0
-    dev: true
 
-  /eslint-visitor-keys@3.4.3:
-    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dev: true
+  eslint-visitor-keys@3.4.3: {}
 
-  /eslint@8.53.0:
-    resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    hasBin: true
+  eslint@8.53.0:
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
       '@eslint-community/regexpp': 4.6.2
@@ -11303,12 +17998,8 @@ packages:
       text-table: 0.2.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /eslint@8.57.0:
-    resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    hasBin: true
+  eslint@8.57.0:
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       '@eslint-community/regexpp': 4.6.2
@@ -11350,61 +18041,36 @@ packages:
       text-table: 0.2.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /espree@9.6.1:
-    resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  espree@9.6.1:
     dependencies:
       acorn: 8.11.3
       acorn-jsx: 5.3.2(acorn@8.11.3)
       eslint-visitor-keys: 3.4.3
-    dev: true
 
-  /esprima@4.0.1:
-    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
+  esprima@4.0.1: {}
 
-  /esquery@1.4.2:
-    resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==}
-    engines: {node: '>=0.10'}
+  esquery@1.4.2:
     dependencies:
       estraverse: 5.3.0
-    dev: true
 
-  /esrecurse@4.3.0:
-    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
-    engines: {node: '>=4.0'}
+  esrecurse@4.3.0:
     dependencies:
       estraverse: 5.3.0
-    dev: true
 
-  /estraverse@5.3.0:
-    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
-    engines: {node: '>=4.0'}
-    dev: true
+  estraverse@5.3.0: {}
 
-  /estree-walker@2.0.2:
-    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+  estree-walker@2.0.2: {}
 
-  /estree-walker@3.0.3:
-    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+  estree-walker@3.0.3:
     dependencies:
       '@types/estree': 1.0.5
 
-  /esutils@2.0.3:
-    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
-    engines: {node: '>=0.10.0'}
-    dev: true
+  esutils@2.0.3: {}
 
-  /etag@1.8.1:
-    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
-    engines: {node: '>= 0.6'}
+  etag@1.8.1: {}
 
-  /event-stream@3.3.4:
-    resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==}
+  event-stream@3.3.4:
     dependencies:
       duplexer: 0.1.2
       from: 0.1.7
@@ -11413,33 +18079,18 @@ packages:
       split: 0.3.3
       stream-combiner: 0.0.4
       through: 2.3.8
-    dev: true
 
-  /event-target-shim@5.0.1:
-    resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
-    engines: {node: '>=6'}
-    dev: false
+  event-target-shim@5.0.1: {}
 
-  /eventemitter2@6.4.7:
-    resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==}
-    dev: true
+  eventemitter2@6.4.7: {}
 
-  /eventemitter3@4.0.7:
-    resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
-    dev: false
+  eventemitter3@4.0.7: {}
 
-  /eventemitter3@5.0.1:
-    resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
-    dev: false
+  eventemitter3@5.0.1: {}
 
-  /events@3.3.0:
-    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
-    engines: {node: '>=0.8.x'}
-    dev: false
+  events@3.3.0: {}
 
-  /execa@0.7.0:
-    resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==}
-    engines: {node: '>=4'}
+  execa@0.7.0:
     dependencies:
       cross-spawn: 5.1.0
       get-stream: 3.0.0
@@ -11448,11 +18099,8 @@ packages:
       p-finally: 1.0.0
       signal-exit: 3.0.7
       strip-eof: 1.0.0
-    dev: false
 
-  /execa@4.1.0:
-    resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
-    engines: {node: '>=10'}
+  execa@4.1.0:
     dependencies:
       cross-spawn: 7.0.3
       get-stream: 5.2.0
@@ -11463,11 +18111,8 @@ packages:
       onetime: 5.1.2
       signal-exit: 3.0.7
       strip-final-newline: 2.0.0
-    dev: true
 
-  /execa@5.1.1:
-    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
-    engines: {node: '>=10'}
+  execa@5.1.1:
     dependencies:
       cross-spawn: 7.0.3
       get-stream: 6.0.1
@@ -11479,9 +18124,7 @@ packages:
       signal-exit: 3.0.7
       strip-final-newline: 2.0.0
 
-  /execa@6.1.0:
-    resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  execa@6.1.0:
     dependencies:
       cross-spawn: 7.0.3
       get-stream: 6.0.1
@@ -11492,11 +18135,8 @@ packages:
       onetime: 6.0.0
       signal-exit: 3.0.7
       strip-final-newline: 3.0.0
-    dev: true
 
-  /execa@8.0.1:
-    resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
-    engines: {node: '>=16.17'}
+  execa@8.0.1:
     dependencies:
       cross-spawn: 7.0.3
       get-stream: 8.0.1
@@ -11508,35 +18148,23 @@ packages:
       signal-exit: 4.1.0
       strip-final-newline: 3.0.0
 
-  /executable@4.1.1:
-    resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
-    engines: {node: '>=4'}
+  executable@4.1.1:
     dependencies:
       pify: 2.3.0
 
-  /exit@0.1.2:
-    resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
-    engines: {node: '>= 0.8.0'}
-    dev: true
+  exit@0.1.2: {}
 
-  /expect@29.7.0:
-    resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  expect@29.7.0:
     dependencies:
       '@jest/expect-utils': 29.7.0
       jest-get-type: 29.6.3
       jest-matcher-utils: 29.7.0
       jest-message-util: 29.7.0
       jest-util: 29.7.0
-    dev: true
 
-  /exponential-backoff@3.1.1:
-    resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
-    dev: false
+  exponential-backoff@3.1.1: {}
 
-  /express@4.18.2:
-    resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
-    engines: {node: '>= 0.10.0'}
+  express@4.18.2:
     dependencies:
       accepts: 1.3.8
       array-flatten: 1.1.1
@@ -11572,37 +18200,54 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /ext-list@2.2.2:
-    resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==}
-    engines: {node: '>=0.10.0'}
+  express@4.19.2:
+    dependencies:
+      accepts: 1.3.8
+      array-flatten: 1.1.1
+      body-parser: 1.20.2
+      content-disposition: 0.5.4
+      content-type: 1.0.5
+      cookie: 0.6.0
+      cookie-signature: 1.0.6
+      debug: 2.6.9
+      depd: 2.0.0
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      etag: 1.8.1
+      finalhandler: 1.2.0
+      fresh: 0.5.2
+      http-errors: 2.0.0
+      merge-descriptors: 1.0.1
+      methods: 1.1.2
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      path-to-regexp: 0.1.7
+      proxy-addr: 2.0.7
+      qs: 6.11.0
+      range-parser: 1.2.1
+      safe-buffer: 5.2.1
+      send: 0.18.0
+      serve-static: 1.15.0
+      setprototypeof: 1.2.0
+      statuses: 2.0.1
+      type-is: 1.6.18
+      utils-merge: 1.0.1
+      vary: 1.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  ext-list@2.2.2:
     dependencies:
       mime-db: 1.52.0
-    dev: false
 
-  /ext-name@5.0.0:
-    resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==}
-    engines: {node: '>=4'}
+  ext-name@5.0.0:
     dependencies:
       ext-list: 2.2.2
       sort-keys-length: 1.0.1
-    dev: false
 
-  /extend@3.0.2:
-    resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
-
-  /external-editor@3.1.0:
-    resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
-    engines: {node: '>=4'}
-    dependencies:
-      chardet: 0.7.0
-      iconv-lite: 0.4.24
-      tmp: 0.0.33
-    dev: true
+  extend@3.0.2: {}
 
-  /extract-zip@2.0.1(supports-color@8.1.1):
-    resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
-    engines: {node: '>= 10.17.0'}
-    hasBin: true
+  extract-zip@2.0.1(supports-color@8.1.1):
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
       get-stream: 5.2.0
@@ -11611,30 +18256,18 @@ packages:
       '@types/yauzl': 2.10.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /extsprintf@1.3.0:
-    resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
-    engines: {'0': node >=0.6.0}
+  extsprintf@1.3.0: {}
 
-  /fast-content-type-parse@1.1.0:
-    resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
-    dev: false
+  fast-content-type-parse@1.1.0: {}
 
-  /fast-decode-uri-component@1.0.1:
-    resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
-    dev: false
+  fast-decode-uri-component@1.0.1: {}
 
-  /fast-deep-equal@3.1.3:
-    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+  fast-deep-equal@3.1.3: {}
 
-  /fast-fifo@1.3.0:
-    resolution: {integrity: sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw==}
-    dev: false
+  fast-fifo@1.3.0: {}
 
-  /fast-glob@3.3.2:
-    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
-    engines: {node: '>=8.6.0'}
+  fast-glob@3.3.2:
     dependencies:
       '@nodelib/fs.stat': 2.0.5
       '@nodelib/fs.walk': 1.2.8
@@ -11642,187 +18275,131 @@ packages:
       merge2: 1.4.1
       micromatch: 4.0.5
 
-  /fast-json-stable-stringify@2.1.0:
-    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+  fast-json-stable-stringify@2.1.0: {}
 
-  /fast-json-stringify@5.8.0:
-    resolution: {integrity: sha512-VVwK8CFMSALIvt14U8AvrSzQAwN/0vaVRiFFUVlpnXSnDGrSkOAO5MtzyN8oQNjLd5AqTW5OZRgyjoNuAuR3jQ==}
+  fast-json-stringify@5.8.0:
     dependencies:
       '@fastify/deepmerge': 1.3.0
-      ajv: 8.12.0
-      ajv-formats: 2.1.1(ajv@8.12.0)
+      ajv: 8.13.0
+      ajv-formats: 2.1.1(ajv@8.13.0)
       fast-deep-equal: 3.1.3
       fast-uri: 2.2.0
       rfdc: 1.3.0
-    dev: false
 
-  /fast-levenshtein@2.0.6:
-    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
-    dev: true
+  fast-levenshtein@2.0.6: {}
 
-  /fast-querystring@1.1.2:
-    resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+  fast-querystring@1.1.2:
     dependencies:
       fast-decode-uri-component: 1.0.1
-    dev: false
 
-  /fast-redact@3.1.2:
-    resolution: {integrity: sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==}
-    engines: {node: '>=6'}
-    dev: false
+  fast-redact@3.1.2: {}
 
-  /fast-safe-stringify@2.1.1:
-    resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+  fast-safe-stringify@2.1.1: {}
 
-  /fast-uri@2.2.0:
-    resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==}
-    dev: false
+  fast-uri@2.2.0: {}
 
-  /fast-xml-parser@4.2.5:
-    resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==}
-    hasBin: true
+  fast-xml-parser@4.2.5:
     dependencies:
       strnum: 1.0.5
-    dev: false
 
-  /fastify-plugin@4.5.0:
-    resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==}
-    dev: false
+  fastify-plugin@4.5.0: {}
 
-  /fastify-raw-body@4.3.0:
-    resolution: {integrity: sha512-F4o8ZIMVx4YoxGfwrZys6wyjl40gF3Yv6AWWRy62ozFAyZBSS831/uyyCAqKYw3tR73g180ryG98yih6To1PUQ==}
-    engines: {node: '>= 10'}
+  fastify-raw-body@4.3.0:
     dependencies:
       fastify-plugin: 4.5.0
       raw-body: 2.5.2
       secure-json-parse: 2.7.0
-    dev: false
 
-  /fastify@4.25.2:
-    resolution: {integrity: sha512-SywRouGleDHvRh054onj+lEZnbC1sBCLkR0UY3oyJwjD4BdZJUrxBqfkfCaqn74pVCwBaRHGuL3nEWeHbHzAfw==}
+  fastify@4.26.2:
     dependencies:
       '@fastify/ajv-compiler': 3.5.0
       '@fastify/error': 3.4.0
       '@fastify/fast-json-stringify-compiler': 4.3.0
       abstract-logging: 2.0.1
-      avvio: 8.2.1
+      avvio: 8.3.0
       fast-content-type-parse: 1.1.0
       fast-json-stringify: 5.8.0
-      find-my-way: 7.7.0
+      find-my-way: 8.2.0
       light-my-request: 5.11.0
       pino: 8.17.0
       process-warning: 3.0.0
       proxy-addr: 2.0.7
       rfdc: 1.3.0
       secure-json-parse: 2.7.0
-      semver: 7.5.4
+      semver: 7.6.0
       toad-cache: 3.3.0
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /fastq@1.15.0:
-    resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
+  fastq@1.15.0:
     dependencies:
       reusify: 1.0.4
 
-  /fb-watchman@2.0.2:
-    resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+  fastq@1.17.1:
+    dependencies:
+      reusify: 1.0.4
+
+  fb-watchman@2.0.2:
     dependencies:
       bser: 2.1.1
-    dev: true
 
-  /fd-slicer@1.1.0:
-    resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
+  fd-slicer@1.1.0:
     dependencies:
       pend: 1.2.0
-    dev: true
 
-  /feed@4.2.2:
-    resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==}
-    engines: {node: '>=0.4.0'}
+  feed@4.2.2:
     dependencies:
       xml-js: 1.6.11
-    dev: false
 
-  /fetch-blob@3.2.0:
-    resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
-    engines: {node: ^12.20 || >= 14.13}
+  fetch-blob@3.2.0:
     dependencies:
       node-domexception: 1.0.0
       web-streams-polyfill: 3.2.1
 
-  /fetch-retry@5.0.4:
-    resolution: {integrity: sha512-LXcdgpdcVedccGg0AZqg+S8lX/FCdwXD92WNZ5k5qsb0irRhSFsBOpcJt7oevyqT2/C2nEE0zSFNdBEpj3YOSw==}
-    dev: true
+  fetch-retry@5.0.4: {}
 
-  /figures@3.2.0:
-    resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
-    engines: {node: '>=8'}
+  figures@3.2.0:
     dependencies:
       escape-string-regexp: 1.0.5
-    dev: true
 
-  /file-entry-cache@6.0.1:
-    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
-    engines: {node: ^10.12.0 || >=12.0.0}
+  file-entry-cache@6.0.1:
     dependencies:
       flat-cache: 3.0.4
-    dev: true
 
-  /file-system-cache@2.3.0:
-    resolution: {integrity: sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==}
+  file-system-cache@2.3.0:
     dependencies:
       fs-extra: 11.1.1
       ramda: 0.29.0
-    dev: true
 
-  /file-type@17.1.6:
-    resolution: {integrity: sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  file-type@17.1.6:
     dependencies:
       readable-web-to-node-stream: 3.0.2
       strtok3: 7.0.0
       token-types: 5.0.1
-    dev: false
 
-  /file-type@19.0.0:
-    resolution: {integrity: sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==}
-    engines: {node: '>=18'}
+  file-type@19.0.0:
     dependencies:
       readable-web-to-node-stream: 3.0.2
       strtok3: 7.0.0
       token-types: 5.0.1
-    dev: false
 
-  /filelist@1.0.4:
-    resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
+  filelist@1.0.4:
     dependencies:
       minimatch: 5.1.2
 
-  /filename-reserved-regex@3.0.0:
-    resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dev: false
+  filename-reserved-regex@3.0.0: {}
 
-  /filenamify@5.1.1:
-    resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==}
-    engines: {node: '>=12.20'}
+  filenamify@5.1.1:
     dependencies:
       filename-reserved-regex: 3.0.0
       strip-outer: 2.0.0
       trim-repeated: 2.0.0
-    dev: false
 
-  /fill-range@7.0.1:
-    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
-    engines: {node: '>=8'}
+  fill-range@7.0.1:
     dependencies:
       to-regex-range: 5.0.1
 
-  /finalhandler@1.2.0:
-    resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
-    engines: {node: '>= 0.8'}
+  finalhandler@1.2.0:
     dependencies:
       debug: 2.6.9
       encodeurl: 1.0.2
@@ -11834,69 +18411,45 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /find-cache-dir@2.1.0:
-    resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
-    engines: {node: '>=6'}
+  find-cache-dir@2.1.0:
     dependencies:
       commondir: 1.0.1
       make-dir: 2.1.0
       pkg-dir: 3.0.0
-    dev: true
 
-  /find-cache-dir@3.3.2:
-    resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
-    engines: {node: '>=8'}
+  find-cache-dir@3.3.2:
     dependencies:
       commondir: 1.0.1
       make-dir: 3.1.0
       pkg-dir: 4.2.0
-    dev: true
 
-  /find-my-way@7.7.0:
-    resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==}
-    engines: {node: '>=14'}
+  find-my-way@8.2.0:
     dependencies:
       fast-deep-equal: 3.1.3
       fast-querystring: 1.1.2
-      safe-regex2: 2.0.0
-    dev: false
+      safe-regex2: 3.1.0
 
-  /find-package-json@1.2.0:
-    resolution: {integrity: sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==}
-    dev: true
+  find-package-json@1.2.0: {}
 
-  /find-up@3.0.0:
-    resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
-    engines: {node: '>=6'}
+  find-up@3.0.0:
     dependencies:
       locate-path: 3.0.0
-    dev: true
 
-  /find-up@4.1.0:
-    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
-    engines: {node: '>=8'}
+  find-up@4.1.0:
     dependencies:
       locate-path: 5.0.0
       path-exists: 4.0.0
 
-  /find-up@5.0.0:
-    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
-    engines: {node: '>=10'}
+  find-up@5.0.0:
     dependencies:
       locate-path: 6.0.0
       path-exists: 4.0.0
-    dev: true
 
-  /find-versions@5.1.0:
-    resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==}
-    engines: {node: '>=12'}
+  find-versions@5.1.0:
     dependencies:
       semver-regex: 4.0.5
-    dev: false
 
-  /fkill@9.0.0:
-    resolution: {integrity: sha512-MdYSsbdCaIRjzo5edthZtWmEZVMfr1qrtYZUHIdO3swCE+CoZA8S5l0s4jDsYlTa9ZiXv0pTgpzE7s4N8NeUOA==}
-    engines: {node: '>=18'}
+  fkill@9.0.0:
     dependencies:
       aggregate-error: 5.0.0
       execa: 8.0.1
@@ -11904,207 +18457,125 @@ packages:
       process-exists: 5.0.0
       ps-list: 8.1.1
       taskkill: 5.0.0
-    dev: true
 
-  /flat-cache@3.0.4:
-    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
-    engines: {node: ^10.12.0 || >=12.0.0}
+  flat-cache@3.0.4:
     dependencies:
       flatted: 3.2.7
       rimraf: 3.0.2
-    dev: true
 
-  /flatted@3.2.7:
-    resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
-    dev: true
+  flatted@3.2.7: {}
 
-  /flow-parser@0.202.0:
-    resolution: {integrity: sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw==}
-    engines: {node: '>=0.4.0'}
-    dev: true
+  flow-parser@0.202.0: {}
 
-  /fluent-ffmpeg@2.1.2:
-    resolution: {integrity: sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==}
-    engines: {node: '>=0.8.0'}
+  fluent-ffmpeg@2.1.2:
     dependencies:
       async: 3.2.4
       which: 1.3.1
-    dev: false
 
-  /follow-redirects@1.15.2(debug@4.3.4):
-    resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
-    engines: {node: '>=4.0'}
-    peerDependencies:
-      debug: '*'
-    peerDependenciesMeta:
-      debug:
-        optional: true
-    dependencies:
+  follow-redirects@1.15.2(debug@4.3.4):
+    optionalDependencies:
       debug: 4.3.4(supports-color@8.1.1)
 
-  /for-each@0.3.3:
-    resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+  for-each@0.3.3:
     dependencies:
       is-callable: 1.2.7
-    dev: true
 
-  /foreground-child@3.1.1:
-    resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
-    engines: {node: '>=14'}
+  foreground-child@3.1.1:
     dependencies:
       cross-spawn: 7.0.3
       signal-exit: 4.1.0
 
-  /forever-agent@0.6.1:
-    resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
+  forever-agent@0.6.1: {}
 
-  /form-data-encoder@2.1.4:
-    resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
-    engines: {node: '>= 14.17'}
+  form-data-encoder@2.1.4: {}
 
-  /form-data-encoder@4.0.2:
-    resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==}
-    engines: {node: '>= 18'}
-    dev: false
+  form-data-encoder@4.0.2: {}
 
-  /form-data@2.3.3:
-    resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
-    engines: {node: '>= 0.12'}
+  form-data@2.3.3:
     dependencies:
       asynckit: 0.4.0
       combined-stream: 1.0.8
       mime-types: 2.1.35
 
-  /form-data@3.0.1:
-    resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
-    engines: {node: '>= 6'}
-    requiresBuild: true
+  form-data@3.0.1:
     dependencies:
       asynckit: 0.4.0
       combined-stream: 1.0.8
       mime-types: 2.1.35
-    dev: false
 
-  /form-data@4.0.0:
-    resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
-    engines: {node: '>= 6'}
+  form-data@4.0.0:
     dependencies:
       asynckit: 0.4.0
       combined-stream: 1.0.8
       mime-types: 2.1.35
 
-  /formdata-polyfill@4.0.10:
-    resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
-    engines: {node: '>=12.20.0'}
+  formdata-polyfill@4.0.10:
     dependencies:
       fetch-blob: 3.2.0
 
-  /forwarded@0.2.0:
-    resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
-    engines: {node: '>= 0.6'}
+  forwarded@0.2.0: {}
 
-  /fresh@0.5.2:
-    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
-    engines: {node: '>= 0.6'}
+  fresh@0.5.2: {}
 
-  /from@0.1.7:
-    resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
-    dev: true
+  from@0.1.7: {}
 
-  /fs-constants@1.0.0:
-    resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
-    dev: true
+  fs-constants@1.0.0: {}
 
-  /fs-extra@11.1.1:
-    resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
-    engines: {node: '>=14.14'}
+  fs-extra@11.1.1:
     dependencies:
       graceful-fs: 4.2.11
       jsonfile: 6.1.0
       universalify: 2.0.0
-    dev: true
 
-  /fs-extra@7.0.1:
-    resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
-    engines: {node: '>=6 <7 || >=8'}
+  fs-extra@7.0.1:
     dependencies:
       graceful-fs: 4.2.11
       jsonfile: 4.0.0
       universalify: 0.1.2
-    dev: true
 
-  /fs-extra@8.1.0:
-    resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
-    engines: {node: '>=6 <7 || >=8'}
+  fs-extra@8.1.0:
     dependencies:
       graceful-fs: 4.2.11
       jsonfile: 4.0.0
       universalify: 0.1.2
-    dev: false
 
-  /fs-extra@9.1.0:
-    resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
-    engines: {node: '>=10'}
+  fs-extra@9.1.0:
     dependencies:
       at-least-node: 1.0.0
       graceful-fs: 4.2.11
       jsonfile: 6.1.0
       universalify: 2.0.0
-    dev: true
 
-  /fs-minipass@1.2.7:
-    resolution: {integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==}
-    requiresBuild: true
+  fs-minipass@1.2.7:
     dependencies:
       minipass: 2.9.0
-    dev: false
     optional: true
 
-  /fs-minipass@2.1.0:
-    resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
-    engines: {node: '>= 8'}
-    requiresBuild: true
+  fs-minipass@2.1.0:
     dependencies:
       minipass: 3.3.6
 
-  /fs-minipass@3.0.2:
-    resolution: {integrity: sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  fs-minipass@3.0.2:
     dependencies:
       minipass: 5.0.0
-    dev: false
 
-  /fs.realpath@1.0.0:
-    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+  fs.realpath@1.0.0: {}
 
-  /fsevents@2.3.3:
-    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
-    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
-    os: [darwin]
-    requiresBuild: true
+  fsevents@2.3.3:
     optional: true
 
-  /function-bind@1.1.2:
-    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+  function-bind@1.1.2: {}
 
-  /function.prototype.name@1.1.5:
-    resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
-    engines: {node: '>= 0.4'}
+  function.prototype.name@1.1.5:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
       functions-have-names: 1.2.3
-    dev: true
 
-  /functions-have-names@1.2.3:
-    resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
-    dev: true
+  functions-have-names@1.2.3: {}
 
-  /gauge@3.0.2:
-    resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
-    engines: {node: '>=10'}
-    requiresBuild: true
+  gauge@3.0.2:
     dependencies:
       aproba: 2.0.0
       color-support: 1.1.3
@@ -12115,42 +18586,26 @@ packages:
       string-width: 4.2.3
       strip-ansi: 6.0.1
       wide-align: 1.1.5
-    dev: false
     optional: true
 
-  /gensync@1.0.0-beta.2:
-    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
-    engines: {node: '>=6.9.0'}
-    dev: true
+  gensync@1.0.0-beta.2: {}
 
-  /get-caller-file@2.0.5:
-    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
-    engines: {node: 6.* || 8.* || >= 10.*}
+  get-caller-file@2.0.5: {}
 
-  /get-func-name@2.0.2:
-    resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
-    dev: true
+  get-func-name@2.0.2: {}
 
-  /get-intrinsic@1.2.1:
-    resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
+  get-intrinsic@1.2.1:
     dependencies:
       function-bind: 1.1.2
       has: 1.0.3
       has-proto: 1.0.1
       has-symbols: 1.0.3
 
-  /get-npm-tarball-url@2.0.3:
-    resolution: {integrity: sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw==}
-    engines: {node: '>=12.17'}
-    dev: true
+  get-npm-tarball-url@2.0.3: {}
 
-  /get-package-type@0.1.0:
-    resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
-    engines: {node: '>=8.0.0'}
-    dev: true
+  get-package-type@0.1.0: {}
 
-  /get-pixels-frame-info-update@3.3.2:
-    resolution: {integrity: sha512-LzVij57X/gK4Y6LpcDdqj+R9WCpD6Sv3ZH85GMA+S3xgPGCz81mHql4GiSnF4GijRjk7TE0ja2sDr8FFYKLe2g==}
+  get-pixels-frame-info-update@3.3.2:
     dependencies:
       data-uri-to-buffer: 0.0.3
       jpeg-js: 0.3.7
@@ -12163,62 +18618,39 @@ packages:
       pngjs: 3.4.0
       request: 2.88.2
       through: 2.3.8
-    dev: false
 
-  /get-stream@3.0.0:
-    resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
-    engines: {node: '>=4'}
-    dev: false
+  get-stream@3.0.0: {}
 
-  /get-stream@5.2.0:
-    resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
-    engines: {node: '>=8'}
+  get-stream@5.2.0:
     dependencies:
       pump: 3.0.0
 
-  /get-stream@6.0.1:
-    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
-    engines: {node: '>=10'}
+  get-stream@6.0.1: {}
 
-  /get-stream@8.0.1:
-    resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
-    engines: {node: '>=16'}
+  get-stream@8.0.1: {}
 
-  /get-symbol-description@1.0.0:
-    resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
-    engines: {node: '>= 0.4'}
+  get-symbol-description@1.0.0:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
-    dev: true
 
-  /get-tsconfig@4.7.2:
-    resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
+  get-tsconfig@4.7.2:
     dependencies:
       resolve-pkg-maps: 1.0.0
-    dev: true
 
-  /getos@3.2.1:
-    resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
+  getos@3.2.1:
     dependencies:
       async: 3.2.4
-    dev: true
 
-  /getpass@0.1.7:
-    resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
+  getpass@0.1.7:
     dependencies:
       assert-plus: 1.0.0
 
-  /gif-encoder@0.4.1:
-    resolution: {integrity: sha512-++rNGpDBgWQ9eXj9JfTBLHMUEd7lDOdzIvFyHQM9yL8ffxkcg4G6jWmsgu/r59Uq6nHc3wcVwtgy3geLnIWunQ==}
-    engines: {node: '>= 0.8.0'}
+  gif-encoder@0.4.1:
     dependencies:
       readable-stream: 1.1.14
-    dev: false
 
-  /giget@1.1.2:
-    resolution: {integrity: sha512-HsLoS07HiQ5oqvObOI+Qb2tyZH4Gj5nYGfF9qQcZNrPw+uEFhdXtgJr01aO2pWadGHucajYDLxxbtQkm97ON2A==}
-    hasBin: true
+  giget@1.1.2:
     dependencies:
       colorette: 2.0.19
       defu: 6.1.4
@@ -12226,46 +18658,28 @@ packages:
       mri: 1.2.0
       node-fetch-native: 1.0.2
       pathe: 1.1.2
-      tar: 6.2.0
+      tar: 6.2.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /github-slugger@2.0.0:
-    resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
-    dev: true
+  github-slugger@2.0.0: {}
 
-  /glob-parent@5.1.2:
-    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
-    engines: {node: '>= 6'}
+  glob-parent@5.1.2:
     dependencies:
       is-glob: 4.0.3
 
-  /glob-parent@6.0.2:
-    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
-    engines: {node: '>=10.13.0'}
+  glob-parent@6.0.2:
     dependencies:
       is-glob: 4.0.3
-    dev: true
 
-  /glob-promise@4.2.2(glob@7.2.3):
-    resolution: {integrity: sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw==}
-    engines: {node: '>=12'}
-    peerDependencies:
-      glob: ^7.1.6
+  glob-promise@4.2.2(glob@7.2.3):
     dependencies:
       '@types/glob': 7.2.0
       glob: 7.2.3
-    dev: true
 
-  /glob-to-regexp@0.4.1:
-    resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
-    dev: true
+  glob-to-regexp@0.4.1: {}
 
-  /glob@10.3.10:
-    resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    hasBin: true
+  glob@10.3.10:
     dependencies:
       foreground-child: 3.1.1
       jackspeak: 2.3.6
@@ -12273,8 +18687,15 @@ packages:
       minipass: 7.0.4
       path-scurry: 1.10.1
 
-  /glob@7.2.3:
-    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+  glob@10.3.12:
+    dependencies:
+      foreground-child: 3.1.1
+      jackspeak: 2.3.6
+      minimatch: 9.0.3
+      minipass: 7.0.4
+      path-scurry: 1.10.2
+
+  glob@7.2.3:
     dependencies:
       fs.realpath: 1.0.0
       inflight: 1.0.6
@@ -12283,9 +18704,7 @@ packages:
       once: 1.4.0
       path-is-absolute: 1.0.1
 
-  /glob@8.1.0:
-    resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
-    engines: {node: '>=12'}
+  glob@8.1.0:
     dependencies:
       fs.realpath: 1.0.0
       inflight: 1.0.6
@@ -12293,35 +18712,25 @@ packages:
       minimatch: 5.1.2
       once: 1.4.0
 
-  /global-dirs@3.0.1:
-    resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
-    engines: {node: '>=10'}
+  global-dirs@3.0.1:
     dependencies:
       ini: 2.0.0
-    dev: true
 
-  /globals@11.12.0:
-    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
-    engines: {node: '>=4'}
-    dev: true
+  globals@11.12.0: {}
 
-  /globals@13.19.0:
-    resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==}
-    engines: {node: '>=8'}
+  globals@13.19.0:
     dependencies:
       type-fest: 0.20.2
-    dev: true
 
-  /globalthis@1.0.3:
-    resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
-    engines: {node: '>= 0.4'}
+  globals@13.24.0:
+    dependencies:
+      type-fest: 0.20.2
+
+  globalthis@1.0.3:
     dependencies:
       define-properties: 1.2.0
-    dev: true
 
-  /globby@11.1.0:
-    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
-    engines: {node: '>=10'}
+  globby@11.1.0:
     dependencies:
       array-union: 2.1.0
       dir-glob: 3.0.1
@@ -12330,21 +18739,14 @@ packages:
       merge2: 1.4.1
       slash: 3.0.0
 
-  /google-protobuf@3.21.2:
-    resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==}
-    requiresBuild: true
-    dev: false
+  google-protobuf@3.21.2:
     optional: true
 
-  /gopd@1.0.1:
-    resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+  gopd@1.0.1:
     dependencies:
       get-intrinsic: 1.2.1
-    dev: true
 
-  /got@11.8.5:
-    resolution: {integrity: sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==}
-    engines: {node: '>=10.19.0'}
+  got@11.8.5:
     dependencies:
       '@sindresorhus/is': 4.6.0
       '@szmarczak/http-timer': 4.0.6
@@ -12357,11 +18759,8 @@ packages:
       lowercase-keys: 2.0.0
       p-cancelable: 2.1.1
       responselike: 2.0.1
-    dev: false
 
-  /got@12.6.1:
-    resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==}
-    engines: {node: '>=14.16'}
+  got@12.6.1:
     dependencies:
       '@sindresorhus/is': 5.3.0
       '@szmarczak/http-timer': 5.0.1
@@ -12375,9 +18774,7 @@ packages:
       p-cancelable: 3.0.0
       responselike: 3.0.0
 
-  /got@14.2.0:
-    resolution: {integrity: sha512-dBq2KkHcQl3AwPoIWsLsQScCPpUgRulz1qZVthjPYKYOPmYfBnekR3vxecjZbm91Vc3JUGnV9mqFX7B+Fe2quw==}
-    engines: {node: '>=20'}
+  got@14.2.1:
     dependencies:
       '@sindresorhus/is': 6.1.0
       '@szmarczak/http-timer': 5.0.1
@@ -12390,27 +18787,16 @@ packages:
       lowercase-keys: 3.0.0
       p-cancelable: 4.0.1
       responselike: 3.0.0
-    dev: false
 
-  /graceful-fs@4.2.11:
-    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+  graceful-fs@4.2.11: {}
 
-  /grapheme-splitter@1.0.4:
-    resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
-    dev: true
+  grapheme-splitter@1.0.4: {}
 
-  /graphemer@1.4.0:
-    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
-    dev: true
+  graphemer@1.4.0: {}
 
-  /graphql@16.8.1:
-    resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==}
-    engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
-    dev: true
+  graphql@16.8.1: {}
 
-  /gunzip-maybe@1.4.2:
-    resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==}
-    hasBin: true
+  gunzip-maybe@1.4.2:
     dependencies:
       browserify-zlib: 0.1.4
       is-deflate: 1.0.0
@@ -12418,17 +18804,10 @@ packages:
       peek-stream: 1.1.3
       pumpify: 1.5.1
       through2: 2.0.5
-    dev: true
 
-  /hammerjs@2.0.8:
-    resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
-    engines: {node: '>=0.8.0'}
-    dev: false
+  hammerjs@2.0.8: {}
 
-  /handlebars@4.7.7:
-    resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==}
-    engines: {node: '>=0.4.7'}
-    hasBin: true
+  handlebars@4.7.7:
     dependencies:
       minimist: 1.2.8
       neo-async: 2.6.2
@@ -12436,196 +18815,105 @@ packages:
       wordwrap: 1.0.0
     optionalDependencies:
       uglify-js: 3.17.4
-    dev: true
-
-  /happy-dom@10.0.3:
-    resolution: {integrity: sha512-WkCP+Z5fX6U5PY+yHP3ElV5D9PoxRAHRWPFq3pG9rg/6Hjf5ak7dozAgSCywsTRUq2qfa8vV8OQvUy5pRXy8EQ==}
-    dependencies:
-      css.escape: 1.5.1
-      entities: 4.5.0
-      iconv-lite: 0.6.3
-      webidl-conversions: 7.0.0
-      whatwg-encoding: 2.0.0
-      whatwg-mimetype: 3.0.0
-    dev: false
 
-  /happy-dom@13.6.2:
-    resolution: {integrity: sha512-Ku+wDqcF/KwFA0dI+xIMZd9Jn020RXjuSil/Vz7gu2yhDC3FsDYZ55qqV9k+SGC4opwb4acisXqVSRxUJMlPbQ==}
-    engines: {node: '>=16.0.0'}
+  happy-dom@14.7.1:
     dependencies:
       entities: 4.5.0
       webidl-conversions: 7.0.0
       whatwg-mimetype: 3.0.0
-    dev: true
 
-  /har-schema@2.0.0:
-    resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
-    engines: {node: '>=4'}
-    dev: false
+  har-schema@2.0.0: {}
 
-  /har-validator@5.1.5:
-    resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==}
-    engines: {node: '>=6'}
-    deprecated: this library is no longer supported
+  har-validator@5.1.5:
     dependencies:
       ajv: 6.12.6
       har-schema: 2.0.0
-    dev: false
 
-  /hard-rejection@2.1.0:
-    resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
-    engines: {node: '>=6'}
-    dev: true
+  hard-rejection@2.1.0: {}
 
-  /has-bigints@1.0.2:
-    resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
-    dev: true
+  has-bigints@1.0.2: {}
 
-  /has-flag@3.0.0:
-    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
-    engines: {node: '>=4'}
-    dev: true
+  has-flag@3.0.0: {}
 
-  /has-flag@4.0.0:
-    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
-    engines: {node: '>=8'}
+  has-flag@4.0.0: {}
 
-  /has-property-descriptors@1.0.0:
-    resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
+  has-property-descriptors@1.0.0:
     dependencies:
       get-intrinsic: 1.2.1
-    dev: true
 
-  /has-proto@1.0.1:
-    resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
-    engines: {node: '>= 0.4'}
+  has-proto@1.0.1: {}
 
-  /has-symbols@1.0.3:
-    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
-    engines: {node: '>= 0.4'}
+  has-symbols@1.0.3: {}
 
-  /has-tostringtag@1.0.0:
-    resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
-    engines: {node: '>= 0.4'}
+  has-tostringtag@1.0.0:
     dependencies:
       has-symbols: 1.0.3
 
-  /has-unicode@2.0.1:
-    resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
-    requiresBuild: true
-    dev: false
+  has-unicode@2.0.1:
     optional: true
 
-  /has@1.0.3:
-    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
-    engines: {node: '>= 0.4.0'}
+  has@1.0.3:
     dependencies:
       function-bind: 1.1.2
 
-  /hash-sum@2.0.0:
-    resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
-    dev: true
+  hash-sum@2.0.0: {}
 
-  /hashlru@2.3.0:
-    resolution: {integrity: sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==}
-    dev: false
+  hashlru@2.3.0: {}
 
-  /hasown@2.0.0:
-    resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
-    engines: {node: '>= 0.4'}
+  hasown@2.0.0:
     dependencies:
       function-bind: 1.1.2
 
-  /hast-util-heading-rank@3.0.0:
-    resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==}
+  hast-util-heading-rank@3.0.0:
     dependencies:
       '@types/hast': 3.0.4
-    dev: true
 
-  /hast-util-is-element@3.0.0:
-    resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
+  hast-util-is-element@3.0.0:
     dependencies:
       '@types/hast': 3.0.4
-    dev: true
 
-  /hast-util-to-string@3.0.0:
-    resolution: {integrity: sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==}
+  hast-util-to-string@3.0.0:
     dependencies:
       '@types/hast': 3.0.4
-    dev: true
 
-  /he@1.2.0:
-    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
-    hasBin: true
-    dev: true
+  he@1.2.0: {}
 
-  /headers-polyfill@4.0.2:
-    resolution: {integrity: sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw==}
-    dev: true
+  headers-polyfill@4.0.2: {}
 
-  /highlight.js@10.7.3:
-    resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
-    dev: false
+  highlight.js@10.7.3: {}
 
-  /highlight.js@11.9.0:
-    resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==}
-    engines: {node: '>=12.0.0'}
-    dev: false
+  highlight.js@11.9.0: {}
 
-  /hosted-git-info@2.8.9:
-    resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
-    dev: true
+  hosted-git-info@2.8.9: {}
 
-  /hosted-git-info@4.1.0:
-    resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
-    engines: {node: '>=10'}
+  hosted-git-info@4.1.0:
     dependencies:
       lru-cache: 6.0.0
-    dev: true
 
-  /hpagent@1.2.0:
-    resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==}
-    engines: {node: '>=14'}
-    dev: false
+  hpagent@1.2.0: {}
 
-  /html-encoding-sniffer@4.0.0:
-    resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
-    engines: {node: '>=18'}
+  html-encoding-sniffer@4.0.0:
     dependencies:
       whatwg-encoding: 3.1.1
-    dev: false
 
-  /html-entities@2.3.2:
-    resolution: {integrity: sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==}
+  html-entities@2.3.2: {}
 
-  /html-escaper@2.0.2:
-    resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
-    dev: true
+  html-escaper@2.0.2: {}
 
-  /html-tags@3.2.0:
-    resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==}
-    engines: {node: '>=8'}
-    dev: true
+  html-tags@3.2.0: {}
 
-  /htmlescape@1.1.1:
-    resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==}
-    engines: {node: '>=0.10'}
-    dev: false
+  htmlescape@1.1.1: {}
 
-  /htmlparser2@8.0.1:
-    resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
+  htmlparser2@8.0.1:
     dependencies:
       domelementtype: 2.3.0
       domhandler: 5.0.3
       domutils: 3.0.1
       entities: 4.5.0
 
-  /http-cache-semantics@4.1.1:
-    resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
+  http-cache-semantics@4.1.1: {}
 
-  /http-errors@2.0.0:
-    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
-    engines: {node: '>= 0.8'}
+  http-errors@2.0.0:
     dependencies:
       depd: 2.0.0
       inherits: 2.0.4
@@ -12633,244 +18921,135 @@ packages:
       statuses: 2.0.1
       toidentifier: 1.0.1
 
-  /http-link-header@1.1.2:
-    resolution: {integrity: sha512-6qz1XhMq/ryde52SZGzVhzi3jcG2KqO16KITkupyQxvW6u7iylm0Fq7r3OpCYsc0S0ELlCiFpuxDcccUwjbEqA==}
-    engines: {node: '>=6.0.0'}
-    dev: false
+  http-link-header@1.1.3: {}
 
-  /http-proxy-agent@7.0.0:
-    resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
-    engines: {node: '>= 14'}
+  http-proxy-agent@7.0.0:
     dependencies:
       agent-base: 7.1.0
       debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /http-signature@1.2.0:
-    resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
-    engines: {node: '>=0.8', npm: '>=1.3.7'}
+  http-signature@1.2.0:
     dependencies:
       assert-plus: 1.0.0
       jsprim: 1.4.2
       sshpk: 1.17.0
-    dev: false
 
-  /http-signature@1.3.6:
-    resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==}
-    engines: {node: '>=0.10'}
+  http-signature@1.3.6:
     dependencies:
       assert-plus: 1.0.0
       jsprim: 2.0.2
       sshpk: 1.17.0
-    dev: true
 
-  /http2-wrapper@1.0.3:
-    resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==}
-    engines: {node: '>=10.19.0'}
+  http2-wrapper@1.0.3:
     dependencies:
       quick-lru: 5.1.1
       resolve-alpn: 1.2.1
-    dev: false
 
-  /http2-wrapper@2.2.1:
-    resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
-    engines: {node: '>=10.19.0'}
+  http2-wrapper@2.2.1:
     dependencies:
       quick-lru: 5.1.1
       resolve-alpn: 1.2.1
 
-  /http_ece@1.2.0:
-    resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==}
-    engines: {node: '>=16'}
-    dev: false
+  http_ece@1.2.0: {}
 
-  /https-proxy-agent@2.2.4:
-    resolution: {integrity: sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==}
-    engines: {node: '>= 4.5.0'}
-    requiresBuild: true
+  https-proxy-agent@2.2.4:
     dependencies:
       agent-base: 4.3.0
       debug: 3.2.7(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
-    dev: false
     optional: true
 
-  /https-proxy-agent@5.0.1:
-    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
-    engines: {node: '>= 6'}
+  https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
       debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
-  /https-proxy-agent@7.0.2:
-    resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==}
-    engines: {node: '>= 14'}
+  https-proxy-agent@7.0.2:
     dependencies:
       agent-base: 7.1.0
       debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /human-signals@1.1.1:
-    resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
-    engines: {node: '>=8.12.0'}
-    dev: true
+  human-signals@1.1.1: {}
 
-  /human-signals@2.1.0:
-    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
-    engines: {node: '>=10.17.0'}
+  human-signals@2.1.0: {}
 
-  /human-signals@3.0.1:
-    resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
-    engines: {node: '>=12.20.0'}
-    dev: true
+  human-signals@3.0.1: {}
 
-  /human-signals@5.0.0:
-    resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
-    engines: {node: '>=16.17.0'}
+  human-signals@5.0.0: {}
 
-  /iconv-lite@0.4.24:
-    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
-    engines: {node: '>=0.10.0'}
+  iconv-lite@0.4.24:
     dependencies:
       safer-buffer: 2.1.2
 
-  /iconv-lite@0.6.3:
-    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
-    engines: {node: '>=0.10.0'}
+  iconv-lite@0.6.3:
     dependencies:
       safer-buffer: 2.1.2
 
-  /idb-keyval@6.2.1:
-    resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==}
-    dev: false
+  idb-keyval@6.2.1: {}
 
-  /ieee754@1.2.1:
-    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+  ieee754@1.2.1: {}
 
-  /ignore-by-default@1.0.1:
-    resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
-    dev: true
+  ignore-by-default@1.0.1: {}
 
-  /ignore-walk@6.0.4:
-    resolution: {integrity: sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  ignore-walk@6.0.4:
     dependencies:
       minimatch: 9.0.3
-    dev: false
 
-  /ignore@5.2.4:
-    resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
-    engines: {node: '>= 4'}
+  ignore@5.2.4: {}
 
-  /immutable@4.2.2:
-    resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==}
+  ignore@5.3.1: {}
 
-  /import-fresh@3.3.0:
-    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
-    engines: {node: '>=6'}
+  immutable@4.2.2: {}
+
+  import-fresh@3.3.0:
     dependencies:
       parent-module: 1.0.1
       resolve-from: 4.0.0
-    dev: true
 
-  /import-lazy@4.0.0:
-    resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
-    engines: {node: '>=8'}
-    dev: true
+  import-lazy@4.0.0: {}
 
-  /import-local@3.1.0:
-    resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
-    engines: {node: '>=8'}
-    hasBin: true
+  import-local@3.1.0:
     dependencies:
       pkg-dir: 4.2.0
       resolve-cwd: 3.0.0
-    dev: true
-
-  /imurmurhash@0.1.4:
-    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
-    engines: {node: '>=0.8.19'}
-
-  /indent-string@4.0.0:
-    resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
-    engines: {node: '>=8'}
-
-  /indent-string@5.0.0:
-    resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /inflight@1.0.6:
-    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
-    dependencies:
-      once: 1.4.0
-      wrappy: 1.0.2
 
-  /inherits@2.0.4:
-    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+  imurmurhash@0.1.4: {}
 
-  /ini@1.3.8:
-    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
-    dev: true
+  indent-string@4.0.0: {}
 
-  /ini@2.0.0:
-    resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==}
-    engines: {node: '>=10'}
-    dev: true
+  indent-string@5.0.0: {}
 
-  /inquirer@8.2.5:
-    resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==}
-    engines: {node: '>=12.0.0'}
+  inflight@1.0.6:
     dependencies:
-      ansi-escapes: 4.3.2
-      chalk: 4.1.2
-      cli-cursor: 3.1.0
-      cli-width: 3.0.0
-      external-editor: 3.1.0
-      figures: 3.2.0
-      lodash: 4.17.21
-      mute-stream: 0.0.8
-      ora: 5.4.1
-      run-async: 2.4.1
-      rxjs: 7.8.1
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-      through: 2.3.8
-      wrap-ansi: 7.0.0
-    dev: true
+      once: 1.4.0
+      wrappy: 1.0.2
 
-  /insert-text-at-cursor@0.3.0:
-    resolution: {integrity: sha512-/nPtyeX9xPUvxZf+r0518B7uqNKlP+LqNJqSiXFEaa2T71rWIwTVXGH7hB9xO/EVdwa5/pWlFCPwShOW81XIxQ==}
-    dev: false
+  inherits@2.0.4: {}
 
-  /install-artifact-from-github@1.3.5:
-    resolution: {integrity: sha512-gZHC7f/cJgXz7MXlHFBxPVMsvIbev1OQN1uKQYKVJDydGNm9oYf9JstbU4Atnh/eSvk41WtEovoRm+8IF686xg==}
-    hasBin: true
-    dev: false
+  ini@1.3.8: {}
 
-  /internal-slot@1.0.5:
-    resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
-    engines: {node: '>= 0.4'}
+  ini@2.0.0: {}
+
+  insert-text-at-cursor@0.3.0: {}
+
+  install-artifact-from-github@1.3.5: {}
+
+  internal-slot@1.0.5:
     dependencies:
       get-intrinsic: 1.2.1
       has: 1.0.3
       side-channel: 1.0.4
-    dev: true
 
-  /intersection-observer@0.12.2:
-    resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
-    dev: true
+  intersection-observer@0.12.2: {}
 
-  /ioredis@5.3.2:
-    resolution: {integrity: sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==}
-    engines: {node: '>=12.22.0'}
+  ioredis@5.4.1:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
@@ -12883,492 +19062,289 @@ packages:
       standard-as-callback: 2.1.0
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /iota-array@1.0.0:
-    resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==}
-    dev: false
+  iota-array@1.0.0: {}
 
-  /ip-address@7.1.0:
-    resolution: {integrity: sha512-V9pWC/VJf2lsXqP7IWJ+pe3P1/HCYGBMZrrnT62niLGjAfCbeiwXMUxaeHvnVlz19O27pvXP4azs+Pj/A0x+SQ==}
-    engines: {node: '>= 10'}
+  ip-address@7.1.0:
     dependencies:
       jsbn: 1.1.0
       sprintf-js: 1.1.2
-    dev: false
 
-  /ip-cidr@3.1.0:
-    resolution: {integrity: sha512-HUCn4snshEX1P8cja/IyU3qk8FVDW8T5zZcegDFbu4w7NojmAhk5NcOgj3M8+0fmumo1afJTPDtJlzsxLdOjtg==}
-    engines: {node: '>=10.0.0'}
+  ip-cidr@3.1.0:
     dependencies:
       ip-address: 7.1.0
       jsbn: 1.1.0
-    dev: false
-
-  /ip-regex@4.3.0:
-    resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==}
-    engines: {node: '>=8'}
 
-  /ip@2.0.0:
-    resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
-    dev: false
+  ip-regex@4.3.0: {}
 
-  /ip@2.0.1:
-    resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==}
-    dev: true
+  ip@2.0.1: {}
 
-  /ipaddr.js@1.9.1:
-    resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
-    engines: {node: '>= 0.10'}
+  ipaddr.js@1.9.1: {}
 
-  /ipaddr.js@2.1.0:
-    resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==}
-    engines: {node: '>= 10'}
+  ipaddr.js@2.2.0: {}
 
-  /irregular-plurals@3.5.0:
-    resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==}
-    engines: {node: '>=8'}
-    dev: true
+  irregular-plurals@3.5.0: {}
 
-  /is-absolute-url@4.0.1:
-    resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dev: true
+  is-absolute-url@4.0.1: {}
 
-  /is-arguments@1.1.1:
-    resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
-    engines: {node: '>= 0.4'}
+  is-arguments@1.1.1:
     dependencies:
       call-bind: 1.0.2
       has-tostringtag: 1.0.0
-    dev: true
 
-  /is-array-buffer@3.0.2:
-    resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
+  is-array-buffer@3.0.2:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
       is-typed-array: 1.1.10
-    dev: true
 
-  /is-arrayish@0.2.1:
-    resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
-    dev: true
+  is-arrayish@0.2.1: {}
 
-  /is-arrayish@0.3.2:
-    resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
-    dev: false
+  is-arrayish@0.3.2: {}
 
-  /is-bigint@1.0.4:
-    resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+  is-bigint@1.0.4:
     dependencies:
       has-bigints: 1.0.2
-    dev: true
 
-  /is-binary-path@2.1.0:
-    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
-    engines: {node: '>=8'}
+  is-binary-path@2.1.0:
     dependencies:
       binary-extensions: 2.2.0
 
-  /is-boolean-object@1.1.2:
-    resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
-    engines: {node: '>= 0.4'}
+  is-boolean-object@1.1.2:
     dependencies:
       call-bind: 1.0.2
       has-tostringtag: 1.0.0
-    dev: true
 
-  /is-buffer@1.1.6:
-    resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
-    dev: false
+  is-buffer@1.1.6: {}
 
-  /is-callable@1.2.7:
-    resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
-    engines: {node: '>= 0.4'}
-    dev: true
+  is-callable@1.2.7: {}
 
-  /is-ci@3.0.1:
-    resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
-    hasBin: true
+  is-ci@3.0.1:
     dependencies:
       ci-info: 3.7.1
-    dev: true
 
-  /is-core-module@2.13.1:
-    resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
+  is-core-module@2.13.1:
     dependencies:
       hasown: 2.0.0
 
-  /is-date-object@1.0.5:
-    resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
-    engines: {node: '>= 0.4'}
+  is-date-object@1.0.5:
     dependencies:
       has-tostringtag: 1.0.0
-    dev: true
 
-  /is-deflate@1.0.0:
-    resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==}
-    dev: true
+  is-deflate@1.0.0: {}
 
-  /is-docker@2.2.1:
-    resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
-    engines: {node: '>=8'}
-    hasBin: true
-    dev: true
+  is-docker@2.2.1: {}
 
-  /is-expression@4.0.0:
-    resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==}
+  is-expression@4.0.0:
     dependencies:
       acorn: 7.4.1
       object-assign: 4.1.1
 
-  /is-extglob@2.1.1:
-    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
-    engines: {node: '>=0.10.0'}
+  is-extglob@2.1.1: {}
 
-  /is-file-animated@1.0.2:
-    resolution: {integrity: sha512-TAYDUkvyBmxqneRU26zzpeHLAgtzEOIsRQWrtDidPT/tFK3Yc0WKgtF3u4oOEAiN0kAuVfl7MTgbD0vXdFDztA==}
-    dev: false
+  is-file-animated@1.0.2: {}
 
-  /is-fullwidth-code-point@3.0.0:
-    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
-    engines: {node: '>=8'}
+  is-fullwidth-code-point@3.0.0: {}
 
-  /is-generator-fn@2.1.0:
-    resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
-    engines: {node: '>=6'}
-    dev: true
+  is-generator-fn@2.1.0: {}
 
-  /is-generator-function@1.0.10:
-    resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
-    engines: {node: '>= 0.4'}
+  is-generator-function@1.0.10:
     dependencies:
       has-tostringtag: 1.0.0
-    dev: true
 
-  /is-glob@4.0.3:
-    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
-    engines: {node: '>=0.10.0'}
+  is-glob@4.0.3:
     dependencies:
       is-extglob: 2.1.1
 
-  /is-gzip@1.0.0:
-    resolution: {integrity: sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
+  is-gzip@1.0.0: {}
 
-  /is-installed-globally@0.4.0:
-    resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
-    engines: {node: '>=10'}
+  is-installed-globally@0.4.0:
     dependencies:
       global-dirs: 3.0.1
       is-path-inside: 3.0.3
-    dev: true
 
-  /is-interactive@1.0.0:
-    resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
-    engines: {node: '>=8'}
-    dev: true
+  is-interactive@1.0.0: {}
 
-  /is-ip@3.1.0:
-    resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==}
-    engines: {node: '>=8'}
+  is-ip@3.1.0:
     dependencies:
       ip-regex: 4.3.0
 
-  /is-lambda@1.0.1:
-    resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
-    dev: false
+  is-lambda@1.0.1: {}
 
-  /is-map@2.0.2:
-    resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
-    dev: true
+  is-map@2.0.2: {}
 
-  /is-nan@1.3.2:
-    resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
-    engines: {node: '>= 0.4'}
+  is-nan@1.3.2:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
-    dev: true
 
-  /is-negative-zero@2.0.2:
-    resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
-    engines: {node: '>= 0.4'}
-    dev: true
+  is-negative-zero@2.0.2: {}
 
-  /is-node-process@1.2.0:
-    resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
-    dev: true
+  is-node-process@1.2.0: {}
 
-  /is-number-object@1.0.7:
-    resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
-    engines: {node: '>= 0.4'}
+  is-number-object@1.0.7:
     dependencies:
       has-tostringtag: 1.0.0
-    dev: true
 
-  /is-number@7.0.0:
-    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
-    engines: {node: '>=0.12.0'}
+  is-number@7.0.0: {}
 
-  /is-path-cwd@2.2.0:
-    resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
-    engines: {node: '>=6'}
-    dev: true
+  is-path-cwd@2.2.0: {}
 
-  /is-path-inside@3.0.3:
-    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
-    engines: {node: '>=8'}
-    dev: true
+  is-path-inside@3.0.3: {}
 
-  /is-plain-obj@1.1.0:
-    resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
-    engines: {node: '>=0.10.0'}
+  is-plain-obj@1.1.0: {}
 
-  /is-plain-obj@4.1.0:
-    resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
-    engines: {node: '>=12'}
-    dev: true
+  is-plain-obj@4.1.0: {}
 
-  /is-plain-object@2.0.4:
-    resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
-    engines: {node: '>=0.10.0'}
+  is-plain-object@2.0.4:
     dependencies:
       isobject: 3.0.1
-    dev: true
 
-  /is-plain-object@5.0.0:
-    resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
-    engines: {node: '>=0.10.0'}
+  is-plain-object@5.0.0: {}
 
-  /is-potential-custom-element-name@1.0.1:
-    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
-    dev: false
+  is-potential-custom-element-name@1.0.1: {}
 
-  /is-promise@2.2.2:
-    resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
+  is-promise@2.2.2: {}
 
-  /is-regex@1.1.4:
-    resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
-    engines: {node: '>= 0.4'}
+  is-regex@1.1.4:
     dependencies:
       call-bind: 1.0.2
       has-tostringtag: 1.0.0
 
-  /is-set@2.0.2:
-    resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
-    dev: true
+  is-set@2.0.2: {}
 
-  /is-shared-array-buffer@1.0.2:
-    resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
+  is-shared-array-buffer@1.0.2:
     dependencies:
       call-bind: 1.0.2
-    dev: true
 
-  /is-stream@1.1.0:
-    resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
-    engines: {node: '>=0.10.0'}
-    dev: false
+  is-stream@1.1.0: {}
 
-  /is-stream@2.0.1:
-    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
-    engines: {node: '>=8'}
+  is-stream@2.0.1: {}
 
-  /is-stream@3.0.0:
-    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  is-stream@3.0.0: {}
 
-  /is-string@1.0.7:
-    resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
-    engines: {node: '>= 0.4'}
+  is-string@1.0.7:
     dependencies:
       has-tostringtag: 1.0.0
-    dev: true
 
-  /is-svg@5.0.0:
-    resolution: {integrity: sha512-sRl7J0oX9yUNamSdc8cwgzh9KBLnQXNzGmW0RVHwg/jEYjGNYHC6UvnYD8+hAeut9WwxRvhG9biK7g/wDGxcMw==}
-    engines: {node: '>=14.16'}
+  is-svg@5.0.0:
     dependencies:
       fast-xml-parser: 4.2.5
-    dev: false
 
-  /is-symbol@1.0.4:
-    resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
-    engines: {node: '>= 0.4'}
+  is-symbol@1.0.4:
     dependencies:
       has-symbols: 1.0.3
-    dev: true
 
-  /is-typed-array@1.1.10:
-    resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==}
-    engines: {node: '>= 0.4'}
+  is-typed-array@1.1.10:
     dependencies:
       available-typed-arrays: 1.0.5
       call-bind: 1.0.2
       for-each: 0.3.3
       gopd: 1.0.1
       has-tostringtag: 1.0.0
-    dev: true
 
-  /is-typedarray@1.0.0:
-    resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
+  is-typedarray@1.0.0: {}
 
-  /is-unicode-supported@0.1.0:
-    resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
-    engines: {node: '>=10'}
-    dev: true
+  is-unicode-supported@0.1.0: {}
 
-  /is-weakmap@2.0.1:
-    resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
-    dev: true
+  is-weakmap@2.0.1: {}
 
-  /is-weakref@1.0.2:
-    resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+  is-weakref@1.0.2:
     dependencies:
       call-bind: 1.0.2
-    dev: true
 
-  /is-weakset@2.0.2:
-    resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
+  is-weakset@2.0.2:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
-    dev: true
 
-  /is-wsl@2.2.0:
-    resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
-    engines: {node: '>=8'}
+  is-wsl@2.2.0:
     dependencies:
       is-docker: 2.2.1
-    dev: true
 
-  /isarray@0.0.1:
-    resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==}
+  isarray@0.0.1: {}
 
-  /isarray@1.0.0:
-    resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+  isarray@1.0.0: {}
 
-  /isarray@2.0.5:
-    resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
-    dev: true
+  isarray@2.0.5: {}
 
-  /isexe@2.0.0:
-    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+  isexe@2.0.0: {}
 
-  /isexe@3.1.1:
-    resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
-    engines: {node: '>=16'}
-    dev: false
+  isexe@3.1.1: {}
 
-  /isobject@3.0.1:
-    resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
+  isobject@3.0.1: {}
 
-  /isstream@0.1.2:
-    resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
+  isstream@0.1.2: {}
 
-  /istanbul-lib-coverage@3.2.2:
-    resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
-    engines: {node: '>=8'}
-    dev: true
+  istanbul-lib-coverage@3.2.2: {}
 
-  /istanbul-lib-instrument@5.2.1:
-    resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
-    engines: {node: '>=8'}
+  istanbul-lib-instrument@5.2.1:
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/parser': 7.23.9
+      '@babel/core': 7.24.0
+      '@babel/parser': 7.24.0
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /istanbul-lib-instrument@6.0.0:
-    resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==}
-    engines: {node: '>=10'}
+  istanbul-lib-instrument@6.0.0:
     dependencies:
-      '@babel/core': 7.23.5
-      '@babel/parser': 7.23.9
+      '@babel/core': 7.24.0
+      '@babel/parser': 7.24.0
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
       semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /istanbul-lib-report@3.0.1:
-    resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
-    engines: {node: '>=10'}
+  istanbul-lib-report@3.0.1:
     dependencies:
       istanbul-lib-coverage: 3.2.2
       make-dir: 4.0.0
       supports-color: 7.2.0
-    dev: true
 
-  /istanbul-lib-source-maps@4.0.1:
-    resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
-    engines: {node: '>=10'}
+  istanbul-lib-source-maps@4.0.1:
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
       istanbul-lib-coverage: 3.2.2
       source-map: 0.6.1
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /istanbul-reports@3.1.6:
-    resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==}
-    engines: {node: '>=8'}
+  istanbul-reports@3.1.6:
     dependencies:
       html-escaper: 2.0.2
       istanbul-lib-report: 3.0.1
-    dev: true
 
-  /iterare@1.2.1:
-    resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
-    engines: {node: '>=6'}
+  iterare@1.2.1: {}
 
-  /jackspeak@2.3.6:
-    resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
-    engines: {node: '>=14'}
+  jackspeak@2.3.6:
     dependencies:
       '@isaacs/cliui': 8.0.2
     optionalDependencies:
       '@pkgjs/parseargs': 0.11.0
 
-  /jake@10.8.5:
-    resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
-    engines: {node: '>=10'}
-    hasBin: true
+  jake@10.8.5:
     dependencies:
       async: 3.2.4
       chalk: 4.1.2
       filelist: 1.0.4
       minimatch: 3.1.2
 
-  /jest-changed-files@29.7.0:
-    resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-changed-files@29.7.0:
     dependencies:
       execa: 5.1.1
       jest-util: 29.7.0
       p-limit: 3.1.0
-    dev: true
 
-  /jest-circus@29.7.0:
-    resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-circus@29.7.0:
     dependencies:
       '@jest/environment': 29.7.0
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -13387,26 +19363,17 @@ packages:
     transitivePeerDependencies:
       - babel-plugin-macros
       - supports-color
-    dev: true
 
-  /jest-cli@29.7.0(@types/node@20.11.22):
-    resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    hasBin: true
-    peerDependencies:
-      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
-    peerDependenciesMeta:
-      node-notifier:
-        optional: true
+  jest-cli@29.7.0(@types/node@20.12.7):
     dependencies:
       '@jest/core': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
       chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.11.22)
+      create-jest: 29.7.0(@types/node@20.12.7)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.11.22)
+      jest-config: 29.7.0(@types/node@20.12.7)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -13415,24 +19382,12 @@ packages:
       - babel-plugin-macros
       - supports-color
       - ts-node
-    dev: true
 
-  /jest-config@29.7.0(@types/node@20.11.22):
-    resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    peerDependencies:
-      '@types/node': '*'
-      ts-node: '>=9.0.0'
-    peerDependenciesMeta:
-      '@types/node':
-        optional: true
-      ts-node:
-        optional: true
+  jest-config@29.7.0(@types/node@20.12.7):
     dependencies:
       '@babel/core': 7.23.5
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
       babel-jest: 29.7.0(@babel/core@7.23.5)
       chalk: 4.1.2
       ci-info: 3.7.1
@@ -13452,72 +19407,54 @@ packages:
       pretty-format: 29.7.0
       slash: 3.0.0
       strip-json-comments: 3.1.1
+    optionalDependencies:
+      '@types/node': 20.12.7
     transitivePeerDependencies:
       - babel-plugin-macros
       - supports-color
-    dev: true
 
-  /jest-diff@29.7.0:
-    resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-diff@29.7.0:
     dependencies:
       chalk: 4.1.2
       diff-sequences: 29.6.3
       jest-get-type: 29.6.3
       pretty-format: 29.7.0
-    dev: true
 
-  /jest-docblock@29.7.0:
-    resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-docblock@29.7.0:
     dependencies:
       detect-newline: 3.1.0
-    dev: true
 
-  /jest-each@29.7.0:
-    resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-each@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
       chalk: 4.1.2
       jest-get-type: 29.6.3
       jest-util: 29.7.0
       pretty-format: 29.7.0
-    dev: true
 
-  /jest-environment-node@29.7.0:
-    resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-environment-node@29.7.0:
     dependencies:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       jest-mock: 29.7.0
       jest-util: 29.7.0
-    dev: true
 
-  /jest-fetch-mock@3.0.3:
-    resolution: {integrity: sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==}
+  jest-fetch-mock@3.0.3(encoding@0.1.13):
     dependencies:
-      cross-fetch: 3.1.6
+      cross-fetch: 3.1.6(encoding@0.1.13)
       promise-polyfill: 8.3.0
     transitivePeerDependencies:
       - encoding
-    dev: true
 
-  /jest-get-type@29.6.3:
-    resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dev: true
+  jest-get-type@29.6.3: {}
 
-  /jest-haste-map@29.7.0:
-    resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-haste-map@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -13528,29 +19465,20 @@ packages:
       walker: 1.0.8
     optionalDependencies:
       fsevents: 2.3.3
-    dev: true
 
-  /jest-leak-detector@29.7.0:
-    resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-leak-detector@29.7.0:
     dependencies:
       jest-get-type: 29.6.3
       pretty-format: 29.7.0
-    dev: true
 
-  /jest-matcher-utils@29.7.0:
-    resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-matcher-utils@29.7.0:
     dependencies:
       chalk: 4.1.2
       jest-diff: 29.7.0
       jest-get-type: 29.6.3
       pretty-format: 29.7.0
-    dev: true
 
-  /jest-message-util@29.7.0:
-    resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-message-util@29.7.0:
     dependencies:
       '@babel/code-frame': 7.23.5
       '@jest/types': 29.6.3
@@ -13561,47 +19489,27 @@ packages:
       pretty-format: 29.7.0
       slash: 3.0.0
       stack-utils: 2.0.6
-    dev: true
 
-  /jest-mock@29.7.0:
-    resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-mock@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       jest-util: 29.7.0
-    dev: true
 
-  /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
-    resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
-    engines: {node: '>=6'}
-    peerDependencies:
-      jest-resolve: '*'
-    peerDependenciesMeta:
-      jest-resolve:
-        optional: true
-    dependencies:
+  jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
+    optionalDependencies:
       jest-resolve: 29.7.0
-    dev: true
 
-  /jest-regex-util@29.6.3:
-    resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dev: true
+  jest-regex-util@29.6.3: {}
 
-  /jest-resolve-dependencies@29.7.0:
-    resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-resolve-dependencies@29.7.0:
     dependencies:
       jest-regex-util: 29.6.3
       jest-snapshot: 29.7.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /jest-resolve@29.7.0:
-    resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-resolve@29.7.0:
     dependencies:
       chalk: 4.1.2
       graceful-fs: 4.2.11
@@ -13612,18 +19520,15 @@ packages:
       resolve: 1.22.8
       resolve.exports: 2.0.0
       slash: 3.0.0
-    dev: true
 
-  /jest-runner@29.7.0:
-    resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-runner@29.7.0:
     dependencies:
       '@jest/console': 29.7.0
       '@jest/environment': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -13641,11 +19546,8 @@ packages:
       source-map-support: 0.5.13
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /jest-runtime@29.7.0:
-    resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-runtime@29.7.0:
     dependencies:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
@@ -13654,7 +19556,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -13671,11 +19573,8 @@ packages:
       strip-bom: 4.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /jest-snapshot@29.7.0:
-    resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-snapshot@29.7.0:
     dependencies:
       '@babel/core': 7.23.5
       '@babel/generator': 7.23.5
@@ -13699,23 +19598,17 @@ packages:
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /jest-util@29.7.0:
-    resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-util@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
       picomatch: 2.3.1
-    dev: true
 
-  /jest-validate@29.7.0:
-    resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-validate@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
       camelcase: 6.3.0
@@ -13723,133 +19616,81 @@ packages:
       jest-get-type: 29.6.3
       leven: 3.1.0
       pretty-format: 29.7.0
-    dev: true
 
-  /jest-watcher@29.7.0:
-    resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-watcher@29.7.0:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
       jest-util: 29.7.0
       string-length: 4.0.2
-    dev: true
 
-  /jest-websocket-mock@2.5.0:
-    resolution: {integrity: sha512-a+UJGfowNIWvtIKIQBHoEWIUqRxxQHFx4CXT+R5KxxKBtEQ5rS3pPOV/5299sHzqbmeCzxxY5qE4+yfXePePig==}
+  jest-websocket-mock@2.5.0:
     dependencies:
       jest-diff: 29.7.0
       mock-socket: 9.3.1
-    dev: true
 
-  /jest-worker@29.7.0:
-    resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  jest-worker@29.7.0:
     dependencies:
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
-    dev: true
 
-  /jest@29.7.0(@types/node@20.11.22):
-    resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    hasBin: true
-    peerDependencies:
-      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
-    peerDependenciesMeta:
-      node-notifier:
-        optional: true
+  jest@29.7.0(@types/node@20.12.7):
     dependencies:
       '@jest/core': 29.7.0
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.11.22)
+      jest-cli: 29.7.0(@types/node@20.12.7)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
       - supports-color
       - ts-node
-    dev: true
 
-  /jju@1.4.0:
-    resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
-    dev: true
+  jju@1.4.0: {}
 
-  /joi@17.11.0:
-    resolution: {integrity: sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==}
+  joi@17.11.0:
     dependencies:
       '@hapi/hoek': 9.3.0
       '@hapi/topo': 5.1.0
       '@sideway/address': 4.1.4
       '@sideway/formula': 3.0.1
       '@sideway/pinpoint': 2.0.0
-    dev: true
-
-  /jpeg-js@0.3.7:
-    resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==}
-    dev: false
 
-  /jpeg-js@0.4.4:
-    resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==}
-    dev: false
+  jpeg-js@0.3.7: {}
 
-  /js-beautify@1.14.9:
-    resolution: {integrity: sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==}
-    engines: {node: '>=12'}
-    hasBin: true
+  js-beautify@1.14.9:
     dependencies:
       config-chain: 1.1.13
       editorconfig: 1.0.4
       glob: 8.1.0
       nopt: 6.0.0
-    dev: true
 
-  /js-stringify@1.0.2:
-    resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==}
+  js-stringify@1.0.2: {}
 
-  /js-tokens@4.0.0:
-    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
-    dev: true
+  js-tokens@4.0.0: {}
 
-  /js-yaml@3.14.1:
-    resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
-    hasBin: true
+  js-yaml@3.14.1:
     dependencies:
       argparse: 1.0.10
       esprima: 4.0.1
-    dev: true
 
-  /js-yaml@4.1.0:
-    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
-    hasBin: true
+  js-yaml@4.1.0:
     dependencies:
       argparse: 2.0.1
 
-  /jsbn@0.1.1:
-    resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
+  jsbn@0.1.1: {}
 
-  /jsbn@1.1.0:
-    resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
-    dev: false
+  jsbn@1.1.0: {}
 
-  /jschardet@3.0.0:
-    resolution: {integrity: sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==}
-    engines: {node: '>=0.1.90'}
+  jschardet@3.0.0: {}
 
-  /jscodeshift@0.15.1(@babel/preset-env@7.23.5):
-    resolution: {integrity: sha512-hIJfxUy8Rt4HkJn/zZPU9ChKfKZM1342waJ1QC2e2YsPcWhM+3BJ4dcfQCzArTrk1jJeNLB341H+qOcEHRxJZg==}
-    hasBin: true
-    peerDependencies:
-      '@babel/preset-env': ^7.1.6
-    peerDependenciesMeta:
-      '@babel/preset-env':
-        optional: true
+  jscodeshift@0.15.1(@babel/preset-env@7.23.5(@babel/core@7.24.0)):
     dependencies:
       '@babel/core': 7.24.0
       '@babel/parser': 7.24.0
@@ -13858,7 +19699,6 @@ packages:
       '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0)
       '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
       '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0)
-      '@babel/preset-env': 7.23.5(@babel/core@7.24.0)
       '@babel/preset-flow': 7.23.3(@babel/core@7.24.0)
       '@babel/preset-typescript': 7.23.3(@babel/core@7.24.0)
       '@babel/register': 7.22.15(@babel/core@7.24.0)
@@ -13872,20 +19712,13 @@ packages:
       recast: 0.23.4
       temp: 0.8.4
       write-file-atomic: 2.4.3
+    optionalDependencies:
+      '@babel/preset-env': 7.23.5(@babel/core@7.24.0)
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /jsdom@23.2.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
-    resolution: {integrity: sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==}
-    engines: {node: '>=18'}
-    peerDependencies:
-      canvas: ^2.11.2
-    peerDependenciesMeta:
-      canvas:
-        optional: true
+  jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
     dependencies:
-      '@asamuzakjp/dom-selector': 2.0.2
       cssstyle: 4.0.1
       data-urls: 5.0.0
       decimal.js: 10.4.3
@@ -13894,6 +19727,7 @@ packages:
       http-proxy-agent: 7.0.0
       https-proxy-agent: 7.0.2
       is-potential-custom-element-name: 1.0.1
+      nwsapi: 2.2.9
       parse5: 7.1.2
       rrweb-cssom: 0.6.0
       saxes: 6.0.0
@@ -13904,463 +19738,283 @@ packages:
       whatwg-encoding: 3.1.1
       whatwg-mimetype: 4.0.0
       whatwg-url: 14.0.0
-      ws: 8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xml-name-validator: 5.0.0
     transitivePeerDependencies:
       - bufferutil
       - supports-color
       - utf-8-validate
-    dev: false
 
-  /jsesc@0.5.0:
-    resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
-    hasBin: true
-    dev: true
+  jsesc@0.5.0: {}
 
-  /jsesc@2.5.2:
-    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
+  jsesc@2.5.2: {}
 
-  /json-buffer@3.0.1:
-    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+  json-buffer@3.0.1: {}
 
-  /json-parse-even-better-errors@2.3.1:
-    resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
-    dev: true
+  json-parse-even-better-errors@2.3.1: {}
 
-  /json-schema-traverse@0.4.1:
-    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+  json-schema-traverse@0.4.1: {}
 
-  /json-schema-traverse@1.0.0:
-    resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+  json-schema-traverse@1.0.0: {}
 
-  /json-schema@0.4.0:
-    resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+  json-schema@0.4.0: {}
 
-  /json-stable-stringify-without-jsonify@1.0.1:
-    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
-    dev: true
+  json-stable-stringify-without-jsonify@1.0.1: {}
 
-  /json-stringify-safe@5.0.1:
-    resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
+  json-stringify-safe@5.0.1: {}
 
-  /json-to-ast@2.1.0:
-    resolution: {integrity: sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==}
-    engines: {node: '>= 4'}
+  json-to-ast@2.1.0:
     dependencies:
       code-error-fragment: 0.0.230
       grapheme-splitter: 1.0.4
-    dev: true
 
-  /json5@1.0.2:
-    resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
-    hasBin: true
+  json5@1.0.2:
     dependencies:
       minimist: 1.2.8
-    dev: true
 
-  /json5@2.2.3:
-    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
-    engines: {node: '>=6'}
-    hasBin: true
+  json5@2.2.3: {}
 
-  /jsonc-parser@3.2.0:
-    resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
-    dev: true
+  jsonc-parser@3.2.0: {}
 
-  /jsonfile@4.0.0:
-    resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+  jsonfile@4.0.0:
     optionalDependencies:
       graceful-fs: 4.2.11
 
-  /jsonfile@5.0.0:
-    resolution: {integrity: sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==}
+  jsonfile@5.0.0:
     dependencies:
       universalify: 0.1.2
     optionalDependencies:
       graceful-fs: 4.2.11
-    dev: false
 
-  /jsonfile@6.1.0:
-    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+  jsonfile@6.1.0:
     dependencies:
       universalify: 2.0.0
     optionalDependencies:
       graceful-fs: 4.2.11
-    dev: true
 
-  /jsonld@8.3.2:
-    resolution: {integrity: sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==}
-    engines: {node: '>=14'}
+  jsonld@8.3.2(web-streams-polyfill@3.2.1):
     dependencies:
-      '@digitalbazaar/http-client': 3.4.1
+      '@digitalbazaar/http-client': 3.4.1(web-streams-polyfill@3.2.1)
       canonicalize: 1.0.8
       lru-cache: 6.0.0
       rdf-canonize: 3.4.0
     transitivePeerDependencies:
       - web-streams-polyfill
-    dev: false
 
-  /jsonpointer@5.0.1:
-    resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
+  jsonpointer@5.0.1: {}
 
-  /jsprim@1.4.2:
-    resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
-    engines: {node: '>=0.6.0'}
+  jsprim@1.4.2:
     dependencies:
       assert-plus: 1.0.0
       extsprintf: 1.3.0
       json-schema: 0.4.0
       verror: 1.10.0
-    dev: false
 
-  /jsprim@2.0.2:
-    resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
-    engines: {'0': node >=0.6.0}
+  jsprim@2.0.2:
     dependencies:
       assert-plus: 1.0.0
       extsprintf: 1.3.0
       json-schema: 0.4.0
       verror: 1.10.0
-    dev: true
 
-  /jsrsasign@11.1.0:
-    resolution: {integrity: sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==}
-    dev: false
+  jsrsasign@11.1.0: {}
 
-  /jssha@3.3.1:
-    resolution: {integrity: sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==}
-    dev: false
+  jssha@3.3.1: {}
 
-  /jstransformer@1.0.0:
-    resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==}
+  jstransformer@1.0.0:
     dependencies:
       is-promise: 2.2.2
       promise: 7.3.1
 
-  /just-extend@4.2.1:
-    resolution: {integrity: sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==}
-    dev: true
+  just-extend@4.2.1: {}
 
-  /jwa@2.0.0:
-    resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==}
+  jwa@2.0.0:
     dependencies:
       buffer-equal-constant-time: 1.0.1
       ecdsa-sig-formatter: 1.0.11
       safe-buffer: 5.2.1
-    dev: false
 
-  /jws@4.0.0:
-    resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
+  jws@4.0.0:
     dependencies:
       jwa: 2.0.0
       safe-buffer: 5.2.1
-    dev: false
 
-  /keyv@4.5.4:
-    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+  keyv@4.5.4:
     dependencies:
       json-buffer: 3.0.1
 
-  /kind-of@6.0.3:
-    resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
+  kind-of@6.0.3: {}
 
-  /kleur@3.0.3:
-    resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
-    engines: {node: '>=6'}
-    dev: true
+  kleur@3.0.3: {}
 
-  /ky-universal@0.11.0(ky@0.33.3):
-    resolution: {integrity: sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==}
-    engines: {node: '>=14.16'}
-    peerDependencies:
-      ky: '>=0.31.4'
-      web-streams-polyfill: '>=3.2.1'
-    peerDependenciesMeta:
-      web-streams-polyfill:
-        optional: true
+  ky-universal@0.11.0(ky@0.33.3)(web-streams-polyfill@3.2.1):
     dependencies:
       abort-controller: 3.0.0
       ky: 0.33.3
       node-fetch: 3.3.2
-    dev: false
+    optionalDependencies:
+      web-streams-polyfill: 3.2.1
 
-  /ky@0.33.3:
-    resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==}
-    engines: {node: '>=14.16'}
-    dev: false
+  ky@0.33.3: {}
 
-  /lazy-ass@1.6.0:
-    resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==}
-    engines: {node: '> 0.8'}
-    dev: true
+  lazy-ass@1.6.0: {}
 
-  /lazy-universal-dotenv@4.0.0:
-    resolution: {integrity: sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg==}
-    engines: {node: '>=14.0.0'}
+  lazy-universal-dotenv@4.0.0:
     dependencies:
       app-root-dir: 1.0.2
       dotenv: 16.0.3
       dotenv-expand: 10.0.0
-    dev: true
 
-  /lazystream@1.0.1:
-    resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
-    engines: {node: '>= 0.6.3'}
+  lazystream@1.0.1:
     dependencies:
       readable-stream: 2.3.7
-    dev: false
 
-  /leven@3.1.0:
-    resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
-    engines: {node: '>=6'}
-    dev: true
+  leven@3.1.0: {}
 
-  /levn@0.4.1:
-    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
-    engines: {node: '>= 0.8.0'}
+  levn@0.4.1:
     dependencies:
       prelude-ls: 1.2.1
       type-check: 0.4.0
-    dev: true
 
-  /light-my-request@5.11.0:
-    resolution: {integrity: sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==}
+  light-my-request@5.11.0:
     dependencies:
       cookie: 0.5.0
       process-warning: 2.2.0
       set-cookie-parser: 2.6.0
-    dev: false
 
-  /lilconfig@3.1.1:
-    resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
-    engines: {node: '>=14'}
-    dev: false
+  lilconfig@3.1.1: {}
 
-  /lines-and-columns@1.2.4:
-    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
-    dev: true
+  lines-and-columns@1.2.4: {}
 
-  /listr2@3.14.0(enquirer@2.3.6):
-    resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==}
-    engines: {node: '>=10.0.0'}
-    peerDependencies:
-      enquirer: '>= 2.3.0 < 3'
-    peerDependenciesMeta:
-      enquirer:
-        optional: true
+  listr2@3.14.0(enquirer@2.3.6):
     dependencies:
       cli-truncate: 2.1.0
       colorette: 2.0.19
-      enquirer: 2.3.6
       log-update: 4.0.0
       p-map: 4.0.0
       rfdc: 1.3.0
       rxjs: 7.8.1
       through: 2.3.8
       wrap-ansi: 7.0.0
-    dev: true
+    optionalDependencies:
+      enquirer: 2.3.6
 
-  /local-pkg@0.4.3:
-    resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
-    engines: {node: '>=14'}
-    dev: true
+  local-pkg@0.4.3: {}
 
-  /locate-path@3.0.0:
-    resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
-    engines: {node: '>=6'}
+  locate-path@3.0.0:
     dependencies:
       p-locate: 3.0.0
       path-exists: 3.0.0
-    dev: true
 
-  /locate-path@5.0.0:
-    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
-    engines: {node: '>=8'}
+  locate-path@5.0.0:
     dependencies:
       p-locate: 4.1.0
 
-  /locate-path@6.0.0:
-    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
-    engines: {node: '>=10'}
+  locate-path@6.0.0:
     dependencies:
       p-locate: 5.0.0
-    dev: true
 
-  /lodash.debounce@4.0.8:
-    resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
-    dev: true
+  lodash.debounce@4.0.8: {}
 
-  /lodash.defaults@4.2.0:
-    resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
-    dev: false
+  lodash.defaults@4.2.0: {}
 
-  /lodash.get@4.4.2:
-    resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
-    dev: true
+  lodash.get@4.4.2: {}
 
-  /lodash.isarguments@3.1.0:
-    resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
-    dev: false
+  lodash.isarguments@3.1.0: {}
 
-  /lodash.isequal@4.5.0:
-    resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
-    dev: true
+  lodash.isequal@4.5.0: {}
 
-  /lodash.memoize@4.1.2:
-    resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
-    dev: false
+  lodash.memoize@4.1.2: {}
 
-  /lodash.merge@4.6.2:
-    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+  lodash.merge@4.6.2: {}
 
-  /lodash.once@4.1.1:
-    resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
-    dev: true
+  lodash.once@4.1.1: {}
 
-  /lodash.uniq@4.5.0:
-    resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
-    dev: false
+  lodash.uniq@4.5.0: {}
 
-  /lodash@4.17.21:
-    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+  lodash@4.17.21: {}
 
-  /log-symbols@4.1.0:
-    resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
-    engines: {node: '>=10'}
+  log-symbols@4.1.0:
     dependencies:
       chalk: 4.1.2
       is-unicode-supported: 0.1.0
-    dev: true
 
-  /log-update@4.0.0:
-    resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
-    engines: {node: '>=10'}
+  log-update@4.0.0:
     dependencies:
       ansi-escapes: 4.3.2
       cli-cursor: 3.1.0
       slice-ansi: 4.0.0
       wrap-ansi: 6.2.0
-    dev: true
 
-  /long@4.0.0:
-    resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==}
-    requiresBuild: true
-    dev: false
+  long@4.0.0: {}
 
-  /longest-streak@3.1.0:
-    resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
-    dev: true
+  longest-streak@3.1.0: {}
 
-  /loose-envify@1.4.0:
-    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
-    hasBin: true
+  loose-envify@1.4.0:
     dependencies:
       js-tokens: 4.0.0
-    dev: true
 
-  /loupe@2.3.7:
-    resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+  loupe@2.3.7:
     dependencies:
       get-func-name: 2.0.2
-    dev: true
 
-  /lowercase-keys@2.0.0:
-    resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
-    engines: {node: '>=8'}
-    dev: false
+  lowercase-keys@2.0.0: {}
 
-  /lowercase-keys@3.0.0:
-    resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  lowercase-keys@3.0.0: {}
 
-  /lru-cache@10.0.2:
-    resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==}
-    engines: {node: 14 || >=16.14}
+  lru-cache@10.0.2:
     dependencies:
       semver: 7.6.0
 
-  /lru-cache@4.1.5:
-    resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+  lru-cache@10.2.2: {}
+
+  lru-cache@4.1.5:
     dependencies:
       pseudomap: 1.0.2
       yallist: 2.1.2
-    dev: false
 
-  /lru-cache@5.1.1:
-    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+  lru-cache@5.1.1:
     dependencies:
       yallist: 3.1.1
-    dev: true
 
-  /lru-cache@6.0.0:
-    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
-    engines: {node: '>=10'}
+  lru-cache@6.0.0:
     dependencies:
       yallist: 4.0.0
 
-  /lru-cache@8.0.4:
-    resolution: {integrity: sha512-E9FF6+Oc/uFLqZCuZwRKUzgFt5Raih6LfxknOSAVTjNkrCZkBf7DQCwJxZQgd9l4eHjIJDGR+E+1QKD1RhThPw==}
-    engines: {node: '>=16.14'}
-    dev: true
+  lru-cache@8.0.4: {}
 
-  /luxon@3.3.0:
-    resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
-    engines: {node: '>=12'}
-    dev: false
+  luxon@3.3.0: {}
 
-  /lz-string@1.5.0:
-    resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
-    hasBin: true
-    dev: true
+  lz-string@1.5.0: {}
 
-  /magic-string@0.27.0:
-    resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
-    engines: {node: '>=12'}
+  magic-string@0.27.0:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.4.15
-    dev: true
 
-  /magic-string@0.30.7:
-    resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
-    engines: {node: '>=12'}
+  magic-string@0.30.10:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.4.15
 
-  /mailcheck@1.1.1:
-    resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
-    dev: false
+  magic-string@0.30.7:
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.4.15
 
-  /make-dir@2.1.0:
-    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
-    engines: {node: '>=6'}
+  mailcheck@1.1.1: {}
+
+  make-dir@2.1.0:
     dependencies:
       pify: 4.0.1
       semver: 5.7.1
-    dev: true
 
-  /make-dir@3.1.0:
-    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
-    engines: {node: '>=8'}
+  make-dir@3.1.0:
     dependencies:
       semver: 6.3.1
 
-  /make-dir@4.0.0:
-    resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
-    engines: {node: '>=10'}
+  make-dir@4.0.0:
     dependencies:
       semver: 7.6.0
-    dev: true
 
-  /make-fetch-happen@13.0.0:
-    resolution: {integrity: sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==}
-    engines: {node: ^16.14.0 || >=18.0.0}
+  make-fetch-happen@13.0.0:
     dependencies:
       '@npmcli/agent': 2.2.0
       cacache: 18.0.0
@@ -14375,60 +20029,35 @@ packages:
       ssri: 10.0.4
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /makeerror@1.0.12:
-    resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+  makeerror@1.0.12:
     dependencies:
       tmpl: 1.0.5
-    dev: true
 
-  /map-obj@1.0.1:
-    resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
+  map-obj@1.0.1: {}
 
-  /map-obj@4.3.0:
-    resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
-    engines: {node: '>=8'}
-    dev: true
+  map-obj@4.3.0: {}
 
-  /map-or-similar@1.5.0:
-    resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==}
-    dev: true
+  map-or-similar@1.5.0: {}
 
-  /map-stream@0.1.0:
-    resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
-    dev: true
+  map-stream@0.1.0: {}
 
-  /markdown-table@3.0.3:
-    resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
-    dev: true
+  markdown-table@3.0.3: {}
 
-  /markdown-to-jsx@7.3.2(react@18.2.0):
-    resolution: {integrity: sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==}
-    engines: {node: '>= 10'}
-    peerDependencies:
-      react: '>= 0.14.0'
+  markdown-to-jsx@7.3.2(react@18.3.1):
     dependencies:
-      react: 18.2.0
-    dev: true
+      react: 18.3.1
 
-  /matter-js@0.19.0:
-    resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==}
-    dev: false
+  matter-js@0.19.0: {}
 
-  /mdast-util-find-and-replace@3.0.1:
-    resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
+  mdast-util-find-and-replace@3.0.1:
     dependencies:
       '@types/mdast': 4.0.3
       escape-string-regexp: 5.0.0
       unist-util-is: 6.0.0
       unist-util-visit-parents: 6.0.1
-    dev: true
 
-  /mdast-util-from-markdown@2.0.0:
-    resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==}
+  mdast-util-from-markdown@2.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       '@types/unist': 3.0.2
@@ -14444,20 +20073,16 @@ packages:
       unist-util-stringify-position: 4.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /mdast-util-gfm-autolink-literal@2.0.0:
-    resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==}
+  mdast-util-gfm-autolink-literal@2.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       ccount: 2.0.1
       devlop: 1.1.0
       mdast-util-find-and-replace: 3.0.1
       micromark-util-character: 2.1.0
-    dev: true
 
-  /mdast-util-gfm-footnote@2.0.0:
-    resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==}
+  mdast-util-gfm-footnote@2.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       devlop: 1.1.0
@@ -14466,20 +20091,16 @@ packages:
       micromark-util-normalize-identifier: 2.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /mdast-util-gfm-strikethrough@2.0.0:
-    resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
+  mdast-util-gfm-strikethrough@2.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       mdast-util-from-markdown: 2.0.0
       mdast-util-to-markdown: 2.1.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /mdast-util-gfm-table@2.0.0:
-    resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
+  mdast-util-gfm-table@2.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       devlop: 1.1.0
@@ -14488,10 +20109,8 @@ packages:
       mdast-util-to-markdown: 2.1.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /mdast-util-gfm-task-list-item@2.0.0:
-    resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
+  mdast-util-gfm-task-list-item@2.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       devlop: 1.1.0
@@ -14499,10 +20118,8 @@ packages:
       mdast-util-to-markdown: 2.1.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /mdast-util-gfm@3.0.0:
-    resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==}
+  mdast-util-gfm@3.0.0:
     dependencies:
       mdast-util-from-markdown: 2.0.0
       mdast-util-gfm-autolink-literal: 2.0.0
@@ -14513,17 +20130,13 @@ packages:
       mdast-util-to-markdown: 2.1.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /mdast-util-phrasing@4.1.0:
-    resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
+  mdast-util-phrasing@4.1.0:
     dependencies:
       '@types/mdast': 4.0.3
       unist-util-is: 6.0.0
-    dev: true
 
-  /mdast-util-to-markdown@2.1.0:
-    resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==}
+  mdast-util-to-markdown@2.1.0:
     dependencies:
       '@types/mdast': 4.0.3
       '@types/unist': 3.0.2
@@ -14533,43 +20146,28 @@ packages:
       micromark-util-decode-string: 2.0.0
       unist-util-visit: 5.0.0
       zwitch: 2.0.4
-    dev: true
 
-  /mdast-util-to-string@4.0.0:
-    resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+  mdast-util-to-string@4.0.0:
     dependencies:
       '@types/mdast': 4.0.3
-    dev: true
 
-  /mdn-data@2.0.28:
-    resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
-    dev: false
+  mdn-data@2.0.28: {}
 
-  /mdn-data@2.0.30:
-    resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
-    dev: false
+  mdn-data@2.0.30: {}
 
-  /media-typer@0.3.0:
-    resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
-    engines: {node: '>= 0.6'}
+  media-typer@0.3.0: {}
 
-  /meilisearch@0.37.0:
-    resolution: {integrity: sha512-LdbK6JmRghCawrmWKJSEQF0OiE82md+YqJGE/U2JcCD8ROwlhTx0KM6NX4rQt0u0VpV0QZVG9umYiu3CSSIJAQ==}
+  meilisearch@0.38.0(encoding@0.1.13):
     dependencies:
-      cross-fetch: 3.1.6
+      cross-fetch: 3.1.6(encoding@0.1.13)
     transitivePeerDependencies:
       - encoding
-    dev: false
 
-  /memoizerific@1.11.3:
-    resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==}
+  memoizerific@1.11.3:
     dependencies:
       map-or-similar: 1.5.0
-    dev: true
 
-  /meow@9.0.0:
-    resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==}
-    engines: {node: '>=10'}
+  meow@9.0.0:
     dependencies:
       '@types/minimist': 1.2.2
       camelcase-keys: 6.2.2
@@ -14583,37 +20181,24 @@ packages:
       trim-newlines: 3.0.1
       type-fest: 0.18.1
       yargs-parser: 20.2.9
-    dev: true
 
-  /merge-descriptors@1.0.1:
-    resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
+  merge-descriptors@1.0.1: {}
 
-  /merge-stream@2.0.0:
-    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+  merge-stream@2.0.0: {}
 
-  /merge2@1.4.1:
-    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
-    engines: {node: '>= 8'}
+  merge2@1.4.1: {}
 
-  /methods@1.1.2:
-    resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
-    engines: {node: '>= 0.6'}
+  methods@1.1.2: {}
 
-  /mfm-js@0.24.0:
-    resolution: {integrity: sha512-6m8N0ElH9/4CA1izhVqmxTfLj5Z9RspdqM/lMew4xU/UTgm4Pf//VpDunpasxbRFjeJSVW+zoVwL4ZPfPtfiQg==}
+  mfm-js@0.24.0:
     dependencies:
       '@twemoji/parser': 15.0.0
-    dev: false
 
-  /microformats-parser@2.0.2:
-    resolution: {integrity: sha512-tUf9DmN4Jq/tGyp1YH2V6D/Cud+9Uc0WhjjUFirqVeHTRkkfLDacv6BQFT7h7HFsD0Z8wja5eKkRgzZU8bv0Fw==}
-    engines: {node: '>=18'}
+  microformats-parser@2.0.2:
     dependencies:
       parse5: 7.1.2
-    dev: false
 
-  /micromark-core-commonmark@2.0.0:
-    resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==}
+  micromark-core-commonmark@2.0.0:
     dependencies:
       decode-named-character-reference: 1.0.2
       devlop: 1.1.0
@@ -14631,19 +20216,15 @@ packages:
       micromark-util-subtokenize: 2.0.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-extension-gfm-autolink-literal@2.0.0:
-    resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==}
+  micromark-extension-gfm-autolink-literal@2.0.0:
     dependencies:
       micromark-util-character: 2.1.0
       micromark-util-sanitize-uri: 2.0.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-extension-gfm-footnote@2.0.0:
-    resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==}
+  micromark-extension-gfm-footnote@2.0.0:
     dependencies:
       devlop: 1.1.0
       micromark-core-commonmark: 2.0.0
@@ -14653,10 +20234,8 @@ packages:
       micromark-util-sanitize-uri: 2.0.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-extension-gfm-strikethrough@2.0.0:
-    resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==}
+  micromark-extension-gfm-strikethrough@2.0.0:
     dependencies:
       devlop: 1.1.0
       micromark-util-chunked: 2.0.0
@@ -14664,36 +20243,28 @@ packages:
       micromark-util-resolve-all: 2.0.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-extension-gfm-table@2.0.0:
-    resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==}
+  micromark-extension-gfm-table@2.0.0:
     dependencies:
       devlop: 1.1.0
       micromark-factory-space: 2.0.0
       micromark-util-character: 2.1.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-extension-gfm-tagfilter@2.0.0:
-    resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==}
+  micromark-extension-gfm-tagfilter@2.0.0:
     dependencies:
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-extension-gfm-task-list-item@2.0.1:
-    resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==}
+  micromark-extension-gfm-task-list-item@2.0.1:
     dependencies:
       devlop: 1.1.0
       micromark-factory-space: 2.0.0
       micromark-util-character: 2.1.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-extension-gfm@3.0.0:
-    resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==}
+  micromark-extension-gfm@3.0.0:
     dependencies:
       micromark-extension-gfm-autolink-literal: 2.0.0
       micromark-extension-gfm-footnote: 2.0.0
@@ -14703,140 +20274,100 @@ packages:
       micromark-extension-gfm-task-list-item: 2.0.1
       micromark-util-combine-extensions: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-factory-destination@2.0.0:
-    resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
+  micromark-factory-destination@2.0.0:
     dependencies:
       micromark-util-character: 2.1.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-factory-label@2.0.0:
-    resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
+  micromark-factory-label@2.0.0:
     dependencies:
       devlop: 1.1.0
       micromark-util-character: 2.1.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-factory-space@2.0.0:
-    resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
+  micromark-factory-space@2.0.0:
     dependencies:
       micromark-util-character: 2.1.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-factory-title@2.0.0:
-    resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==}
+  micromark-factory-title@2.0.0:
     dependencies:
       micromark-factory-space: 2.0.0
       micromark-util-character: 2.1.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-factory-whitespace@2.0.0:
-    resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==}
+  micromark-factory-whitespace@2.0.0:
     dependencies:
       micromark-factory-space: 2.0.0
       micromark-util-character: 2.1.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-util-character@2.1.0:
-    resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
+  micromark-util-character@2.1.0:
     dependencies:
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-util-chunked@2.0.0:
-    resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==}
+  micromark-util-chunked@2.0.0:
     dependencies:
       micromark-util-symbol: 2.0.0
-    dev: true
 
-  /micromark-util-classify-character@2.0.0:
-    resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==}
+  micromark-util-classify-character@2.0.0:
     dependencies:
       micromark-util-character: 2.1.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-util-combine-extensions@2.0.0:
-    resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==}
+  micromark-util-combine-extensions@2.0.0:
     dependencies:
       micromark-util-chunked: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-util-decode-numeric-character-reference@2.0.1:
-    resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==}
+  micromark-util-decode-numeric-character-reference@2.0.1:
     dependencies:
       micromark-util-symbol: 2.0.0
-    dev: true
 
-  /micromark-util-decode-string@2.0.0:
-    resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==}
+  micromark-util-decode-string@2.0.0:
     dependencies:
       decode-named-character-reference: 1.0.2
       micromark-util-character: 2.1.0
       micromark-util-decode-numeric-character-reference: 2.0.1
       micromark-util-symbol: 2.0.0
-    dev: true
 
-  /micromark-util-encode@2.0.0:
-    resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
-    dev: true
+  micromark-util-encode@2.0.0: {}
 
-  /micromark-util-html-tag-name@2.0.0:
-    resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
-    dev: true
+  micromark-util-html-tag-name@2.0.0: {}
 
-  /micromark-util-normalize-identifier@2.0.0:
-    resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==}
+  micromark-util-normalize-identifier@2.0.0:
     dependencies:
       micromark-util-symbol: 2.0.0
-    dev: true
 
-  /micromark-util-resolve-all@2.0.0:
-    resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==}
+  micromark-util-resolve-all@2.0.0:
     dependencies:
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-util-sanitize-uri@2.0.0:
-    resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
+  micromark-util-sanitize-uri@2.0.0:
     dependencies:
       micromark-util-character: 2.1.0
       micromark-util-encode: 2.0.0
       micromark-util-symbol: 2.0.0
-    dev: true
 
-  /micromark-util-subtokenize@2.0.0:
-    resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==}
+  micromark-util-subtokenize@2.0.0:
     dependencies:
       devlop: 1.1.0
       micromark-util-chunked: 2.0.0
       micromark-util-symbol: 2.0.0
       micromark-util-types: 2.0.0
-    dev: true
 
-  /micromark-util-symbol@2.0.0:
-    resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
-    dev: true
+  micromark-util-symbol@2.0.0: {}
 
-  /micromark-util-types@2.0.0:
-    resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
-    dev: true
+  micromark-util-types@2.0.0: {}
 
-  /micromark@4.0.0:
-    resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
+  micromark@4.0.0:
     dependencies:
       '@types/debug': 4.1.12
       debug: 4.3.4(supports-color@8.1.1)
@@ -14857,244 +20388,150 @@ packages:
       micromark-util-types: 2.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /micromatch@4.0.5:
-    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
-    engines: {node: '>=8.6'}
+  micromatch@4.0.5:
     dependencies:
       braces: 3.0.2
       picomatch: 2.3.1
 
-  /mime-db@1.52.0:
-    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
-    engines: {node: '>= 0.6'}
+  mime-db@1.52.0: {}
 
-  /mime-types@2.1.35:
-    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
-    engines: {node: '>= 0.6'}
+  mime-types@2.1.35:
     dependencies:
       mime-db: 1.52.0
 
-  /mime@1.6.0:
-    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
-    engines: {node: '>=4'}
-    hasBin: true
+  mime@1.6.0: {}
 
-  /mime@3.0.0:
-    resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
-    engines: {node: '>=10.0.0'}
-    hasBin: true
-    dev: false
+  mime@3.0.0: {}
 
-  /mimic-fn@2.1.0:
-    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
-    engines: {node: '>=6'}
+  mimic-fn@2.1.0: {}
 
-  /mimic-fn@4.0.0:
-    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
-    engines: {node: '>=12'}
+  mimic-fn@4.0.0: {}
 
-  /mimic-response@1.0.1:
-    resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
-    engines: {node: '>=4'}
-    dev: false
+  mimic-response@1.0.1: {}
 
-  /mimic-response@3.1.0:
-    resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
-    engines: {node: '>=10'}
+  mimic-response@3.1.0: {}
 
-  /mimic-response@4.0.0:
-    resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  mimic-response@4.0.0: {}
 
-  /min-indent@1.0.1:
-    resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
-    engines: {node: '>=4'}
-    dev: true
+  min-indent@1.0.1: {}
 
-  /minimalistic-assert@1.0.1:
-    resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
-    dev: false
+  minimalistic-assert@1.0.1: {}
 
-  /minimatch@3.1.2:
-    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+  minimatch@3.0.8:
     dependencies:
       brace-expansion: 1.1.11
 
-  /minimatch@5.1.2:
-    resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==}
-    engines: {node: '>=10'}
+  minimatch@3.1.2:
+    dependencies:
+      brace-expansion: 1.1.11
+
+  minimatch@5.1.2:
+    dependencies:
+      brace-expansion: 2.0.1
+
+  minimatch@9.0.1:
     dependencies:
       brace-expansion: 2.0.1
 
-  /minimatch@9.0.1:
-    resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
-    engines: {node: '>=16 || 14 >=14.17'}
+  minimatch@9.0.3:
     dependencies:
       brace-expansion: 2.0.1
-    dev: true
 
-  /minimatch@9.0.3:
-    resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
-    engines: {node: '>=16 || 14 >=14.17'}
+  minimatch@9.0.4:
     dependencies:
       brace-expansion: 2.0.1
 
-  /minimist-options@4.1.0:
-    resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
-    engines: {node: '>= 6'}
+  minimist-options@4.1.0:
     dependencies:
       arrify: 1.0.1
       is-plain-obj: 1.1.0
       kind-of: 6.0.3
-    dev: true
 
-  /minimist@1.2.8:
-    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+  minimist@1.2.8: {}
 
-  /minipass-collect@1.0.2:
-    resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
-    engines: {node: '>= 8'}
+  minipass-collect@1.0.2:
     dependencies:
       minipass: 3.3.6
-    dev: false
 
-  /minipass-fetch@3.0.3:
-    resolution: {integrity: sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  minipass-fetch@3.0.3:
     dependencies:
       minipass: 5.0.0
       minipass-sized: 1.0.3
       minizlib: 2.1.2
     optionalDependencies:
       encoding: 0.1.13
-    dev: false
 
-  /minipass-flush@1.0.5:
-    resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
-    engines: {node: '>= 8'}
+  minipass-flush@1.0.5:
     dependencies:
       minipass: 3.3.6
-    dev: false
 
-  /minipass-pipeline@1.2.4:
-    resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
-    engines: {node: '>=8'}
+  minipass-pipeline@1.2.4:
     dependencies:
       minipass: 3.3.6
-    dev: false
 
-  /minipass-sized@1.0.3:
-    resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==}
-    engines: {node: '>=8'}
+  minipass-sized@1.0.3:
     dependencies:
       minipass: 3.3.6
-    dev: false
 
-  /minipass@2.9.0:
-    resolution: {integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==}
-    requiresBuild: true
+  minipass@2.9.0:
     dependencies:
       safe-buffer: 5.2.1
       yallist: 3.1.1
-    dev: false
     optional: true
 
-  /minipass@3.3.6:
-    resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
-    engines: {node: '>=8'}
+  minipass@3.3.6:
     dependencies:
       yallist: 4.0.0
 
-  /minipass@5.0.0:
-    resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
-    engines: {node: '>=8'}
+  minipass@5.0.0: {}
 
-  /minipass@7.0.4:
-    resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
-    engines: {node: '>=16 || 14 >=14.17'}
+  minipass@7.0.4: {}
 
-  /minizlib@1.3.3:
-    resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==}
-    requiresBuild: true
+  minizlib@1.3.3:
     dependencies:
       minipass: 2.9.0
-    dev: false
     optional: true
 
-  /minizlib@2.1.2:
-    resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
-    engines: {node: '>= 8'}
+  minizlib@2.1.2:
     dependencies:
       minipass: 3.3.6
       yallist: 4.0.0
 
-  /mkdirp-classic@0.5.3:
-    resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
-    dev: true
+  mkdirp-classic@0.5.3: {}
 
-  /mkdirp@0.5.6:
-    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
-    hasBin: true
+  mkdirp@0.5.6:
     dependencies:
       minimist: 1.2.8
 
-  /mkdirp@1.0.4:
-    resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
-    engines: {node: '>=10'}
-    hasBin: true
-    requiresBuild: true
+  mkdirp@1.0.4: {}
 
-  /mkdirp@2.1.6:
-    resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dev: false
+  mkdirp@2.1.6: {}
 
-  /mlly@1.5.0:
-    resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==}
+  mlly@1.5.0:
     dependencies:
       acorn: 8.11.3
       pathe: 1.1.2
       pkg-types: 1.0.3
       ufo: 1.3.2
-    dev: true
 
-  /mnemonist@0.39.6:
-    resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==}
+  mnemonist@0.39.6:
     dependencies:
       obliterator: 2.0.4
-    dev: false
 
-  /mock-socket@9.3.1:
-    resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==}
-    engines: {node: '>= 8'}
-    dev: true
+  mock-socket@9.3.1: {}
 
-  /mri@1.2.0:
-    resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
-    engines: {node: '>=4'}
-    dev: true
+  mri@1.2.0: {}
 
-  /ms@2.0.0:
-    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+  ms@2.0.0: {}
 
-  /ms@2.1.2:
-    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+  ms@2.1.2: {}
 
-  /ms@2.1.3:
-    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+  ms@2.1.3: {}
 
-  /ms@3.0.0-canary.1:
-    resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==}
-    engines: {node: '>=12.13'}
-    dev: false
+  ms@3.0.0-canary.1: {}
 
-  /msgpackr-extract@3.0.2:
-    resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==}
-    hasBin: true
-    requiresBuild: true
+  msgpackr-extract@3.0.2:
     dependencies:
       node-gyp-build-optional-packages: 5.0.7
     optionalDependencies:
@@ -15104,67 +20541,42 @@ packages:
       '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.2
       '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.2
       '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.2
-    dev: false
     optional: true
 
-  /msgpackr@1.10.1:
-    resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==}
+  msgpackr@1.10.1:
     optionalDependencies:
       msgpackr-extract: 3.0.2
-    dev: false
 
-  /msw-storybook-addon@2.0.0-beta.1(msw@2.1.7):
-    resolution: {integrity: sha512-DRyIAMK3waEfC+pKTyiIq68OZfiZ4WZGUVAn6J4YwCRpDdoCvLzzoC2spN0Jgegx4dEmJ7589ATnS14NxqeBig==}
-    peerDependencies:
-      msw: ^2.0.0
+  msw-storybook-addon@2.0.1(msw@2.2.14(typescript@5.4.5)):
     dependencies:
       is-node-process: 1.2.0
-      msw: 2.1.7(typescript@5.3.3)
-    dev: true
+      msw: 2.2.14(typescript@5.4.5)
 
-  /msw@2.1.7(typescript@5.3.3):
-    resolution: {integrity: sha512-yTIYqEMqDSrdbVMrfmqP6rTKQsnIbglTvVmAHDWwNegyXPXRcV+RjsaFEqubRS266gwWCDLm9YdOkWSKLdDvJQ==}
-    engines: {node: '>=18'}
-    hasBin: true
-    requiresBuild: true
-    peerDependencies:
-      typescript: '>= 4.7.x <= 5.3.x'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  msw@2.2.14(typescript@5.4.5):
     dependencies:
       '@bundled-es-modules/cookie': 2.0.0
       '@bundled-es-modules/statuses': 1.0.1
+      '@inquirer/confirm': 3.1.6
       '@mswjs/cookies': 1.1.0
-      '@mswjs/interceptors': 0.25.16
+      '@mswjs/interceptors': 0.26.15
       '@open-draft/until': 2.1.0
       '@types/cookie': 0.6.0
       '@types/statuses': 2.0.4
       chalk: 4.1.2
-      chokidar: 3.5.3
       graphql: 16.8.1
       headers-polyfill: 4.0.2
-      inquirer: 8.2.5
       is-node-process: 1.2.0
       outvariant: 1.4.2
       path-to-regexp: 6.2.1
       strict-event-emitter: 0.5.1
       type-fest: 4.9.0
-      typescript: 5.3.3
       yargs: 17.7.2
-    dev: true
-
-  /muggle-string@0.3.1:
-    resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
-    dev: true
+    optionalDependencies:
+      typescript: 5.4.5
 
-  /muggle-string@0.4.1:
-    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
-    dev: true
+  muggle-string@0.4.1: {}
 
-  /multer@1.4.4-lts.1:
-    resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==}
-    engines: {node: '>= 6.0.0'}
+  multer@1.4.4-lts.1:
     dependencies:
       append-field: 1.0.0
       busboy: 1.6.0
@@ -15174,219 +20586,138 @@ packages:
       type-is: 1.6.18
       xtend: 4.0.2
 
-  /multi-integer-range@3.0.0:
-    resolution: {integrity: sha512-uQzynjVJ8F7x5wjaK0g4Ybhy2TvO/pk96+YHyS5g1W4GuUEV6HMebZ8HcRwWgKIRCUT2MLbM5uCKwYcAqkS+8Q==}
-    dev: false
+  multi-integer-range@3.0.0: {}
 
-  /mute-stream@0.0.8:
-    resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
-    dev: true
+  mute-stream@1.0.0: {}
 
-  /mylas@2.1.13:
-    resolution: {integrity: sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==}
-    engines: {node: '>=12.0.0'}
-    dev: false
+  mylas@2.1.13: {}
 
-  /mz@2.7.0:
-    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+  mz@2.7.0:
     dependencies:
       any-promise: 1.3.0
       object-assign: 4.1.1
       thenify-all: 1.6.0
-    dev: false
 
-  /nan@2.18.0:
-    resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==}
-    dev: false
+  nan@2.18.0: {}
 
-  /nanoid@3.3.7:
-    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
-    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
-    hasBin: true
+  nanoid@3.3.7: {}
 
-  /nanoid@5.0.6:
-    resolution: {integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==}
-    engines: {node: ^18 || >=20}
-    hasBin: true
-    dev: false
+  nanoid@5.0.7: {}
 
-  /natural-compare@1.4.0:
-    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
-    dev: true
+  natural-compare@1.4.0: {}
 
-  /ncp@2.0.0:
-    resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==}
-    hasBin: true
-    dev: true
+  ncp@2.0.0: {}
 
-  /ndarray-ops@1.2.2:
-    resolution: {integrity: sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==}
+  ndarray-ops@1.2.2:
     dependencies:
       cwise-compiler: 1.1.3
-    dev: false
 
-  /ndarray-pack@1.2.1:
-    resolution: {integrity: sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==}
+  ndarray-pack@1.2.1:
     dependencies:
       cwise-compiler: 1.1.3
       ndarray: 1.0.19
-    dev: false
 
-  /ndarray@1.0.18:
-    resolution: {integrity: sha512-jUz6G+CIsEsqs2VlB1EvaQSAA0Jkf8YKm7eFBleKyhiQjYWzTxXqHzWEOm3jFoGCpxGh4DnPUYHB4ECWE+n9SQ==}
+  ndarray@1.0.18:
     dependencies:
       iota-array: 1.0.0
       is-buffer: 1.1.6
-    dev: false
 
-  /ndarray@1.0.19:
-    resolution: {integrity: sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==}
+  ndarray@1.0.19:
     dependencies:
       iota-array: 1.0.0
       is-buffer: 1.1.6
-    dev: false
 
-  /needle@2.9.1:
-    resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==}
-    engines: {node: '>= 4.4.x'}
-    hasBin: true
+  needle@2.9.1:
     dependencies:
       debug: 3.2.7(supports-color@8.1.1)
       iconv-lite: 0.4.24
       sax: 1.2.4
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /negotiator@0.6.3:
-    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
-    engines: {node: '>= 0.6'}
+  negotiator@0.6.3: {}
 
-  /neo-async@2.6.2:
-    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
-    dev: true
+  neo-async@2.6.2: {}
 
-  /nested-property@4.0.0:
-    resolution: {integrity: sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==}
-    dev: false
+  nested-property@4.0.0: {}
 
-  /netmask@2.0.2:
-    resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
-    engines: {node: '>= 0.4.0'}
+  netmask@2.0.2: {}
 
-  /nise@5.1.4:
-    resolution: {integrity: sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==}
+  nice-napi@1.0.2:
+    dependencies:
+      node-addon-api: 3.2.1
+      node-gyp-build: 4.6.0
+    optional: true
+
+  nise@5.1.4:
     dependencies:
       '@sinonjs/commons': 2.0.0
       '@sinonjs/fake-timers': 10.3.0
       '@sinonjs/text-encoding': 0.7.2
       just-extend: 4.2.1
       path-to-regexp: 1.8.0
-    dev: true
 
-  /node-abort-controller@3.1.1:
-    resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
-    dev: false
+  node-abort-controller@3.1.1: {}
 
-  /node-bitmap@0.0.1:
-    resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==}
-    engines: {node: '>=v0.6.5'}
-    dev: false
+  node-addon-api@3.2.1:
+    optional: true
 
-  /node-dir@0.1.17:
-    resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==}
-    engines: {node: '>= 0.10.5'}
+  node-bitmap@0.0.1: {}
+
+  node-dir@0.1.17:
     dependencies:
       minimatch: 3.1.2
-    dev: true
 
-  /node-domexception@1.0.0:
-    resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
-    engines: {node: '>=10.5.0'}
+  node-domexception@1.0.0: {}
 
-  /node-fetch-native@1.0.2:
-    resolution: {integrity: sha512-KIkvH1jl6b3O7es/0ShyCgWLcfXxlBrLBbP3rOr23WArC66IMcU4DeZEeYEOwnopYhawLTn7/y+YtmASe8DFVQ==}
-    dev: true
+  node-fetch-native@1.0.2: {}
 
-  /node-fetch@2.6.11:
-    resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==}
-    engines: {node: 4.x || >=6.0.0}
-    requiresBuild: true
-    peerDependencies:
-      encoding: ^0.1.0
-    peerDependenciesMeta:
-      encoding:
-        optional: true
+  node-fetch@2.6.11(encoding@0.1.13):
     dependencies:
       whatwg-url: 5.0.0
-    dev: false
+    optionalDependencies:
+      encoding: 0.1.13
 
-  /node-fetch@2.7.0:
-    resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
-    engines: {node: 4.x || >=6.0.0}
-    peerDependencies:
-      encoding: ^0.1.0
-    peerDependenciesMeta:
-      encoding:
-        optional: true
+  node-fetch@2.7.0(encoding@0.1.13):
     dependencies:
       whatwg-url: 5.0.0
+    optionalDependencies:
+      encoding: 0.1.13
 
-  /node-fetch@3.3.2:
-    resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  node-fetch@3.3.2:
     dependencies:
       data-uri-to-buffer: 4.0.0
       fetch-blob: 3.2.0
       formdata-polyfill: 4.0.10
 
-  /node-gyp-build-optional-packages@5.0.7:
-    resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==}
-    hasBin: true
-    requiresBuild: true
-    dev: false
+  node-gyp-build-optional-packages@5.0.7:
     optional: true
 
-  /node-gyp-build@4.6.0:
-    resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
-    hasBin: true
-    requiresBuild: true
+  node-gyp-build@4.6.0:
+    optional: true
 
-  /node-gyp@10.0.1:
-    resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==}
-    engines: {node: ^16.14.0 || >=18.0.0}
-    hasBin: true
+  node-gyp@10.0.1:
     dependencies:
       env-paths: 2.2.1
       exponential-backoff: 3.1.1
-      glob: 10.3.10
+      glob: 10.3.12
       graceful-fs: 4.2.11
       make-fetch-happen: 13.0.0
       nopt: 7.2.0
       proc-log: 3.0.0
-      semver: 7.5.4
-      tar: 6.2.0
+      semver: 7.6.0
+      tar: 6.2.1
       which: 4.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /node-int64@0.4.0:
-    resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
-    dev: true
+  node-int64@0.4.0: {}
 
-  /node-releases@2.0.14:
-    resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+  node-releases@2.0.14: {}
 
-  /nodemailer@6.9.10:
-    resolution: {integrity: sha512-qtoKfGFhvIFW5kLfrkw2R6Nm6Ur4LNUMykyqu6n9BRKJuyQrqEGwdXXUAbwWEKt33dlWUGXb7rzmJP/p4+O+CA==}
-    engines: {node: '>=6.0.0'}
-    dev: false
+  nodemailer@6.9.13: {}
 
-  /nodemon@3.0.2:
-    resolution: {integrity: sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==}
-    engines: {node: '>=10'}
-    hasBin: true
+  nodemon@3.0.2:
     dependencies:
       chokidar: 3.5.3
       debug: 4.3.4(supports-color@5.5.0)
@@ -15398,12 +20729,8 @@ packages:
       supports-color: 5.5.0
       touch: 3.1.0
       undefsafe: 2.0.5
-    dev: true
 
-  /nodemon@3.1.0:
-    resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==}
-    engines: {node: '>=10'}
-    hasBin: true
+  nodemon@3.1.0:
     dependencies:
       chokidar: 3.5.3
       debug: 4.3.4(supports-color@5.5.0)
@@ -15415,267 +20742,165 @@ packages:
       supports-color: 5.5.0
       touch: 3.1.0
       undefsafe: 2.0.5
-    dev: true
 
-  /nofilter@3.1.0:
-    resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==}
-    engines: {node: '>=12.19'}
-    dev: false
+  nofilter@3.1.0: {}
 
-  /nopt@1.0.10:
-    resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==}
-    hasBin: true
+  nopt@1.0.10:
     dependencies:
       abbrev: 1.1.1
-    dev: true
 
-  /nopt@5.0.0:
-    resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
-    engines: {node: '>=6'}
-    hasBin: true
-    requiresBuild: true
+  nopt@5.0.0:
     dependencies:
       abbrev: 1.1.1
-    dev: false
     optional: true
 
-  /nopt@6.0.0:
-    resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
-    hasBin: true
+  nopt@6.0.0:
     dependencies:
       abbrev: 1.1.1
-    dev: true
 
-  /nopt@7.2.0:
-    resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
-    hasBin: true
+  nopt@7.2.0:
     dependencies:
       abbrev: 2.0.0
-    dev: false
 
-  /normalize-package-data@2.5.0:
-    resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
+  normalize-package-data@2.5.0:
     dependencies:
       hosted-git-info: 2.8.9
       resolve: 1.22.8
       semver: 5.7.1
       validate-npm-package-license: 3.0.4
-    dev: true
 
-  /normalize-package-data@3.0.3:
-    resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==}
-    engines: {node: '>=10'}
+  normalize-package-data@3.0.3:
     dependencies:
       hosted-git-info: 4.1.0
       is-core-module: 2.13.1
       semver: 7.6.0
       validate-npm-package-license: 3.0.4
-    dev: true
 
-  /normalize-path@3.0.0:
-    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
-    engines: {node: '>=0.10.0'}
+  normalize-path@3.0.0: {}
 
-  /normalize-url@6.1.0:
-    resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
-    engines: {node: '>=10'}
-    dev: false
+  normalize-url@6.1.0: {}
 
-  /normalize-url@8.0.0:
-    resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
-    engines: {node: '>=14.16'}
+  normalize-url@8.0.0: {}
 
-  /npm-run-path@2.0.2:
-    resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
-    engines: {node: '>=4'}
+  npm-run-path@2.0.2:
     dependencies:
       path-key: 2.0.1
-    dev: false
 
-  /npm-run-path@4.0.1:
-    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
-    engines: {node: '>=8'}
+  npm-run-path@4.0.1:
     dependencies:
       path-key: 3.1.1
 
-  /npm-run-path@5.1.0:
-    resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  npm-run-path@5.1.0:
     dependencies:
       path-key: 4.0.0
 
-  /npmlog@5.0.1:
-    resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
-    requiresBuild: true
+  npmlog@5.0.1:
     dependencies:
       are-we-there-yet: 2.0.0
       console-control-strings: 1.1.0
       gauge: 3.0.2
       set-blocking: 2.0.0
-    dev: false
     optional: true
 
-  /nsfwjs@2.4.2(@tensorflow/tfjs@4.4.0):
-    resolution: {integrity: sha512-i4Pp2yt59qPQgeZFyg3wXFBX52uSeu/hkDoqdZfe+sILRxNBUu0VDogj7Lmqak0GlrXviS/wLiVeIx40IDUu7A==}
-    peerDependencies:
-      '@tensorflow/tfjs': ^3.18.0
+  nsfwjs@2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)):
     dependencies:
       '@nsfw-filter/gif-frames': 1.0.2
-      '@tensorflow/tfjs': 4.4.0(seedrandom@3.0.5)
-    dev: false
+      '@tensorflow/tfjs': 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
 
-  /nth-check@2.1.1:
-    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+  nth-check@2.1.1:
     dependencies:
       boolbase: 1.0.0
 
-  /oauth-sign@0.9.0:
-    resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
-    dev: false
+  nwsapi@2.2.9: {}
 
-  /oauth2orize-pkce@0.1.2:
-    resolution: {integrity: sha512-grto2UYhXHi9GLE3IBgBBbV87xci55+bCyjpVuxKyzol6I5Rg0K1MiTuXE+JZk54R86SG2wqXODMiZYHraPpxw==}
-    dev: false
+  oauth-sign@0.9.0: {}
 
-  /oauth2orize@1.12.0:
-    resolution: {integrity: sha512-j4XtFDQUBsvUHPjUmvmNDUDMYed2MphMIJBhyxVVe8hGCjkuYnjIsW+D9qk8c5ciXRdnk6x6tEbiO6PLeOZdCQ==}
-    engines: {node: '>= 0.4.0'}
+  oauth2orize-pkce@0.1.2: {}
+
+  oauth2orize@1.12.0:
     dependencies:
       debug: 2.6.9
       uid2: 0.0.4
       utils-merge: 1.0.1
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /oauth@0.10.0:
-    resolution: {integrity: sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==}
-    dev: false
+  oauth@0.10.0: {}
 
-  /object-assign@4.1.1:
-    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
-    engines: {node: '>=0.10.0'}
+  object-assign@4.1.1: {}
 
-  /object-inspect@1.12.3:
-    resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
+  object-inspect@1.12.3: {}
 
-  /object-is@1.1.5:
-    resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==}
-    engines: {node: '>= 0.4'}
+  object-is@1.1.5:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
-    dev: true
 
-  /object-keys@1.1.1:
-    resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
-    engines: {node: '>= 0.4'}
-    dev: true
+  object-keys@1.1.1: {}
 
-  /object.assign@4.1.4:
-    resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==}
-    engines: {node: '>= 0.4'}
+  object.assign@4.1.4:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       has-symbols: 1.0.3
       object-keys: 1.1.1
-    dev: true
 
-  /object.fromentries@2.0.7:
-    resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
-    engines: {node: '>= 0.4'}
+  object.fromentries@2.0.7:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
-    dev: true
 
-  /object.groupby@1.0.1:
-    resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==}
+  object.groupby@1.0.1:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
       get-intrinsic: 1.2.1
-    dev: true
 
-  /object.values@1.1.7:
-    resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
-    engines: {node: '>= 0.4'}
+  object.values@1.1.7:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
-    dev: true
 
-  /obliterator@2.0.4:
-    resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==}
-    dev: false
+  obliterator@2.0.4: {}
 
-  /oblivious-set@1.4.0:
-    resolution: {integrity: sha512-szyd0ou0T8nsAqHtprRcP3WidfsN1TnAR5yWXf2mFCEr5ek3LEOkT6EZ/92Xfs74HIdyhG5WkGxIssMU0jBaeg==}
-    engines: {node: '>=16'}
-    dev: false
+  oblivious-set@1.4.0: {}
 
-  /obuf@1.1.2:
-    resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
-    dev: true
+  obuf@1.1.2: {}
 
-  /omggif@1.0.10:
-    resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==}
-    dev: false
+  omggif@1.0.10: {}
 
-  /on-exit-leak-free@2.1.0:
-    resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==}
-    dev: false
+  on-exit-leak-free@2.1.0: {}
 
-  /on-finished@2.4.1:
-    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
-    engines: {node: '>= 0.8'}
+  on-finished@2.4.1:
     dependencies:
       ee-first: 1.1.1
 
-  /on-headers@1.0.2:
-    resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==}
-    engines: {node: '>= 0.8'}
-    dev: true
+  on-headers@1.0.2: {}
 
-  /once@1.4.0:
-    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+  once@1.4.0:
     dependencies:
       wrappy: 1.0.2
 
-  /onetime@5.1.2:
-    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
-    engines: {node: '>=6'}
+  onetime@5.1.2:
     dependencies:
       mimic-fn: 2.1.0
 
-  /onetime@6.0.0:
-    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
-    engines: {node: '>=12'}
+  onetime@6.0.0:
     dependencies:
       mimic-fn: 4.0.0
 
-  /open@8.4.2:
-    resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
-    engines: {node: '>=12'}
+  open@8.4.2:
     dependencies:
       define-lazy-prop: 2.0.0
       is-docker: 2.2.1
       is-wsl: 2.2.0
-    dev: true
 
-  /openapi-types@12.1.3:
-    resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
-    dev: true
+  openapi-types@12.1.3: {}
 
-  /openapi-typescript@6.7.3:
-    resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==}
-    hasBin: true
+  openapi-typescript@6.7.3:
     dependencies:
       ansi-colors: 4.1.3
       fast-glob: 3.3.2
@@ -15683,16 +20908,8 @@ packages:
       supports-color: 9.4.0
       undici: 5.28.2
       yargs-parser: 21.1.1
-    dev: true
-
-  /opentype.js@0.4.11:
-    resolution: {integrity: sha512-GthxucX/6aftfLdeU5Ho7o7zmQcC8uVtqdcelVq12X++ndxwBZG8Xb5rFEKT7nEcWDD2P1x+TNuJ70jtj1Mbpw==}
-    hasBin: true
-    dev: false
 
-  /optionator@0.9.3:
-    resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
-    engines: {node: '>= 0.8.0'}
+  optionator@0.9.3:
     dependencies:
       '@aashutoshrathi/word-wrap': 1.2.6
       deep-is: 0.1.4
@@ -15700,11 +20917,8 @@ packages:
       levn: 0.4.1
       prelude-ls: 1.2.1
       type-check: 0.4.0
-    dev: true
 
-  /ora@5.4.1:
-    resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
-    engines: {node: '>=10'}
+  ora@5.4.1:
     dependencies:
       bl: 4.1.0
       chalk: 4.1.2
@@ -15715,323 +20929,190 @@ packages:
       log-symbols: 4.1.0
       strip-ansi: 6.0.1
       wcwidth: 1.0.1
-    dev: true
 
-  /os-filter-obj@2.0.0:
-    resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==}
-    engines: {node: '>=4'}
+  os-filter-obj@2.0.0:
     dependencies:
       arch: 2.2.0
-    dev: false
-
-  /os-tmpdir@1.0.2:
-    resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
-    engines: {node: '>=0.10.0'}
-    dev: true
 
-  /os-utils@0.0.14:
-    resolution: {integrity: sha512-ajB8csaHLBvJOYsHJkp8YdO2FvlBbf/ZxaYQwXXRDyQ84UoE+uTuLXxqd0shekXMX6Qr/pt/DDyLMRAMsgfWzg==}
-    dev: false
+  os-utils@0.0.14: {}
 
-  /ospath@1.2.2:
-    resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
-    dev: true
+  ospath@1.2.2: {}
 
-  /otpauth@9.2.2:
-    resolution: {integrity: sha512-2VcnYRUmq1dNckIfySNYP32ITWp1bvTeAEW0BSCR6G3GBf3a5zb9E+ubY62t3Dma9RjoHlvd7QpmzHfJZRkiNg==}
+  otpauth@9.2.3:
     dependencies:
       jssha: 3.3.1
-    dev: false
 
-  /outvariant@1.4.2:
-    resolution: {integrity: sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==}
-    dev: true
+  outvariant@1.4.2: {}
 
-  /p-cancelable@2.1.1:
-    resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
-    engines: {node: '>=8'}
-    dev: false
+  p-cancelable@2.1.1: {}
 
-  /p-cancelable@3.0.0:
-    resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
-    engines: {node: '>=12.20'}
+  p-cancelable@3.0.0: {}
 
-  /p-cancelable@4.0.1:
-    resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
-    engines: {node: '>=14.16'}
-    dev: false
+  p-cancelable@4.0.1: {}
 
-  /p-finally@1.0.0:
-    resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
-    engines: {node: '>=4'}
-    dev: false
+  p-finally@1.0.0: {}
 
-  /p-limit@2.3.0:
-    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
-    engines: {node: '>=6'}
+  p-limit@2.3.0:
     dependencies:
       p-try: 2.2.0
 
-  /p-limit@3.1.0:
-    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
-    engines: {node: '>=10'}
+  p-limit@3.1.0:
     dependencies:
       yocto-queue: 0.1.0
 
-  /p-limit@4.0.0:
-    resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  p-limit@4.0.0:
     dependencies:
       yocto-queue: 1.0.0
-    dev: true
 
-  /p-locate@3.0.0:
-    resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
-    engines: {node: '>=6'}
+  p-locate@3.0.0:
     dependencies:
       p-limit: 2.3.0
-    dev: true
 
-  /p-locate@4.1.0:
-    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
-    engines: {node: '>=8'}
+  p-locate@4.1.0:
     dependencies:
       p-limit: 2.3.0
 
-  /p-locate@5.0.0:
-    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
-    engines: {node: '>=10'}
+  p-locate@5.0.0:
     dependencies:
       p-limit: 3.1.0
-    dev: true
 
-  /p-map@4.0.0:
-    resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
-    engines: {node: '>=10'}
+  p-map@4.0.0:
     dependencies:
       aggregate-error: 3.1.0
 
-  /p-queue@6.6.2:
-    resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==}
-    engines: {node: '>=8'}
+  p-queue@6.6.2:
     dependencies:
       eventemitter3: 4.0.7
       p-timeout: 3.2.0
-    dev: false
 
-  /p-timeout@3.2.0:
-    resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
-    engines: {node: '>=8'}
+  p-timeout@3.2.0:
     dependencies:
       p-finally: 1.0.0
-    dev: false
-
-  /p-try@2.2.0:
-    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
-    engines: {node: '>=6'}
 
-  /packet-reader@1.0.0:
-    resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
-    dev: false
+  p-try@2.2.0: {}
 
-  /pako@0.2.9:
-    resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
-    dev: true
+  pako@0.2.9: {}
 
-  /parent-module@1.0.1:
-    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
-    engines: {node: '>=6'}
+  parent-module@1.0.1:
     dependencies:
       callsites: 3.1.0
-    dev: true
 
-  /parse-data-uri@0.2.0:
-    resolution: {integrity: sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==}
+  parse-data-uri@0.2.0:
     dependencies:
       data-uri-to-buffer: 0.0.3
-    dev: false
 
-  /parse-json@5.2.0:
-    resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
-    engines: {node: '>=8'}
+  parse-json@5.2.0:
     dependencies:
       '@babel/code-frame': 7.23.5
       error-ex: 1.3.2
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
-    dev: true
 
-  /parse-srcset@1.0.2:
-    resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
-    dev: false
+  parse-srcset@1.0.2: {}
 
-  /parse5-htmlparser2-tree-adapter@6.0.1:
-    resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
+  parse5-htmlparser2-tree-adapter@6.0.1:
     dependencies:
       parse5: 6.0.1
-    dev: false
 
-  /parse5-htmlparser2-tree-adapter@7.0.0:
-    resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
+  parse5-htmlparser2-tree-adapter@7.0.0:
     dependencies:
       domhandler: 5.0.3
       parse5: 7.1.2
 
-  /parse5@5.1.1:
-    resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==}
-    dev: false
+  parse5@5.1.1: {}
 
-  /parse5@6.0.1:
-    resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
-    dev: false
+  parse5@6.0.1: {}
 
-  /parse5@7.1.2:
-    resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+  parse5@7.1.2:
     dependencies:
       entities: 4.5.0
 
-  /parseurl@1.3.3:
-    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
-    engines: {node: '>= 0.8'}
+  parseurl@1.3.3: {}
 
-  /path-browserify@1.0.1:
-    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
-    dev: true
+  path-browserify@1.0.1: {}
 
-  /path-exists@3.0.0:
-    resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
-    engines: {node: '>=4'}
-    dev: true
+  path-exists@3.0.0: {}
 
-  /path-exists@4.0.0:
-    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
-    engines: {node: '>=8'}
+  path-exists@4.0.0: {}
 
-  /path-is-absolute@1.0.1:
-    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
-    engines: {node: '>=0.10.0'}
+  path-is-absolute@1.0.1: {}
 
-  /path-key@2.0.1:
-    resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
-    engines: {node: '>=4'}
-    dev: false
+  path-key@2.0.1: {}
 
-  /path-key@3.1.1:
-    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
-    engines: {node: '>=8'}
+  path-key@3.1.1: {}
 
-  /path-key@4.0.0:
-    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
-    engines: {node: '>=12'}
+  path-key@4.0.0: {}
 
-  /path-parse@1.0.7:
-    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+  path-parse@1.0.7: {}
 
-  /path-scurry@1.10.1:
-    resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
-    engines: {node: '>=16 || 14 >=14.17'}
+  path-scurry@1.10.1:
     dependencies:
       lru-cache: 10.0.2
       minipass: 7.0.4
 
-  /path-to-regexp@0.1.7:
-    resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
+  path-scurry@1.10.2:
+    dependencies:
+      lru-cache: 10.2.2
+      minipass: 7.0.4
 
-  /path-to-regexp@1.8.0:
-    resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==}
+  path-to-regexp@0.1.7: {}
+
+  path-to-regexp@1.8.0:
     dependencies:
       isarray: 0.0.1
-    dev: true
 
-  /path-to-regexp@3.2.0:
-    resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==}
+  path-to-regexp@3.2.0: {}
 
-  /path-to-regexp@6.2.1:
-    resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
-    dev: true
+  path-to-regexp@6.2.1: {}
 
-  /path-type@4.0.0:
-    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
-    engines: {node: '>=8'}
+  path-type@4.0.0: {}
 
-  /pathe@1.1.2:
-    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
-    dev: true
+  pathe@1.1.2: {}
 
-  /pathval@1.1.1:
-    resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
-    dev: true
+  pathval@1.1.1: {}
 
-  /pause-stream@0.0.11:
-    resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
+  pause-stream@0.0.11:
     dependencies:
       through: 2.3.8
-    dev: true
 
-  /peek-readable@5.0.0:
-    resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==}
-    engines: {node: '>=14.16'}
-    dev: false
+  peek-readable@5.0.0: {}
 
-  /peek-stream@1.1.3:
-    resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==}
+  peek-stream@1.1.3:
     dependencies:
       buffer-from: 1.1.2
       duplexify: 3.7.1
       through2: 2.0.5
-    dev: true
 
-  /pend@1.2.0:
-    resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
-    dev: true
+  pend@1.2.0: {}
 
-  /performance-now@2.1.0:
-    resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
+  performance-now@2.1.0: {}
 
-  /pg-cloudflare@1.1.1:
-    resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
-    requiresBuild: true
-    dev: false
+  pg-cloudflare@1.1.1:
     optional: true
 
-  /pg-connection-string@2.6.2:
-    resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==}
-    dev: false
+  pg-connection-string@2.6.4: {}
 
-  /pg-int8@1.0.1:
-    resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
-    engines: {node: '>=4.0.0'}
+  pg-int8@1.0.1: {}
 
-  /pg-numeric@1.0.2:
-    resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
-    engines: {node: '>=4'}
-    dev: true
+  pg-numeric@1.0.2: {}
 
-  /pg-pool@3.6.1(pg@8.11.3):
-    resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==}
-    peerDependencies:
-      pg: '>=8.0'
+  pg-pool@3.6.2(pg@8.11.5):
     dependencies:
-      pg: 8.11.3
-    dev: false
+      pg: 8.11.5
 
-  /pg-protocol@1.6.0:
-    resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
+  pg-protocol@1.6.0: {}
 
-  /pg-types@2.2.0:
-    resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
-    engines: {node: '>=4'}
+  pg-protocol@1.6.1: {}
+
+  pg-types@2.2.0:
     dependencies:
       pg-int8: 1.0.1
       postgres-array: 2.0.0
       postgres-bytea: 1.0.0
       postgres-date: 1.0.7
       postgres-interval: 1.2.0
-    dev: false
 
-  /pg-types@4.0.1:
-    resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==}
-    engines: {node: '>=10'}
+  pg-types@4.0.1:
     dependencies:
       pg-int8: 1.0.1
       pg-numeric: 1.0.2
@@ -16040,76 +21121,43 @@ packages:
       postgres-date: 2.0.1
       postgres-interval: 3.0.0
       postgres-range: 1.1.3
-    dev: true
 
-  /pg@8.11.3:
-    resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==}
-    engines: {node: '>= 8.0.0'}
-    peerDependencies:
-      pg-native: '>=3.0.1'
-    peerDependenciesMeta:
-      pg-native:
-        optional: true
+  pg@8.11.5:
     dependencies:
-      buffer-writer: 2.0.0
-      packet-reader: 1.0.0
-      pg-connection-string: 2.6.2
-      pg-pool: 3.6.1(pg@8.11.3)
-      pg-protocol: 1.6.0
+      pg-connection-string: 2.6.4
+      pg-pool: 3.6.2(pg@8.11.5)
+      pg-protocol: 1.6.1
       pg-types: 2.2.0
       pgpass: 1.0.5
     optionalDependencies:
       pg-cloudflare: 1.1.1
-    dev: false
 
-  /pgpass@1.0.5:
-    resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
+  pgpass@1.0.5:
     dependencies:
       split2: 4.1.0
-    dev: false
 
-  /photoswipe@5.4.3:
-    resolution: {integrity: sha512-9UC6oJBK4oXFZ5HcdlcvGkfEHsVrmE4csUdCQhEjHYb3PvPLO3PG7UhnPuOgjxwmhq5s17Un5NUdum01LgBDng==}
-    engines: {node: '>= 0.12.0'}
-    dev: false
+  photoswipe@5.4.3: {}
 
-  /picocolors@1.0.0:
-    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+  picocolors@1.0.0: {}
 
-  /picomatch@2.3.1:
-    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
-    engines: {node: '>=8.6'}
+  picomatch@2.3.1: {}
 
-  /pid-port@1.0.0:
-    resolution: {integrity: sha512-LSNBeKChRPA4Xlrs6+zV588G1hSrFvANtPV5rt/5MPfSPK3V9XPWxx1d29svsrOjngT9ifLisXWCLS7DvO9ZhQ==}
-    engines: {node: '>=18'}
+  pid-port@1.0.0:
     dependencies:
       execa: 8.0.1
-    dev: true
 
-  /pify@2.3.0:
-    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
-    engines: {node: '>=0.10.0'}
+  pify@2.3.0: {}
 
-  /pify@4.0.1:
-    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
-    engines: {node: '>=6'}
-    dev: true
+  pify@4.0.1: {}
 
-  /pino-abstract-transport@1.1.0:
-    resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==}
+  pino-abstract-transport@1.1.0:
     dependencies:
       readable-stream: 4.3.0
       split2: 4.1.0
-    dev: false
 
-  /pino-std-serializers@6.1.0:
-    resolution: {integrity: sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==}
-    dev: false
+  pino-std-serializers@6.1.0: {}
 
-  /pino@8.17.0:
-    resolution: {integrity: sha512-ey+Mku+PVPhvxglLXMg1l1zQMwSHuNrKC3MD40EDZbkckJmmuY7DYZLIOwwjZ8ix/Nvhe9dZt5H99cgkot9bAw==}
-    hasBin: true
+  pino@8.17.0:
     dependencies:
       atomic-sleep: 1.0.0
       fast-redact: 3.1.2
@@ -16122,611 +21170,344 @@ packages:
       safe-stable-stringify: 2.4.2
       sonic-boom: 3.7.0
       thread-stream: 2.3.0
-    dev: false
 
-  /pirates@4.0.5:
-    resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
-    engines: {node: '>= 6'}
-    dev: true
+  pirates@4.0.5: {}
 
-  /pkce-challenge@4.1.0:
-    resolution: {integrity: sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==}
-    engines: {node: '>=16.20.0'}
-    dev: false
+  piscina@4.4.0:
+    optionalDependencies:
+      nice-napi: 1.0.2
 
-  /pkg-dir@3.0.0:
-    resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
-    engines: {node: '>=6'}
+  pkce-challenge@4.1.0: {}
+
+  pkg-dir@3.0.0:
     dependencies:
       find-up: 3.0.0
-    dev: true
 
-  /pkg-dir@4.2.0:
-    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
-    engines: {node: '>=8'}
+  pkg-dir@4.2.0:
     dependencies:
       find-up: 4.1.0
-    dev: true
 
-  /pkg-dir@5.0.0:
-    resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==}
-    engines: {node: '>=10'}
+  pkg-dir@5.0.0:
     dependencies:
       find-up: 5.0.0
-    dev: true
 
-  /pkg-types@1.0.3:
-    resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
+  pkg-types@1.0.3:
     dependencies:
       jsonc-parser: 3.2.0
       mlly: 1.5.0
       pathe: 1.1.2
-    dev: true
 
-  /plimit-lit@1.5.0:
-    resolution: {integrity: sha512-Eb/MqCb1Iv/ok4m1FqIXqvUKPISufcjZ605hl3KM/n8GaX8zfhtgdLwZU3vKjuHGh2O9Rjog/bHTq8ofIShdng==}
+  plimit-lit@1.5.0:
     dependencies:
       queue-lit: 1.5.0
-    dev: false
 
-  /plur@4.0.0:
-    resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==}
-    engines: {node: '>=10'}
+  plur@4.0.0:
     dependencies:
       irregular-plurals: 3.5.0
-    dev: true
 
-  /pngjs-nozlib@1.0.0:
-    resolution: {integrity: sha512-N1PggqLp9xDqwAoKvGohmZ3m4/N9xpY0nDZivFqQLcpLHmliHnCp9BuNCsOeqHWMuEEgFjpEaq9dZq6RZyy0fA==}
-    engines: {iojs: '>= 1.0.0', node: '>=0.10.0'}
-    dev: false
+  pngjs-nozlib@1.0.0: {}
 
-  /pngjs@3.4.0:
-    resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
-    engines: {node: '>=4.0.0'}
-    dev: false
+  pngjs@3.4.0: {}
 
-  /pngjs@5.0.0:
-    resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
-    engines: {node: '>=10.13.0'}
-    dev: false
+  pngjs@5.0.0: {}
 
-  /polished@4.2.2:
-    resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==}
-    engines: {node: '>=10'}
+  polished@4.2.2:
     dependencies:
       '@babel/runtime': 7.23.4
-    dev: true
 
-  /postcss-calc@9.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.2.2
+  postcss-calc@9.0.1(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-selector-parser: 6.0.15
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-colormin@6.0.3(postcss@8.4.35):
-    resolution: {integrity: sha512-ECpkS+UZRyAtu/kjive2/1mihP+GNtgC8kcdU8ueWZi1ZVxMNnRziCLdhrWECJhEtSWijfX2Cl9XTTCK/hjGaA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-colormin@6.1.0(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0
       caniuse-api: 3.0.0
       colord: 2.9.3
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-convert-values@6.0.4(postcss@8.4.35):
-    resolution: {integrity: sha512-YT2yrGzPXoQD3YeA2kBo/696qNwn7vI+15AOS2puXWEvSWqdCqlOyDWRy5GNnOc9ACRGOkuQ4ESQEqPJBWt/GA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-convert-values@6.1.0(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-discard-comments@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-discard-comments@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /postcss-discard-duplicates@6.0.2(postcss@8.4.35):
-    resolution: {integrity: sha512-U2rsj4w6pAGROCCcD13LP2eBIi1whUsXs4kgE6xkIuGfkbxCBSKhkCTWyowFd66WdVlLv0uM1euJKIgmdmZObg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-discard-duplicates@6.0.3(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /postcss-discard-empty@6.0.2(postcss@8.4.35):
-    resolution: {integrity: sha512-rj6pVC2dVCJrP0Y2RkYTQEbYaCf4HEm+R/2StQgJqGHxAa3+KcYslNQhcRqjLHtl/4wpzipJluaJLqBj6d5eDQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-discard-empty@6.0.3(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /postcss-discard-overridden@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-discard-overridden@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /postcss-merge-longhand@6.0.3(postcss@8.4.35):
-    resolution: {integrity: sha512-kF/y3DU8CRt+SX3tP/aG+2gkZI2Z7OXDsPU7FgxIJmuyhQQ1EHceIYcsp/alvzCm2P4c37Sfdu8nNrHc+YeyLg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-merge-longhand@6.0.5(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-      stylehacks: 6.0.3(postcss@8.4.35)
-    dev: false
+      stylehacks: 6.1.1(postcss@8.4.38)
 
-  /postcss-merge-rules@6.0.4(postcss@8.4.35):
-    resolution: {integrity: sha512-97iF3UJ5v8N1BWy38y+0l+Z8o5/9uGlEgtWic2PJPzoRrLB6Gxg8TVG93O0EK52jcLeMsywre26AUlX1YAYeHA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-merge-rules@6.1.1(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0
       caniuse-api: 3.0.0
-      cssnano-utils: 4.0.1(postcss@8.4.35)
-      postcss: 8.4.35
-      postcss-selector-parser: 6.0.15
-    dev: false
+      cssnano-utils: 4.0.2(postcss@8.4.38)
+      postcss: 8.4.38
+      postcss-selector-parser: 6.0.16
 
-  /postcss-minify-font-values@6.0.2(postcss@8.4.35):
-    resolution: {integrity: sha512-IedzbVMoX0a7VZWjSYr5qJ6C37rws8kl8diPBeMZLJfWKkgXuMFY5R/OxPegn/q9tK9ztd0XRH3aR0u2t+A7uQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-minify-font-values@6.1.0(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-minify-gradients@6.0.2(postcss@8.4.35):
-    resolution: {integrity: sha512-vP5mF7iI6/5fcpv+rSfwWQekOE+8I1i7/7RjZPGuIjj6eUaZVeG4XZYZrroFuw1WQd51u2V32wyQFZ+oYdE7CA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-minify-gradients@6.0.3(postcss@8.4.38):
     dependencies:
       colord: 2.9.3
-      cssnano-utils: 4.0.1(postcss@8.4.35)
-      postcss: 8.4.35
+      cssnano-utils: 4.0.2(postcss@8.4.38)
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-minify-params@6.0.3(postcss@8.4.35):
-    resolution: {integrity: sha512-j4S74d3AAeCK5eGdQndXSrkxusV2ekOxbXGnlnZthMyZBBvSDiU34CihTASbJxuVB3bugudmwolS7+Dgs5OyOQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-minify-params@6.1.0(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0
-      cssnano-utils: 4.0.1(postcss@8.4.35)
-      postcss: 8.4.35
+      cssnano-utils: 4.0.2(postcss@8.4.38)
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-minify-selectors@6.0.2(postcss@8.4.35):
-    resolution: {integrity: sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-minify-selectors@6.0.4(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-      postcss-selector-parser: 6.0.15
-    dev: false
+      postcss: 8.4.38
+      postcss-selector-parser: 6.0.16
 
-  /postcss-normalize-charset@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-charset@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /postcss-normalize-display-values@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-display-values@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-normalize-positions@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-positions@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-normalize-repeat-style@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-repeat-style@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-normalize-string@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-string@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-normalize-timing-functions@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-timing-functions@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-normalize-unicode@6.0.3(postcss@8.4.35):
-    resolution: {integrity: sha512-T2Bb3gXz0ASgc3ori2dzjv6j/P2IantreaC6fT8tWjqYUiqMAh5jGIkdPwEV2FaucjQlCLeFJDJh2BeSugE1ig==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-unicode@6.1.0(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-normalize-url@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-url@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-normalize-whitespace@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-normalize-whitespace@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-ordered-values@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-ordered-values@6.0.2(postcss@8.4.38):
     dependencies:
-      cssnano-utils: 4.0.1(postcss@8.4.35)
-      postcss: 8.4.35
+      cssnano-utils: 4.0.2(postcss@8.4.38)
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-reduce-initial@6.0.3(postcss@8.4.35):
-    resolution: {integrity: sha512-w4QIR9pEa1N4xMx3k30T1vLZl6udVK2RmNqrDXhBXX9L0mBj2a8ADs8zkbaEH7eUy1m30Wyr5EBgHN31Yq1JvA==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-reduce-initial@6.1.0(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0
       caniuse-api: 3.0.0
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /postcss-reduce-transforms@6.0.1(postcss@8.4.35):
-    resolution: {integrity: sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-reduce-transforms@6.0.2(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
-    dev: false
 
-  /postcss-selector-parser@6.0.15:
-    resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==}
-    engines: {node: '>=4'}
+  postcss-selector-parser@6.0.15:
     dependencies:
       cssesc: 3.0.0
       util-deprecate: 1.0.2
 
-  /postcss-svgo@6.0.2(postcss@8.4.35):
-    resolution: {integrity: sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ==}
-    engines: {node: ^14 || ^16 || >= 18}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-selector-parser@6.0.16:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss-svgo@6.0.3(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
+      postcss: 8.4.38
       postcss-value-parser: 4.2.0
       svgo: 3.2.0
-    dev: false
 
-  /postcss-unique-selectors@6.0.2(postcss@8.4.35):
-    resolution: {integrity: sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  postcss-unique-selectors@6.0.4(postcss@8.4.38):
     dependencies:
-      postcss: 8.4.35
-      postcss-selector-parser: 6.0.15
-    dev: false
+      postcss: 8.4.38
+      postcss-selector-parser: 6.0.16
 
-  /postcss-value-parser@4.2.0:
-    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
-    dev: false
+  postcss-value-parser@4.2.0: {}
 
-  /postcss@8.4.35:
-    resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
-    engines: {node: ^10 || ^12 || >=14}
+  postcss@8.4.38:
     dependencies:
       nanoid: 3.3.7
       picocolors: 1.0.0
-      source-map-js: 1.0.2
+      source-map-js: 1.2.0
 
-  /postgres-array@2.0.0:
-    resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
-    engines: {node: '>=4'}
-    dev: false
+  postgres-array@2.0.0: {}
 
-  /postgres-array@3.0.2:
-    resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
-    engines: {node: '>=12'}
-    dev: true
+  postgres-array@3.0.2: {}
 
-  /postgres-bytea@1.0.0:
-    resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
-    engines: {node: '>=0.10.0'}
-    dev: false
+  postgres-bytea@1.0.0: {}
 
-  /postgres-bytea@3.0.0:
-    resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
-    engines: {node: '>= 6'}
+  postgres-bytea@3.0.0:
     dependencies:
       obuf: 1.1.2
-    dev: true
 
-  /postgres-date@1.0.7:
-    resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
-    engines: {node: '>=0.10.0'}
-    dev: false
+  postgres-date@1.0.7: {}
 
-  /postgres-date@2.0.1:
-    resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==}
-    engines: {node: '>=12'}
-    dev: true
+  postgres-date@2.0.1: {}
 
-  /postgres-interval@1.2.0:
-    resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
-    engines: {node: '>=0.10.0'}
+  postgres-interval@1.2.0:
     dependencies:
       xtend: 4.0.2
-    dev: false
 
-  /postgres-interval@3.0.0:
-    resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
-    engines: {node: '>=12'}
-    dev: true
+  postgres-interval@3.0.0: {}
 
-  /postgres-range@1.1.3:
-    resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==}
-    dev: true
+  postgres-range@1.1.3: {}
 
-  /prelude-ls@1.2.1:
-    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
-    engines: {node: '>= 0.8.0'}
-    dev: true
+  prelude-ls@1.2.1: {}
 
-  /prettier@3.2.5:
-    resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dev: true
+  prettier@3.2.5: {}
 
-  /pretty-bytes@5.6.0:
-    resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
-    engines: {node: '>=6'}
-    dev: true
+  pretty-bytes@5.6.0: {}
 
-  /pretty-format@27.5.1:
-    resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
-    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+  pretty-format@27.5.1:
     dependencies:
       ansi-regex: 5.0.1
       ansi-styles: 5.2.0
       react-is: 17.0.2
-    dev: true
 
-  /pretty-format@29.7.0:
-    resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+  pretty-format@29.7.0:
     dependencies:
       '@jest/schemas': 29.6.3
       ansi-styles: 5.2.0
       react-is: 18.2.0
-    dev: true
 
-  /pretty-hrtime@1.0.3:
-    resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==}
-    engines: {node: '>= 0.8'}
-    dev: true
+  pretty-hrtime@1.0.3: {}
 
-  /private-ip@2.3.3:
-    resolution: {integrity: sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw==}
+  private-ip@2.3.3:
     dependencies:
       ip-regex: 4.3.0
-      ipaddr.js: 2.1.0
+      ipaddr.js: 2.2.0
       is-ip: 3.1.0
       netmask: 2.0.2
 
-  /probe-image-size@7.2.3:
-    resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==}
+  probe-image-size@7.2.3:
     dependencies:
       lodash.merge: 4.6.2
       needle: 2.9.1
       stream-parser: 0.3.1
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /proc-log@3.0.0:
-    resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
-    dev: false
+  proc-log@3.0.0: {}
 
-  /process-exists@5.0.0:
-    resolution: {integrity: sha512-6QPRh5fyHD8MaXr4GYML8K/YY0Sq5dKHGIOrAKS3cYpHQdmygFCcijIu1dVoNKAZ0TWAMoeh8KDK9dF8auBkJA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  process-exists@5.0.0:
     dependencies:
       ps-list: 8.1.1
-    dev: true
 
-  /process-nextick-args@2.0.1:
-    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+  process-nextick-args@2.0.1: {}
 
-  /process-warning@2.2.0:
-    resolution: {integrity: sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==}
-    dev: false
+  process-warning@2.2.0: {}
 
-  /process-warning@3.0.0:
-    resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==}
-    dev: false
+  process-warning@3.0.0: {}
 
-  /process@0.11.10:
-    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
-    engines: {node: '>= 0.6.0'}
+  process@0.11.10: {}
 
-  /progress@2.0.3:
-    resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
-    engines: {node: '>=0.4.0'}
-    requiresBuild: true
-    dev: false
+  progress@2.0.3:
     optional: true
 
-  /promise-limit@2.7.0:
-    resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==}
-    dev: false
+  promise-limit@2.7.0: {}
 
-  /promise-polyfill@8.3.0:
-    resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==}
-    dev: true
+  promise-polyfill@8.3.0: {}
 
-  /promise-retry@2.0.1:
-    resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==}
-    engines: {node: '>=10'}
+  promise-retry@2.0.1:
     dependencies:
       err-code: 2.0.3
       retry: 0.12.0
-    dev: false
 
-  /promise@7.3.1:
-    resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==}
+  promise@7.3.1:
     dependencies:
       asap: 2.0.6
 
-  /prompts@2.4.2:
-    resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
-    engines: {node: '>= 6'}
+  prompts@2.4.2:
     dependencies:
       kleur: 3.0.3
       sisteransi: 1.0.5
-    dev: true
 
-  /prop-types@15.8.1:
-    resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+  prop-types@15.8.1:
     dependencies:
       loose-envify: 1.4.0
       object-assign: 4.1.1
       react-is: 16.13.1
-    dev: true
 
-  /proto-list@1.2.4:
-    resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
-    dev: true
+  proto-list@1.2.4: {}
 
-  /proxy-addr@2.0.7:
-    resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
-    engines: {node: '>= 0.10'}
+  proxy-addr@2.0.7:
     dependencies:
       forwarded: 0.2.0
       ipaddr.js: 1.9.1
 
-  /proxy-from-env@1.0.0:
-    resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==}
-    dev: true
+  proxy-from-env@1.0.0: {}
 
-  /proxy-from-env@1.1.0:
-    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
-    dev: true
+  proxy-from-env@1.1.0: {}
 
-  /ps-list@8.1.1:
-    resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dev: true
+  ps-list@8.1.1: {}
 
-  /ps-tree@1.2.0:
-    resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==}
-    engines: {node: '>= 0.10'}
-    hasBin: true
+  ps-tree@1.2.0:
     dependencies:
       event-stream: 3.3.4
-    dev: true
 
-  /pseudomap@1.0.2:
-    resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
-    dev: false
+  pseudomap@1.0.2: {}
 
-  /psl@1.9.0:
-    resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
+  psl@1.9.0: {}
 
-  /pstree.remy@1.1.8:
-    resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
-    dev: true
+  pstree.remy@1.1.8: {}
 
-  /pug-attrs@3.0.0:
-    resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==}
+  pug-attrs@3.0.0:
     dependencies:
       constantinople: 4.0.1
       js-stringify: 1.0.2
       pug-runtime: 3.0.1
 
-  /pug-code-gen@3.0.2:
-    resolution: {integrity: sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==}
+  pug-code-gen@3.0.2:
     dependencies:
       constantinople: 4.0.1
       doctypes: 1.1.0
@@ -16737,11 +21518,9 @@ packages:
       void-elements: 3.1.0
       with: 7.0.2
 
-  /pug-error@2.0.0:
-    resolution: {integrity: sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==}
+  pug-error@2.0.0: {}
 
-  /pug-filters@4.0.0:
-    resolution: {integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==}
+  pug-filters@4.0.0:
     dependencies:
       constantinople: 4.0.1
       jstransformer: 1.0.0
@@ -16749,44 +21528,36 @@ packages:
       pug-walk: 2.0.0
       resolve: 1.22.8
 
-  /pug-lexer@5.0.1:
-    resolution: {integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==}
+  pug-lexer@5.0.1:
     dependencies:
       character-parser: 2.2.0
       is-expression: 4.0.0
       pug-error: 2.0.0
 
-  /pug-linker@4.0.0:
-    resolution: {integrity: sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==}
+  pug-linker@4.0.0:
     dependencies:
       pug-error: 2.0.0
       pug-walk: 2.0.0
 
-  /pug-load@3.0.0:
-    resolution: {integrity: sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==}
+  pug-load@3.0.0:
     dependencies:
       object-assign: 4.1.1
       pug-walk: 2.0.0
 
-  /pug-parser@6.0.0:
-    resolution: {integrity: sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==}
+  pug-parser@6.0.0:
     dependencies:
       pug-error: 2.0.0
       token-stream: 1.0.0
 
-  /pug-runtime@3.0.1:
-    resolution: {integrity: sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==}
+  pug-runtime@3.0.1: {}
 
-  /pug-strip-comments@2.0.0:
-    resolution: {integrity: sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==}
+  pug-strip-comments@2.0.0:
     dependencies:
       pug-error: 2.0.0
 
-  /pug-walk@2.0.0:
-    resolution: {integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==}
+  pug-walk@2.0.0: {}
 
-  /pug@3.0.2:
-    resolution: {integrity: sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==}
+  pug@3.0.2:
     dependencies:
       pug-code-gen: 3.0.2
       pug-filters: 4.0.0
@@ -16797,194 +21568,113 @@ packages:
       pug-runtime: 3.0.1
       pug-strip-comments: 2.0.0
 
-  /pump@2.0.1:
-    resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
+  pump@2.0.1:
     dependencies:
       end-of-stream: 1.4.4
       once: 1.4.0
-    dev: true
 
-  /pump@3.0.0:
-    resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
+  pump@3.0.0:
     dependencies:
       end-of-stream: 1.4.4
       once: 1.4.0
 
-  /pumpify@1.5.1:
-    resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
+  pumpify@1.5.1:
     dependencies:
       duplexify: 3.7.1
       inherits: 2.0.4
       pump: 2.0.1
-    dev: true
-
-  /punycode@2.3.1:
-    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
-    engines: {node: '>=6'}
 
-  /pure-rand@6.0.0:
-    resolution: {integrity: sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==}
-    dev: true
+  punycode@2.3.1: {}
 
-  /pureimage@0.3.17:
-    resolution: {integrity: sha512-JV4hfYF1BXxDwbSR8hjhVEhVTxwmAXos8uIXQ7Bw2eWrUEpLDJnQoQ8WLlWAO4TMGJ7mp9n6gvLKJ6MSaGUkXQ==}
-    engines: {node: '>=0.8'}
-    dependencies:
-      jpeg-js: 0.4.4
-      opentype.js: 0.4.11
-      pngjs: 3.4.0
-    dev: false
+  pure-rand@6.0.0: {}
 
-  /pvtsutils@1.3.5:
-    resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==}
+  pvtsutils@1.3.5:
     dependencies:
       tslib: 2.6.2
-    dev: false
 
-  /pvutils@1.1.3:
-    resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
-    engines: {node: '>=6.0.0'}
-    dev: false
+  pvutils@1.1.3: {}
 
-  /qrcode@1.5.3:
-    resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
-    engines: {node: '>=10.13.0'}
-    hasBin: true
+  qrcode@1.5.3:
     dependencies:
       dijkstrajs: 1.0.2
       encode-utf8: 1.0.3
       pngjs: 5.0.0
       yargs: 15.4.1
-    dev: false
 
-  /qs@6.10.4:
-    resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==}
-    engines: {node: '>=0.6'}
+  qs@6.10.4:
     dependencies:
       side-channel: 1.0.4
-    dev: true
 
-  /qs@6.11.0:
-    resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
-    engines: {node: '>=0.6'}
+  qs@6.11.0:
     dependencies:
       side-channel: 1.0.4
 
-  /qs@6.11.1:
-    resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==}
-    engines: {node: '>=0.6'}
+  qs@6.11.1:
     dependencies:
       side-channel: 1.0.4
-    dev: true
 
-  /qs@6.5.3:
-    resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
-    engines: {node: '>=0.6'}
-    dev: false
+  qs@6.5.3: {}
 
-  /querystringify@2.2.0:
-    resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+  querystringify@2.2.0: {}
 
-  /queue-lit@1.5.0:
-    resolution: {integrity: sha512-IslToJ4eiCEE9xwMzq3viOO5nH8sUWUCwoElrhNMozzr9IIt2qqvB4I+uHu/zJTQVqc9R5DFwok4ijNK1pU3fA==}
-    dev: false
+  queue-lit@1.5.0: {}
 
-  /queue-microtask@1.2.3:
-    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+  queue-microtask@1.2.3: {}
 
-  /queue-tick@1.0.1:
-    resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
-    dev: false
+  queue-tick@1.0.1: {}
 
-  /quick-format-unescaped@4.0.4:
-    resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
-    dev: false
+  quick-format-unescaped@4.0.4: {}
 
-  /quick-lru@4.0.1:
-    resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==}
-    engines: {node: '>=8'}
-    dev: true
+  quick-lru@4.0.1: {}
 
-  /quick-lru@5.1.1:
-    resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
-    engines: {node: '>=10'}
+  quick-lru@5.1.1: {}
 
-  /ramda@0.29.0:
-    resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==}
-    dev: true
+  ramda@0.29.0: {}
 
-  /random-seed@0.3.0:
-    resolution: {integrity: sha512-y13xtn3kcTlLub3HKWXxJNeC2qK4mB59evwZ5EkeRlolx+Bp2ztF7LbcZmyCnOqlHQrLnfuNbi1sVmm9lPDlDA==}
-    engines: {node: '>= 0.6.0'}
+  random-seed@0.3.0:
     dependencies:
       json-stringify-safe: 5.0.1
-    dev: false
 
-  /range-parser@1.2.1:
-    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
-    engines: {node: '>= 0.6'}
+  range-parser@1.2.1: {}
 
-  /ratelimiter@3.4.1:
-    resolution: {integrity: sha512-5FJbRW/Jkkdk29ksedAfWFkQkhbUrMx3QJGwMKAypeIiQf4yrLW+gtPKZiaWt4zPrtw1uGufOjGO7UGM6VllsQ==}
-    dev: false
+  ratelimiter@3.4.1: {}
 
-  /raw-body@2.5.1:
-    resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
-    engines: {node: '>= 0.8'}
+  raw-body@2.5.1:
     dependencies:
       bytes: 3.1.2
       http-errors: 2.0.0
       iconv-lite: 0.4.24
       unpipe: 1.0.0
 
-  /raw-body@2.5.2:
-    resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
-    engines: {node: '>= 0.8'}
+  raw-body@2.5.2:
     dependencies:
       bytes: 3.1.2
       http-errors: 2.0.0
       iconv-lite: 0.4.24
       unpipe: 1.0.0
 
-  /rdf-canonize@3.4.0:
-    resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==}
-    engines: {node: '>=12'}
+  rdf-canonize@3.4.0:
     dependencies:
       setimmediate: 1.0.5
-    dev: false
 
-  /re2@1.20.9:
-    resolution: {integrity: sha512-ZYcPTFr5ha2xq3WQjBDTF9CWPSDK1z28MLh5UFRxc//7X8BNQ3A7yR7ITnP0jO346661ertdKVFqw1qoL3FMEQ==}
-    requiresBuild: true
+  re2@1.20.10:
     dependencies:
       install-artifact-from-github: 1.3.5
       nan: 2.18.0
       node-gyp: 10.0.1
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /react-colorful@5.6.1(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
-    peerDependencies:
-      react: '>=16.8.0'
-      react-dom: '>=16.8.0'
+  react-colorful@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
 
-  /react-docgen-typescript@2.2.2(typescript@5.3.3):
-    resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
-    peerDependencies:
-      typescript: '>= 4.3.x'
+  react-docgen-typescript@2.2.2(typescript@5.4.5):
     dependencies:
-      typescript: 5.3.3
-    dev: true
+      typescript: 5.4.5
 
-  /react-docgen@7.0.1:
-    resolution: {integrity: sha512-rCz0HBIT0LWbIM+///LfRrJoTKftIzzwsYDf0ns5KwaEjejMHQRtphcns+IXFHDNY9pnz6G8l/JbbI6pD4EAIA==}
-    engines: {node: '>=16.14.0'}
+  react-docgen@7.0.1:
     dependencies:
       '@babel/core': 7.24.0
       '@babel/traverse': 7.24.0
@@ -16998,84 +21688,54 @@ packages:
       strip-indent: 4.0.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /react-dom@18.2.0(react@18.2.0):
-    resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
-    peerDependencies:
-      react: ^18.2.0
+  react-dom@18.3.1(react@18.3.1):
     dependencies:
       loose-envify: 1.4.0
-      react: 18.2.0
-      scheduler: 0.23.0
-    dev: true
+      react: 18.3.1
+      scheduler: 0.23.2
 
-  /react-element-to-jsx-string@15.0.0(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==}
-    peerDependencies:
-      react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0
-      react-dom: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0
+  react-element-to-jsx-string@15.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
       '@base2/pretty-print-object': 1.0.1
       is-plain-object: 5.0.0
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
       react-is: 18.1.0
-    dev: true
 
-  /react-is@16.13.1:
-    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
-    dev: true
+  react-is@16.13.1: {}
 
-  /react-is@17.0.2:
-    resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
-    dev: true
+  react-is@17.0.2: {}
 
-  /react-is@18.1.0:
-    resolution: {integrity: sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==}
-    dev: true
+  react-is@18.1.0: {}
 
-  /react-is@18.2.0:
-    resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
-    dev: true
+  react-is@18.2.0: {}
 
-  /react@18.2.0:
-    resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
-    engines: {node: '>=0.10.0'}
+  react@18.3.1:
     dependencies:
       loose-envify: 1.4.0
-    dev: true
 
-  /read-pkg-up@7.0.1:
-    resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
-    engines: {node: '>=8'}
+  read-pkg-up@7.0.1:
     dependencies:
       find-up: 4.1.0
       read-pkg: 5.2.0
       type-fest: 0.8.1
-    dev: true
 
-  /read-pkg@5.2.0:
-    resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
-    engines: {node: '>=8'}
+  read-pkg@5.2.0:
     dependencies:
       '@types/normalize-package-data': 2.4.1
       normalize-package-data: 2.5.0
       parse-json: 5.2.0
       type-fest: 0.6.0
-    dev: true
 
-  /readable-stream@1.1.14:
-    resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==}
+  readable-stream@1.1.14:
     dependencies:
       core-util-is: 1.0.3
       inherits: 2.0.4
       isarray: 0.0.1
       string_decoder: 0.10.31
-    dev: false
 
-  /readable-stream@2.3.7:
-    resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
+  readable-stream@2.3.7:
     dependencies:
       core-util-is: 1.0.3
       inherits: 2.0.4
@@ -17085,145 +21745,91 @@ packages:
       string_decoder: 1.1.1
       util-deprecate: 1.0.2
 
-  /readable-stream@3.6.0:
-    resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
-    engines: {node: '>= 6'}
+  readable-stream@3.6.0:
     dependencies:
       inherits: 2.0.4
       string_decoder: 1.3.0
       util-deprecate: 1.0.2
 
-  /readable-stream@4.3.0:
-    resolution: {integrity: sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  readable-stream@4.3.0:
     dependencies:
       abort-controller: 3.0.0
       buffer: 6.0.3
       events: 3.3.0
       process: 0.11.10
-    dev: false
 
-  /readable-web-to-node-stream@3.0.2:
-    resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==}
-    engines: {node: '>=8'}
+  readable-web-to-node-stream@3.0.2:
     dependencies:
       readable-stream: 3.6.0
-    dev: false
 
-  /readdir-glob@1.1.2:
-    resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==}
+  readdir-glob@1.1.2:
     dependencies:
       minimatch: 5.1.2
-    dev: false
 
-  /readdirp@3.6.0:
-    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
-    engines: {node: '>=8.10.0'}
+  readdirp@3.6.0:
     dependencies:
       picomatch: 2.3.1
 
-  /real-require@0.2.0:
-    resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
-    engines: {node: '>= 12.13.0'}
-    dev: false
+  real-require@0.2.0: {}
 
-  /recast@0.23.4:
-    resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==}
-    engines: {node: '>= 4'}
+  recast@0.23.4:
     dependencies:
       assert: 2.1.0
       ast-types: 0.16.1
       esprima: 4.0.1
       source-map: 0.6.1
       tslib: 2.6.2
-    dev: true
 
-  /recast@0.23.6:
-    resolution: {integrity: sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==}
-    engines: {node: '>= 4'}
+  recast@0.23.6:
     dependencies:
       ast-types: 0.16.1
       esprima: 4.0.1
       source-map: 0.6.1
       tiny-invariant: 1.3.3
       tslib: 2.6.2
-    dev: true
 
-  /reconnecting-websocket@4.4.0:
-    resolution: {integrity: sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==}
-    dev: false
+  reconnecting-websocket@4.4.0: {}
 
-  /redent@3.0.0:
-    resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
-    engines: {node: '>=8'}
+  redent@3.0.0:
     dependencies:
       indent-string: 4.0.0
       strip-indent: 3.0.0
-    dev: true
 
-  /redis-errors@1.2.0:
-    resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
-    engines: {node: '>=4'}
-    dev: false
+  redis-errors@1.2.0: {}
 
-  /redis-info@3.1.0:
-    resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==}
+  redis-info@3.1.0:
     dependencies:
       lodash: 4.17.21
-    dev: false
 
-  /redis-lock@0.1.4:
-    resolution: {integrity: sha512-7/+zu86XVQfJVx1nHTzux5reglDiyUCDwmW7TSlvVezfhH2YLc/Rc8NE0ejQG+8/0lwKzm29/u/4+ogKeLosiA==}
-    engines: {node: '>=0.6'}
-    dev: false
+  redis-lock@0.1.4: {}
 
-  /redis-parser@3.0.0:
-    resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
-    engines: {node: '>=4'}
+  redis-parser@3.0.0:
     dependencies:
       redis-errors: 1.2.0
-    dev: false
 
-  /reflect-metadata@0.2.1:
-    resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==}
+  reflect-metadata@0.2.2: {}
 
-  /regenerate-unicode-properties@10.1.0:
-    resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
-    engines: {node: '>=4'}
+  regenerate-unicode-properties@10.1.0:
     dependencies:
       regenerate: 1.4.2
-    dev: true
 
-  /regenerate@1.4.2:
-    resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
-    dev: true
+  regenerate@1.4.2: {}
 
-  /regenerator-runtime@0.13.11:
-    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
-    requiresBuild: true
-    dev: false
+  regenerator-runtime@0.13.11: {}
 
-  /regenerator-runtime@0.14.0:
-    resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
+  regenerator-runtime@0.14.0: {}
 
-  /regenerator-transform@0.15.2:
-    resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
+  regenerator-transform@0.15.2:
     dependencies:
       '@babel/runtime': 7.23.4
-    dev: true
 
-  /regexp.prototype.flags@1.5.0:
-    resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
-    engines: {node: '>= 0.4'}
+  regexp.prototype.flags@1.5.0:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       functions-have-names: 1.2.3
-    dev: true
 
-  /regexpu-core@5.3.2:
-    resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
-    engines: {node: '>=4'}
+  regexpu-core@5.3.2:
     dependencies:
       '@babel/regjsgen': 0.8.0
       regenerate: 1.4.2
@@ -17231,17 +21837,12 @@ packages:
       regjsparser: 0.9.1
       unicode-match-property-ecmascript: 2.0.0
       unicode-match-property-value-ecmascript: 2.1.0
-    dev: true
 
-  /regjsparser@0.9.1:
-    resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
-    hasBin: true
+  regjsparser@0.9.1:
     dependencies:
       jsesc: 0.5.0
-    dev: true
 
-  /rehype-external-links@3.0.0:
-    resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==}
+  rehype-external-links@3.0.0:
     dependencies:
       '@types/hast': 3.0.4
       '@ungap/structured-clone': 1.2.0
@@ -17249,20 +21850,16 @@ packages:
       is-absolute-url: 4.0.1
       space-separated-tokens: 2.0.2
       unist-util-visit: 5.0.0
-    dev: true
 
-  /rehype-slug@6.0.0:
-    resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==}
+  rehype-slug@6.0.0:
     dependencies:
       '@types/hast': 3.0.4
       github-slugger: 2.0.0
       hast-util-heading-rank: 3.0.0
       hast-util-to-string: 3.0.0
       unist-util-visit: 5.0.0
-    dev: true
 
-  /remark-gfm@4.0.0:
-    resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==}
+  remark-gfm@4.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       mdast-util-gfm: 3.0.0
@@ -17272,10 +21869,8 @@ packages:
       unified: 11.0.4
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /remark-parse@11.0.0:
-    resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
+  remark-parse@11.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       mdast-util-from-markdown: 2.0.0
@@ -17283,34 +21878,24 @@ packages:
       unified: 11.0.4
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /remark-stringify@11.0.0:
-    resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
+  remark-stringify@11.0.0:
     dependencies:
       '@types/mdast': 4.0.3
       mdast-util-to-markdown: 2.1.0
       unified: 11.0.4
-    dev: true
 
-  /rename@1.0.4:
-    resolution: {integrity: sha512-YMM6Fn3lrFOCjhORKjj+z/yizj8WSzv3F3YUlpJA20fteWCb0HbJU19nvuRBPUM5dWgxJcHP+kix3M+5NowJyA==}
+  rename@1.0.4:
     dependencies:
       debug: 2.6.9
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /request-progress@3.0.0:
-    resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==}
+  request-progress@3.0.0:
     dependencies:
       throttleit: 1.0.0
-    dev: true
 
-  /request@2.88.2:
-    resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
-    engines: {node: '>= 6'}
-    deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
+  request@2.88.2:
     dependencies:
       aws-sign2: 0.7.0
       aws4: 1.12.0
@@ -17332,302 +21917,184 @@ packages:
       tough-cookie: 2.5.0
       tunnel-agent: 0.6.0
       uuid: 3.4.0
-    dev: false
 
-  /require-directory@2.1.1:
-    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
-    engines: {node: '>=0.10.0'}
+  require-directory@2.1.1: {}
 
-  /require-from-string@2.0.2:
-    resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
-    engines: {node: '>=0.10.0'}
+  require-from-string@2.0.2: {}
 
-  /require-main-filename@2.0.0:
-    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
-    dev: false
+  require-main-filename@2.0.0: {}
 
-  /requires-port@1.0.0:
-    resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+  requires-port@1.0.0: {}
 
-  /resolve-alpn@1.2.1:
-    resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
+  resolve-alpn@1.2.1: {}
 
-  /resolve-cwd@3.0.0:
-    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
-    engines: {node: '>=8'}
+  resolve-cwd@3.0.0:
     dependencies:
       resolve-from: 5.0.0
-    dev: true
 
-  /resolve-from@4.0.0:
-    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
-    engines: {node: '>=4'}
-    dev: true
+  resolve-from@4.0.0: {}
 
-  /resolve-from@5.0.0:
-    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
-    engines: {node: '>=8'}
-    dev: true
+  resolve-from@5.0.0: {}
 
-  /resolve-pkg-maps@1.0.0:
-    resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
-    dev: true
+  resolve-pkg-maps@1.0.0: {}
 
-  /resolve.exports@2.0.0:
-    resolution: {integrity: sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==}
-    engines: {node: '>=10'}
-    dev: true
+  resolve.exports@2.0.0: {}
 
-  /resolve@1.19.0:
-    resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==}
+  resolve@1.19.0:
     dependencies:
       is-core-module: 2.13.1
       path-parse: 1.0.7
-    dev: true
 
-  /resolve@1.22.8:
-    resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
-    hasBin: true
+  resolve@1.22.8:
     dependencies:
       is-core-module: 2.13.1
       path-parse: 1.0.7
       supports-preserve-symlinks-flag: 1.0.0
 
-  /responselike@2.0.1:
-    resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+  responselike@2.0.1:
     dependencies:
       lowercase-keys: 2.0.0
-    dev: false
 
-  /responselike@3.0.0:
-    resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==}
-    engines: {node: '>=14.16'}
+  responselike@3.0.0:
     dependencies:
       lowercase-keys: 3.0.0
 
-  /restore-cursor@3.1.0:
-    resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
-    engines: {node: '>=8'}
+  restore-cursor@3.1.0:
     dependencies:
       onetime: 5.1.2
       signal-exit: 3.0.7
-    dev: true
 
-  /ret@0.2.2:
-    resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==}
-    engines: {node: '>=4'}
-    dev: false
+  ret@0.4.3: {}
 
-  /retry@0.12.0:
-    resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
-    engines: {node: '>= 4'}
-    dev: false
+  retry@0.12.0: {}
 
-  /reusify@1.0.4:
-    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
-    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+  reusify@1.0.4: {}
 
-  /rfdc@1.3.0:
-    resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
+  rfdc@1.3.0: {}
 
-  /rimraf@2.6.3:
-    resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
-    hasBin: true
+  rimraf@2.6.3:
     dependencies:
       glob: 7.2.3
-    dev: true
 
-  /rimraf@2.7.1:
-    resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
-    hasBin: true
-    requiresBuild: true
+  rimraf@2.7.1:
     dependencies:
       glob: 7.2.3
-    dev: false
     optional: true
 
-  /rimraf@3.0.2:
-    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
-    hasBin: true
+  rimraf@3.0.2:
     dependencies:
       glob: 7.2.3
 
-  /rimraf@5.0.5:
-    resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dependencies:
-      glob: 10.3.10
-
-  /rollup@4.12.0:
-    resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
-    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
-    hasBin: true
+  rollup@4.17.2:
     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.17.2
+      '@rollup/rollup-android-arm64': 4.17.2
+      '@rollup/rollup-darwin-arm64': 4.17.2
+      '@rollup/rollup-darwin-x64': 4.17.2
+      '@rollup/rollup-linux-arm-gnueabihf': 4.17.2
+      '@rollup/rollup-linux-arm-musleabihf': 4.17.2
+      '@rollup/rollup-linux-arm64-gnu': 4.17.2
+      '@rollup/rollup-linux-arm64-musl': 4.17.2
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2
+      '@rollup/rollup-linux-riscv64-gnu': 4.17.2
+      '@rollup/rollup-linux-s390x-gnu': 4.17.2
+      '@rollup/rollup-linux-x64-gnu': 4.17.2
+      '@rollup/rollup-linux-x64-musl': 4.17.2
+      '@rollup/rollup-win32-arm64-msvc': 4.17.2
+      '@rollup/rollup-win32-ia32-msvc': 4.17.2
+      '@rollup/rollup-win32-x64-msvc': 4.17.2
       fsevents: 2.3.3
 
-  /rrweb-cssom@0.6.0:
-    resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
-    dev: false
+  rrweb-cssom@0.6.0: {}
 
-  /rss-parser@3.13.0:
-    resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==}
+  rss-parser@3.13.0:
     dependencies:
       entities: 2.2.0
       xml2js: 0.5.0
-    dev: false
-
-  /run-async@2.4.1:
-    resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
-    engines: {node: '>=0.12.0'}
-    dev: true
 
-  /run-parallel@1.2.0:
-    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+  run-parallel@1.2.0:
     dependencies:
       queue-microtask: 1.2.3
 
-  /rxjs@7.8.1:
-    resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
+  rxjs@7.8.1:
     dependencies:
       tslib: 2.6.2
 
-  /safe-array-concat@1.0.0:
-    resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==}
-    engines: {node: '>=0.4'}
+  safe-array-concat@1.0.0:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
       has-symbols: 1.0.3
       isarray: 2.0.5
-    dev: true
 
-  /safe-buffer@5.1.2:
-    resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+  safe-buffer@5.1.2: {}
 
-  /safe-buffer@5.2.1:
-    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+  safe-buffer@5.2.1: {}
 
-  /safe-regex-test@1.0.0:
-    resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
+  safe-regex-test@1.0.0:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
       is-regex: 1.1.4
-    dev: true
 
-  /safe-regex2@2.0.0:
-    resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==}
+  safe-regex2@3.1.0:
     dependencies:
-      ret: 0.2.2
-    dev: false
+      ret: 0.4.3
 
-  /safe-stable-stringify@2.4.2:
-    resolution: {integrity: sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==}
-    engines: {node: '>=10'}
-    dev: false
+  safe-stable-stringify@2.4.2: {}
 
-  /safer-buffer@2.1.2:
-    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+  safer-buffer@2.1.2: {}
 
-  /sanitize-html@2.12.1:
-    resolution: {integrity: sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==}
+  sanitize-html@2.13.0:
     dependencies:
       deepmerge: 4.2.2
       escape-string-regexp: 4.0.0
       htmlparser2: 8.0.1
       is-plain-object: 5.0.0
       parse-srcset: 1.0.2
-      postcss: 8.4.35
-    dev: false
+      postcss: 8.4.38
 
-  /sass@1.71.1:
-    resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==}
-    engines: {node: '>=14.0.0'}
-    hasBin: true
+  sass@1.76.0:
     dependencies:
       chokidar: 3.5.3
       immutable: 4.2.2
-      source-map-js: 1.0.2
+      source-map-js: 1.2.0
 
-  /sax@1.2.4:
-    resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
-    dev: false
+  sax@1.2.4: {}
 
-  /saxes@6.0.0:
-    resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
-    engines: {node: '>=v12.22.7'}
+  saxes@6.0.0:
     dependencies:
       xmlchars: 2.2.0
-    dev: false
 
-  /scheduler@0.23.0:
-    resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
+  scheduler@0.23.2:
     dependencies:
       loose-envify: 1.4.0
-    dev: true
 
-  /secure-json-parse@2.7.0:
-    resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
-    dev: false
+  secure-json-parse@2.7.0: {}
 
-  /seedrandom@3.0.5:
-    resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==}
-    dev: false
+  seedrandom@3.0.5: {}
 
-  /semver-regex@4.0.5:
-    resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==}
-    engines: {node: '>=12'}
-    dev: false
+  semver-regex@4.0.5: {}
 
-  /semver-truncate@2.0.0:
-    resolution: {integrity: sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==}
-    engines: {node: '>=8'}
+  semver-truncate@2.0.0:
     dependencies:
       semver: 6.3.1
-    dev: false
 
-  /semver@5.7.1:
-    resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
-    hasBin: true
-    dev: true
+  semver@5.7.1: {}
 
-  /semver@6.3.1:
-    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
-    hasBin: true
+  semver@6.3.1: {}
 
-  /semver@7.5.4:
-    resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
-    engines: {node: '>=10'}
-    hasBin: true
+  semver@7.5.4:
     dependencies:
       lru-cache: 6.0.0
 
-  /semver@7.6.0:
-    resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
-    engines: {node: '>=10'}
-    hasBin: true
+  semver@7.6.0:
     dependencies:
       lru-cache: 6.0.0
 
-  /send@0.18.0:
-    resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
-    engines: {node: '>= 0.8.0'}
+  send@0.18.0:
     dependencies:
       debug: 2.6.9
       depd: 2.0.0
@@ -17645,9 +22112,7 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /serve-static@1.15.0:
-    resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
-    engines: {node: '>= 0.8.0'}
+  serve-static@1.15.0:
     dependencies:
       encodeurl: 1.0.2
       escape-html: 1.0.3
@@ -17656,114 +22121,78 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /set-blocking@2.0.0:
-    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
-    dev: false
+  set-blocking@2.0.0: {}
 
-  /set-cookie-parser@2.6.0:
-    resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
-    dev: false
+  set-cookie-parser@2.6.0: {}
 
-  /setimmediate@1.0.5:
-    resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
-    dev: false
+  setimmediate@1.0.5: {}
 
-  /setprototypeof@1.2.0:
-    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+  setprototypeof@1.2.0: {}
 
-  /sha.js@2.4.11:
-    resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
-    hasBin: true
+  sha.js@2.4.11:
     dependencies:
       inherits: 2.0.4
       safe-buffer: 5.2.1
-    dev: false
 
-  /shallow-clone@3.0.1:
-    resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
-    engines: {node: '>=8'}
+  shallow-clone@3.0.1:
     dependencies:
       kind-of: 6.0.3
-    dev: true
 
-  /sharp@0.33.2:
-    resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==}
-    engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0}
-    requiresBuild: true
+  sharp@0.33.3:
     dependencies:
       color: 4.2.3
-      detect-libc: 2.0.2
-      semver: 7.5.4
+      detect-libc: 2.0.3
+      semver: 7.6.0
     optionalDependencies:
-      '@img/sharp-darwin-arm64': 0.33.2
-      '@img/sharp-darwin-x64': 0.33.2
-      '@img/sharp-libvips-darwin-arm64': 1.0.1
-      '@img/sharp-libvips-darwin-x64': 1.0.1
-      '@img/sharp-libvips-linux-arm': 1.0.1
-      '@img/sharp-libvips-linux-arm64': 1.0.1
-      '@img/sharp-libvips-linux-s390x': 1.0.1
-      '@img/sharp-libvips-linux-x64': 1.0.1
-      '@img/sharp-libvips-linuxmusl-arm64': 1.0.1
-      '@img/sharp-libvips-linuxmusl-x64': 1.0.1
-      '@img/sharp-linux-arm': 0.33.2
-      '@img/sharp-linux-arm64': 0.33.2
-      '@img/sharp-linux-s390x': 0.33.2
-      '@img/sharp-linux-x64': 0.33.2
-      '@img/sharp-linuxmusl-arm64': 0.33.2
-      '@img/sharp-linuxmusl-x64': 0.33.2
-      '@img/sharp-wasm32': 0.33.2
-      '@img/sharp-win32-ia32': 0.33.2
-      '@img/sharp-win32-x64': 0.33.2
-    dev: false
-
-  /shebang-command@1.2.0:
-    resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
-    engines: {node: '>=0.10.0'}
+      '@img/sharp-darwin-arm64': 0.33.3
+      '@img/sharp-darwin-x64': 0.33.3
+      '@img/sharp-libvips-darwin-arm64': 1.0.2
+      '@img/sharp-libvips-darwin-x64': 1.0.2
+      '@img/sharp-libvips-linux-arm': 1.0.2
+      '@img/sharp-libvips-linux-arm64': 1.0.2
+      '@img/sharp-libvips-linux-s390x': 1.0.2
+      '@img/sharp-libvips-linux-x64': 1.0.2
+      '@img/sharp-libvips-linuxmusl-arm64': 1.0.2
+      '@img/sharp-libvips-linuxmusl-x64': 1.0.2
+      '@img/sharp-linux-arm': 0.33.3
+      '@img/sharp-linux-arm64': 0.33.3
+      '@img/sharp-linux-s390x': 0.33.3
+      '@img/sharp-linux-x64': 0.33.3
+      '@img/sharp-linuxmusl-arm64': 0.33.3
+      '@img/sharp-linuxmusl-x64': 0.33.3
+      '@img/sharp-wasm32': 0.33.3
+      '@img/sharp-win32-ia32': 0.33.3
+      '@img/sharp-win32-x64': 0.33.3
+
+  shebang-command@1.2.0:
     dependencies:
       shebang-regex: 1.0.0
-    dev: false
 
-  /shebang-command@2.0.0:
-    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
-    engines: {node: '>=8'}
+  shebang-command@2.0.0:
     dependencies:
       shebang-regex: 3.0.0
 
-  /shebang-regex@1.0.0:
-    resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
-    engines: {node: '>=0.10.0'}
-    dev: false
+  shebang-regex@1.0.0: {}
 
-  /shebang-regex@3.0.0:
-    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
-    engines: {node: '>=8'}
+  shebang-regex@3.0.0: {}
 
-  /shiki@1.2.0:
-    resolution: {integrity: sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==}
+  shiki@1.4.0:
     dependencies:
-      '@shikijs/core': 1.2.0
-    dev: false
+      '@shikijs/core': 1.4.0
 
-  /side-channel@1.0.4:
-    resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+  side-channel@1.0.4:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
       object-inspect: 1.12.3
 
-  /siginfo@2.0.0:
-    resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
-    dev: true
+  siginfo@2.0.0: {}
 
-  /signal-exit@3.0.7:
-    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+  signal-exit@3.0.7: {}
 
-  /signal-exit@4.1.0:
-    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
-    engines: {node: '>=14'}
+  signal-exit@4.1.0: {}
 
-  /simple-oauth2@5.0.0:
-    resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==}
+  simple-oauth2@5.0.0:
     dependencies:
       '@hapi/hoek': 10.0.1
       '@hapi/wreck': 18.0.1
@@ -17771,23 +22200,16 @@ packages:
       joi: 17.11.0
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /simple-swizzle@0.2.2:
-    resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+  simple-swizzle@0.2.2:
     dependencies:
       is-arrayish: 0.3.2
-    dev: false
-
-  /simple-update-notifier@2.0.0:
-    resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
-    engines: {node: '>=10'}
+
+  simple-update-notifier@2.0.0:
     dependencies:
       semver: 7.5.4
-    dev: true
 
-  /sinon@16.1.3:
-    resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==}
+  sinon@16.1.3:
     dependencies:
       '@sinonjs/commons': 3.0.0
       '@sinonjs/fake-timers': 10.3.0
@@ -17795,131 +22217,49 @@ packages:
       diff: 5.1.0
       nise: 5.1.4
       supports-color: 7.2.0
-    dev: true
 
-  /sisteransi@1.0.5:
-    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
-    dev: true
+  sisteransi@1.0.5: {}
 
-  /slacc-android-arm-eabi@0.0.10:
-    resolution: {integrity: sha512-U3dVBuM1m8rT1D/w6S4knJ/uscNwsCR+MKxSQFbgDJEh8Atv+ovuC+FMGuaBT4iOQjpMj5dWSsN3ZPjVeo3hgA==}
-    engines: {node: '>= 10'}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: false
+  slacc-android-arm-eabi@0.0.10:
     optional: true
 
-  /slacc-android-arm64@0.0.10:
-    resolution: {integrity: sha512-guVp88sW+4j1clTSXMzyDJHG8ondVnd8/FMKXIOfzKCEwSwX3uBxsuyHqtGvXkEwyZAGsBUy13Ei/PZAwElwYA==}
-    engines: {node: '>= 10'}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: false
+  slacc-android-arm64@0.0.10:
     optional: true
 
-  /slacc-darwin-arm64@0.0.10:
-    resolution: {integrity: sha512-633qnOMTP7egvd5IeljAOku0tnxlBXSoCRu7HiT0yeXxN9y5Tbg2X2/FaRzstI36lClfIJ0Lavne4mOw/90z9A==}
-    engines: {node: '>= 10'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  slacc-darwin-arm64@0.0.10:
     optional: true
 
-  /slacc-darwin-universal@0.0.10:
-    resolution: {integrity: sha512-x5kEqRMTEQTi3NCufPEukWvaWqcOL+7EkP18ZCCiajcWH83jWnT8DOSGOmmLYdrXd0B7ZZcbd8GyLp3i5zu8PA==}
-    engines: {node: '>= 10'}
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  slacc-darwin-universal@0.0.10:
     optional: true
 
-  /slacc-darwin-x64@0.0.10:
-    resolution: {integrity: sha512-5gQYboy/4T6Bj3sVXiCpM3EvF1sK/Zx1Nq5YBMUuYb2GzrIwywghHbCD6bK4JYGvNsLN7r4PC45ZUB4gVkU8yA==}
-    engines: {node: '>= 10'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: false
+  slacc-darwin-x64@0.0.10:
     optional: true
 
-  /slacc-freebsd-x64@0.0.10:
-    resolution: {integrity: sha512-Jmi5YszELef/aCzYto+LwiNGhCk5mrlJfTJU/pOI91HBbrZlV+aRyIsPCcxAMg5yPsPQuyRljrDouVYrPzNmjw==}
-    engines: {node: '>= 10'}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: false
+  slacc-freebsd-x64@0.0.10:
     optional: true
 
-  /slacc-linux-arm-gnueabihf@0.0.10:
-    resolution: {integrity: sha512-9lTM3DGtISQlZYSKrMuQyKCiUnHYRcy04mY6HF1ywYcQ2sqfv3bKEnrypVewepIFUtytlIGzkgpiUAk/ghYGoA==}
-    engines: {node: '>= 10'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  slacc-linux-arm-gnueabihf@0.0.10:
     optional: true
 
-  /slacc-linux-arm64-gnu@0.0.10:
-    resolution: {integrity: sha512-qXrNWSINXOjHRO3c9idGm8DeOAjAjG1xHY8WiplCoHWgsZf3E7V+sPhWqRUaGQEvftsJg40+cFYREBaLQhpAVQ==}
-    engines: {node: '>= 10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  slacc-linux-arm64-gnu@0.0.10:
     optional: true
 
-  /slacc-linux-arm64-musl@0.0.10:
-    resolution: {integrity: sha512-3lUX7752f6Okn54aONioaA+9M5TvifqXBAart+u2lNXEdWmmh003cVSU2Vcwg7nJ9lLHtju2DkDmKKfJjFuShA==}
-    engines: {node: '>= 10'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  slacc-linux-arm64-musl@0.0.10:
     optional: true
 
-  /slacc-linux-x64-gnu@0.0.10:
-    resolution: {integrity: sha512-BxxvylF9zlOLRLCpiyMvKTIUpdLlpetNBJ+DSMDh5+Ggq+AmQz2NUGawmcBJw58F8nMCj9TpWLlGNWc2AuY+JQ==}
-    engines: {node: '>= 10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  slacc-linux-x64-gnu@0.0.10:
     optional: true
 
-  /slacc-linux-x64-musl@0.0.10:
-    resolution: {integrity: sha512-TYJi8LOtJiTFcZvka4du7bMjF9Bz1RHRwyLnScr5E5yjjgoLRrsvgSu7bxp87xH+rgJ3CdEwE3w3Ux8EiewHpA==}
-    engines: {node: '>= 10'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: false
+  slacc-linux-x64-musl@0.0.10:
     optional: true
 
-  /slacc-win32-arm64-msvc@0.0.10:
-    resolution: {integrity: sha512-1CHPLiDB4exzFyT5ndtJDsRRhBxNg8mGz6I6eJEMjelGkJR2KZPT9LZuby/1bS/bcVOr7zuJvGNfbEGBeHRwPQ==}
-    engines: {node: '>= 10'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+  slacc-win32-arm64-msvc@0.0.10:
     optional: true
 
-  /slacc-win32-x64-msvc@0.0.10:
-    resolution: {integrity: sha512-wAXBy5yKCAzfYWjVlyPpu6PscD+j4QhCQEy0wZaVuzNyx60HpXWcTZxxVnMR730Y7tfc7cBxSI8NtRb8RguSgg==}
-    engines: {node: '>= 10'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: false
+  slacc-win32-x64-msvc@0.0.10:
     optional: true
 
-  /slacc@0.0.10:
-    resolution: {integrity: sha512-2jgms2/4mLr1AMq4oloAwPdKQK9RQvgmoEpMIxvC+HeHMwCR0XxB7gr/rKo4iLOKJ6gx02mnBU0JHWcTIonpmA==}
-    engines: {node: '>= 10'}
+  slacc@0.0.10:
     optionalDependencies:
       slacc-android-arm-eabi: 0.0.10
       slacc-android-arm64: 0.0.10
@@ -17934,153 +22274,95 @@ packages:
       slacc-linux-x64-musl: 0.0.10
       slacc-win32-arm64-msvc: 0.0.10
       slacc-win32-x64-msvc: 0.0.10
-    dev: false
 
-  /slash@3.0.0:
-    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
-    engines: {node: '>=8'}
+  slash@3.0.0: {}
 
-  /slice-ansi@3.0.0:
-    resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
-    engines: {node: '>=8'}
+  slice-ansi@3.0.0:
     dependencies:
       ansi-styles: 4.3.0
       astral-regex: 2.0.0
       is-fullwidth-code-point: 3.0.0
-    dev: true
 
-  /slice-ansi@4.0.0:
-    resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
-    engines: {node: '>=10'}
+  slice-ansi@4.0.0:
     dependencies:
       ansi-styles: 4.3.0
       astral-regex: 2.0.0
       is-fullwidth-code-point: 3.0.0
-    dev: true
 
-  /smart-buffer@4.2.0:
-    resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
-    engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
-    dev: false
+  smart-buffer@4.2.0: {}
 
-  /socks-proxy-agent@8.0.2:
-    resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==}
-    engines: {node: '>= 14'}
+  socks-proxy-agent@8.0.2:
     dependencies:
       agent-base: 7.1.0
       debug: 4.3.4(supports-color@8.1.1)
       socks: 2.7.1
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /socks@2.7.1:
-    resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==}
-    engines: {node: '>= 10.13.0', npm: '>= 3.0.0'}
+  socks@2.7.1:
     dependencies:
-      ip: 2.0.0
+      ip: 2.0.1
       smart-buffer: 4.2.0
-    dev: false
 
-  /sonic-boom@3.7.0:
-    resolution: {integrity: sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==}
+  sonic-boom@3.7.0:
     dependencies:
       atomic-sleep: 1.0.0
-    dev: false
 
-  /sort-keys-length@1.0.1:
-    resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==}
-    engines: {node: '>=0.10.0'}
+  sort-keys-length@1.0.1:
     dependencies:
       sort-keys: 1.1.2
-    dev: false
 
-  /sort-keys@1.1.2:
-    resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==}
-    engines: {node: '>=0.10.0'}
+  sort-keys@1.1.2:
     dependencies:
       is-plain-obj: 1.1.0
-    dev: false
 
-  /sortablejs@1.14.0:
-    resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
-    dev: false
+  sortablejs@1.14.0: {}
 
-  /source-map-js@1.0.2:
-    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
-    engines: {node: '>=0.10.0'}
+  source-map-js@1.0.2: {}
 
-  /source-map-support@0.5.13:
-    resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
+  source-map-js@1.2.0: {}
+
+  source-map-support@0.5.13:
     dependencies:
       buffer-from: 1.1.2
       source-map: 0.6.1
-    dev: true
 
-  /source-map-support@0.5.21:
-    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+  source-map-support@0.5.21:
     dependencies:
       buffer-from: 1.1.2
       source-map: 0.6.1
 
-  /source-map@0.6.1:
-    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
-    engines: {node: '>=0.10.0'}
+  source-map@0.6.1: {}
 
-  /source-map@0.7.4:
-    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
-    engines: {node: '>= 8'}
-    dev: false
+  source-map@0.7.4: {}
 
-  /space-separated-tokens@2.0.2:
-    resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
-    dev: true
+  space-separated-tokens@2.0.2: {}
 
-  /spdx-correct@3.1.1:
-    resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
+  spdx-correct@3.1.1:
     dependencies:
       spdx-expression-parse: 3.0.1
       spdx-license-ids: 3.0.12
-    dev: true
 
-  /spdx-exceptions@2.3.0:
-    resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
-    dev: true
+  spdx-exceptions@2.3.0: {}
 
-  /spdx-expression-parse@3.0.1:
-    resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
+  spdx-expression-parse@3.0.1:
     dependencies:
       spdx-exceptions: 2.3.0
       spdx-license-ids: 3.0.12
-    dev: true
 
-  /spdx-license-ids@3.0.12:
-    resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==}
-    dev: true
+  spdx-license-ids@3.0.12: {}
 
-  /split2@4.1.0:
-    resolution: {integrity: sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==}
-    engines: {node: '>= 10.x'}
-    dev: false
+  split2@4.1.0: {}
 
-  /split@0.3.3:
-    resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==}
+  split@0.3.3:
     dependencies:
       through: 2.3.8
-    dev: true
 
-  /sprintf-js@1.0.3:
-    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
-    requiresBuild: true
+  sprintf-js@1.0.3: {}
 
-  /sprintf-js@1.1.2:
-    resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==}
-    dev: false
+  sprintf-js@1.1.2: {}
 
-  /sshpk@1.17.0:
-    resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==}
-    engines: {node: '>=0.10.0'}
-    hasBin: true
+  sshpk@1.17.0:
     dependencies:
       asn1: 0.2.6
       assert-plus: 1.0.0
@@ -18092,32 +22374,19 @@ packages:
       safer-buffer: 2.1.2
       tweetnacl: 0.14.5
 
-  /ssri@10.0.4:
-    resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  ssri@10.0.4:
     dependencies:
       minipass: 5.0.0
-    dev: false
 
-  /stack-utils@2.0.6:
-    resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
-    engines: {node: '>=10'}
+  stack-utils@2.0.6:
     dependencies:
       escape-string-regexp: 2.0.0
-    dev: true
 
-  /stackback@0.0.2:
-    resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
-    dev: true
+  stackback@0.0.2: {}
 
-  /standard-as-callback@2.1.0:
-    resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
-    dev: false
+  standard-as-callback@2.1.0: {}
 
-  /start-server-and-test@2.0.3:
-    resolution: {integrity: sha512-QsVObjfjFZKJE6CS6bSKNwWZCKBG6975/jKRPPGFfFh+yOQglSeGXiNWjzgQNXdphcBI9nXbyso9tPfX4YAUhg==}
-    engines: {node: '>=16'}
-    hasBin: true
+  start-server-and-test@2.0.3:
     dependencies:
       arg: 5.0.2
       bluebird: 3.7.2
@@ -18129,32 +22398,33 @@ packages:
       wait-on: 7.2.0(debug@4.3.4)
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /statuses@2.0.1:
-    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
-    engines: {node: '>= 0.8'}
+  statuses@2.0.1: {}
 
-  /std-env@3.7.0:
-    resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
-    dev: true
+  std-env@3.7.0: {}
 
-  /stop-iteration-iterator@1.0.0:
-    resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
-    engines: {node: '>= 0.4'}
+  stop-iteration-iterator@1.0.0:
     dependencies:
       internal-slot: 1.0.5
-    dev: true
 
-  /store2@2.14.2:
-    resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
-    dev: true
+  store2@2.14.2: {}
 
-  /storybook@8.0.9(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-/Mvij0Br5bUwJpCvqAUZMEDIWmdRxEyllvVj8Ukw5lIWJePxfpSsz4px5jg9+R6B9tO8sQSqjg4HJvQ/pZk8Tg==}
-    hasBin: true
+  storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+    dependencies:
+      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/core-events': 8.0.9
+      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/preview-api': 8.0.9
+      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/types': 8.0.9
+    optionalDependencies:
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+
+  storybook@8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3):
     dependencies:
-      '@storybook/cli': 8.0.9(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/cli': 8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - '@babel/preset-env'
       - bufferutil
@@ -18163,258 +22433,157 @@ packages:
       - react-dom
       - supports-color
       - utf-8-validate
-    dev: true
 
-  /stream-browserify@3.0.0:
-    resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==}
+  stream-browserify@3.0.0:
     dependencies:
       inherits: 2.0.4
       readable-stream: 3.6.0
-    dev: false
 
-  /stream-combiner@0.0.4:
-    resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==}
+  stream-combiner@0.0.4:
     dependencies:
       duplexer: 0.1.2
-    dev: true
 
-  /stream-parser@0.3.1:
-    resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==}
+  stream-parser@0.3.1:
     dependencies:
       debug: 2.6.9
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /stream-shift@1.0.1:
-    resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
-    dev: true
+  stream-shift@1.0.1: {}
 
-  /stream-wormhole@1.1.0:
-    resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
-    engines: {node: '>=4.0.0'}
-    dev: false
+  stream-wormhole@1.1.0: {}
 
-  /streamsearch@1.1.0:
-    resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
-    engines: {node: '>=10.0.0'}
+  streamsearch@1.1.0: {}
 
-  /streamx@2.15.0:
-    resolution: {integrity: sha512-HcxY6ncGjjklGs1xsP1aR71INYcsXFJet5CU1CHqihQ2J5nOsbd4OjgjHO42w/4QNv9gZb3BueV+Vxok5pLEXg==}
+  streamx@2.15.0:
     dependencies:
       fast-fifo: 1.3.0
       queue-tick: 1.0.1
-    dev: false
 
-  /strict-event-emitter-types@2.0.0:
-    resolution: {integrity: sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==}
-    dev: false
+  strict-event-emitter-types@2.0.0: {}
 
-  /strict-event-emitter@0.5.1:
-    resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==}
-    dev: true
+  strict-event-emitter@0.5.1: {}
 
-  /string-argv@0.3.1:
-    resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
-    engines: {node: '>=0.6.19'}
-    dev: true
+  string-argv@0.3.1: {}
 
-  /string-length@4.0.2:
-    resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
-    engines: {node: '>=10'}
+  string-length@4.0.2:
     dependencies:
       char-regex: 1.0.2
       strip-ansi: 6.0.1
-    dev: true
 
-  /string-width@4.2.3:
-    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
-    engines: {node: '>=8'}
+  string-width@4.2.3:
     dependencies:
       emoji-regex: 8.0.0
       is-fullwidth-code-point: 3.0.0
       strip-ansi: 6.0.1
 
-  /string-width@5.1.2:
-    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
-    engines: {node: '>=12'}
+  string-width@5.1.2:
     dependencies:
       eastasianwidth: 0.2.0
       emoji-regex: 9.2.2
       strip-ansi: 7.1.0
 
-  /string.prototype.trim@1.2.7:
-    resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==}
-    engines: {node: '>= 0.4'}
+  string.prototype.trim@1.2.7:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
-    dev: true
 
-  /string.prototype.trimend@1.0.6:
-    resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==}
+  string.prototype.trimend@1.0.6:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
-    dev: true
 
-  /string.prototype.trimstart@1.0.6:
-    resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==}
+  string.prototype.trimstart@1.0.6:
     dependencies:
       call-bind: 1.0.2
       define-properties: 1.2.0
       es-abstract: 1.22.1
-    dev: true
 
-  /string_decoder@0.10.31:
-    resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==}
-    dev: false
+  string_decoder@0.10.31: {}
 
-  /string_decoder@1.1.1:
-    resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+  string_decoder@1.1.1:
     dependencies:
       safe-buffer: 5.1.2
 
-  /string_decoder@1.3.0:
-    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+  string_decoder@1.3.0:
     dependencies:
       safe-buffer: 5.2.1
 
-  /stringz@2.1.0:
-    resolution: {integrity: sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==}
+  stringz@2.1.0:
     dependencies:
       char-regex: 1.0.2
-    dev: false
 
-  /strip-ansi@6.0.1:
-    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
-    engines: {node: '>=8'}
+  strip-ansi@6.0.1:
     dependencies:
       ansi-regex: 5.0.1
 
-  /strip-ansi@7.1.0:
-    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
-    engines: {node: '>=12'}
+  strip-ansi@7.1.0:
     dependencies:
       ansi-regex: 6.0.1
 
-  /strip-bom@3.0.0:
-    resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
-    engines: {node: '>=4'}
+  strip-bom@3.0.0: {}
 
-  /strip-bom@4.0.0:
-    resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
-    engines: {node: '>=8'}
-    dev: true
+  strip-bom@4.0.0: {}
 
-  /strip-eof@1.0.0:
-    resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
-    engines: {node: '>=0.10.0'}
-    dev: false
+  strip-eof@1.0.0: {}
 
-  /strip-final-newline@2.0.0:
-    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
-    engines: {node: '>=6'}
+  strip-final-newline@2.0.0: {}
 
-  /strip-final-newline@3.0.0:
-    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
-    engines: {node: '>=12'}
+  strip-final-newline@3.0.0: {}
 
-  /strip-indent@3.0.0:
-    resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
-    engines: {node: '>=8'}
+  strip-indent@3.0.0:
     dependencies:
       min-indent: 1.0.1
-    dev: true
 
-  /strip-indent@4.0.0:
-    resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
-    engines: {node: '>=12'}
+  strip-indent@4.0.0:
     dependencies:
       min-indent: 1.0.1
-    dev: true
 
-  /strip-json-comments@3.1.1:
-    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
-    engines: {node: '>=8'}
-    dev: true
+  strip-json-comments@3.1.1: {}
 
-  /strip-literal@1.3.0:
-    resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==}
+  strip-literal@1.3.0:
     dependencies:
       acorn: 8.11.3
-    dev: true
 
-  /strip-outer@2.0.0:
-    resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dev: false
+  strip-outer@2.0.0: {}
 
-  /strnum@1.0.5:
-    resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
-    dev: false
+  strnum@1.0.5: {}
 
-  /strtok3@7.0.0:
-    resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==}
-    engines: {node: '>=14.16'}
+  strtok3@7.0.0:
     dependencies:
       '@tokenizer/token': 0.3.0
       peek-readable: 5.0.0
-    dev: false
 
-  /stylehacks@6.0.3(postcss@8.4.35):
-    resolution: {integrity: sha512-KzBqjnqktc8/I0ERCb+lGq06giF/JxDbw2r9kEVhen9noHeIDRtMWUp9r62sOk+/2bbX6sFG1GhsS7ToXG0PEg==}
-    engines: {node: ^14 || ^16 || >=18.0}
-    peerDependencies:
-      postcss: ^8.4.31
+  stylehacks@6.1.1(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0
-      postcss: 8.4.35
-      postcss-selector-parser: 6.0.15
-    dev: false
+      postcss: 8.4.38
+      postcss-selector-parser: 6.0.16
 
-  /supports-color@5.5.0:
-    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
-    engines: {node: '>=4'}
+  supports-color@5.5.0:
     dependencies:
       has-flag: 3.0.0
-    dev: true
 
-  /supports-color@7.2.0:
-    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
-    engines: {node: '>=8'}
+  supports-color@7.2.0:
     dependencies:
       has-flag: 4.0.0
 
-  /supports-color@8.1.1:
-    resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
-    engines: {node: '>=10'}
+  supports-color@8.1.1:
     dependencies:
       has-flag: 4.0.0
 
-  /supports-color@9.4.0:
-    resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==}
-    engines: {node: '>=12'}
-    dev: true
+  supports-color@9.4.0: {}
 
-  /supports-hyperlinks@2.3.0:
-    resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
-    engines: {node: '>=8'}
+  supports-hyperlinks@2.3.0:
     dependencies:
       has-flag: 4.0.0
       supports-color: 7.2.0
-    dev: true
 
-  /supports-preserve-symlinks-flag@1.0.0:
-    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
-    engines: {node: '>= 0.4'}
+  supports-preserve-symlinks-flag@1.0.0: {}
 
-  /svgo@3.2.0:
-    resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==}
-    engines: {node: '>=14.0.0'}
-    hasBin: true
+  svgo@3.2.0:
     dependencies:
       '@trysound/sax': 0.2.0
       commander: 7.2.0
@@ -18423,51 +22592,33 @@ packages:
       css-what: 6.1.0
       csso: 5.0.5
       picocolors: 1.0.0
-    dev: false
 
-  /symbol-tree@3.2.4:
-    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
-    dev: false
+  symbol-tree@3.2.4: {}
 
-  /systeminformation@5.22.0:
-    resolution: {integrity: sha512-oAP80ymt8ssrAzjX8k3frbL7ys6AotqC35oikG6/SG15wBw+tG9nCk4oPaXIhEaAOAZ8XngxUv3ORq2IuR3r4Q==}
-    engines: {node: '>=8.0.0'}
-    os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
-    hasBin: true
-    dev: false
+  systeminformation@5.22.7: {}
 
-  /tar-fs@2.1.1:
-    resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
+  tar-fs@2.1.1:
     dependencies:
       chownr: 1.1.4
       mkdirp-classic: 0.5.3
       pump: 3.0.0
       tar-stream: 2.2.0
-    dev: true
 
-  /tar-stream@2.2.0:
-    resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
-    engines: {node: '>=6'}
+  tar-stream@2.2.0:
     dependencies:
       bl: 4.1.0
       end-of-stream: 1.4.4
       fs-constants: 1.0.0
       inherits: 2.0.4
       readable-stream: 3.6.0
-    dev: true
 
-  /tar-stream@3.1.6:
-    resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==}
+  tar-stream@3.1.6:
     dependencies:
       b4a: 1.6.4
       fast-fifo: 1.3.0
       streamx: 2.15.0
-    dev: false
 
-  /tar@4.4.19:
-    resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==}
-    engines: {node: '>=4.5'}
-    requiresBuild: true
+  tar@4.4.19:
     dependencies:
       chownr: 1.1.4
       fs-minipass: 1.2.7
@@ -18476,12 +22627,9 @@ packages:
       mkdirp: 0.5.6
       safe-buffer: 5.2.1
       yallist: 3.1.1
-    dev: false
     optional: true
 
-  /tar@6.2.0:
-    resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==}
-    engines: {node: '>=10'}
+  tar@6.2.1:
     dependencies:
       chownr: 2.0.0
       fs-minipass: 2.1.0
@@ -18490,285 +22638,160 @@ packages:
       mkdirp: 1.0.4
       yallist: 4.0.0
 
-  /taskkill@5.0.0:
-    resolution: {integrity: sha512-+HRtZ40Vc+6YfCDWCeAsixwxJgMbPY4HHuTgzPYH3JXvqHWUlsCfy+ylXlAKhFNcuLp4xVeWeFBUhDk+7KYUvQ==}
-    engines: {node: '>=14.16'}
+  taskkill@5.0.0:
     dependencies:
       execa: 6.1.0
-    dev: true
 
-  /telejson@7.2.0:
-    resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==}
+  telejson@7.2.0:
     dependencies:
       memoizerific: 1.11.3
-    dev: true
 
-  /temp-dir@2.0.0:
-    resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
-    engines: {node: '>=8'}
-    dev: true
+  temp-dir@2.0.0: {}
 
-  /temp@0.8.4:
-    resolution: {integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==}
-    engines: {node: '>=6.0.0'}
+  temp@0.8.4:
     dependencies:
       rimraf: 2.6.3
-    dev: true
 
-  /tempy@1.0.1:
-    resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==}
-    engines: {node: '>=10'}
+  tempy@1.0.1:
     dependencies:
       del: 6.1.1
       is-stream: 2.0.1
       temp-dir: 2.0.0
       type-fest: 0.16.0
       unique-string: 2.0.0
-    dev: true
 
-  /terser@5.28.1:
-    resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==}
-    engines: {node: '>=10'}
-    hasBin: true
+  terser@5.30.3:
     dependencies:
       '@jridgewell/source-map': 0.3.5
       acorn: 8.11.3
       commander: 2.20.3
       source-map-support: 0.5.21
 
-  /test-exclude@6.0.0:
-    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
-    engines: {node: '>=8'}
+  test-exclude@6.0.0:
     dependencies:
       '@istanbuljs/schema': 0.1.3
       glob: 7.2.3
       minimatch: 3.1.2
-    dev: true
 
-  /text-decoding@1.0.0:
-    resolution: {integrity: sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==}
-    dev: false
+  text-table@0.2.0: {}
 
-  /text-table@0.2.0:
-    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
-    dev: true
-
-  /textarea-caret@3.1.0:
-    resolution: {integrity: sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==}
-    dev: false
+  textarea-caret@3.1.0: {}
 
-  /thenify-all@1.6.0:
-    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
-    engines: {node: '>=0.8'}
+  thenify-all@1.6.0:
     dependencies:
       thenify: 3.3.1
-    dev: false
 
-  /thenify@3.3.1:
-    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+  thenify@3.3.1:
     dependencies:
       any-promise: 1.3.0
-    dev: false
 
-  /thread-stream@2.3.0:
-    resolution: {integrity: sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==}
+  thread-stream@2.3.0:
     dependencies:
       real-require: 0.2.0
-    dev: false
 
-  /three@0.162.0:
-    resolution: {integrity: sha512-xfCYj4RnlozReCmUd+XQzj6/5OjDNHBy5nT6rVwrOKGENAvpXe2z1jL+DZYaMu4/9pNsjH/4Os/VvS9IrH7IOQ==}
-    dev: false
+  three@0.164.1: {}
 
-  /throttle-debounce@5.0.0:
-    resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==}
-    engines: {node: '>=12.22'}
-    dev: false
+  throttle-debounce@5.0.0: {}
 
-  /throttleit@1.0.0:
-    resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==}
-    dev: true
+  throttleit@1.0.0: {}
 
-  /through2@2.0.5:
-    resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
+  through2@2.0.5:
     dependencies:
       readable-stream: 2.3.7
       xtend: 4.0.2
-    dev: true
-
-  /through@2.3.4:
-    resolution: {integrity: sha512-DwbmSAcABsMazNkLOJJSLRC3gfh4cPxUxJCn9npmvbcI6undhgoJ2ShvEOgZrW8BH62Gyr9jKboGbfFcmY5VsQ==}
-    dev: false
 
-  /through@2.3.8:
-    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+  through@2.3.4: {}
 
-  /tiny-invariant@1.3.1:
-    resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
-    dev: true
+  through@2.3.8: {}
 
-  /tiny-invariant@1.3.3:
-    resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
-    dev: true
+  tiny-invariant@1.3.1: {}
 
-  /tiny-lru@10.0.1:
-    resolution: {integrity: sha512-Vst+6kEsWvb17Zpz14sRJV/f8bUWKhqm6Dc+v08iShmIJ/WxqWytHzCTd6m88pS33rE2zpX34TRmOpAJPloNCA==}
-    engines: {node: '>=6'}
-    dev: false
+  tiny-invariant@1.3.3: {}
 
-  /tinybench@2.6.0:
-    resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
-    dev: true
+  tiny-lru@10.0.1: {}
 
-  /tinycolor2@1.6.0:
-    resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
-    dev: false
+  tinybench@2.6.0: {}
 
-  /tinypool@0.7.0:
-    resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
-    engines: {node: '>=14.0.0'}
-    dev: true
+  tinycolor2@1.6.0: {}
 
-  /tinyspy@2.2.0:
-    resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==}
-    engines: {node: '>=14.0.0'}
-    dev: true
+  tinypool@0.7.0: {}
 
-  /tmp@0.0.33:
-    resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
-    engines: {node: '>=0.6.0'}
-    dependencies:
-      os-tmpdir: 1.0.2
-    dev: true
+  tinyspy@2.2.0: {}
 
-  /tmp@0.2.2:
-    resolution: {integrity: sha512-ETcvHhaIc9J2MDEAH6N67j9bvBvu/3Gb764qaGhwtFvjtvhegqoqSpofgeyq1Sc24mW5pdyUDs9HP5j3ehkxRw==}
-    engines: {node: '>=14'}
-    dependencies:
-      rimraf: 5.0.5
+  tmp@0.2.3: {}
 
-  /tmpl@1.0.5:
-    resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
-    dev: true
+  tmpl@1.0.5: {}
 
-  /to-data-view@1.1.0:
-    resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==}
-    dev: false
+  to-data-view@1.1.0: {}
 
-  /to-fast-properties@2.0.0:
-    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
-    engines: {node: '>=4'}
+  to-fast-properties@2.0.0: {}
 
-  /to-regex-range@5.0.1:
-    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
-    engines: {node: '>=8.0'}
+  to-regex-range@5.0.1:
     dependencies:
       is-number: 7.0.0
 
-  /toad-cache@3.3.0:
-    resolution: {integrity: sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==}
-    engines: {node: '>=12'}
-    dev: false
+  toad-cache@3.3.0: {}
 
-  /tocbot@4.21.1:
-    resolution: {integrity: sha512-IfajhBTeg0HlMXu1f+VMbPef05QpDTsZ9X2Yn1+8npdaXsXg/+wrm9Ze1WG5OS1UDC3qJ5EQN/XOZ3gfXjPFCw==}
-    dev: true
+  toad-cache@3.7.0: {}
 
-  /toidentifier@1.0.1:
-    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
-    engines: {node: '>=0.6'}
+  tocbot@4.21.1: {}
+
+  toidentifier@1.0.1: {}
 
-  /token-stream@1.0.0:
-    resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==}
+  token-stream@1.0.0: {}
 
-  /token-types@5.0.1:
-    resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==}
-    engines: {node: '>=14.16'}
+  token-types@5.0.1:
     dependencies:
       '@tokenizer/token': 0.3.0
       ieee754: 1.2.1
-    dev: false
 
-  /touch@3.1.0:
-    resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==}
-    hasBin: true
+  touch@3.1.0:
     dependencies:
       nopt: 1.0.10
-    dev: true
 
-  /tough-cookie@2.5.0:
-    resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
-    engines: {node: '>=0.8'}
+  tough-cookie@2.5.0:
     dependencies:
       psl: 1.9.0
       punycode: 2.3.1
-    dev: false
 
-  /tough-cookie@4.1.3:
-    resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
-    engines: {node: '>=6'}
+  tough-cookie@4.1.3:
     dependencies:
       psl: 1.9.0
       punycode: 2.3.1
       universalify: 0.2.0
       url-parse: 1.5.10
 
-  /tr46@0.0.3:
-    resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
-    requiresBuild: true
+  tr46@0.0.3: {}
 
-  /tr46@5.0.0:
-    resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
-    engines: {node: '>=18'}
+  tr46@5.0.0:
     dependencies:
       punycode: 2.3.1
-    dev: false
 
-  /trace-redirect@1.0.6:
-    resolution: {integrity: sha512-UUfa1DjjU5flcjMdaFIiIEGDTyu2y/IiMjOX4uGXa7meKBS4vD4f2Uy/tken9Qkd4Jsm4sRsfZcIIPqrRVF3Mg==}
+  trace-redirect@1.0.6: {}
 
-  /trim-newlines@3.0.1:
-    resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
-    engines: {node: '>=8'}
-    dev: true
+  trim-newlines@3.0.1: {}
 
-  /trim-repeated@2.0.0:
-    resolution: {integrity: sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==}
-    engines: {node: '>=12'}
+  trim-repeated@2.0.0:
     dependencies:
       escape-string-regexp: 5.0.0
-    dev: false
 
-  /trough@2.2.0:
-    resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
-    dev: true
+  trough@2.2.0: {}
 
-  /ts-api-utils@1.0.1(typescript@5.3.3):
-    resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
-    engines: {node: '>=16.13.0'}
-    peerDependencies:
-      typescript: '>=4.2.0'
+  ts-api-utils@1.0.1(typescript@5.3.3):
     dependencies:
       typescript: 5.3.3
-    dev: true
 
-  /ts-case-convert@2.0.2:
-    resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
-    dev: true
-    bundledDependencies: []
+  ts-api-utils@1.3.0(typescript@5.4.5):
+    dependencies:
+      typescript: 5.4.5
 
-  /ts-dedent@2.2.0:
-    resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
-    engines: {node: '>=6.10'}
-    dev: true
+  ts-case-convert@2.0.2: {}
 
-  /ts-map@1.0.3:
-    resolution: {integrity: sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==}
-    dev: true
+  ts-dedent@2.2.0: {}
 
-  /tsc-alias@1.8.8:
-    resolution: {integrity: sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==}
-    hasBin: true
+  ts-map@1.0.3: {}
+
+  tsc-alias@1.8.8:
     dependencies:
       chokidar: 3.5.3
       commander: 9.5.0
@@ -18776,29 +22799,21 @@ packages:
       mylas: 2.1.13
       normalize-path: 3.0.0
       plimit-lit: 1.5.0
-    dev: false
 
-  /tsconfig-paths@3.15.0:
-    resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+  tsconfig-paths@3.15.0:
     dependencies:
       '@types/json5': 0.0.29
       json5: 1.0.2
       minimist: 1.2.8
       strip-bom: 3.0.0
-    dev: true
 
-  /tsconfig-paths@4.2.0:
-    resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
-    engines: {node: '>=6'}
+  tsconfig-paths@4.2.0:
     dependencies:
       json5: 2.2.3
       minimist: 1.2.8
       strip-bom: 3.0.0
 
-  /tsd@0.30.7:
-    resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==}
-    engines: {node: '>=14.16'}
-    hasBin: true
+  tsd@0.30.7:
     dependencies:
       '@tsd/typescript': 5.3.3
       eslint-formatter-pretty: 4.1.0
@@ -18807,189 +22822,81 @@ packages:
       meow: 9.0.0
       path-exists: 4.0.0
       read-pkg-up: 7.0.1
-    dev: true
 
-  /tslib@1.14.1:
-    resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+  tslib@1.14.1: {}
 
-  /tslib@2.6.2:
-    resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
+  tslib@2.6.2: {}
 
-  /tsx@4.4.0:
-    resolution: {integrity: sha512-4fwcEjRUxW20ciSaMB8zkpGwCPxuRGnadDuj/pBk5S9uT29zvWz15PK36GrKJo45mSJomDxVejZ73c6lr3811Q==}
-    engines: {node: '>=18.0.0'}
-    hasBin: true
+  tsx@4.4.0:
     dependencies:
       esbuild: 0.18.20
       get-tsconfig: 4.7.2
     optionalDependencies:
       fsevents: 2.3.3
-    dev: true
 
-  /tunnel-agent@0.6.0:
-    resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+  tunnel-agent@0.6.0:
     dependencies:
       safe-buffer: 5.2.1
 
-  /tweetnacl@0.14.5:
-    resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
+  tweetnacl@0.14.5: {}
 
-  /type-check@0.4.0:
-    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
-    engines: {node: '>= 0.8.0'}
+  type-check@0.4.0:
     dependencies:
       prelude-ls: 1.2.1
-    dev: true
 
-  /type-detect@4.0.8:
-    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
-    engines: {node: '>=4'}
+  type-detect@4.0.8: {}
 
-  /type-fest@0.16.0:
-    resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
-    engines: {node: '>=10'}
-    dev: true
+  type-fest@0.16.0: {}
 
-  /type-fest@0.18.1:
-    resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}
-    engines: {node: '>=10'}
-    dev: true
+  type-fest@0.18.1: {}
 
-  /type-fest@0.20.2:
-    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
-    engines: {node: '>=10'}
-    dev: true
+  type-fest@0.20.2: {}
 
-  /type-fest@0.21.3:
-    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
-    engines: {node: '>=10'}
-    dev: true
+  type-fest@0.21.3: {}
 
-  /type-fest@0.6.0:
-    resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
-    engines: {node: '>=8'}
-    dev: true
+  type-fest@0.6.0: {}
 
-  /type-fest@0.8.1:
-    resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
-    engines: {node: '>=8'}
-    dev: true
+  type-fest@0.8.1: {}
 
-  /type-fest@2.19.0:
-    resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
-    engines: {node: '>=12.20'}
-    dev: true
+  type-fest@2.19.0: {}
 
-  /type-fest@4.9.0:
-    resolution: {integrity: sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==}
-    engines: {node: '>=16'}
-    dev: true
+  type-fest@4.9.0: {}
 
-  /type-is@1.6.18:
-    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
-    engines: {node: '>= 0.6'}
+  type-is@1.6.18:
     dependencies:
       media-typer: 0.3.0
       mime-types: 2.1.35
 
-  /typed-array-buffer@1.0.0:
-    resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
-    engines: {node: '>= 0.4'}
+  typed-array-buffer@1.0.0:
     dependencies:
       call-bind: 1.0.2
       get-intrinsic: 1.2.1
       is-typed-array: 1.1.10
-    dev: true
 
-  /typed-array-byte-length@1.0.0:
-    resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==}
-    engines: {node: '>= 0.4'}
+  typed-array-byte-length@1.0.0:
     dependencies:
       call-bind: 1.0.2
       for-each: 0.3.3
       has-proto: 1.0.1
       is-typed-array: 1.1.10
-    dev: true
 
-  /typed-array-byte-offset@1.0.0:
-    resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==}
-    engines: {node: '>= 0.4'}
+  typed-array-byte-offset@1.0.0:
     dependencies:
       available-typed-arrays: 1.0.5
       call-bind: 1.0.2
       for-each: 0.3.3
       has-proto: 1.0.1
       is-typed-array: 1.1.10
-    dev: true
 
-  /typed-array-length@1.0.4:
-    resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
+  typed-array-length@1.0.4:
     dependencies:
       call-bind: 1.0.2
       for-each: 0.3.3
       is-typed-array: 1.1.10
-    dev: true
 
-  /typedarray@0.0.6:
-    resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+  typedarray@0.0.6: {}
 
-  /typeorm@0.3.20(ioredis@5.3.2)(pg@8.11.3):
-    resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==}
-    engines: {node: '>=16.13.0'}
-    hasBin: true
-    peerDependencies:
-      '@google-cloud/spanner': ^5.18.0
-      '@sap/hana-client': ^2.12.25
-      better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0
-      hdb-pool: ^0.1.6
-      ioredis: ^5.0.4
-      mongodb: ^5.8.0
-      mssql: ^9.1.1 || ^10.0.1
-      mysql2: ^2.2.5 || ^3.0.1
-      oracledb: ^6.3.0
-      pg: ^8.5.1
-      pg-native: ^3.0.0
-      pg-query-stream: ^4.0.0
-      redis: ^3.1.1 || ^4.0.0
-      sql.js: ^1.4.0
-      sqlite3: ^5.0.3
-      ts-node: ^10.7.0
-      typeorm-aurora-data-api-driver: ^2.0.0
-    peerDependenciesMeta:
-      '@google-cloud/spanner':
-        optional: true
-      '@sap/hana-client':
-        optional: true
-      better-sqlite3:
-        optional: true
-      hdb-pool:
-        optional: true
-      ioredis:
-        optional: true
-      mongodb:
-        optional: true
-      mssql:
-        optional: true
-      mysql2:
-        optional: true
-      oracledb:
-        optional: true
-      pg:
-        optional: true
-      pg-native:
-        optional: true
-      pg-query-stream:
-        optional: true
-      redis:
-        optional: true
-      sql.js:
-        optional: true
-      sqlite3:
-        optional: true
-      ts-node:
-        optional: true
-      typeorm-aurora-data-api-driver:
-        optional: true
+  typeorm@0.3.20(ioredis@5.4.1)(pg@8.11.5):
     dependencies:
       '@sqltools/formatter': 1.2.5
       app-root-path: 3.1.0
@@ -19000,97 +22907,64 @@ packages:
       debug: 4.3.4(supports-color@8.1.1)
       dotenv: 16.0.3
       glob: 10.3.10
-      ioredis: 5.3.2
       mkdirp: 2.1.6
-      pg: 8.11.3
-      reflect-metadata: 0.2.1
+      reflect-metadata: 0.2.2
       sha.js: 2.4.11
       tslib: 2.6.2
       uuid: 9.0.1
       yargs: 17.7.2
+    optionalDependencies:
+      ioredis: 5.4.1
+      pg: 8.11.5
     transitivePeerDependencies:
       - supports-color
-    dev: false
 
-  /typescript@5.3.3:
-    resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
-    engines: {node: '>=14.17'}
-    hasBin: true
+  typescript@5.3.3: {}
 
-  /ufo@1.3.2:
-    resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==}
-    dev: true
+  typescript@5.4.2: {}
 
-  /uglify-js@3.17.4:
-    resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}
-    engines: {node: '>=0.8.0'}
-    hasBin: true
-    requiresBuild: true
-    dev: true
+  typescript@5.4.5: {}
+
+  ufo@1.3.2: {}
+
+  uglify-js@3.17.4:
     optional: true
 
-  /uid2@0.0.4:
-    resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
-    dev: false
+  uid2@0.0.4: {}
 
-  /uid@2.0.2:
-    resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
-    engines: {node: '>=8'}
+  uid@2.0.2:
     dependencies:
       '@lukeed/csprng': 1.0.1
 
-  /ulid@2.3.0:
-    resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==}
-    hasBin: true
-    dev: false
+  ulid@2.3.0: {}
 
-  /unbox-primitive@1.0.2:
-    resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+  unbox-primitive@1.0.2:
     dependencies:
       call-bind: 1.0.2
       has-bigints: 1.0.2
       has-symbols: 1.0.3
       which-boxed-primitive: 1.0.2
-    dev: true
 
-  /undefsafe@2.0.5:
-    resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
-    dev: true
+  undefsafe@2.0.5: {}
 
-  /undici-types@5.26.5:
-    resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+  undici-types@5.26.5: {}
 
-  /undici@5.28.2:
-    resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==}
-    engines: {node: '>=14.0'}
+  undici@5.28.2:
     dependencies:
       '@fastify/busboy': 2.1.0
 
-  /unicode-canonical-property-names-ecmascript@2.0.0:
-    resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
-    engines: {node: '>=4'}
-    dev: true
+  unicode-canonical-property-names-ecmascript@2.0.0: {}
 
-  /unicode-match-property-ecmascript@2.0.0:
-    resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
-    engines: {node: '>=4'}
+  unicode-match-property-ecmascript@2.0.0:
     dependencies:
       unicode-canonical-property-names-ecmascript: 2.0.0
       unicode-property-aliases-ecmascript: 2.1.0
-    dev: true
 
-  /unicode-match-property-value-ecmascript@2.1.0:
-    resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==}
-    engines: {node: '>=4'}
-    dev: true
+  unicode-match-property-value-ecmascript@2.1.0: {}
 
-  /unicode-property-aliases-ecmascript@2.1.0:
-    resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
-    engines: {node: '>=4'}
-    dev: true
+  unicode-property-aliases-ecmascript@2.1.0: {}
 
-  /unified@11.0.4:
-    resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==}
+  unified@11.0.4:
     dependencies:
       '@types/unist': 3.0.2
       bail: 2.0.2
@@ -19099,241 +22973,152 @@ packages:
       is-plain-obj: 4.1.0
       trough: 2.2.0
       vfile: 6.0.1
-    dev: true
 
-  /uniq@1.0.1:
-    resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==}
-    dev: false
+  uniq@1.0.1: {}
 
-  /unique-filename@3.0.0:
-    resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  unique-filename@3.0.0:
     dependencies:
       unique-slug: 4.0.0
-    dev: false
 
-  /unique-slug@4.0.0:
-    resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
-    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+  unique-slug@4.0.0:
     dependencies:
       imurmurhash: 0.1.4
-    dev: false
 
-  /unique-string@2.0.0:
-    resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
-    engines: {node: '>=8'}
+  unique-string@2.0.0:
     dependencies:
       crypto-random-string: 2.0.0
-    dev: true
 
-  /unist-util-is@6.0.0:
-    resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+  unist-util-is@6.0.0:
     dependencies:
       '@types/unist': 3.0.2
-    dev: true
 
-  /unist-util-stringify-position@4.0.0:
-    resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+  unist-util-stringify-position@4.0.0:
     dependencies:
       '@types/unist': 3.0.2
-    dev: true
 
-  /unist-util-visit-parents@6.0.1:
-    resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+  unist-util-visit-parents@6.0.1:
     dependencies:
       '@types/unist': 3.0.2
       unist-util-is: 6.0.0
-    dev: true
 
-  /unist-util-visit@5.0.0:
-    resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+  unist-util-visit@5.0.0:
     dependencies:
       '@types/unist': 3.0.2
       unist-util-is: 6.0.0
       unist-util-visit-parents: 6.0.1
-    dev: true
 
-  /universalify@0.1.2:
-    resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
-    engines: {node: '>= 4.0.0'}
+  universalify@0.1.2: {}
 
-  /universalify@0.2.0:
-    resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
-    engines: {node: '>= 4.0.0'}
+  universalify@0.2.0: {}
 
-  /universalify@2.0.0:
-    resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
-    engines: {node: '>= 10.0.0'}
-    dev: true
+  universalify@2.0.0: {}
 
-  /unload@2.4.1:
-    resolution: {integrity: sha512-IViSAm8Z3sRBYA+9wc0fLQmU9Nrxb16rcDmIiR6Y9LJSZzI7QY5QsDhqPpKOjAn0O9/kfK1TfNEMMAGPTIraPw==}
-    dev: false
+  unload@2.4.1: {}
 
-  /unpipe@1.0.0:
-    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
-    engines: {node: '>= 0.8'}
+  unpipe@1.0.0: {}
 
-  /unplugin@1.4.0:
-    resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
+  unplugin@1.4.0:
     dependencies:
       acorn: 8.11.3
       chokidar: 3.5.3
       webpack-sources: 3.2.3
       webpack-virtual-modules: 0.5.0
-    dev: true
 
-  /untildify@4.0.0:
-    resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
-    engines: {node: '>=8'}
-    dev: true
+  untildify@4.0.0: {}
 
-  /update-browserslist-db@1.0.13(browserslist@4.22.2):
-    resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
-    hasBin: true
-    peerDependencies:
-      browserslist: '>= 4.21.0'
+  update-browserslist-db@1.0.13(browserslist@4.22.2):
     dependencies:
       browserslist: 4.22.2
       escalade: 3.1.1
       picocolors: 1.0.0
-    dev: true
 
-  /update-browserslist-db@1.0.13(browserslist@4.23.0):
-    resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
-    hasBin: true
-    peerDependencies:
-      browserslist: '>= 4.21.0'
+  update-browserslist-db@1.0.13(browserslist@4.23.0):
     dependencies:
       browserslist: 4.23.0
       escalade: 3.1.1
       picocolors: 1.0.0
 
-  /uri-js@4.4.1:
-    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+  uri-js@4.4.1:
     dependencies:
       punycode: 2.3.1
 
-  /url-parse@1.5.10:
-    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+  url-parse@1.5.10:
     dependencies:
       querystringify: 2.2.0
       requires-port: 1.0.0
 
-  /utf-8-validate@6.0.3:
-    resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==}
-    engines: {node: '>=6.14.2'}
-    requiresBuild: true
+  utf-8-validate@6.0.3:
     dependencies:
       node-gyp-build: 4.6.0
+    optional: true
 
-  /util-deprecate@1.0.2:
-    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+  util-deprecate@1.0.2: {}
 
-  /util@0.12.5:
-    resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
+  util@0.12.5:
     dependencies:
       inherits: 2.0.4
       is-arguments: 1.1.1
       is-generator-function: 1.0.10
       is-typed-array: 1.1.10
       which-typed-array: 1.1.11
-    dev: true
 
-  /utils-merge@1.0.1:
-    resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
-    engines: {node: '>= 0.4.0'}
+  utils-merge@1.0.1: {}
 
-  /uuid@3.4.0:
-    resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
-    deprecated: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
-    hasBin: true
-    dev: false
+  uuid@3.4.0: {}
 
-  /uuid@8.3.2:
-    resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
-    hasBin: true
+  uuid@8.3.2: {}
 
-  /uuid@9.0.1:
-    resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
-    hasBin: true
+  uuid@9.0.1: {}
 
-  /v-code-diff@1.9.0(vue@3.4.21):
-    resolution: {integrity: sha512-alg6krCxFvwTob/rJq+3LzjdIbLb/ni8tS8YmBbI0wckOkbJuN1cShFJ6XEkm82tMgpv5NYEeWLEWhggeV7BDg==}
-    requiresBuild: true
-    peerDependencies:
-      '@vue/composition-api': ^1.4.9
-      vue: ^2.6.0 || >=3.0.0
-    peerDependenciesMeta:
-      '@vue/composition-api':
-        optional: true
+  v-code-diff@1.11.0(vue@3.4.26(typescript@5.4.5)):
     dependencies:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.9.0
-      vue: 3.4.21(typescript@5.3.3)
-      vue-demi: 0.14.7(vue@3.4.21)
-    dev: false
+      vue: 3.4.26(typescript@5.4.5)
+      vue-demi: 0.14.7(vue@3.4.26(typescript@5.4.5))
+      vue-i18n: 9.13.1(vue@3.4.26(typescript@5.4.5))
 
-  /v8-to-istanbul@9.2.0:
-    resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==}
-    engines: {node: '>=10.12.0'}
+  v8-to-istanbul@9.2.0:
     dependencies:
       '@jridgewell/trace-mapping': 0.3.18
       '@types/istanbul-lib-coverage': 2.0.4
       convert-source-map: 2.0.0
-    dev: true
 
-  /validate-npm-package-license@3.0.4:
-    resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+  validate-npm-package-license@3.0.4:
     dependencies:
       spdx-correct: 3.1.1
       spdx-expression-parse: 3.0.1
-    dev: true
 
-  /validator@13.9.0:
-    resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==}
-    engines: {node: '>= 0.10'}
-    dev: true
+  validator@13.9.0: {}
 
-  /vary@1.1.2:
-    resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
-    engines: {node: '>= 0.8'}
+  vary@1.1.2: {}
 
-  /verror@1.10.0:
-    resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
-    engines: {'0': node >=0.6.0}
+  verror@1.10.0:
     dependencies:
       assert-plus: 1.0.0
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vfile-message@4.0.2:
-    resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
+  vfile-message@4.0.2:
     dependencies:
       '@types/unist': 3.0.2
       unist-util-stringify-position: 4.0.0
-    dev: true
 
-  /vfile@6.0.1:
-    resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
+  vfile@6.0.1:
     dependencies:
       '@types/unist': 3.0.2
       unist-util-stringify-position: 4.0.0
       vfile-message: 4.0.2
-    dev: true
 
-  /vite-node@0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1):
-    resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
-    engines: {node: '>=v14.18.0'}
-    hasBin: true
+  vite-node@0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3):
     dependencies:
       cac: 6.7.14
       debug: 4.3.4(supports-color@8.1.1)
       mlly: 1.5.0
       pathe: 1.1.2
       picocolors: 1.0.0
-      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19341,97 +23126,34 @@ packages:
       - sass
       - stylus
       - sugarss
-      - supports-color
-      - terser
-    dev: true
-
-  /vite-plugin-turbosnap@1.0.3:
-    resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
-    dev: true
-
-  /vite@5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1):
-    resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    hasBin: true
-    peerDependencies:
-      '@types/node': ^18.0.0 || >=20.0.0
-      less: '*'
-      lightningcss: ^1.21.0
-      sass: '*'
-      stylus: '*'
-      sugarss: '*'
-      terser: ^5.4.0
-    peerDependenciesMeta:
-      '@types/node':
-        optional: true
-      less:
-        optional: true
-      lightningcss:
-        optional: true
-      sass:
-        optional: true
-      stylus:
-        optional: true
-      sugarss:
-        optional: true
-      terser:
-        optional: true
+      - supports-color
+      - terser
+
+  vite-plugin-turbosnap@1.0.3: {}
+
+  vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3):
     dependencies:
-      '@types/node': 20.11.22
-      esbuild: 0.19.11
-      postcss: 8.4.35
-      rollup: 4.12.0
-      sass: 1.71.1
-      terser: 5.28.1
+      esbuild: 0.20.2
+      postcss: 8.4.38
+      rollup: 4.17.2
     optionalDependencies:
+      '@types/node': 20.12.7
       fsevents: 2.3.3
+      sass: 1.76.0
+      terser: 5.30.3
 
-  /vitest-fetch-mock@0.2.2(vitest@0.34.6):
-    resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==}
-    engines: {node: '>=14.14.0'}
-    peerDependencies:
-      vitest: '>=0.16.0'
+  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)):
     dependencies:
-      cross-fetch: 3.1.6
-      vitest: 0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1)
+      cross-fetch: 3.1.6(encoding@0.1.13)
+      vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
     transitivePeerDependencies:
       - encoding
-    dev: true
 
-  /vitest@0.34.6(happy-dom@13.6.2)(sass@1.71.1)(terser@5.28.1):
-    resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
-    engines: {node: '>=v14.18.0'}
-    hasBin: true
-    peerDependencies:
-      '@edge-runtime/vm': '*'
-      '@vitest/browser': '*'
-      '@vitest/ui': '*'
-      happy-dom: '*'
-      jsdom: '*'
-      playwright: '*'
-      safaridriver: '*'
-      webdriverio: '*'
-    peerDependenciesMeta:
-      '@edge-runtime/vm':
-        optional: true
-      '@vitest/browser':
-        optional: true
-      '@vitest/ui':
-        optional: true
-      happy-dom:
-        optional: true
-      jsdom:
-        optional: true
-      playwright:
-        optional: true
-      safaridriver:
-        optional: true
-      webdriverio:
-        optional: true
+  vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3):
     dependencies:
       '@types/chai': 4.3.11
       '@types/chai-subset': 1.3.5
-      '@types/node': 20.11.22
+      '@types/node': 20.12.7
       '@vitest/expect': 0.34.6
       '@vitest/runner': 0.34.6
       '@vitest/snapshot': 0.34.6
@@ -19442,7 +23164,6 @@ packages:
       cac: 6.7.14
       chai: 4.3.10
       debug: 4.3.4(supports-color@8.1.1)
-      happy-dom: 13.6.2
       local-pkg: 0.4.3
       magic-string: 0.30.7
       pathe: 1.1.2
@@ -19451,9 +23172,12 @@ packages:
       strip-literal: 1.3.0
       tinybench: 2.6.0
       tinypool: 0.7.0
-      vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
-      vite-node: 0.34.6(@types/node@20.11.22)(sass@1.71.1)(terser@5.28.1)
+      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      vite-node: 0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
       why-is-node-running: 2.2.2
+    optionalDependencies:
+      happy-dom: 14.7.1
+      jsdom: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -19462,114 +23186,63 @@ packages:
       - sugarss
       - supports-color
       - terser
-    dev: true
 
-  /void-elements@3.1.0:
-    resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
-    engines: {node: '>=0.10.0'}
+  void-elements@3.1.0: {}
 
-  /vscode-jsonrpc@8.2.0:
-    resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
-    engines: {node: '>=14.0.0'}
-    dev: false
+  vscode-jsonrpc@8.2.0: {}
 
-  /vscode-languageclient@9.0.1:
-    resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==}
-    engines: {vscode: ^1.82.0}
+  vscode-languageclient@9.0.1:
     dependencies:
       minimatch: 5.1.2
       semver: 7.6.0
       vscode-languageserver-protocol: 3.17.5
-    dev: false
 
-  /vscode-languageserver-protocol@3.17.5:
-    resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
+  vscode-languageserver-protocol@3.17.5:
     dependencies:
       vscode-jsonrpc: 8.2.0
       vscode-languageserver-types: 3.17.5
-    dev: false
 
-  /vscode-languageserver-textdocument@1.0.11:
-    resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==}
-    dev: false
+  vscode-languageserver-textdocument@1.0.11: {}
 
-  /vscode-languageserver-types@3.17.5:
-    resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
-    dev: false
+  vscode-languageserver-types@3.17.5: {}
 
-  /vscode-languageserver@9.0.1:
-    resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
-    hasBin: true
+  vscode-languageserver@9.0.1:
     dependencies:
       vscode-languageserver-protocol: 3.17.5
-    dev: false
 
-  /vue-component-meta@2.0.16(typescript@5.3.3):
-    resolution: {integrity: sha512-IyIMClUMYcKxAL34GqdPbR4V45MUeHXqQiZlHxeYMV5Qcqp4M+CEmtGpF//XBSS138heDkYkceHAtJQjLUB1Lw==}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  vue-component-meta@2.0.16(typescript@5.4.5):
     dependencies:
       '@volar/typescript': 2.2.0
-      '@vue/language-core': 2.0.16(typescript@5.3.3)
+      '@vue/language-core': 2.0.16(typescript@5.4.5)
       path-browserify: 1.0.1
-      typescript: 5.3.3
       vue-component-type-helpers: 2.0.16
-    dev: true
-
-  /vue-component-type-helpers@1.8.27:
-    resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
-    dev: true
+    optionalDependencies:
+      typescript: 5.4.5
 
-  /vue-component-type-helpers@1.8.4:
-    resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
-    dev: true
+  vue-component-type-helpers@1.8.4: {}
 
-  /vue-component-type-helpers@2.0.16:
-    resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
-    dev: true
+  vue-component-type-helpers@2.0.16: {}
 
-  /vue-demi@0.14.7(vue@3.4.21):
-    resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
-    engines: {node: '>=12'}
-    hasBin: true
-    requiresBuild: true
-    peerDependencies:
-      '@vue/composition-api': ^1.0.0-rc.1
-      vue: ^3.0.0-0 || ^2.6.0
-    peerDependenciesMeta:
-      '@vue/composition-api':
-        optional: true
+  vue-demi@0.14.7(vue@3.4.26(typescript@5.4.5)):
     dependencies:
-      vue: 3.4.21(typescript@5.3.3)
-    dev: false
+      vue: 3.4.26(typescript@5.4.5)
 
-  /vue-docgen-api@4.75.1(vue@3.4.21):
-    resolution: {integrity: sha512-MECZ3uExz+ssmhD/2XrFoQQs93y17IVO1KDYTp8nr6i9GNrk67AAto6QAtilW1H/pTDPMkQxJ7w/25ZIqVtfAA==}
-    peerDependencies:
-      vue: '>=2'
+  vue-docgen-api@4.75.1(vue@3.4.26(typescript@5.4.5)):
     dependencies:
       '@babel/parser': 7.24.0
       '@babel/types': 7.24.0
       '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-sfc': 3.4.21
+      '@vue/compiler-sfc': 3.4.26
       ast-types: 0.16.1
       hash-sum: 2.0.0
       lru-cache: 8.0.4
       pug: 3.0.2
       recast: 0.23.4
       ts-map: 1.0.3
-      vue: 3.4.21(typescript@5.3.3)
-      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.21)
-    dev: true
+      vue: 3.4.26(typescript@5.4.5)
+      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.26(typescript@5.4.5))
 
-  /vue-eslint-parser@9.4.2(eslint@8.57.0):
-    resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==}
-    engines: {node: ^14.17.0 || >=16.0.0}
-    peerDependencies:
-      eslint: '>=6.0.0'
+  vue-eslint-parser@9.4.2(eslint@8.57.0):
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
@@ -19581,70 +23254,50 @@ packages:
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
-    dev: true
 
-  /vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.21):
-    resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==}
-    peerDependencies:
-      vue: '>=2'
+  vue-i18n@9.13.1(vue@3.4.26(typescript@5.4.5)):
     dependencies:
-      vue: 3.4.21(typescript@5.3.3)
-    dev: true
+      '@intlify/core-base': 9.13.1
+      '@intlify/shared': 9.13.1
+      '@vue/devtools-api': 6.6.1
+      vue: 3.4.26(typescript@5.4.5)
 
-  /vue-template-compiler@2.7.14:
-    resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
+  vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.26(typescript@5.4.5)):
+    dependencies:
+      vue: 3.4.26(typescript@5.4.5)
+
+  vue-template-compiler@2.7.14:
     dependencies:
       de-indent: 1.0.2
       he: 1.2.0
-    dev: true
 
-  /vue-tsc@1.8.27(typescript@5.3.3):
-    resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==}
-    hasBin: true
-    peerDependencies:
-      typescript: '*'
+  vue-tsc@2.0.16(typescript@5.4.5):
     dependencies:
-      '@volar/typescript': 1.11.1
-      '@vue/language-core': 1.8.27(typescript@5.3.3)
-      semver: 7.5.4
-      typescript: 5.3.3
-    dev: true
+      '@volar/typescript': 2.2.0
+      '@vue/language-core': 2.0.16(typescript@5.4.5)
+      semver: 7.6.0
+      typescript: 5.4.5
 
-  /vue@3.4.21(typescript@5.3.3):
-    resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
+  vue@3.4.26(typescript@5.4.5):
     dependencies:
-      '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-sfc': 3.4.21
-      '@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
+      '@vue/compiler-dom': 3.4.26
+      '@vue/compiler-sfc': 3.4.26
+      '@vue/runtime-dom': 3.4.26
+      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.4.5))
+      '@vue/shared': 3.4.26
+    optionalDependencies:
+      typescript: 5.4.5
 
-  /vuedraggable@4.1.0(vue@3.4.21):
-    resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
-    peerDependencies:
-      vue: ^3.0.1
+  vuedraggable@4.1.0(vue@3.4.26(typescript@5.4.5)):
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.4.21(typescript@5.3.3)
-    dev: false
+      vue: 3.4.26(typescript@5.4.5)
 
-  /w3c-xmlserializer@5.0.0:
-    resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
-    engines: {node: '>=18'}
+  w3c-xmlserializer@5.0.0:
     dependencies:
       xml-name-validator: 5.0.0
-    dev: false
 
-  /wait-on@7.2.0(debug@4.3.4):
-    resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==}
-    engines: {node: '>=12.0.0'}
-    hasBin: true
+  wait-on@7.2.0(debug@4.3.4):
     dependencies:
       axios: 1.6.2(debug@4.3.4)
       joi: 17.11.0
@@ -19653,32 +23306,21 @@ packages:
       rxjs: 7.8.1
     transitivePeerDependencies:
       - debug
-    dev: true
 
-  /walker@1.0.8:
-    resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+  walker@1.0.8:
     dependencies:
       makeerror: 1.0.12
-    dev: true
 
-  /watchpack@2.4.0:
-    resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
-    engines: {node: '>=10.13.0'}
+  watchpack@2.4.0:
     dependencies:
       glob-to-regexp: 0.4.1
       graceful-fs: 4.2.11
-    dev: true
 
-  /wcwidth@1.0.1:
-    resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+  wcwidth@1.0.1:
     dependencies:
       defaults: 1.0.4
-    dev: true
 
-  /web-push@3.6.7:
-    resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==}
-    engines: {node: '>= 16'}
-    hasBin: true
+  web-push@3.6.7:
     dependencies:
       asn1.js: 5.4.1
       http_ece: 1.2.0
@@ -19687,289 +23329,168 @@ packages:
       minimist: 1.2.8
     transitivePeerDependencies:
       - supports-color
-    dev: false
-
-  /web-streams-polyfill@3.2.1:
-    resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
-    engines: {node: '>= 8'}
 
-  /webidl-conversions@3.0.1:
-    resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
-    requiresBuild: true
+  web-streams-polyfill@3.2.1: {}
 
-  /webidl-conversions@7.0.0:
-    resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
-    engines: {node: '>=12'}
+  webidl-conversions@3.0.1: {}
 
-  /webpack-sources@3.2.3:
-    resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
-    engines: {node: '>=10.13.0'}
-    dev: true
+  webidl-conversions@7.0.0: {}
 
-  /webpack-virtual-modules@0.5.0:
-    resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
-    dev: true
+  webpack-sources@3.2.3: {}
 
-  /whatwg-encoding@2.0.0:
-    resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
-    engines: {node: '>=12'}
-    dependencies:
-      iconv-lite: 0.6.3
-    dev: false
+  webpack-virtual-modules@0.5.0: {}
 
-  /whatwg-encoding@3.1.1:
-    resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
-    engines: {node: '>=18'}
+  whatwg-encoding@3.1.1:
     dependencies:
       iconv-lite: 0.6.3
-    dev: false
 
-  /whatwg-mimetype@3.0.0:
-    resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
-    engines: {node: '>=12'}
+  whatwg-mimetype@3.0.0: {}
 
-  /whatwg-mimetype@4.0.0:
-    resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
-    engines: {node: '>=18'}
-    dev: false
+  whatwg-mimetype@4.0.0: {}
 
-  /whatwg-url@14.0.0:
-    resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
-    engines: {node: '>=18'}
+  whatwg-url@14.0.0:
     dependencies:
       tr46: 5.0.0
       webidl-conversions: 7.0.0
-    dev: false
 
-  /whatwg-url@5.0.0:
-    resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+  whatwg-url@5.0.0:
     dependencies:
       tr46: 0.0.3
       webidl-conversions: 3.0.1
 
-  /which-boxed-primitive@1.0.2:
-    resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+  which-boxed-primitive@1.0.2:
     dependencies:
       is-bigint: 1.0.4
       is-boolean-object: 1.1.2
       is-number-object: 1.0.7
       is-string: 1.0.7
       is-symbol: 1.0.4
-    dev: true
 
-  /which-collection@1.0.1:
-    resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
+  which-collection@1.0.1:
     dependencies:
       is-map: 2.0.2
       is-set: 2.0.2
       is-weakmap: 2.0.1
       is-weakset: 2.0.2
-    dev: true
 
-  /which-module@2.0.0:
-    resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
-    dev: false
+  which-module@2.0.0: {}
 
-  /which-typed-array@1.1.11:
-    resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==}
-    engines: {node: '>= 0.4'}
+  which-typed-array@1.1.11:
     dependencies:
       available-typed-arrays: 1.0.5
       call-bind: 1.0.2
       for-each: 0.3.3
       gopd: 1.0.1
       has-tostringtag: 1.0.0
-    dev: true
 
-  /which@1.3.1:
-    resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
-    hasBin: true
+  which@1.3.1:
     dependencies:
       isexe: 2.0.0
-    dev: false
 
-  /which@2.0.2:
-    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
-    engines: {node: '>= 8'}
-    hasBin: true
+  which@2.0.2:
     dependencies:
       isexe: 2.0.0
 
-  /which@4.0.0:
-    resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==}
-    engines: {node: ^16.13.0 || >=18.0.0}
-    hasBin: true
+  which@4.0.0:
     dependencies:
       isexe: 3.1.1
-    dev: false
 
-  /why-is-node-running@2.2.2:
-    resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
-    engines: {node: '>=8'}
-    hasBin: true
+  why-is-node-running@2.2.2:
     dependencies:
       siginfo: 2.0.0
       stackback: 0.0.2
-    dev: true
 
-  /wide-align@1.1.5:
-    resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
-    requiresBuild: true
+  wide-align@1.1.5:
     dependencies:
       string-width: 4.2.3
-    dev: false
     optional: true
 
-  /with@7.0.2:
-    resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
-    engines: {node: '>= 10.0.0'}
+  with@7.0.2:
     dependencies:
       '@babel/parser': 7.23.9
       '@babel/types': 7.23.5
       assert-never: 1.2.1
       babel-walk: 3.0.0-canary-5
 
-  /wordwrap@1.0.0:
-    resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
-    dev: true
+  wordwrap@1.0.0: {}
 
-  /wrap-ansi@6.2.0:
-    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
-    engines: {node: '>=8'}
+  wrap-ansi@6.2.0:
     dependencies:
       ansi-styles: 4.3.0
       string-width: 4.2.3
       strip-ansi: 6.0.1
 
-  /wrap-ansi@7.0.0:
-    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
-    engines: {node: '>=10'}
+  wrap-ansi@7.0.0:
     dependencies:
       ansi-styles: 4.3.0
       string-width: 4.2.3
       strip-ansi: 6.0.1
 
-  /wrap-ansi@8.1.0:
-    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
-    engines: {node: '>=12'}
+  wrap-ansi@8.1.0:
     dependencies:
       ansi-styles: 6.2.1
       string-width: 5.1.2
       strip-ansi: 7.1.0
 
-  /wrappy@1.0.2:
-    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+  wrappy@1.0.2: {}
 
-  /write-file-atomic@2.4.3:
-    resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==}
+  write-file-atomic@2.4.3:
     dependencies:
       graceful-fs: 4.2.11
       imurmurhash: 0.1.4
       signal-exit: 3.0.7
-    dev: true
 
-  /write-file-atomic@4.0.2:
-    resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+  write-file-atomic@4.0.2:
     dependencies:
       imurmurhash: 0.1.4
       signal-exit: 3.0.7
-    dev: true
 
-  /ws@8.16.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
-    resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
-    engines: {node: '>=10.0.0'}
-    peerDependencies:
-      bufferutil: ^4.0.1
-      utf-8-validate: '>=5.0.2'
-    peerDependenciesMeta:
-      bufferutil:
-        optional: true
-      utf-8-validate:
-        optional: true
-    dependencies:
+  ws@8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+    optionalDependencies:
       bufferutil: 4.0.7
       utf-8-validate: 6.0.3
 
-  /xev@3.0.2:
-    resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==}
-    dev: false
+  xev@3.0.2: {}
 
-  /xml-js@1.6.11:
-    resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==}
-    hasBin: true
+  xml-js@1.6.11:
     dependencies:
       sax: 1.2.4
-    dev: false
 
-  /xml-name-validator@4.0.0:
-    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
-    engines: {node: '>=12'}
-    dev: true
+  xml-name-validator@4.0.0: {}
 
-  /xml-name-validator@5.0.0:
-    resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
-    engines: {node: '>=18'}
-    dev: false
+  xml-name-validator@5.0.0: {}
 
-  /xml2js@0.5.0:
-    resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
-    engines: {node: '>=4.0.0'}
+  xml2js@0.5.0:
     dependencies:
       sax: 1.2.4
       xmlbuilder: 11.0.1
-    dev: false
 
-  /xmlbuilder@11.0.1:
-    resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
-    engines: {node: '>=4.0'}
-    dev: false
+  xmlbuilder@11.0.1: {}
 
-  /xmlchars@2.2.0:
-    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
-    dev: false
+  xmlchars@2.2.0: {}
 
-  /xtend@4.0.2:
-    resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
-    engines: {node: '>=0.4'}
+  xtend@4.0.2: {}
 
-  /y18n@4.0.3:
-    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
-    dev: false
+  y18n@4.0.3: {}
 
-  /y18n@5.0.8:
-    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
-    engines: {node: '>=10'}
+  y18n@5.0.8: {}
 
-  /yallist@2.1.2:
-    resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
-    dev: false
+  yallist@2.1.2: {}
 
-  /yallist@3.1.1:
-    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+  yallist@3.1.1: {}
 
-  /yallist@4.0.0:
-    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+  yallist@4.0.0: {}
 
-  /yargs-parser@18.1.3:
-    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
-    engines: {node: '>=6'}
+  yargs-parser@18.1.3:
     dependencies:
       camelcase: 5.3.1
       decamelize: 1.2.0
-    dev: false
 
-  /yargs-parser@20.2.9:
-    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
-    engines: {node: '>=10'}
+  yargs-parser@20.2.9: {}
 
-  /yargs-parser@21.1.1:
-    resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
-    engines: {node: '>=12'}
+  yargs-parser@21.1.1: {}
 
-  /yargs@15.4.1:
-    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
-    engines: {node: '>=8'}
+  yargs@15.4.1:
     dependencies:
       cliui: 6.0.0
       decamelize: 1.2.0
@@ -19982,11 +23503,8 @@ packages:
       which-module: 2.0.0
       y18n: 4.0.3
       yargs-parser: 18.1.3
-    dev: false
 
-  /yargs@16.2.0:
-    resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
-    engines: {node: '>=10'}
+  yargs@16.2.0:
     dependencies:
       cliui: 7.0.4
       escalade: 3.1.1
@@ -19995,11 +23513,8 @@ packages:
       string-width: 4.2.3
       y18n: 5.0.8
       yargs-parser: 20.2.9
-    dev: false
 
-  /yargs@17.7.2:
-    resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
-    engines: {node: '>=12'}
+  yargs@17.7.2:
     dependencies:
       cliui: 8.0.1
       escalade: 3.1.1
@@ -20009,98 +23524,27 @@ packages:
       y18n: 5.0.8
       yargs-parser: 21.1.1
 
-  /yauzl@2.10.0:
-    resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
+  yauzl@2.10.0:
     dependencies:
       buffer-crc32: 0.2.13
       fd-slicer: 1.1.0
-    dev: true
 
-  /yocto-queue@0.1.0:
-    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
-    engines: {node: '>=10'}
+  yocto-queue@0.1.0: {}
 
-  /yocto-queue@1.0.0:
-    resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
-    engines: {node: '>=12.20'}
-    dev: true
+  yocto-queue@1.0.0: {}
 
-  /z-schema@5.0.5:
-    resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==}
-    engines: {node: '>=8.0.0'}
-    hasBin: true
+  z-schema@5.0.5:
     dependencies:
       lodash.get: 4.4.2
       lodash.isequal: 4.5.0
       validator: 13.9.0
     optionalDependencies:
       commander: 9.5.0
-    dev: true
-
-  /zip-stream@5.0.1:
-    resolution: {integrity: sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==}
-    engines: {node: '>= 12.0.0'}
-    dependencies:
-      archiver-utils: 4.0.1
-      compress-commons: 5.0.1
-      readable-stream: 3.6.0
-    dev: false
-
-  /zwitch@2.0.4:
-    resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
-    dev: true
-
-  '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz':
-    resolution: {tarball: https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz}
-    name: '@aiscript-dev/aiscript-languageserver'
-    version: 0.1.6
-    hasBin: true
-    dependencies:
-      seedrandom: 3.0.5
-      stringz: 2.1.0
-      uuid: 9.0.1
-      vscode-languageserver: 9.0.1
-      vscode-languageserver-textdocument: 1.0.11
-    dev: false
 
-  github.com/aiscript-dev/aiscript-vscode/3f79d6f0550369267220aa67702287948d885424:
-    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424}
-    name: aiscript-vscode
-    version: 0.1.4
-    engines: {vscode: ^1.83.0}
+  zip-stream@6.0.1:
     dependencies:
-      '@aiscript-dev/aiscript-languageserver': '@github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz'
-      vscode-languageclient: 9.0.1
-    dev: false
+      archiver-utils: 5.0.2
+      compress-commons: 6.0.2
+      readable-stream: 4.3.0
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9)(@storybook/components@8.0.9)(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9)(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9)(@storybook/types@8.0.9)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
-    id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640
-    name: storybook-addon-misskey-theme
-    version: 0.0.0
-    peerDependencies:
-      '@storybook/blocks': ^7.0.0-rc.4
-      '@storybook/components': ^7.0.0-rc.4
-      '@storybook/core-events': ^7.0.0-rc.4
-      '@storybook/manager-api': ^7.0.0-rc.4
-      '@storybook/preview-api': ^7.0.0-rc.4
-      '@storybook/theming': ^7.0.0-rc.4
-      '@storybook/types': ^7.0.0-rc.4
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
-    dependencies:
-      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 8.0.9
-      '@storybook/manager-api': 8.0.9(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 8.0.9
-      '@storybook/theming': 8.0.9(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 8.0.9
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
+  zwitch@2.0.4: {}
diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs
index e7684d7cc9e4..b5aa5eb4ab55 100644
--- a/scripts/build-assets.mjs
+++ b/scripts/build-assets.mjs
@@ -34,7 +34,7 @@ async function copyFrontendFonts() {
 }
 
 async function copyFrontendTablerIcons() {
-  await fs.cp('./packages/frontend/node_modules/@tabler/icons-webfont', './built/_frontend_dist_/tabler-icons', { dereference: true, recursive: true });
+  await fs.cp('./packages/frontend/node_modules/@tabler/icons-webfont/dist', './built/_frontend_dist_/tabler-icons', { dereference: true, recursive: true });
 }
 
 async function copyFrontendLocales() {

From fc77ad9355f74ec4b4b155a9d5624850b3dff351 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Mon, 6 May 2024 20:37:04 +0900
Subject: [PATCH 187/266] refactor(frontend): provide linkNavigationBehavior

---
 packages/frontend/src/components/MkAbuseReport.vue   |  2 +-
 packages/frontend/src/components/MkLink.vue          |  4 ++--
 packages/frontend/src/components/MkMention.vue       |  4 ++--
 packages/frontend/src/components/global/MkA.vue      | 12 +++++-------
 .../components/global/MkMisskeyFlavoredMarkdown.ts   | 10 ++++------
 packages/frontend/src/components/global/MkUrl.vue    |  4 ++--
 6 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index ab65ea7ec70e..a28e7c25592d 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</div>
 	<div class="detail">
 		<div>
-			<Mfm :text="report.comment" :linkBehavior="'window'"/>
+			<Mfm :text="report.comment" :linkNavigationBehavior="'window'"/>
 		</div>
 		<hr/>
 		<div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link" :behavior="'window'">@{{ report.reporter.username }}</MkA></div>
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index bd1bd0e24a81..5d54a58e97d9 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <component
 	:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target"
-	:behavior="props.behavior"
+	:behavior="props.navigationBehavior"
 	:title="url"
 >
 	<slot></slot>
@@ -25,7 +25,7 @@ import { MkABehavior } from '@/components/global/MkA.vue';
 const props = withDefaults(defineProps<{
 	url: string;
 	rel?: null | string;
-	behavior?: MkABehavior;
+	navigationBehavior?: MkABehavior;
 }>(), {
 });
 
diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue
index cbefecf03a0b..bfb49a416ef3 100644
--- a/packages/frontend/src/components/MkMention.vue
+++ b/packages/frontend/src/components/MkMention.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }" :behavior="behavior">
+<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }" :behavior="navigationBehavior">
 	<img :class="$style.icon" :src="avatarUrl" alt="">
 	<span>
 		<span>@{{ username }}</span>
@@ -26,7 +26,7 @@ import { MkABehavior } from '@/components/global/MkA.vue';
 const props = defineProps<{
 	username: string;
 	host: string;
-	behavior?: MkABehavior;
+	navigationBehavior?: MkABehavior;
 }>();
 
 const canonical = props.host === localHost ? `@${props.username}` : `@${props.username}@${toUnicode(props.host)}`;
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index b64acacc3214..d1e9113c48b9 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -14,7 +14,7 @@ export type MkABehavior = 'window' | 'browser' | null;
 </script>
 
 <script lang="ts" setup>
-import { computed, shallowRef } from 'vue';
+import { computed, inject, shallowRef } from 'vue';
 import * as os from '@/os.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
@@ -30,7 +30,7 @@ const props = withDefaults(defineProps<{
 	behavior: null,
 });
 
-const linkBehaviour = props.behavior;
+const behavior = props.behavior ?? inject<MkABehavior>('linkNavigationBehavior', null);
 
 const el = shallowRef<HTMLElement>();
 
@@ -86,15 +86,13 @@ function openWindow() {
 }
 
 function nav(ev: MouseEvent) {
-	if (props.behavior === 'browser') {
+	if (behavior === 'browser') {
 		location.href = props.to;
 		return;
 	}
 
-	if (props.behavior) {
-		if (props.behavior === 'window') {
-			return openWindow();
-		}
+	if (behavior === 'window') {
+		return openWindow();
 	}
 
 	if (ev.shiftKey) {
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 6e880fc3227b..cab8d9c704f5 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { VNode, h, SetupContext } from 'vue';
+import { VNode, h, SetupContext, provide } from 'vue';
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import MkUrl from '@/components/global/MkUrl.vue';
@@ -43,7 +43,7 @@ type MfmProps = {
 	parsedNodes?: mfm.MfmNode[] | null;
 	enableEmojiMenu?: boolean;
 	enableEmojiMenuReaction?: boolean;
-	linkBehavior?: MkABehavior;
+	linkNavigationBehavior?: MkABehavior;
 };
 
 type MfmEvents = {
@@ -52,6 +52,8 @@ type MfmEvents = {
 
 // eslint-disable-next-line import/no-default-export
 export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEvents>['emit'] }) {
+	provide('linkNavigationBehavior', props.linkNavigationBehavior);
+
 	const isNote = props.isNote ?? true;
 	const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat : false : false;
 
@@ -343,7 +345,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					url: token.props.url,
 					rel: 'nofollow noopener',
-					behavior: props.linkBehavior,
 				})];
 			}
 
@@ -352,7 +353,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					url: token.props.url,
 					rel: 'nofollow noopener',
-					behavior: props.linkBehavior,
 				}, genEl(token.children, scale, true))];
 			}
 
@@ -361,7 +361,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					host: (token.props.host == null && props.author && props.author.host != null ? props.author.host : token.props.host) ?? host,
 					username: token.props.username,
-					behavior: props.linkBehavior,
 				})];
 			}
 
@@ -370,7 +369,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 					key: Math.random(),
 					to: isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
 					style: 'color:var(--hashtag);',
-					behavior: props.linkBehavior,
 				}, `#${token.props.hashtag}`)];
 			}
 
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 1c2f3ccedb56..9d4cd559d9a4 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <component
 	:is="self ? 'MkA' : 'a'" ref="el" :class="$style.root" class="_link" :[attr]="self ? props.url.substring(local.length) : props.url" :rel="rel ?? 'nofollow noopener'" :target="target"
-	:behavior = "props.behavior"
+	:behavior="props.navigationBehavior"
 	@contextmenu.stop="() => {}"
 >
 	<template v-if="!self">
@@ -38,7 +38,7 @@ const props = withDefaults(defineProps<{
 	url: string;
 	rel?: string;
 	showUrlPreview?: boolean;
-	behavior?: MkABehavior;
+	navigationBehavior?: MkABehavior;
 }>(), {
 	showUrlPreview: true,
 });

From c639f30d39d3c4d08405df8a3eab541486efda9a Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Mon, 6 May 2024 20:41:39 +0900
Subject: [PATCH 188/266] Update CHANGELOG.md

---
 CHANGELOG.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c54fa6ed78c2..d95ea3fc3883 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,7 +39,7 @@
 - Enhance: Playを手動でリロードできるように
 - Enhance: 通報のコメント内のリンクをクリックした際、ウィンドウで開くように
 - Enhance: `Ui:C:postForm` および `Ui:C:postFormButton` に `localOnly` と `visibility` を設定できるように
-- Chore: AiScriptを0.18.0にバージョンアップ
+- Enhance: AiScriptを0.18.0にバージョンアップ
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
@@ -63,6 +63,8 @@
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
 - Enhance: misskey-dev/summaly@5.1.0の取り込み(プレビュー生成処理の効率化)
+- Enhance: ドライブのファイルがNSFWかどうか個別に連合されるように (#13756)
+  - 可能な場合、ノートの添付ファイルのセンシティブ判定がファイル単位になります
 - Fix: リモートから配送されたアクティビティにJSON-LD compactionをかける
 - Fix: フォローリクエストを作成する際に既存のものは削除するように  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/440)
@@ -78,8 +80,6 @@
 - Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
 - Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
 - Fix: AP Link等は添付ファイル扱いしないようになど (#13754)
-- Enhance: ドライブのファイルがNSFWかどうか個別に連合されるように (#13756)
-  - 可能な場合、ノートの添付ファイルのセンシティブ判定がファイル単位になります
 
 ## 2024.3.1
 

From 73a5b6cec904f251bcd043018e00480b84887f19 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 6 May 2024 11:50:00 +0000
Subject: [PATCH 189/266] Bump version to 2024.5.0-beta.0

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d95ea3fc3883..bc00d5975c9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## Unreleased
+## 2024.5.0
 
 ### Note
 - コントロールパネル内にあるサマリープロキシの設定個所がセキュリティから全般へ変更となります。

From 455543b36e1bf7b4cda56b96a7ef20b2473aa654 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 May 2024 21:36:05 +0900
Subject: [PATCH 190/266] change package.json

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 23e0ea0ee5b9..6a377d899b29 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.3.1",
+	"version": "2024.5.0-beta.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 6badc7f3eea8..294c23086b8e 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.3.1",
+	"version": "2024.5.0-beta.0",
 	"description": "Misskey SDK for JavaScript",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",

From 313515c6817479c2d781c808e96acad7c41e61d3 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 7 May 2024 01:45:00 +0000
Subject: [PATCH 191/266] Bump version to 2024.5.0-beta.1

---
 package.json                     | 148 +++++++++++++++----------------
 packages/misskey-js/package.json | 122 ++++++++++++-------------
 2 files changed, 135 insertions(+), 135 deletions(-)

diff --git a/package.json b/package.json
index 6a377d899b29..bc5d396d6c09 100644
--- a/package.json
+++ b/package.json
@@ -1,76 +1,76 @@
 {
-	"name": "misskey",
-	"version": "2024.5.0-beta.0",
-	"codename": "nasubi",
-	"repository": {
-		"type": "git",
-		"url": "https://github.com/misskey-dev/misskey.git"
-	},
-	"packageManager": "pnpm@9.0.6",
-	"workspaces": [
-		"packages/frontend",
-		"packages/backend",
-		"packages/sw",
-		"packages/misskey-js",
-		"packages/misskey-reversi",
-		"packages/misskey-bubble-game"
-	],
-	"private": true,
-	"scripts": {
-		"build-pre": "node ./scripts/build-pre.js",
-		"build-assets": "node ./scripts/build-assets.mjs",
-		"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
-		"build-storybook": "pnpm --filter frontend build-storybook",
-		"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
-		"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
-		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
-		"init": "pnpm migrate",
-		"migrate": "cd packages/backend && pnpm migrate",
-		"revert": "cd packages/backend && pnpm revert",
-		"check:connect": "cd packages/backend && pnpm check:connect",
-		"migrateandstart": "pnpm migrate && pnpm start",
-		"watch": "pnpm dev",
-		"dev": "node scripts/dev.mjs",
-		"lint": "pnpm -r lint",
-		"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
-		"cy:run": "pnpm cypress run",
-		"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
-		"jest": "cd packages/backend && pnpm jest",
-		"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
-		"test": "pnpm -r test",
-		"test-and-coverage": "pnpm -r test-and-coverage",
-		"clean": "node ./scripts/clean.js",
-		"clean-all": "node ./scripts/clean-all.js",
-		"cleanall": "pnpm clean-all"
-	},
-	"resolutions": {
-		"chokidar": "3.5.3",
-		"lodash": "4.17.21"
-	},
-	"dependencies": {
-		"cssnano": "6.1.2",
-		"execa": "8.0.1",
-		"fast-glob": "3.3.2",
-		"ignore-walk": "6.0.4",
-		"js-yaml": "4.1.0",
-		"postcss": "8.4.38",
-		"tar": "6.2.1",
-		"terser": "5.30.3",
-		"typescript": "5.4.5",
-		"esbuild": "0.20.2",
-		"glob": "10.3.12"
-	},
-	"devDependencies": {
-		"@types/node": "20.12.7",
-		"@typescript-eslint/eslint-plugin": "7.7.1",
-		"@typescript-eslint/parser": "7.7.1",
-		"cross-env": "7.0.3",
-		"cypress": "13.7.3",
-		"eslint": "8.57.0",
-		"ncp": "2.0.0",
-		"start-server-and-test": "2.0.3"
-	},
-	"optionalDependencies": {
-		"@tensorflow/tfjs-core": "4.4.0"
-	}
+  "name": "misskey",
+  "version": "2024.5.0-beta.1",
+  "codename": "nasubi",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/misskey-dev/misskey.git"
+  },
+  "packageManager": "pnpm@9.0.6",
+  "workspaces": [
+    "packages/frontend",
+    "packages/backend",
+    "packages/sw",
+    "packages/misskey-js",
+    "packages/misskey-reversi",
+    "packages/misskey-bubble-game"
+  ],
+  "private": true,
+  "scripts": {
+    "build-pre": "node ./scripts/build-pre.js",
+    "build-assets": "node ./scripts/build-assets.mjs",
+    "build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
+    "build-storybook": "pnpm --filter frontend build-storybook",
+    "build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
+    "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
+    "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
+    "init": "pnpm migrate",
+    "migrate": "cd packages/backend && pnpm migrate",
+    "revert": "cd packages/backend && pnpm revert",
+    "check:connect": "cd packages/backend && pnpm check:connect",
+    "migrateandstart": "pnpm migrate && pnpm start",
+    "watch": "pnpm dev",
+    "dev": "node scripts/dev.mjs",
+    "lint": "pnpm -r lint",
+    "cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
+    "cy:run": "pnpm cypress run",
+    "e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
+    "jest": "cd packages/backend && pnpm jest",
+    "jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
+    "test": "pnpm -r test",
+    "test-and-coverage": "pnpm -r test-and-coverage",
+    "clean": "node ./scripts/clean.js",
+    "clean-all": "node ./scripts/clean-all.js",
+    "cleanall": "pnpm clean-all"
+  },
+  "resolutions": {
+    "chokidar": "3.5.3",
+    "lodash": "4.17.21"
+  },
+  "dependencies": {
+    "cssnano": "6.1.2",
+    "execa": "8.0.1",
+    "fast-glob": "3.3.2",
+    "ignore-walk": "6.0.4",
+    "js-yaml": "4.1.0",
+    "postcss": "8.4.38",
+    "tar": "6.2.1",
+    "terser": "5.30.3",
+    "typescript": "5.4.5",
+    "esbuild": "0.20.2",
+    "glob": "10.3.12"
+  },
+  "devDependencies": {
+    "@types/node": "20.12.7",
+    "@typescript-eslint/eslint-plugin": "7.7.1",
+    "@typescript-eslint/parser": "7.7.1",
+    "cross-env": "7.0.3",
+    "cypress": "13.7.3",
+    "eslint": "8.57.0",
+    "ncp": "2.0.0",
+    "start-server-and-test": "2.0.3"
+  },
+  "optionalDependencies": {
+    "@tensorflow/tfjs-core": "4.4.0"
+  }
 }
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 294c23086b8e..e7766cda12ba 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,63 +1,63 @@
 {
-	"type": "module",
-	"name": "misskey-js",
-	"version": "2024.5.0-beta.0",
-	"description": "Misskey SDK for JavaScript",
-	"main": "./built/index.js",
-	"types": "./built/index.d.ts",
-	"exports": {
-		".": {
-			"import": "./built/index.js",
-			"types": "./built/index.d.ts"
-		},
-		"./*": {
-			"import": "./built/*",
-			"types": "./built/*"
-		}
-	},
-	"scripts": {
-		"build": "node ./build.js",
-		"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
-		"tsd": "tsd",
-		"api": "pnpm api-extractor run --local --verbose",
-		"api-prod": "pnpm api-extractor run --verbose",
-		"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
-		"typecheck": "tsc --noEmit",
-		"lint": "pnpm typecheck && pnpm eslint",
-		"jest": "jest --coverage --detectOpenHandles",
-		"test": "pnpm jest && pnpm tsd",
-		"update-autogen-code": "pnpm --filter misskey-js-type-generator generate && ncp generator/built/autogen src/autogen"
-	},
-	"repository": {
-		"type": "git",
-		"url": "git+https://github.com/misskey-dev/misskey.js.git"
-	},
-	"devDependencies": {
-		"@microsoft/api-extractor": "7.43.1",
-		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@swc/jest": "0.2.36",
-		"@types/jest": "29.5.12",
-		"@types/node": "20.12.7",
-		"@typescript-eslint/eslint-plugin": "7.7.1",
-		"@typescript-eslint/parser": "7.7.1",
-		"eslint": "8.57.0",
-		"jest": "29.7.0",
-		"jest-fetch-mock": "3.0.3",
-		"jest-websocket-mock": "2.5.0",
-		"mock-socket": "9.3.1",
-		"ncp": "2.0.0",
-		"nodemon": "3.1.0",
-		"execa": "8.0.1",
-		"tsd": "0.30.7",
-		"typescript": "5.4.5",
-		"esbuild": "0.19.11",
-		"glob": "10.3.12"
-	},
-	"files": [
-		"built"
-	],
-	"dependencies": {
-		"eventemitter3": "5.0.1",
-		"reconnecting-websocket": "4.4.0"
-	}
+  "type": "module",
+  "name": "misskey-js",
+  "version": "2024.5.0-beta.1",
+  "description": "Misskey SDK for JavaScript",
+  "main": "./built/index.js",
+  "types": "./built/index.d.ts",
+  "exports": {
+    ".": {
+      "import": "./built/index.js",
+      "types": "./built/index.d.ts"
+    },
+    "./*": {
+      "import": "./built/*",
+      "types": "./built/*"
+    }
+  },
+  "scripts": {
+    "build": "node ./build.js",
+    "watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
+    "tsd": "tsd",
+    "api": "pnpm api-extractor run --local --verbose",
+    "api-prod": "pnpm api-extractor run --verbose",
+    "eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
+    "typecheck": "tsc --noEmit",
+    "lint": "pnpm typecheck && pnpm eslint",
+    "jest": "jest --coverage --detectOpenHandles",
+    "test": "pnpm jest && pnpm tsd",
+    "update-autogen-code": "pnpm --filter misskey-js-type-generator generate && ncp generator/built/autogen src/autogen"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/misskey-dev/misskey.js.git"
+  },
+  "devDependencies": {
+    "@microsoft/api-extractor": "7.43.1",
+    "@misskey-dev/eslint-plugin": "1.0.0",
+    "@swc/jest": "0.2.36",
+    "@types/jest": "29.5.12",
+    "@types/node": "20.12.7",
+    "@typescript-eslint/eslint-plugin": "7.7.1",
+    "@typescript-eslint/parser": "7.7.1",
+    "eslint": "8.57.0",
+    "jest": "29.7.0",
+    "jest-fetch-mock": "3.0.3",
+    "jest-websocket-mock": "2.5.0",
+    "mock-socket": "9.3.1",
+    "ncp": "2.0.0",
+    "nodemon": "3.1.0",
+    "execa": "8.0.1",
+    "tsd": "0.30.7",
+    "typescript": "5.4.5",
+    "esbuild": "0.19.11",
+    "glob": "10.3.12"
+  },
+  "files": [
+    "built"
+  ],
+  "dependencies": {
+    "eventemitter3": "5.0.1",
+    "reconnecting-websocket": "4.4.0"
+  }
 }

From 0fd06e3f0d8589176d33acf2b15decd0643eac29 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 May 2024 11:07:16 +0900
Subject: [PATCH 192/266] fix

---
 package.json                     | 148 +++++++++++++++----------------
 packages/misskey-js/package.json | 122 ++++++++++++-------------
 2 files changed, 135 insertions(+), 135 deletions(-)

diff --git a/package.json b/package.json
index bc5d396d6c09..e05fbdcca74d 100644
--- a/package.json
+++ b/package.json
@@ -1,76 +1,76 @@
 {
-  "name": "misskey",
-  "version": "2024.5.0-beta.1",
-  "codename": "nasubi",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/misskey-dev/misskey.git"
-  },
-  "packageManager": "pnpm@9.0.6",
-  "workspaces": [
-    "packages/frontend",
-    "packages/backend",
-    "packages/sw",
-    "packages/misskey-js",
-    "packages/misskey-reversi",
-    "packages/misskey-bubble-game"
-  ],
-  "private": true,
-  "scripts": {
-    "build-pre": "node ./scripts/build-pre.js",
-    "build-assets": "node ./scripts/build-assets.mjs",
-    "build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
-    "build-storybook": "pnpm --filter frontend build-storybook",
-    "build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
-    "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
-    "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
-    "init": "pnpm migrate",
-    "migrate": "cd packages/backend && pnpm migrate",
-    "revert": "cd packages/backend && pnpm revert",
-    "check:connect": "cd packages/backend && pnpm check:connect",
-    "migrateandstart": "pnpm migrate && pnpm start",
-    "watch": "pnpm dev",
-    "dev": "node scripts/dev.mjs",
-    "lint": "pnpm -r lint",
-    "cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
-    "cy:run": "pnpm cypress run",
-    "e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
-    "jest": "cd packages/backend && pnpm jest",
-    "jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
-    "test": "pnpm -r test",
-    "test-and-coverage": "pnpm -r test-and-coverage",
-    "clean": "node ./scripts/clean.js",
-    "clean-all": "node ./scripts/clean-all.js",
-    "cleanall": "pnpm clean-all"
-  },
-  "resolutions": {
-    "chokidar": "3.5.3",
-    "lodash": "4.17.21"
-  },
-  "dependencies": {
-    "cssnano": "6.1.2",
-    "execa": "8.0.1",
-    "fast-glob": "3.3.2",
-    "ignore-walk": "6.0.4",
-    "js-yaml": "4.1.0",
-    "postcss": "8.4.38",
-    "tar": "6.2.1",
-    "terser": "5.30.3",
-    "typescript": "5.4.5",
-    "esbuild": "0.20.2",
-    "glob": "10.3.12"
-  },
-  "devDependencies": {
-    "@types/node": "20.12.7",
-    "@typescript-eslint/eslint-plugin": "7.7.1",
-    "@typescript-eslint/parser": "7.7.1",
-    "cross-env": "7.0.3",
-    "cypress": "13.7.3",
-    "eslint": "8.57.0",
-    "ncp": "2.0.0",
-    "start-server-and-test": "2.0.3"
-  },
-  "optionalDependencies": {
-    "@tensorflow/tfjs-core": "4.4.0"
-  }
+	"name": "misskey",
+	"version": "2024.5.0-beta.1",
+	"codename": "nasubi",
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/misskey-dev/misskey.git"
+	},
+	"packageManager": "pnpm@9.0.6",
+	"workspaces": [
+		"packages/frontend",
+		"packages/backend",
+		"packages/sw",
+		"packages/misskey-js",
+		"packages/misskey-reversi",
+		"packages/misskey-bubble-game"
+	],
+	"private": true,
+	"scripts": {
+		"build-pre": "node ./scripts/build-pre.js",
+		"build-assets": "node ./scripts/build-assets.mjs",
+		"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
+		"build-storybook": "pnpm --filter frontend build-storybook",
+		"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
+		"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
+		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
+		"init": "pnpm migrate",
+		"migrate": "cd packages/backend && pnpm migrate",
+		"revert": "cd packages/backend && pnpm revert",
+		"check:connect": "cd packages/backend && pnpm check:connect",
+		"migrateandstart": "pnpm migrate && pnpm start",
+		"watch": "pnpm dev",
+		"dev": "node scripts/dev.mjs",
+		"lint": "pnpm -r lint",
+		"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
+		"cy:run": "pnpm cypress run",
+		"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
+		"jest": "cd packages/backend && pnpm jest",
+		"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
+		"test": "pnpm -r test",
+		"test-and-coverage": "pnpm -r test-and-coverage",
+		"clean": "node ./scripts/clean.js",
+		"clean-all": "node ./scripts/clean-all.js",
+		"cleanall": "pnpm clean-all"
+	},
+	"resolutions": {
+		"chokidar": "3.5.3",
+		"lodash": "4.17.21"
+	},
+	"dependencies": {
+		"cssnano": "6.1.2",
+		"execa": "8.0.1",
+		"fast-glob": "3.3.2",
+		"ignore-walk": "6.0.4",
+		"js-yaml": "4.1.0",
+		"postcss": "8.4.38",
+		"tar": "6.2.1",
+		"terser": "5.30.3",
+		"typescript": "5.4.5",
+		"esbuild": "0.20.2",
+		"glob": "10.3.12"
+	},
+	"devDependencies": {
+		"@types/node": "20.12.7",
+		"@typescript-eslint/eslint-plugin": "7.7.1",
+		"@typescript-eslint/parser": "7.7.1",
+		"cross-env": "7.0.3",
+		"cypress": "13.7.3",
+		"eslint": "8.57.0",
+		"ncp": "2.0.0",
+		"start-server-and-test": "2.0.3"
+	},
+	"optionalDependencies": {
+		"@tensorflow/tfjs-core": "4.4.0"
+	}
 }
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index e7766cda12ba..cc5d5bdbf4fa 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,63 +1,63 @@
 {
-  "type": "module",
-  "name": "misskey-js",
-  "version": "2024.5.0-beta.1",
-  "description": "Misskey SDK for JavaScript",
-  "main": "./built/index.js",
-  "types": "./built/index.d.ts",
-  "exports": {
-    ".": {
-      "import": "./built/index.js",
-      "types": "./built/index.d.ts"
-    },
-    "./*": {
-      "import": "./built/*",
-      "types": "./built/*"
-    }
-  },
-  "scripts": {
-    "build": "node ./build.js",
-    "watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
-    "tsd": "tsd",
-    "api": "pnpm api-extractor run --local --verbose",
-    "api-prod": "pnpm api-extractor run --verbose",
-    "eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
-    "typecheck": "tsc --noEmit",
-    "lint": "pnpm typecheck && pnpm eslint",
-    "jest": "jest --coverage --detectOpenHandles",
-    "test": "pnpm jest && pnpm tsd",
-    "update-autogen-code": "pnpm --filter misskey-js-type-generator generate && ncp generator/built/autogen src/autogen"
-  },
-  "repository": {
-    "type": "git",
-    "url": "git+https://github.com/misskey-dev/misskey.js.git"
-  },
-  "devDependencies": {
-    "@microsoft/api-extractor": "7.43.1",
-    "@misskey-dev/eslint-plugin": "1.0.0",
-    "@swc/jest": "0.2.36",
-    "@types/jest": "29.5.12",
-    "@types/node": "20.12.7",
-    "@typescript-eslint/eslint-plugin": "7.7.1",
-    "@typescript-eslint/parser": "7.7.1",
-    "eslint": "8.57.0",
-    "jest": "29.7.0",
-    "jest-fetch-mock": "3.0.3",
-    "jest-websocket-mock": "2.5.0",
-    "mock-socket": "9.3.1",
-    "ncp": "2.0.0",
-    "nodemon": "3.1.0",
-    "execa": "8.0.1",
-    "tsd": "0.30.7",
-    "typescript": "5.4.5",
-    "esbuild": "0.19.11",
-    "glob": "10.3.12"
-  },
-  "files": [
-    "built"
-  ],
-  "dependencies": {
-    "eventemitter3": "5.0.1",
-    "reconnecting-websocket": "4.4.0"
-  }
+	"type": "module",
+	"name": "misskey-js",
+	"version": "2024.5.0-beta.1",
+	"description": "Misskey SDK for JavaScript",
+	"main": "./built/index.js",
+	"types": "./built/index.d.ts",
+	"exports": {
+		".": {
+			"import": "./built/index.js",
+			"types": "./built/index.d.ts"
+		},
+		"./*": {
+			"import": "./built/*",
+			"types": "./built/*"
+		}
+	},
+	"scripts": {
+		"build": "node ./build.js",
+		"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
+		"tsd": "tsd",
+		"api": "pnpm api-extractor run --local --verbose",
+		"api-prod": "pnpm api-extractor run --verbose",
+		"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
+		"typecheck": "tsc --noEmit",
+		"lint": "pnpm typecheck && pnpm eslint",
+		"jest": "jest --coverage --detectOpenHandles",
+		"test": "pnpm jest && pnpm tsd",
+		"update-autogen-code": "pnpm --filter misskey-js-type-generator generate && ncp generator/built/autogen src/autogen"
+	},
+	"repository": {
+		"type": "git",
+		"url": "git+https://github.com/misskey-dev/misskey.js.git"
+	},
+	"devDependencies": {
+		"@microsoft/api-extractor": "7.43.1",
+		"@misskey-dev/eslint-plugin": "1.0.0",
+		"@swc/jest": "0.2.36",
+		"@types/jest": "29.5.12",
+		"@types/node": "20.12.7",
+		"@typescript-eslint/eslint-plugin": "7.7.1",
+		"@typescript-eslint/parser": "7.7.1",
+		"eslint": "8.57.0",
+		"jest": "29.7.0",
+		"jest-fetch-mock": "3.0.3",
+		"jest-websocket-mock": "2.5.0",
+		"mock-socket": "9.3.1",
+		"ncp": "2.0.0",
+		"nodemon": "3.1.0",
+		"execa": "8.0.1",
+		"tsd": "0.30.7",
+		"typescript": "5.4.5",
+		"esbuild": "0.19.11",
+		"glob": "10.3.12"
+	},
+	"files": [
+		"built"
+	],
+	"dependencies": {
+		"eventemitter3": "5.0.1",
+		"reconnecting-websocket": "4.4.0"
+	}
 }

From f5d57c02c7edbf71c4f2eaff789dfd093513027d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 May 2024 14:38:43 +0900
Subject: [PATCH 193/266] dev: modify release manager to set indent type

---
 .github/workflows/release-with-dispatch.yml | 3 +++
 .github/workflows/release-with-ready.yml    | 1 +
 2 files changed, 4 insertions(+)

diff --git a/.github/workflows/release-with-dispatch.yml b/.github/workflows/release-with-dispatch.yml
index 1a954739d959..bc6448cb37d8 100644
--- a/.github/workflows/release-with-dispatch.yml
+++ b/.github/workflows/release-with-dispatch.yml
@@ -61,6 +61,7 @@ jobs:
         -
 
       use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+      indent: ${{ vars.INDENT }}
     secrets:
       RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
       RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
@@ -75,6 +76,7 @@ jobs:
       pr_number: ${{ needs.get-pr.outputs.pr_number }}
       package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
       use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+      indent: ${{ vars.INDENT }}
     secrets:
       RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
       RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
@@ -115,6 +117,7 @@ jobs:
       #  }
       package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
       use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+      indent: ${{ vars.INDENT }}
     secrets:
       RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
       RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
diff --git a/.github/workflows/release-with-ready.yml b/.github/workflows/release-with-ready.yml
index b64ed20791b0..139503e563e8 100644
--- a/.github/workflows/release-with-ready.yml
+++ b/.github/workflows/release-with-ready.yml
@@ -33,6 +33,7 @@ jobs:
       pr_number: ${{ github.event.pull_request.number }}
       package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
       use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
+      indent: ${{ vars.INDENT }}
     secrets:
       RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
       RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}

From b298897bdedc988800f72798cdbf2dbcd2e994ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 10 May 2024 15:32:23 +0900
Subject: [PATCH 194/266] =?UTF-8?q?fix(backend):=20=E4=B8=8D=E8=A6=81?=
 =?UTF-8?q?=E3=81=AAUserProfile=E3=81=AE=E5=8F=96=E5=BE=97=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#13812)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): 不要なuserProfileの取得を修正

* fix: pnpm@9.0.6 to pnpm@9.1.0

* Revert "fix: pnpm@9.0.6 to pnpm@9.1.0"

This reverts commit eaf265ec2cf255cadeaa516d5b668134bc397211.
---
 packages/backend/src/core/entities/UserEntityService.ts | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index df2b27d70974..d85f31a9895a 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -637,18 +637,17 @@ export class UserEntityService implements OnModuleInit {
 		}
 		const _userIds = _users.map(u => u.id);
 
-		// -- 特に前提条件のない値群を取得
-
-		const profilesMap = await this.userProfilesRepository.findBy({ userId: In(_userIds) })
-			.then(profiles => new Map(profiles.map(p => [p.userId, p])));
-
 		// -- 実行者の有無や指定スキーマの種別によって要否が異なる値群を取得
 
+		let profilesMap: Map<MiUser['id'], MiUserProfile> = new Map();
 		let userRelations: Map<MiUser['id'], UserRelation> = new Map();
 		let userMemos: Map<MiUser['id'], string | null> = new Map();
 		let pinNotes: Map<MiUser['id'], MiUserNotePining[]> = new Map();
 
 		if (options?.schema !== 'UserLite') {
+			profilesMap = await this.userProfilesRepository.findBy({ userId: In(_userIds) })
+				.then(profiles => new Map(profiles.map(p => [p.userId, p])));
+
 			const meId = me ? me.id : null;
 			if (meId) {
 				userMemos = await this.userMemosRepository.findBy({ userId: meId })

From f6af6d9679305b36dc993a310462a6065248ae1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 10 May 2024 15:33:25 +0900
Subject: [PATCH 195/266] =?UTF-8?q?fix(backend):=20UserEntityService.getRe?=
 =?UTF-8?q?lations=E3=81=AE=E5=8F=96=E5=BE=97=E5=87=A6=E7=90=86=E3=82=92?=
 =?UTF-8?q?=E8=BB=BD=E9=87=8F=E5=8C=96=20(#13811)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): UserEntityService.getRelationsの取得処理を軽量化

* rollback
---
 .../src/core/entities/UserEntityService.ts    | 49 +++++++++++++------
 1 file changed, 35 insertions(+), 14 deletions(-)

diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index d85f31a9895a..b80a1ec206b4 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -249,20 +249,41 @@ export class UserEntityService implements OnModuleInit {
 		] = await Promise.all([
 			this.followingsRepository.findBy({ followerId: me })
 				.then(f => new Map(f.map(it => [it.followeeId, it]))),
-			this.followingsRepository.findBy({ followeeId: me })
-				.then(it => it.map(it => it.followerId)),
-			this.followRequestsRepository.findBy({ followerId: me })
-				.then(it => it.map(it => it.followeeId)),
-			this.followRequestsRepository.findBy({ followeeId: me })
-				.then(it => it.map(it => it.followerId)),
-			this.blockingsRepository.findBy({ blockerId: me })
-				.then(it => it.map(it => it.blockeeId)),
-			this.blockingsRepository.findBy({ blockeeId: me })
-				.then(it => it.map(it => it.blockerId)),
-			this.mutingsRepository.findBy({ muterId: me })
-				.then(it => it.map(it => it.muteeId)),
-			this.renoteMutingsRepository.findBy({ muterId: me })
-				.then(it => it.map(it => it.muteeId)),
+			this.followingsRepository.createQueryBuilder('f')
+				.select('f.followerId')
+				.where('f.followeeId = :me', { me })
+				.getRawMany<{ f_followerId: string }>()
+				.then(it => it.map(it => it.f_followerId)),
+			this.followRequestsRepository.createQueryBuilder('f')
+				.select('f.followeeId')
+				.where('f.followerId = :me', { me })
+				.getRawMany<{ f_followeeId: string }>()
+				.then(it => it.map(it => it.f_followeeId)),
+			this.followRequestsRepository.createQueryBuilder('f')
+				.select('f.followerId')
+				.where('f.followeeId = :me', { me })
+				.getRawMany<{ f_followerId: string }>()
+				.then(it => it.map(it => it.f_followerId)),
+			this.blockingsRepository.createQueryBuilder('b')
+				.select('b.blockeeId')
+				.where('b.blockerId = :me', { me })
+				.getRawMany<{ b_blockeeId: string }>()
+				.then(it => it.map(it => it.b_blockeeId)),
+			this.blockingsRepository.createQueryBuilder('b')
+				.select('b.blockerId')
+				.where('b.blockeeId = :me', { me })
+				.getRawMany<{ b_blockerId: string }>()
+				.then(it => it.map(it => it.b_blockerId)),
+			this.mutingsRepository.createQueryBuilder('m')
+				.select('m.muteeId')
+				.where('m.muterId = :me', { me })
+				.getRawMany<{ m_muteeId: string }>()
+				.then(it => it.map(it => it.m_muteeId)),
+			this.renoteMutingsRepository.createQueryBuilder('m')
+				.select('m.muteeId')
+				.where('m.muterId = :me', { me })
+				.getRawMany<{ m_muteeId: string }>()
+				.then(it => it.map(it => it.m_muteeId)),
 		]);
 
 		return new Map(

From 12ae9a2b23436732ef453c6bca5f22bbca229d2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=B5=E3=82=8B=E3=81=B5=E3=82=8B?= <git@fluoride.dev>
Date: Mon, 13 May 2024 11:19:19 +0900
Subject: [PATCH 196/266] =?UTF-8?q?feat:=20DevContainer=E3=81=ABpnpm?=
 =?UTF-8?q?=E3=82=92=E3=82=A4=E3=83=B3=E3=82=B9=E3=83=88=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=E9=9A=9B=E3=80=81corepack=E3=82=92=E4=BD=BF?=
 =?UTF-8?q?=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#1382?=
 =?UTF-8?q?1)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .devcontainer/devcontainer.json | 6 ++----
 .devcontainer/init.sh           | 2 ++
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 182ee2fbb2cc..31b6212cb5c4 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -4,12 +4,10 @@
 	"service": "app",
 	"workspaceFolder": "/workspace",
 	"features": {
-		"ghcr.io/devcontainers-contrib/features/pnpm:2": {
-			"version": "8.9.2"
-		},
 		"ghcr.io/devcontainers/features/node:1": {
 			"version": "20.12.2"
-		}
+		},
+		"ghcr.io/devcontainers-contrib/features/corepack:1": {}
 	},
 	"forwardPorts": [3000],
 	"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh",
diff --git a/.devcontainer/init.sh b/.devcontainer/init.sh
index bcad3e6d8527..729e1a9d2d9d 100755
--- a/.devcontainer/init.sh
+++ b/.devcontainer/init.sh
@@ -4,6 +4,8 @@ set -xe
 
 sudo chown -R node /workspace
 git submodule update --init
+corepack install
+corepack enable
 pnpm config set store-dir /home/node/.local/share/pnpm/store
 pnpm install --frozen-lockfile
 cp .devcontainer/devcontainer.yml .config/default.yml

From 9b0fc317514a9c6ec8f400a317e67fdeba160e7a Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 14 May 2024 19:18:30 +0900
Subject: [PATCH 197/266] Update FUNDING.yml

---
 .github/FUNDING.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index c6b2a1611c49..d42b58abc09c 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,4 @@
 # These are supported funding model platforms
 
+github: [misskey-dev]
 patreon: syuilo

From def7b8c55e342aea855e56da634994c02f56f600 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 18 May 2024 12:42:26 +0900
Subject: [PATCH 198/266] fix(frontend): fix Chromatic test fails (#13826)

* fix: attempt to fix Chromatic test fails

* chore: add comment
---
 packages/frontend/src/components/MkModal.vue              | 5 ++++-
 .../frontend/src/components/global/MkAd.stories.impl.ts   | 8 +++++++-
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index eb240da75978..9e69ab2207f5 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -276,8 +276,11 @@ const align = () => {
 const onOpened = () => {
 	emit('opened');
 
+	// NOTE: Chromatic テストの際に undefined になる場合がある
+	if (content.value == null) return;
+
 	// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
-	const el = content.value!.children[0];
+	const el = content.value.children[0];
 	el.addEventListener('mousedown', ev => {
 		contentClicking = true;
 		window.addEventListener('mouseup', ev => {
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index a1d274382fa3..aef26ab92d64 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -11,6 +11,10 @@ import { i18n } from '@/i18n.js';
 
 let lock: Promise<undefined> | undefined;
 
+function sleep(ms: number) {
+	return new Promise(resolve => setTimeout(resolve, ms));
+}
+
 const common = {
 	render(args) {
 		return {
@@ -43,6 +47,8 @@ const common = {
 		lock = new Promise(r => resolve = r);
 
 		try {
+			// NOTE: sleep しないと何故か落ちる
+			await sleep(100);
 			const canvas = within(canvasElement);
 			const a = canvas.getByRole<HTMLAnchorElement>('link');
 			// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
@@ -53,7 +59,7 @@ const common = {
 			const i = buttons[0];
 			await expect(i).toBeInTheDocument();
 			await userEvent.click(i);
-			// await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
+			await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
 			await expect(a).not.toBeInTheDocument();
 			await expect(i).not.toBeInTheDocument();
 			buttons = canvas.getAllByRole<HTMLButtonElement>('button');

From ba62b7378bb13b384bd9db27acb0013fb90b53b3 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 18 May 2024 18:52:17 +0900
Subject: [PATCH 199/266] fix(storybook): fix wrong `tabler-icons` CSS path
 (#13828)

---
 packages/frontend/.storybook/preview-head.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html
index 4722fe7f5f46..ae42fd49bc6a 100644
--- a/packages/frontend/.storybook/preview-head.html
+++ b/packages/frontend/.storybook/preview-head.html
@@ -5,7 +5,7 @@
 
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
-<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.3.0/tabler-icons.min.css">
+<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.3.0/dist/tabler-icons.min.css">
 <link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
 <style>
 	html {

From acf84a2516c08b7d3a26a2521a9bef5543303ed2 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 20 May 2024 08:28:28 +0900
Subject: [PATCH 200/266] =?UTF-8?q?FTT=E3=81=8C=E6=9C=89=E5=8A=B9=E3=81=8B?=
 =?UTF-8?q?=E3=81=A4sinceId=E3=81=AE=E3=81=BF=E3=82=92=E6=8C=87=E5=AE=9A?=
 =?UTF-8?q?=E3=81=97=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AB=E5=B8=B0=E3=81=A3?=
 =?UTF-8?q?=E3=81=A6=E6=9D=A5=E3=82=8B=E3=83=AC=E3=82=B9=E3=83=9D=E3=83=B3?=
 =?UTF-8?q?=E3=82=B9=E3=81=8C=E9=80=86=E9=A0=86=E3=81=A7=E3=81=82=E3=82=8B?=
 =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13837)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: FTTが有効かつsinceIdのみを指定した場合に帰って来るレスポンスが逆順である問題

* docs(changelog): FTTが有効かつsinceIdのみを指定した場合に帰って来るレスポンスが逆順である問題を修正
---
 CHANGELOG.md                                        |  1 +
 .../src/core/FanoutTimelineEndpointService.ts       | 13 +++++--------
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d95ea3fc3883..280c63b2a749 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -80,6 +80,7 @@
 - Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
 - Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
 - Fix: AP Link等は添付ファイル扱いしないようになど (#13754)
+- Fix: FTTが有効かつsinceIdのみを指定した場合に帰って来るレスポンスが逆順である問題を修正
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 884723ff819f..d5058f37c2a5 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -61,8 +61,8 @@ export class FanoutTimelineEndpointService {
 		// 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える
 		if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]);
 
-		const shouldPrepend = ps.sinceId && !ps.untilId;
-		const idCompare: (a: string, b: string) => number = shouldPrepend ? (a, b) => a < b ? -1 : 1 : (a, b) => a > b ? -1 : 1;
+		const ascending = ps.sinceId && !ps.untilId;
+		const idCompare: (a: string, b: string) => number = ascending ? (a, b) => a < b ? -1 : 1 : (a, b) => a > b ? -1 : 1;
 
 		const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
 
@@ -142,9 +142,7 @@ export class FanoutTimelineEndpointService {
 
 				if (ps.allowPartial ? redisTimeline.length !== 0 : redisTimeline.length >= ps.limit) {
 					// 十分Redisからとれた
-					const result = redisTimeline.slice(0, ps.limit);
-					if (shouldPrepend) result.reverse();
-					return result;
+					return redisTimeline.slice(0, ps.limit);
 				}
 			}
 
@@ -152,8 +150,7 @@ export class FanoutTimelineEndpointService {
 			const remainingToRead = ps.limit - redisTimeline.length;
 			let dbUntil: string | null;
 			let dbSince: string | null;
-			if (shouldPrepend) {
-				redisTimeline.reverse();
+			if (ascending) {
 				dbUntil = ps.untilId;
 				dbSince = noteIds[noteIds.length - 1];
 			} else {
@@ -161,7 +158,7 @@ export class FanoutTimelineEndpointService {
 				dbSince = ps.sinceId;
 			}
 			const gotFromDb = await ps.dbFallback(dbUntil, dbSince, remainingToRead);
-			return shouldPrepend ? [...gotFromDb, ...redisTimeline] : [...redisTimeline, ...gotFromDb];
+			return [...redisTimeline, ...gotFromDb];
 		}
 
 		return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);

From 4d0db37d2e5baddea3995e222bddca7032052ef1 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 20 May 2024 18:05:46 +0900
Subject: [PATCH 201/266] fix notification limit with exclude/include types
 (#13836)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: /i/notificationsがsinceIdのみのときに正しく動かない問題

Fix #10902 again

* chore: use exclusive range to fetch data

* fix: フィルタによって通知が0件だった場合でもリトライするように

* docs(changelog): `/i/notifications`に includeTypes`か`excludeTypes`を指定しているとき、通知が存在するのに空配列を返すことがある問題を修正
---
 CHANGELOG.md                                  |  1 +
 .../server/api/endpoints/i/notifications.ts   | 68 +++++++++++++------
 2 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 280c63b2a749..0a19f32db619 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -81,6 +81,7 @@
 - Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
 - Fix: AP Link等は添付ファイル扱いしないようになど (#13754)
 - Fix: FTTが有効かつsinceIdのみを指定した場合に帰って来るレスポンスが逆順である問題を修正
+- Fix: `/i/notifications`に `includeTypes`か`excludeTypes`を指定しているとき、通知が存在するのに空配列を返すことがある問題を修正
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts
index 320d9fdb0039..2f619380e9f8 100644
--- a/packages/backend/src/server/api/endpoints/i/notifications.ts
+++ b/packages/backend/src/server/api/endpoints/i/notifications.ts
@@ -7,7 +7,7 @@ import { In } from 'typeorm';
 import * as Redis from 'ioredis';
 import { Inject, Injectable } from '@nestjs/common';
 import type { NotesRepository } from '@/models/_.js';
-import { obsoleteNotificationTypes, notificationTypes, FilterUnionByProperty } from '@/types.js';
+import { FilterUnionByProperty, notificationTypes, obsoleteNotificationTypes } from '@/types.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteReadService } from '@/core/NoteReadService.js';
 import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js';
@@ -84,27 +84,51 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][];
 			const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][];
 
-			const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1
-			const notificationsRes = await this.redisClient.xrevrange(
-				`notificationTimeline:${me.id}`,
-				ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : '+',
-				ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : '-',
-				'COUNT', limit);
-
-			if (notificationsRes.length === 0) {
-				return [];
-			}
-
-			let notifications = notificationsRes.map(x => JSON.parse(x[1][1])).filter(x => x.id !== ps.untilId && x !== ps.sinceId) as MiNotification[];
-
-			if (includeTypes && includeTypes.length > 0) {
-				notifications = notifications.filter(notification => includeTypes.includes(notification.type));
-			} else if (excludeTypes && excludeTypes.length > 0) {
-				notifications = notifications.filter(notification => !excludeTypes.includes(notification.type));
-			}
-
-			if (notifications.length === 0) {
-				return [];
+			let sinceTime = ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime().toString() : null;
+			let untilTime = ps.untilId ? this.idService.parse(ps.untilId).date.getTime().toString() : null;
+
+			let notifications: MiNotification[];
+			for (;;) {
+				let notificationsRes: [id: string, fields: string[]][];
+
+				// sinceidのみの場合は古い順、そうでない場合は新しい順。 QueryService.makePaginationQueryも参照
+				if (sinceTime && !untilTime) {
+					notificationsRes = await this.redisClient.xrange(
+						`notificationTimeline:${me.id}`,
+						'(' + sinceTime,
+						'+',
+						'COUNT', ps.limit);
+				} else {
+					notificationsRes = await this.redisClient.xrevrange(
+						`notificationTimeline:${me.id}`,
+						untilTime ? '(' + untilTime : '+',
+						sinceTime ? '(' + sinceTime : '-',
+						'COUNT', ps.limit);
+				}
+
+				if (notificationsRes.length === 0) {
+					return [];
+				}
+
+				notifications = notificationsRes.map(x => JSON.parse(x[1][1])) as MiNotification[];
+
+				if (includeTypes && includeTypes.length > 0) {
+					notifications = notifications.filter(notification => includeTypes.includes(notification.type));
+				} else if (excludeTypes && excludeTypes.length > 0) {
+					notifications = notifications.filter(notification => !excludeTypes.includes(notification.type));
+				}
+
+				if (notifications.length !== 0) {
+					// 通知が1件以上ある場合は返す
+					break;
+				}
+
+				// フィルタしたことで通知が0件になった場合、次のページを取得する
+				if (ps.sinceId && !ps.untilId) {
+					sinceTime = notificationsRes[notificationsRes.length - 1][0];
+				} else {
+					untilTime = notificationsRes[notificationsRes.length - 1][0];
+				}
 			}
 
 			// Mark all as read

From f6df94070b0fc1ca862d560b17488bd718c2ec85 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 20 May 2024 18:08:20 +0900
Subject: [PATCH 202/266] Exclude channel notes from featured polls (#13838)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(backend): add `channelId` to `MiPoll` as a Denormalized field

* feat(backend): option to exclude polls in channels

* chore: exclude channel notes from featured polls

* docs(changelog): みつけるのアンケート欄にてチャンネルのアンケートが含まれてしまう問題を修正

* fix: missing license header
---
 CHANGELOG.md                                  |  1 +
 ...29964060-ChannelIdDenormalizedForMiPoll.js | 21 +++++++++++++++++++
 .../backend/src/core/NoteCreateService.ts     |  1 +
 packages/backend/src/models/Poll.ts           |  9 ++++++++
 .../endpoints/notes/polls/recommendation.ts   |  7 +++++++
 .../frontend/src/pages/explore.featured.vue   |  3 +++
 packages/misskey-js/src/autogen/types.ts      |  2 ++
 7 files changed, 44 insertions(+)
 create mode 100644 packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a19f32db619..d0b98db96a87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@
   - 「アカウントを見つけやすくする」が有効なユーザーか
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 - Fix: 正規化されていない状態のhashtagが連合されてきたhtmlに含まれているとhashtagが正しくhashtagに復元されない問題を修正
+- Fix: みつけるのアンケート欄にてチャンネルのアンケートが含まれてしまう問題を修正
 
 ### Client
 - Feat: アップロードするファイルの名前をランダム文字列にできるように
diff --git a/packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js b/packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js
new file mode 100644
index 000000000000..f736378c04c9
--- /dev/null
+++ b/packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js
@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class ChannelIdDenormalizedForMiPoll1716129964060 {
+    name = 'ChannelIdDenormalizedForMiPoll1716129964060'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "poll" ADD "channelId" character varying(32)`);
+        await queryRunner.query(`COMMENT ON COLUMN "poll"."channelId" IS '[Denormalized]'`);
+        await queryRunner.query(`CREATE INDEX "IDX_c1240fcc9675946ea5d6c2860e" ON "poll" ("channelId") `);
+        await queryRunner.query(`UPDATE "poll" SET "channelId" = "note"."channelId" FROM "note" WHERE "poll"."noteId" = "note"."id"`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`DROP INDEX "public"."IDX_c1240fcc9675946ea5d6c2860e"`);
+        await queryRunner.query(`COMMENT ON COLUMN "poll"."channelId" IS '[Denormalized]'`);
+        await queryRunner.query(`ALTER TABLE "poll" DROP COLUMN "channelId"`);
+    }
+}
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 32104fea9072..e5580f36d111 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -473,6 +473,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 						noteVisibility: insert.visibility,
 						userId: user.id,
 						userHost: user.host,
+						channelId: insert.channelId,
 					});
 
 					await transactionalEntityManager.insert(MiPoll, poll);
diff --git a/packages/backend/src/models/Poll.ts b/packages/backend/src/models/Poll.ts
index c2693dbb193c..ca985c8b243a 100644
--- a/packages/backend/src/models/Poll.ts
+++ b/packages/backend/src/models/Poll.ts
@@ -8,6 +8,7 @@ import { noteVisibilities } from '@/types.js';
 import { id } from './util/id.js';
 import { MiNote } from './Note.js';
 import type { MiUser } from './User.js';
+import type { MiChannel } from "@/models/Channel.js";
 
 @Entity('poll')
 export class MiPoll {
@@ -58,6 +59,14 @@ export class MiPoll {
 		comment: '[Denormalized]',
 	})
 	public userHost: string | null;
+
+	@Index()
+	@Column({
+		...id(),
+		nullable: true,
+		comment: '[Denormalized]',
+	})
+	public channelId: MiChannel['id'] | null;
 	//#endregion
 
 	constructor(data: Partial<MiPoll>) {
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
index ba38573065c3..4fd6f8682dc0 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
@@ -32,6 +32,7 @@ export const paramDef = {
 	properties: {
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
 		offset: { type: 'integer', default: 0 },
+		excludeChannels: { type: 'boolean', default: false },
 	},
 	required: [],
 } as const;
@@ -86,6 +87,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			query.setParameters(mutingQuery.getParameters());
 			//#endregion
 
+			//#region exclude channels
+			if (ps.excludeChannels) {
+				query.andWhere('poll.channelId IS NULL');
+			}
+			//#endregion
+
 			const polls = await query
 				.orderBy('poll.noteId', 'DESC')
 				.limit(ps.limit)
diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue
index b5c8e7016686..cfdb235d3a59 100644
--- a/packages/frontend/src/pages/explore.featured.vue
+++ b/packages/frontend/src/pages/explore.featured.vue
@@ -29,6 +29,9 @@ const paginationForPolls = {
 	endpoint: 'notes/polls/recommendation' as const,
 	limit: 10,
 	offsetMode: true,
+	params: {
+		excludeChannels: true,
+	},
 };
 
 const tab = ref('notes');
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 1b9f1304d581..302587ccfa6b 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -21019,6 +21019,8 @@ export type operations = {
           limit?: number;
           /** @default 0 */
           offset?: number;
+          /** @default false */
+          excludeChannels?: boolean;
         };
       };
     };

From ed74f7b4a88223fe11b63d424bb0f90768a88926 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 20 May 2024 18:55:42 +0900
Subject: [PATCH 203/266] ci: use pnpm version from packageManager field in the
 package.json. (#13825)

---
 .github/workflows/check-misskey-js-autogen.yml |  4 +---
 .github/workflows/get-api-diff.yml             |  5 +----
 .github/workflows/lint.yml                     | 15 +++------------
 .github/workflows/on-release-created.yml       |  5 +----
 .github/workflows/storybook.yml                |  5 +----
 .github/workflows/test-backend.yml             | 10 ++--------
 .github/workflows/test-frontend.yml            | 10 ++--------
 .github/workflows/test-production.yml          |  5 +----
 .github/workflows/validate-api-json.yml        |  5 +----
 9 files changed, 13 insertions(+), 51 deletions(-)

diff --git a/.github/workflows/check-misskey-js-autogen.yml b/.github/workflows/check-misskey-js-autogen.yml
index 9052b2e3722e..39acad8bc348 100644
--- a/.github/workflows/check-misskey-js-autogen.yml
+++ b/.github/workflows/check-misskey-js-autogen.yml
@@ -24,9 +24,7 @@ jobs:
           ref: refs/pull/${{ github.event.pull_request.number }}/merge
 
       - name: setup pnpm
-        uses: pnpm/action-setup@v3
-        with:
-          version: 9
+        uses: pnpm/action-setup@v4
 
       - name: setup node
         id: setup-node
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 146e0686e584..9b9c8f11c413 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -32,10 +32,7 @@ jobs:
         ref: ${{ matrix.ref }}
         submodules: true
     - name: Install pnpm
-      uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+      uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
       with:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 9a269014ab82..76616ec5a71a 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -27,10 +27,7 @@ jobs:
       with:
         fetch-depth: 0
         submodules: true
-    - uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+    - uses: pnpm/action-setup@v4
     - uses: actions/setup-node@v4.0.2
       with:
         node-version-file: '.node-version'
@@ -54,10 +51,7 @@ jobs:
       with:
         fetch-depth: 0
         submodules: true
-    - uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+    - uses: pnpm/action-setup@v4
     - uses: actions/setup-node@v4.0.2
       with:
         node-version-file: '.node-version'
@@ -80,10 +74,7 @@ jobs:
       with:
         fetch-depth: 0
         submodules: true
-    - uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+    - uses: pnpm/action-setup@v4
     - uses: actions/setup-node@v4.0.2
       with:
         node-version-file: '.node-version'
diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml
index 52463d7542f9..edfdab99e930 100644
--- a/.github/workflows/on-release-created.yml
+++ b/.github/workflows/on-release-created.yml
@@ -24,10 +24,7 @@ jobs:
         with:
           submodules: true
       - name: Install pnpm
-        uses: pnpm/action-setup@v3
-        with:
-          version: 9
-          run_install: false
+        uses: pnpm/action-setup@v4
       - name: Use Node.js ${{ matrix.node-version }}
         uses: actions/setup-node@v4.0.2
         with:
diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml
index 3bc354b331c7..c52883ffdde9 100644
--- a/.github/workflows/storybook.yml
+++ b/.github/workflows/storybook.yml
@@ -34,10 +34,7 @@ jobs:
         echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT
         git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3)
     - name: Install pnpm
-      uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+      uses: pnpm/action-setup@v4
     - name: Use Node.js 20.x
       uses: actions/setup-node@v4.0.2
       with:
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 525cd0916b0f..b1c54bb3e77e 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -41,10 +41,7 @@ jobs:
       with:
         submodules: true
     - name: Install pnpm
-      uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+      uses: pnpm/action-setup@v4
     - name: Install FFmpeg
       uses: FedericoCarboni/setup-ffmpeg@v3
     - name: Use Node.js ${{ matrix.node-version }}
@@ -93,10 +90,7 @@ jobs:
         with:
           submodules: true
       - name: Install pnpm
-        uses: pnpm/action-setup@v3
-        with:
-          version: 9
-          run_install: false
+        uses: pnpm/action-setup@v4
       - name: Use Node.js ${{ matrix.node-version }}
         uses: actions/setup-node@v4.0.2
         with:
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 9df3c983935a..9d5053b82aa8 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -33,10 +33,7 @@ jobs:
       with:
         submodules: true
     - name: Install pnpm
-      uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+      uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
       with:
@@ -91,10 +88,7 @@ jobs:
     #- uses: browser-actions/setup-firefox@latest
     #  if: ${{ matrix.browser == 'firefox' }}
     - name: Install pnpm
-      uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+      uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
       with:
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index 24a530e07370..7f8db652938f 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -23,10 +23,7 @@ jobs:
       with:
         submodules: true
     - name: Install pnpm
-      uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+      uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
       with:
diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml
index 229c447893bc..24340e7d81f2 100644
--- a/.github/workflows/validate-api-json.yml
+++ b/.github/workflows/validate-api-json.yml
@@ -24,10 +24,7 @@ jobs:
       with:
         submodules: true
     - name: Install pnpm
-      uses: pnpm/action-setup@v3
-      with:
-        version: 9
-        run_install: false
+      uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
       uses: actions/setup-node@v4.0.2
       with:

From 5836bd85df4fe511f0ab766349eb4c9d1e1e5fdf Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 20 May 2024 19:25:50 +0900
Subject: [PATCH 204/266] =?UTF-8?q?fix:=20=E8=A4=87=E6=95=B0id=E3=82=92?=
 =?UTF-8?q?=E6=8C=87=E5=AE=9A=E3=81=99=E3=82=8B`users/show`=E3=81=8C?=
 =?UTF-8?q?=E9=96=A2=E4=BF=82=E3=81=AA=E3=81=84=E3=83=A6=E3=83=BC=E3=82=B6?=
 =?UTF-8?q?=E3=82=92=E8=BF=94=E3=81=99=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82?=
 =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1376?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: 複数idを指定する`users/show`が関係ないユーザを返すことがある問題を修正

* test: fix misskey js test

* chore: user/showがnullを返さないように

* chore: pass lambda instead of pushVisibleUser
---
 CHANGELOG.md                                           |  1 +
 packages/backend/src/misc/json-schema.ts               |  2 +-
 .../backend/src/server/api/endpoints/users/show.ts     |  4 +++-
 packages/frontend/src/components/MkPostForm.vue        | 10 +++-------
 4 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d0b98db96a87..a9944d4b5eaa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -83,6 +83,7 @@
 - Fix: AP Link等は添付ファイル扱いしないようになど (#13754)
 - Fix: FTTが有効かつsinceIdのみを指定した場合に帰って来るレスポンスが逆順である問題を修正
 - Fix: `/i/notifications`に `includeTypes`か`excludeTypes`を指定しているとき、通知が存在するのに空配列を返すことがある問題を修正
+- Fix: 複数idを指定する`users/show`が関係ないユーザを返すことがある問題を修正
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index a620d7c94b8c..41e5bfe9e4ad 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -228,7 +228,7 @@ export type SchemaTypeDef<p extends Schema> =
 			p['items']['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<NonNullable<p['items']['allOf']>>>[] :
 			never
 		) :
-		p['items'] extends NonNullable<Schema> ? SchemaTypeDef<p['items']>[] :
+		p['items'] extends NonNullable<Schema> ? SchemaType<p['items']>[] :
 		any[]
 	) :
 	p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & PartialIntersection<UnionSchemaType<p['anyOf']>> :
diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts
index bd81989cb9c4..26cfa921c56a 100644
--- a/packages/backend/src/server/api/endpoints/users/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/show.ts
@@ -110,9 +110,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				});
 
 				// リクエストされた通りに並べ替え
+				// 順番は保持されるけど数は減ってる可能性がある
 				const _users: MiUser[] = [];
 				for (const id of ps.userIds) {
-					_users.push(users.find(x => x.id === id)!);
+					const user = users.find(x => x.id === id);
+					if (user != null) _users.push(user);
 				}
 
 				return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 7dbc1272986f..41d603e40ff7 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -190,7 +190,7 @@ const localOnly = ref(props.initialLocalOnly ?? (defaultStore.state.rememberNote
 const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility));
 const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]);
 if (props.initialVisibleUsers) {
-	props.initialVisibleUsers.forEach(pushVisibleUser);
+	props.initialVisibleUsers.forEach(u => pushVisibleUser(u));
 }
 const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
 const autocomplete = ref(null);
@@ -336,7 +336,7 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
 			misskeyApi('users/show', {
 				userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId),
 			}).then(users => {
-				users.forEach(pushVisibleUser);
+				users.forEach(u => pushVisibleUser(u));
 			});
 		}
 
@@ -967,11 +967,7 @@ onMounted(() => {
 				}
 				if (draft.data.visibleUserIds) {
 					misskeyApi('users/show', { userIds: draft.data.visibleUserIds }).then(users => {
-						for (let i = 0; i < users.length; i++) {
-							if (users[i].id === draft.data.visibleUserIds[i]) {
-								pushVisibleUser(users[i]);
-							}
-						}
+						users.forEach(u => pushVisibleUser(u));
 					});
 				}
 			}

From 367bf0c8fcd96ff56ce1016e52fcb4751331440a Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 20 May 2024 23:21:11 +0900
Subject: [PATCH 205/266] fix: `/share` with unicode characters in the URL
 (#13846)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: `/share` with unicode characters in the URL

* docs(changelog): `/share` で日本語等を含むurlがurlエンコードされない問題を修正
---
 CHANGELOG.md                          |  1 +
 packages/frontend/src/pages/share.vue | 29 ++++++++++++++++++++++++++-
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9944d4b5eaa..8d1b0a010a3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -60,6 +60,7 @@
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
 - Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
 - Fix: 連合なしの状態の読み書きができない問題を修正
+- Fix: `/share` で日本語等を含むurlがurlエンコードされない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index 680934e7ce78..37f6558d6432 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -64,7 +64,34 @@ async function init() {
 	// Googleニュース対策
 	if (text?.startsWith(`${title.value}.\n`)) noteText += text.replace(`${title.value}.\n`, '');
 	else if (text && title.value !== text) noteText += `${text}\n`;
-	if (url) noteText += `${url}`;
+	if (url) {
+		try {
+			// Normalize the URL to URL-encoded and puny-coded from with the URL constructor.
+			//
+			// It's common to use unicode characters in the URL for better visibility of URL
+			//     like: https://ja.wikipedia.org/wiki/ミスキー
+			//  or like: https://藍.moe/
+			// However, in the MFM, the unicode characters must be URL-encoded to be parsed as `url` node
+			//     like: https://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%B9%E3%82%AD%E3%83%BC
+			//  or like: https://xn--931a.moe/
+			// Therefore, we need to normalize the URL to URL-encoded form.
+			//
+			// The URL constructor will parse the URL and normalize unicode characters
+			//   in the host to punycode and in the path component to URL-encoded form.
+			//   (see url.spec.whatwg.org)
+			//
+			// In addition, the current MFM renderer decodes the URL-encoded path and / punycode encoded host name so
+			//   this normalization doesn't make the visible URL ugly.
+			//   (see MkUrl.vue)
+
+			noteText += new URL(url).href;
+		} catch {
+			// fallback to original URL if the URL is invalid.
+			// note that this is extremely rare since the `url` parameter is designed to share a URL and
+			// the URL constructor will throw TypeError only if failure, which means the URL is not valid.
+			noteText += url;
+		}
+	}
 	initialText.value = noteText.trim();
 
 	if (visibility.value === 'specified') {

From 1d4e6393f34179c40a91ad3f674c94a5d3942264 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Tue, 21 May 2024 10:10:59 +0900
Subject: [PATCH 206/266] ci: verify locale data (#13849)

* ci: verify locale data

* ci: separate workflows

* ci: missing installation
---
 .github/workflows/locale.yml | 27 ++++++++++++++++++
 locales/verify.js            | 53 ++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)
 create mode 100644 .github/workflows/locale.yml
 create mode 100644 locales/verify.js

diff --git a/.github/workflows/locale.yml b/.github/workflows/locale.yml
new file mode 100644
index 000000000000..de2247e77277
--- /dev/null
+++ b/.github/workflows/locale.yml
@@ -0,0 +1,27 @@
+name: Lint
+
+on:
+  push:
+    paths:
+      - locales/**
+  pull_request:
+    paths:
+      - locales/**
+
+jobs:
+  locale_verify:
+    runs-on: ubuntu-latest
+    continue-on-error: true
+    steps:
+    - uses: actions/checkout@v4.1.1
+      with:
+        fetch-depth: 0
+        submodules: true
+    - uses: pnpm/action-setup@v4
+    - uses: actions/setup-node@v4.0.2
+      with:
+        node-version-file: '.node-version'
+        cache: 'pnpm'
+    - run: corepack enable
+    - run: pnpm i --frozen-lockfile
+    - run: cd locales && node verify.js
diff --git a/locales/verify.js b/locales/verify.js
new file mode 100644
index 000000000000..a8e9875d6eda
--- /dev/null
+++ b/locales/verify.js
@@ -0,0 +1,53 @@
+import locales from './index.js';
+
+let valid = true;
+
+function writeError(type, lang, tree, data) {
+	process.stderr.write(JSON.stringify({ type, lang, tree, data }));
+	process.stderr.write('\n');
+	valid = false;
+}
+
+function verify(expected, actual, lang, trace) {
+	for (let key in expected) {
+		if (!Object.prototype.hasOwnProperty.call(actual, key)) {
+			continue;
+		}
+		if (typeof expected[key] === 'object') {
+			if (typeof actual[key] !== 'object') {
+				writeError('mismatched_type', lang, trace ? `${trace}.${key}` : key, { expected: 'object', actual: typeof actual[key] });
+				continue;
+			}
+			verify(expected[key], actual[key], lang, trace ? `${trace}.${key}` : key);
+		} else if (typeof expected[key] === 'string') {
+			switch (typeof actual[key]) {
+				case 'object':
+					writeError('mismatched_type', lang, trace ? `${trace}.${key}` : key, { expected: 'string', actual: 'object' });
+					break;
+				case 'undefined':
+					continue;
+				case 'string':
+					const expectedParameters = new Set(expected[key].match(/\{[^}]+\}/g)?.map((s) => s.slice(1, -1)));
+					const actualParameters = new Set(actual[key].match(/\{[^}]+\}/g)?.map((s) => s.slice(1, -1)));
+					for (let parameter of expectedParameters) {
+						if (!actualParameters.has(parameter)) {
+							writeError('missing_parameter', lang, trace ? `${trace}.${key}` : key, { parameter });
+						}
+					}
+			}
+		}
+	}
+}
+
+const { ['ja-JP']: original, ...verifiees } = locales;
+
+for (let lang in verifiees) {
+	if (!Object.prototype.hasOwnProperty.call(locales, lang)) {
+		continue;
+	}
+	verify(original, verifiees[lang], lang);
+}
+
+if (!valid) {
+	process.exit(1);
+}

From 3fba7686f8413442ff8b6e1149de36f81e75dfe1 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 21 May 2024 10:14:58 +0900
Subject: [PATCH 207/266] New Crowdin updates (#13500)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Romanian)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (Danish)

* New translations ja-jp.yml (Greek)

* New translations ja-jp.yml (Hungarian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Dutch)

* New translations ja-jp.yml (Norwegian)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Turkish)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Croatian)

* New translations ja-jp.yml (Uyghur)

* New translations ja-jp.yml (Lojban)

* New translations ja-jp.yml (Sinhala)

* New translations ja-jp.yml (Uzbek)

* New translations ja-jp.yml (Kannada)

* New translations ja-jp.yml (Lao)

* New translations ja-jp.yml (Haitian Creole)

* New translations ja-jp.yml (Kabyle)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Sinhala)

* New translations ja-jp.yml (Sinhala)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Vietnamese)
---
 locales/ar-SA.yml   |  16 +++-
 locales/bn-BD.yml   |   1 -
 locales/ca-ES.yml   |  25 +++--
 locales/cs-CZ.yml   |   6 +-
 locales/da-DK.yml   |   1 -
 locales/de-DE.yml   |   2 -
 locales/el-GR.yml   |   1 -
 locales/en-US.yml   |  45 ++++++++-
 locales/es-ES.yml   |  63 +++++++++++-
 locales/fr-FR.yml   |  43 +++++++--
 locales/hr-HR.yml   |   1 -
 locales/ht-HT.yml   |   1 -
 locales/hu-HU.yml   |   1 -
 locales/id-ID.yml   | 139 ++++++++++++++++++++++++++-
 locales/it-IT.yml   | 140 ++++++++++++++++++---------
 locales/ja-KS.yml   |  77 ++++++++++++++-
 locales/jbo-EN.yml  |   1 -
 locales/kab-KAB.yml |   1 -
 locales/kn-IN.yml   |   1 -
 locales/ko-GS.yml   |  87 ++++++++++-------
 locales/ko-KR.yml   | 227 ++++++++++++++++++++++++++------------------
 locales/lo-LA.yml   |   1 -
 locales/nl-NL.yml   |   1 -
 locales/no-NO.yml   |   1 -
 locales/pl-PL.yml   | 160 ++++++++++++++++++++++++++++++-
 locales/pt-PT.yml   |  15 ++-
 locales/ro-RO.yml   |   1 -
 locales/ru-RU.yml   |  17 +++-
 locales/si-LK.yml   |  19 +++-
 locales/sk-SK.yml   |   1 -
 locales/sv-SE.yml   |   1 -
 locales/th-TH.yml   |  49 +++++++++-
 locales/tr-TR.yml   |   1 -
 locales/ug-CN.yml   |   1 -
 locales/uk-UA.yml   |   1 -
 locales/uz-UZ.yml   |   1 -
 locales/vi-VN.yml   |  77 ++++++++++++++-
 locales/zh-CN.yml   |  53 +++++++++--
 locales/zh-TW.yml   |  55 +++++++++--
 39 files changed, 1073 insertions(+), 261 deletions(-)

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 17c8f24fa548..88707fe1118e 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -123,6 +123,7 @@ reactions: "التفاعلات"
 reactionSettingDescription2: "اسحب لترتيب ، انقر للحذف ، استخدم \"+\" للإضافة."
 rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات"
 attachCancel: "أزل المرفق"
+deleteFile: "حُذف الملف"
 markAsSensitive: "علّمه كمحتوى حساس"
 unmarkAsSensitive: "ألغ تعيينه كمحتوى حساس"
 enterFileName: "ادخل اسم الملف"
@@ -1565,8 +1566,21 @@ _webhookSettings:
     reaction: "عند التفاعل"
 _moderationLogTypes:
   suspend: "علِق"
+  deleteDriveFile: "حُذف الملف"
+  deleteNote: "حُذفت الملاحظة"
+  createGlobalAnnouncement: "أُنشئ إعلان عام"
+  createUserAnnouncement: "أُنشئ إعلان مستخدم"
+  updateGlobalAnnouncement: "حُدث إعلان عام"
+  updateUserAnnouncement: "حُدث إعلان مستخدم"
   resetPassword: "أعد تعيين كلمتك السرية"
   createInvitation: "ولِّد دعوة"
 _reversi:
   total: "المجموع"
-
+  lookingForPlayer: "يبحث عن خصم..."
+  gameCanceled: "أُلغيت اللعبة."
+  opponentHasSettingsChanged: "غيَر الخصم إعدادته."
+  showBoardLabels: "اعرض ترقيم الصفوف والأعمدة على اللوح"
+  useAvatarAsStone: "حوَل الحجارة إلى صور مستخدمين"
+_offlineScreen:
+  title: "غير متصل - يتعذر الاتصال بالخادم"
+  header: "يتعذر الاتصال بالخادم"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index 2a23cda06bda..dc5d315aed9f 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -1347,4 +1347,3 @@ _moderationLogTypes:
   resetPassword: "পাসওয়ার্ড রিসেট করুন"
 _reversi:
   total: "মোট"
-
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 2ea6bd9309d5..d035555c73af 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -400,6 +400,7 @@ name: "Nom"
 antennaSource: "Font de l'antena"
 antennaKeywords: "Paraules clau a seguir"
 antennaExcludeKeywords: "Paraules clau a excloure"
+antennaExcludeBots: "Exclou els bots"
 antennaKeywordsDescription: "Separar amb espais per la condició AND o amb salts de línia per la condició OR."
 notifyAntenna: "Notifica'm les publicacions noves"
 withFileAntenna: "Només les publicacions amb fitxers"
@@ -494,6 +495,7 @@ emojiStyle: "Estil d'emoji"
 native: "Nadiu"
 disableDrawer: "No mostrar els menús en calaixos"
 showNoteActionsOnlyHover: "Només mostra accions de la nota en passar amb el cursor"
+showReactionsCount: "Mostra el nombre de reaccions a les publicacions"
 noHistory: "No hi ha un registre previ"
 signinHistory: "Historial d'autenticacions"
 enableAdvancedMfm: "Habilitar l'MFM avançat"
@@ -543,7 +545,7 @@ objectStorageUseProxyDesc: "Desactiva'l si no faràs servir un Proxy per les con
 objectStorageSetPublicRead: "Configurar les pujades com públiques "
 s3ForcePathStyleDesc: "Si s3ForcePathStyle es troba activat el nom del dipòsit s'ha d'incloure a l'adreça URL en comtes del nom del host. Potser que necessitis activar-ho quan facis servir, per exemple, Minio a un servidor propi."
 serverLogs: "Registres del servidor"
-deleteAll: "Esborrar tot"
+deleteAll: "Elimina-ho tot"
 showFixedPostForm: "Mostrar el formulari per escriure a l'inici de la línia de temps"
 showFixedPostFormInChannel: "Mostrar el formulari d'escriptura al principi de la línia de temps (Canals)"
 withRepliesByDefaultForNewlyFollowed: "Inclou les respostes d'usuaris nous seguits a la línia de temps per defecte."
@@ -691,9 +693,9 @@ reporter: "Denunciant "
 reporteeOrigin: "Origen de la denúncia "
 reporterOrigin: "Origen del denunciant"
 forwardReport: "Transferir la denúncia a una instància remota"
-forwardReportIsAnonymous: "En comptes del teu compte, es farà servir un compte anònim com a denunciat a la instància remota."
-send: "Enviar"
-abuseMarkAsResolved: "Marcar la denúncia com a resolta"
+forwardReportIsAnonymous: "En lloc del teu compte, es farà servir un compte anònim com a denunciant al servidor remot."
+send: "Envia"
+abuseMarkAsResolved: "Marca la denúncia com a resolta"
 openInNewTab: "Obre a una pestanya nova"
 openInSideView: "Obre a una vista lateral"
 defaultNavigationBehaviour: "Navegació per defecte"
@@ -853,7 +855,7 @@ customCss: "CSS personalitzat"
 customCssWarn: "Aquesta configuració només hauries de configurar-la si saps que fas. Si poses valors inadequats pots fer que el client deixi de funcionar correctament."
 global: "Global"
 squareAvatars: "Mostrar avatars quadrats"
-sent: "Enviar"
+sent: "Envia"
 received: "Rebut"
 searchResult: "Resultats de la cerca"
 hashtags: "Etiquetes"
@@ -991,6 +993,7 @@ neverShow: "No mostrar més "
 remindMeLater: "Recorda-m'ho més tard"
 didYouLikeMisskey: "T'està agradant Misskey?"
 pleaseDonate: "A {host} fem servir el software lliure Misskey. Considera fer un donatiu a Misskey perquè pugui continuar el seu desenvolupament!"
+correspondingSourceIsAvailable: "El codi font corresponent està disponible a {anchor}."
 roles: "Rols"
 role: "Rols"
 noRole: "No s'han trobat rols"
@@ -1159,6 +1162,7 @@ showRenotes: "Mostrar impulsos"
 edited: "Editat"
 notificationRecieveConfig: "Paràmetres de notificacions"
 mutualFollow: "Seguidor mutu"
+followingOrFollower: "Seguit o seguidor"
 fileAttachedOnly: "Només notes amb adjunts"
 showRepliesToOthersInTimeline: "Mostrar les respostes a altres a la línia de temps"
 hideRepliesToOthersInTimeline: "Amagar les respostes a altres a la línia de temps"
@@ -1168,6 +1172,9 @@ confirmShowRepliesAll: "Aquesta opció no té marxa enrere. Vols mostrar les tev
 confirmHideRepliesAll: "Aquesta opció no té marxa enrere. Vols ocultar les teves respostes a tots els usuaris que segueixes a la línia de temps?"
 externalServices: "Serveis externs"
 sourceCode: "Codi font"
+repositoryUrl: "URL del repositori"
+feedback: "Opinió"
+feedbackUrl: "URL per a opinar"
 impressum: "Impressum"
 impressumUrl: "Adreça URL impressum"
 impressumDescription: "A països, com Alemanya, la inclusió de la informació de contacte de l'operador (un Impressum) és requereix de manera legal per llocs comercials."
@@ -1203,6 +1210,7 @@ soundWillBePlayed: "Es reproduiran efectes de so"
 showReplay: "Veure reproducció"
 replay: "Reproduir"
 replaying: "Reproduint"
+endReplay: "Tanca la redifusió"
 ranking: "Classificació"
 lastNDays: "Últims {n} dies"
 backToTitle: "Torna al títol"
@@ -1210,7 +1218,12 @@ hemisphere: "Geolocalització"
 withSensitive: "Incloure notes amb fitxers sensibles"
 userSaysSomethingSensitive: "La publicació de {name} conte material sensible"
 enableHorizontalSwipe: "Lliscar per canviar de pestanya"
+loading: "S’està carregant"
 surrender: "Cancel·lar "
+gameRetry: "Torna a provar"
+notUsePleaseLeaveBlank: "Si no voleu usar-ho, deixeu-ho en blanc"
+useTotp: "Usa una contrasenya d'un sol ús"
+useBackupCode: "Usa un codi de recuperació"
 _bubbleGame:
   howToPlay: "Com es juga"
   _howToPlay:
@@ -1915,7 +1928,6 @@ _2fa:
   registerTOTP: "Registrar una aplicació autenticadora"
   step1: "Primer instal·la una aplicació autenticadora (com {a} o {b}) al teu dispositiu."
   step2: "Després escaneja el codi QR que es mostra en aquesta pantalla."
-  step2Click: "Fent clic en aquest codi QR et permetrà registrar l'autenticació de doble factor a la teva clau de seguretat o en l'aplicació d'autenticació del teu dispositiu."
   step2Uri: "Escriu la següent URI si estàs fent servir una aplicació d'escriptori "
   step3Title: "Escriu un codi d'autenticació"
   step3: "Escriu el codi d'autenticació (token) que es mostra a la teva aplicació per finalitzar la configuració."
@@ -2255,4 +2267,3 @@ _externalResourceInstaller:
       title: "Paràmetres no vàlids "
 _reversi:
   total: "Total"
-
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index cbf5c33c18ee..cff533976ec6 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1664,7 +1664,6 @@ _2fa:
   registerTOTP: "Registrovat aplikaci autentizátoru"
   step1: "Nejprve si do zařízení nainstalujte aplikaci pro ověřování (například {a} nebo {b})."
   step2: "Poté naskenujte QR kód zobrazený na této obrazovce."
-  step2Click: "Kliknutím na tento QR kód můžete zaregistrovat 2FA do bezpečnostního klíče nebo aplikace autentizace telefonu."
   step3Title: "Zadejte ověřovací kód"
   step3: "Pro dokončení nastavení zadejte token poskytnutý vaší aplikací."
   step4: "Od této chvíle budou všechny budoucí pokusy o přihlášení vyžadovat tento přihlašovací token."
@@ -1718,7 +1717,7 @@ _auth:
   shareAccessTitle: "Udělovat oprávnění k aplikacím"
   shareAccess: "Chcete autorizovat \"{name}\" pro přístup k tomuto účtu?"
   shareAccessAsk: "Opravdu chcete této aplikaci povolit přístup k vašemu účtu?"
-  permission: "{jméno} požaduje tato oprávnění"
+  permission: "{name} požaduje tato oprávnění"
   permissionAsk: "Tato aplikace požaduje následující oprávnění"
   pleaseGoBack: "Vraťte se prosím zpět do aplikace"
   callback: "Návrat k aplikaci"
@@ -1942,7 +1941,7 @@ _notification:
   youGotMention: "{name} vás zmínil"
   youGotReply: "{name} vám odpověděl"
   youGotQuote: "{name} vás citoval"
-  youRenoted: "Poznámka od {jméno}"
+  youRenoted: "Poznámka od {name}"
   youWereFollowed: "Máte nového následovníka"
   youReceivedFollowRequest: "Obdrželi jste žádost o sledování"
   yourFollowRequestAccepted: "Vaše žádost o sledování byla přijata"
@@ -2025,4 +2024,3 @@ _moderationLogTypes:
   createInvitation: "Vygenerovat pozvánku"
 _reversi:
   total: "Celkem"
-
diff --git a/locales/da-DK.yml b/locales/da-DK.yml
index d1fbec9f6791..08c15ed092fc 100644
--- a/locales/da-DK.yml
+++ b/locales/da-DK.yml
@@ -1,3 +1,2 @@
 ---
 _lang_: "Dansk"
-
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 9a22a7b4451d..3b3993825569 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1819,7 +1819,6 @@ _2fa:
   registerTOTP: "Authentifizierungs-App registrieren"
   step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät."
   step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät."
-  step2Click: "Durch Klicken dieses QR-Codes kannst du Verifikation mit deinem Security-Token oder einer App registrieren."
   step2Uri: "Nutzt du ein Desktopprogramm, gib folgende URI eingeben"
   step3Title: "Authentifizierungsscode eingeben"
   step3: "Gib zum Abschluss den Code (Token) ein, der von deiner App angezeigt wird."
@@ -2289,4 +2288,3 @@ _reversi:
   black: "Schwarz"
   white: "Weiß"
   total: "Gesamt"
-
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index bb5639a74127..2098c7ef50ec 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -398,4 +398,3 @@ _moderationLogTypes:
   suspend: "Αποβολή"
 _reversi:
   total: "Σύνολο"
-
diff --git a/locales/en-US.yml b/locales/en-US.yml
index d00f23632cc1..10e9fd778e70 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -130,7 +130,7 @@ overwriteFromPinnedEmojis: "Override from general settings"
 reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add."
 rememberNoteVisibility: "Remember note visibility settings"
 attachCancel: "Remove attachment"
-deleteFile: "File deleted"
+deleteFile: "Delete file"
 markAsSensitive: "Mark as sensitive"
 unmarkAsSensitive: "Unmark as sensitive"
 enterFileName: "Enter filename"
@@ -400,6 +400,7 @@ name: "Name"
 antennaSource: "Antenna source"
 antennaKeywords: "Keywords to listen to"
 antennaExcludeKeywords: "Keywords to exclude"
+antennaExcludeBots: "Exclude bot accounts"
 antennaKeywordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition."
 notifyAntenna: "Notify about new notes"
 withFileAntenna: "Only notes with files"
@@ -494,6 +495,7 @@ emojiStyle: "Emoji style"
 native: "Native"
 disableDrawer: "Don't use drawer-style menus"
 showNoteActionsOnlyHover: "Only show note actions on hover"
+showReactionsCount: "See the number of reactions in notes"
 noHistory: "No history available"
 signinHistory: "Login history"
 enableAdvancedMfm: "Enable advanced MFM"
@@ -1223,6 +1225,16 @@ enableHorizontalSwipe: "Swipe to switch tabs"
 loading: "Loading"
 surrender: "Cancel"
 gameRetry: "Retry"
+notUsePleaseLeaveBlank: "Leave blank if not used"
+useTotp: "Enter the One-Time Password"
+useBackupCode: "Use the backup codes"
+launchApp: "Launch the app"
+useNativeUIForVideoAudioPlayer: "Use UI of browser when play video and audio"
+keepOriginalFilename: "Keep original file name"
+keepOriginalFilenameDescription: "If you turn off this setting, files names will be replaced with random string automatically when you upload files."
+noDescription: "There is not the explanation"
+alwaysConfirmFollow: "Always confirm when following"
+inquiry: "Contact"
 _bubbleGame:
   howToPlay: "How to play"
   hold: "Hold"
@@ -1682,6 +1694,11 @@ _role:
     roleAssignedTo: "Assigned to manual roles"
     isLocal: "Local user"
     isRemote: "Remote user"
+    isCat: "Cat Users"
+    isBot: "Bot Users"
+    isSuspended: "Suspended user"
+    isLocked: "Private accounts"
+    isExplorable: "Effective user of \"make an account discoverable\""
     createdLessThan: "Less than X has passed since account creation"
     createdMoreThan: "More than X has passed since account creation"
     followersLessThanOrEq: "Has X or fewer followers"
@@ -1751,6 +1768,7 @@ _plugin:
   installWarn: "Please do not install untrustworthy plugins."
   manage: "Manage plugins"
   viewSource: "View source"
+  viewLog: "Show log"
 _preferencesBackups:
   list: "Created backups"
   saveNew: "Save new backup"
@@ -1940,7 +1958,6 @@ _2fa:
   registerTOTP: "Register authenticator app"
   step1: "First, install an authentication app (such as {a} or {b}) on your device."
   step2: "Then, scan the QR code displayed on this screen."
-  step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app."
   step2Uri: "Enter the following URI if you are using a desktop program"
   step3Title: "Enter an authentication code"
   step3: "Enter the authentication code (token) provided by your app to finish setup."
@@ -1964,6 +1981,7 @@ _2fa:
   backupCodesDescription: "You can use these codes to gain access to your account in case of becoming unable to use your two-factor authentificator app. Each can only be used once. Please keep them in a safe place."
   backupCodeUsedWarning: "A backup code has been used. Please reconfigure two-factor authentification as soon as possible if you are no longer able to use it."
   backupCodesExhaustedWarning: "All backup codes have been used. Should you lose access to your two-factor authentification app, you will be unable to access this account. Please reconfigure two-factor authentification."
+  moreDetailedGuideHere: "Here is detailed guide"
 _permissions:
   "read:account": "View your account information"
   "write:account": "Edit your account information"
@@ -2225,6 +2243,7 @@ _play:
   title: "Title"
   script: "Script"
   summary: "Description"
+  visibilityDescription: "Putting it private means it won't be visible on your profile, but anyone that has the URL can still access it."
 _pages:
   newPage: "Create a new Page"
   editPage: "Edit this Page"
@@ -2269,6 +2288,8 @@ _pages:
     section: "Section"
     image: "Images"
     button: "Button"
+    dynamic: "Dynamic Blocks"
+    dynamicDescription: "This block has been abolished. Please use {play} from now on."
     note: "Embedded note"
     _note:
       id: "Note ID"
@@ -2298,6 +2319,7 @@ _notification:
   sendTestNotification: "Send test notification"
   notificationWillBeDisplayedLikeThis: "Notifications look like this"
   reactedBySomeUsers: "{n} users reacted"
+  likedBySomeUsers: "{n} users liked your note"
   renotedBySomeUsers: "Renote from {n} users"
   followedBySomeUsers: "Followed by {n} users"
   flushNotification: "Clear notifications"
@@ -2524,4 +2546,21 @@ _reversi:
 _offlineScreen:
   title: "Offline - cannot connect to the server"
   header: "Unable to connect to the server"
-
+_urlPreviewSetting:
+  title: "URL preview settings"
+  enable: "Enable URL preview"
+  timeout: "Time out when getting preview (ms)"
+  timeoutDescription: "If it takes longer than this value to get the preview, the preview won’t be generated."
+  maximumContentLength: "Maximum Content-Length (bytes)"
+  maximumContentLengthDescription: "If Content-Length is higher than this value, the preview won't be generated."
+  requireContentLength: "Generate the preview only if you could get Content-Length"
+  requireContentLengthDescription: "If other server doesn't return Content-Length, the preview won't be generated."
+  userAgent: "User-Agent"
+  userAgentDescription: "Sets the User-Agent to be used when retrieving previews. If left blank, the default User-Agent will be used."
+  summaryProxy: "Proxy endpoints that generate previews"
+  summaryProxyDescription: "Not Misskey itself, but generate previews using Summaly Proxy."
+  summaryProxyDescription2: "The following parameters are linked to the proxy as a query string. If the proxy does not support them, the values are ignored."
+_mediaControls:
+  pip: "Picture in Picture"
+  playbackRate: "Playback Speed"
+  loop: "Loop playback"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 246ec2360427..2e05364c312f 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -235,7 +235,7 @@ done: "Terminado"
 processing: "Procesando"
 preview: "Vista previa"
 default: "Predeterminado"
-defaultValueIs: "Predeterminado"
+defaultValueIs: "Por defecto: {value}"
 noCustomEmojis: "No hay emojis personalizados"
 noJobs: "No hay trabajos"
 federating: "Federando"
@@ -400,6 +400,7 @@ name: "Nombre"
 antennaSource: "Origen de la antena"
 antennaKeywords: "Palabras clave para recibir"
 antennaExcludeKeywords: "Palabras clave para excluir"
+antennaExcludeBots: "Excluir bots"
 antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR"
 notifyAntenna: "Notificar nueva nota"
 withFileAntenna: "Sólo notas con archivos adjuntados"
@@ -494,6 +495,7 @@ emojiStyle: "Estilo de emoji"
 native: "Nativo"
 disableDrawer: "No mostrar los menús en cajones"
 showNoteActionsOnlyHover: "Mostrar acciones de la nota sólo al pasar el cursor"
+showReactionsCount: "Mostrar el número de reacciones en las notas"
 noHistory: "No hay datos en el historial"
 signinHistory: "Historial de ingresos"
 enableAdvancedMfm: "Habilitar MFM avanzado"
@@ -991,6 +993,7 @@ neverShow: "No mostrar de nuevo"
 remindMeLater: "Recordar después"
 didYouLikeMisskey: "¿Te gusta Misskey?"
 pleaseDonate: "{host} usa el software gratuito Misskey. Por favor ¡Considera donar al proyecto principal para que podamos continuar!"
+correspondingSourceIsAvailable: "El código fuente correspondiente se encuentra disponible en {anchor}"
 roles: "Roles"
 role: "Rol"
 noRole: "Rol no encontrado"
@@ -1042,6 +1045,7 @@ sensitiveWords: "Palabras sensibles"
 sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de línea"
 sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
 prohibitedWords: "Palabras explícitas"
+prohibitedWordsDescription: "Activa un error cuando se intenta publicar una nota que contiene una o varias palabras prohibidas. Se pueden establecer varias palabras, una por línea."
 prohibitedWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
 hiddenTags: "Hashtags ocultos"
 hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por línea."
@@ -1158,6 +1162,7 @@ showRenotes: "Mostrar renotas"
 edited: "Editado"
 notificationRecieveConfig: "Ajustes de Notificaciones"
 mutualFollow: "Os seguís mutuamente"
+followingOrFollower: "Siguiendo o seguidor"
 fileAttachedOnly: "Solo notas con archivos"
 showRepliesToOthersInTimeline: "Mostrar respuestas a otros en la línea de tiempo"
 hideRepliesToOthersInTimeline: "Ocultar respuestas a otros en la línea de tiempo"
@@ -1167,6 +1172,12 @@ confirmShowRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres
 confirmHideRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres ocultar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
 externalServices: "Servicios Externos"
 sourceCode: "Código fuente"
+sourceCodeIsNotYetProvided: "El código fuente aún no está disponible. Contacta con el administrador para solucionarlo."
+repositoryUrl: "URL del repositorio"
+repositoryUrlDescription: "Si estás usando Misskey tal cual (sin cambios en el código fuente), entra en https://github.com/misskey-dev/misskey"
+repositoryUrlOrTarballRequired: "Si no has publicado un repositorio aún, deberás publicar un tarball en su lugar. Mira el archivo .config/example.yml para más información."
+feedback: "Comentarios"
+feedbackUrl: "URL de comentarios"
 impressum: "Impressum"
 impressumUrl: "Impressum URL"
 impressumDescription: "En algunos países, como Alemania, la inclusión del operador de datos (el Impressum) es requerido legalmente para sitios web comerciales."
@@ -1202,6 +1213,8 @@ soundWillBePlayed: "Se reproducirán efectos sonoros"
 showReplay: "Ver reproducción"
 replay: "Reproducir"
 replaying: "Reproduciendo"
+endReplay: "Terminar reproducción"
+copyReplayData: "Copiar datos de reproducción"
 ranking: "Clasificación"
 lastNDays: "Últimos {n} días"
 backToTitle: "Regresar al inicio"
@@ -1209,9 +1222,28 @@ hemisphere: "Región"
 withSensitive: "Mostrar notas que contengan material sensible"
 userSaysSomethingSensitive: "La publicación de {name} contiene material sensible"
 enableHorizontalSwipe: "Deslice para cambiar de pestaña"
+loading: "Cargando"
 surrender: "detener"
+gameRetry: "Reintentar"
+notUsePleaseLeaveBlank: "Dejar en blanco si no se usa"
+useTotp: "Introduce la contraseña de un solo uso"
+useBackupCode: "Usar códigos de respaldo"
+launchApp: "Ejecutar la app"
+useNativeUIForVideoAudioPlayer: "Usar la interfaz del navegador cuando se reproduce audio y vídeo"
+keepOriginalFilename: "Mantener el nombre original del archivo"
+noDescription: "No hay descripción"
+alwaysConfirmFollow: "Confirmar siempre cuando se sigue a alguien"
 _bubbleGame:
   howToPlay: "Cómo jugar"
+  hold: "Mantener"
+  _score:
+    score: "Puntos"
+    scoreYen: "Cantidad de dinero ganada"
+    highScore: "Puntuación más alta"
+    maxChain: "Número máximo de cadenas"
+    yen: "{yen} Yenes"
+    estimatedQty: "{qty} Piezas"
+    scoreSweets: "{onigiriQtyWithUnit} Onigiris"
   _howToPlay:
     section1: "Ajuste la posición y deje caer el objeto en la caja"
     section2: "Cuando dos objetos del mismo tipo se tocan, cambian a otro tipo y consigues puntos"
@@ -1329,7 +1361,7 @@ _serverSettings:
 _accountMigration:
   moveFrom: "Trasladar de otra cuenta a ésta"
   moveFromSub: "Crear un alias para otra cuenta."
-  moveFromLabel: "Cuenta desde la que se realiza el traslado:"
+  moveFromLabel: "Cuenta desde la que se realiza el traslado #{n}"
   moveFromDescription: "Si quieres transferir seguidores de otra cuenta a esta cuenta y trasladarlos, tendrás que crear un alias aquí. Asegúrate de crearlo antes de realizar el traslado. Introduce la cuenta desde la que estás moviendo los seguidores así: @person@instance.com"
   moveTo: "Mover esta cuenta a una nueva"
   moveToLabel: "Cuenta destino:"
@@ -1588,8 +1620,11 @@ _achievements:
       description: "Tutorial completado"
     _bubbleGameExplodingHead:
       title: "🤯"
+      description: "El objeto más grande en el juego de burbujas"
     _bubbleGameDoubleExplodingHead:
       title: "Doble 🤯"
+      description: "Dos de los objetos más grandes en el juego de burbujas al mismo tiempo"
+      flavor: "Puedes llenar el bento un poco de esta forma 🤯 🤯."
 _role:
   new: "Crear rol"
   edit: "Editar rol"
@@ -1630,6 +1665,7 @@ _role:
     gtlAvailable: "Explorar la línea de tiempo global"
     ltlAvailable: "Explorar la línea de tiempo local"
     canPublicNote: "Permitir la publicación"
+    mentionMax: "Número máximo de menciones en una nota"
     canInvite: "Puede crear códigos de invitación"
     inviteLimit: "Límite de invitaciones"
     inviteLimitCycle: "Enfriamiento del límite de invitaciones"
@@ -1653,8 +1689,13 @@ _role:
     canUseTranslator: "Uso de traductor"
     avatarDecorationLimit: "Número máximo de decoraciones de avatar"
   _condition:
+    roleAssignedTo: "Asignado a roles manuales"
     isLocal: "Usuario local"
     isRemote: "Usuario remoto"
+    isCat: "Usuarios Gato"
+    isBot: "Usuarios Bot"
+    isSuspended: "Usuario suspendido"
+    isLocked: "Cuentas privadas"
     createdLessThan: "Menos de X han pasado desde la creación de la cuenta"
     createdMoreThan: "Más de X han pasado desde la creación de la cuenta"
     followersLessThanOrEq: "Tiene X o menos seguidores"
@@ -1724,6 +1765,7 @@ _plugin:
   installWarn: "Por favor no instale plugins que no son de confianza"
   manage: "Gestionar plugins"
   viewSource: "Ver la fuente"
+  viewLog: "Ver log"
 _preferencesBackups:
   list: "Respaldos creados"
   saveNew: "Guardar nuevo respaldo"
@@ -1753,6 +1795,8 @@ _aboutMisskey:
   contributors: "Principales colaboradores"
   allContributors: "Todos los colaboradores"
   source: "Código fuente"
+  original: "Original"
+  thisIsModifiedVersion: "{name} usa una versión modificada de Misskey."
   translation: "Traducir Misskey"
   donate: "Donar a Misskey"
   morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
@@ -1911,7 +1955,6 @@ _2fa:
   registerTOTP: "Registrar aplicación autenticadora"
   step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
   step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla."
-  step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app.\nTocar este código QR te permitirá registrar la autenticación 2FA a tu llave de seguridad o aplicación autenticadora."
   step2Uri: "Si usas una aplicación de escritorio, introduce en ella la siguiente URL."
   step3Title: "Ingresa un código de autenticación"
   step3: "Para terminar, ingrese el token mostrado en la aplicación."
@@ -1935,6 +1978,7 @@ _2fa:
   backupCodesDescription: "En caso de que no puedas usar tu aplicación de autenticación, podrás usar los códigos de respaldo que figuran abajo para acceder a tu cuenta. Asegúrate de guardar en lugar seguro los códigos de respaldo. Cada uno de los códigos de respaldo es de un solo uso."
   backupCodeUsedWarning: "Has usado todos los códigos de respaldo. Si dejas de tener acceso a tu aplicación de autenticación, no podrás volver a iniciar sesión en tu cuenta. Por favor, reconfigura tu aplicación de autenticación lo antes posible."
   backupCodesExhaustedWarning: "Has usado todos los códigos de respaldo. Si dejas de tener acceso a tu aplicación de autenticación, no podrás volver a iniciar sesión en la cuenta que figura arriba. Por favor, reconfigura tu aplicación de autenticación lo antes posible."
+  moreDetailedGuideHere: "Guía detallada"
 _permissions:
   "read:account": "Ver información de la cuenta"
   "write:account": "Editar información de la cuenta"
@@ -1976,6 +2020,7 @@ _permissions:
   "write:admin:delete-account": "Eliminar cuentas de usuario"
   "write:admin:delete-all-files-of-a-user": "Eliminar todos los archivos de un usuario"
   "read:admin:index-stats": "Ver datos indexados"
+  "read:admin:table-stats": "Ver estadísticas de las tablas de la base de datos"
   "read:admin:user-ips": "Ver dirección IP de usuario"
   "read:admin:meta": "Ver metadatos de la instancia"
   "write:admin:reset-password": "Restablecer contraseñas de usuario"
@@ -2195,6 +2240,7 @@ _play:
   title: "Título"
   script: "Script"
   summary: "Descripción"
+  visibilityDescription: "Poniéndola como privada significa que no será visible en tu perfil, pero cualquiera que tenga la URL aún podrá acceder a ella."
 _pages:
   newPage: "Crear página"
   editPage: "Editar página"
@@ -2239,6 +2285,8 @@ _pages:
     section: "Sección"
     image: "Imagen"
     button: "Botón"
+    dynamic: "Bloques Dinámicos"
+    dynamicDescription: "Los bloques dinámicos están obsoletos. A partir de ahora, utiliza {play} por favor."
     note: "Nota embebida"
     _note:
       id: "Id de la nota"
@@ -2448,4 +2496,11 @@ _reversi:
   reversi: "Reversi"
   won: "{name} ha ganado"
   total: "Total"
-
+_urlPreviewSetting:
+  timeout: "Timeout de la carga de vista previa de las URLs (ms)"
+  maximumContentLength: "Content-Length Máximo (bytes)"
+  userAgent: "User-Agent"
+_mediaControls:
+  pip: "Picture in Picture"
+  playbackRate: "Velocidad de reproducción"
+  loop: "Reproducción en bucle"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 9629726f54bd..58a11a5cc4ea 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -129,7 +129,7 @@ overwriteFromPinnedEmojisForReaction: "Remplacer par les émojis épinglés pour
 overwriteFromPinnedEmojis: "Remplacer par les émojis épinglés globalement"
 reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter."
 rememberNoteVisibility: "Se souvenir de la visibilité des notes"
-attachCancel: "Supprimer le fichier attaché"
+attachCancel: "Supprimer le fichier joint"
 deleteFile: "Fichier supprimé"
 markAsSensitive: "Marquer comme sensible"
 unmarkAsSensitive: "Supprimer le marquage comme sensible"
@@ -400,6 +400,7 @@ name: "Nom"
 antennaSource: "Source de l’antenne"
 antennaKeywords: "Mots clés à recevoir"
 antennaExcludeKeywords: "Mots clés à exclure"
+antennaExcludeBots: "Exclure les comptes robot"
 antennaKeywordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR."
 notifyAntenna: "Me notifier pour les nouvelles notes"
 withFileAntenna: "Notes ayant des fichiers joints uniquement"
@@ -494,6 +495,7 @@ emojiStyle: "Style des émojis"
 native: "Natif"
 disableDrawer: "Les menus ne s'affichent pas dans le tiroir"
 showNoteActionsOnlyHover: "Afficher les actions de note uniquement au survol"
+showReactionsCount: "Afficher le nombre de réactions des notes"
 noHistory: "Pas d'historique"
 signinHistory: "Historique de connexion"
 enableAdvancedMfm: "Activer la MFM avancée"
@@ -541,6 +543,7 @@ objectStorageUseSSLDesc: "Désactivez cette option si vous n'utilisez pas HTTPS
 objectStorageUseProxy: "Se connecter via proxy"
 objectStorageUseProxyDesc: "Désactivez cette option si vous n'utilisez pas de proxy pour la connexion API"
 objectStorageSetPublicRead: "Régler sur « public » lors de l'envoi"
+s3ForcePathStyleDesc: "Si s3ForcePathStyle est activé, le nom du compartiment doit être spécifié comme une partie du chemin de l'URL plutôt que le nom d'hôte. Il faudra peut-être l'activer lors de l'utilisation d'une instance de Minio autohébergée, etc."
 serverLogs: "Journal du serveur"
 deleteAll: "Supprimer tout"
 showFixedPostForm: "Afficher le formulaire de publication en haut du fil d'actualité"
@@ -655,7 +658,7 @@ testEmail: "Tester la distribution de courriel"
 wordMute: "Filtre de mots"
 hardWordMute: "Filtre de mots dur"
 regexpError: "Erreur d’expression régulière"
-regexpErrorDescription: "Une erreur s'est produite dans l'expression régulière sur la ligne {ligne} de votre mot muet {tab} :"
+regexpErrorDescription: "Une erreur s'est produite dans l'expression régulière sur la ligne {line} de votre mot muet {tab} :"
 instanceMute: "Instance en sourdine"
 userSaysSomething: "{name} a dit quelque chose"
 makeActive: "Activer"
@@ -675,6 +678,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, tous les appareils seront déconnectés. "
+theKeywordWhenSearchingForCustomEmoji: "Ce mot-clé est utilisé lors de la recherche des émojis personnalisés."
 setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant par des espaces."
 fileIdOrUrl: "ID du fichier ou URL"
 behavior: "Comportement"
@@ -989,6 +993,7 @@ neverShow: "Ne plus afficher"
 remindMeLater: "Peut-être plus tard"
 didYouLikeMisskey: "Avez-vous aimé Misskey ?"
 pleaseDonate: "Misskey est le logiciel libre utilisé par {host}. Merci de faire un don pour que nous puissions continuer à le développer !"
+correspondingSourceIsAvailable: "Le code source correspondant est disponible à {anchor}"
 roles: "Rôles"
 role: "Rôles"
 noRole: "Aucun rôle"
@@ -1003,6 +1008,7 @@ youCannotCreateAnymore: "Vous avez atteint la limite de création."
 cannotPerformTemporary: "Temporairement indisponible"
 cannotPerformTemporaryDescription: "Temporairement indisponible puisque le nombre d'opérations dépasse la limite. Veuillez patienter un peu, puis réessayer."
 invalidParamError: "Paramètres invalides"
+invalidParamErrorDescription: "Les paramètres de la requête sont invalides. Il s'agit généralement d'un bogue, mais cela peut aussi être causé par un excès de caractères ou quelque chose de similaire."
 permissionDeniedError: "Opération refusée"
 permissionDeniedErrorDescription: "Ce compte n'a pas la permission d'effectuer cette opération."
 preset: "Préréglage"
@@ -1016,6 +1022,7 @@ thisPostMayBeAnnoyingCancel: "Annuler"
 thisPostMayBeAnnoyingIgnore: "Publier quand-même"
 collapseRenotes: "Réduire les renotes déjà vues"
 internalServerError: "Erreur interne du serveur"
+internalServerErrorDescription: "Une erreur inattendue s'est produite sur le serveur."
 copyErrorInfo: "Copier les détails de l’erreur"
 joinThisServer: "S'inscrire à cette instance"
 exploreOtherServers: "Trouver une autre instance"
@@ -1035,8 +1042,10 @@ nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j'
 rolesAssignedToMe: "Rôles attribués à moi"
 resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?"
 sensitiveWords: "Mots sensibles"
+sensitiveWordsDescription: "Définir la visibilité des notes contenant un mot défini ici au fil principal automatiquement. Vous pouvez définir plusieurs valeurs en les séparant par des sauts de ligne."
 sensitiveWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
 prohibitedWords: "Mots interdits"
+prohibitedWordsDescription: "Publier une note contenant un mot défini ici produira une erreur. Vous pouvez définir plusieurs valeurs en les séparant par des sauts de ligne."
 prohibitedWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
 hiddenTags: "Hashtags cachés"
 hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
@@ -1082,9 +1091,11 @@ pleaseConfirmBelowBeforeSignup: "Pour vous inscrire sur cette instance, vous dev
 pleaseAgreeAllToContinue: "Pour continuer, veuillez accepter tous les champs ci-dessus."
 continue: "Continuer"
 preservedUsernames: "Noms d'utilisateur·rice réservés"
+preservedUsernamesDescription: "Énumérez les noms d'utilisateur à réserver, séparés par des nouvelles lignes. Les noms d'utilisateur spécifiés ici ne seront plus utilisables lors de la création d'un compte, sauf la création manuelle par un administrateur. De plus, les comptes existants ne seront pas affectés."
 createNoteFromTheFile: "Rédiger une note de ce fichier"
 archive: "Archive"
 channelArchiveConfirmTitle: "Voulez-vous vraiment archiver {name} ?"
+channelArchiveConfirmDescription: "Une fois archivé, le canal n'apparaîtra plus dans la liste des canaux ni dans les résultats de recherche, et la publication des nouvelles notes sera impossible."
 thisChannelArchived: "Ce canal a été archivé."
 displayOfNote: "Affichage de la note"
 initialAccountSetting: "Configuration initiale du profil"
@@ -1113,6 +1124,8 @@ createWithOptions: "Options"
 createCount: "Quantité à créer"
 inviteCodeCreated: "Code d'invitation créé"
 inviteLimitExceeded: "Vous avez atteint la limite de codes d'invitation que vous pouvez générer."
+createLimitRemaining: "Codes d'invitation pouvant être créés : {limit} restants"
+inviteLimitResetCycle: "Vous pouvez créer jusqu'à {limit} codes d'invitation en {time}."
 expirationDate: "Date d’expiration"
 noExpirationDate: "Ne pas expirer"
 inviteCodeUsedAt: "Code d'invitation utilisé à"
@@ -1132,11 +1145,14 @@ forYou: "Pour vous"
 currentAnnouncements: "Annonces actuelles"
 pastAnnouncements: "Annonces passées"
 youHaveUnreadAnnouncements: "Il y a des annonces non lues."
+useSecurityKey: "Suivez les instructions de votre navigateur ou de votre appareil pour utiliser une clé de sécurité ou une clé d'accès."
 replies: "Réponses"
 renotes: "Renotes"
 loadReplies: "Inclure les réponses"
 loadConversation: "Afficher la conversation"
 pinnedList: "Liste épinglée"
+keepScreenOn: "Garder l'écran toujours allumé"
+verifiedLink: "Votre propriété de ce lien a été vérifiée"
 notifyNotes: "Notifier à propos des nouvelles notes"
 unnotifyNotes: "Ne pas notifier pour la publication des notes"
 authentication: "Authentification"
@@ -1146,6 +1162,7 @@ showRenotes: "Afficher les renotes"
 edited: "Modifié"
 notificationRecieveConfig: "Paramètres des notifications"
 mutualFollow: "Abonnement mutuel"
+followingOrFollower: "Abonnement ou abonné"
 fileAttachedOnly: "Avec fichiers joints seulement"
 showRepliesToOthersInTimeline: "Afficher les réponses aux autres dans le fil"
 hideRepliesToOthersInTimeline: "Masquer les réponses aux autres dans le fil"
@@ -1201,6 +1218,8 @@ ranking: "Classement"
 lastNDays: "Derniers {n} jours"
 backToTitle: "Retourner au titre"
 hemisphere: "Votre région"
+withSensitive: "Afficher les notes contenant des fichiers joints sensibles"
+userSaysSomethingSensitive: "Note de {name} contenant des fichiers joints sensibles"
 enableHorizontalSwipe: "Glisser pour changer d'onglet"
 loading: "Chargement en cours"
 surrender: "Annuler"
@@ -1212,15 +1231,25 @@ _bubbleGame:
     score: "Score"
     scoreYen: "Montant gagné"
     highScore: "Meilleur score"
+    maxChain: "Nombre maximum de chaînes"
     yen: "{yen} yens"
+    estimatedQty: "{qty} pièces"
 _announcement:
   forExistingUsers: "Pour les utilisateurs existants seulement"
+  needConfirmationToRead: "Exiger la confirmation de la lecture"
+  needConfirmationToReadDescription: "Si activé, afficher un dialogue de confirmation quand l'annonce est marquée comme lue. Aussi, elle sera exclue de « marquer tout comme lu » ."
+  end: "Archiver l'annonce"
+  tooManyActiveAnnouncementDescription: "Un grand nombre d'annonces actives peut baisser l'expérience utilisateur. Considérez d'archiver les annonces obsolètes."
   readConfirmTitle: "Marquer comme lu ?"
+  readConfirmText: "Cela marquera le contenu de  « {title} » comme lu."
   shouldNotBeUsedToPresentPermanentInfo: "Puisque cela pourrait nuire considérablement à l'expérience utilisateur pour les nouveaux utilisateurs, il est recommandé d'utiliser les annonces pour afficher des informations temporaires plutôt que des informations persistantes."
   dialogAnnouncementUxWarn: "Avoir deux ou plus annonces de style dialogue en même temps pourrait nuire considérablement à l'expérience utilisateur. Veuillez les utiliser avec caution."
   silence: "Ne pas me notifier"
   silenceDescription: "Si activée, vous ne recevrez pas de notifications sur les annonces et n'aurez pas besoin de les marquer comme lues."
 _initialAccountSetting:
+  accountCreated: "Votre compte a été créé avec succès !"
+  letsStartAccountSetup: "Procédons au réglage initial du compte."
+  letsFillYourProfile: "Commençons par configurer votre profil !"
   profileSetting: "Paramètres du profil"
   privacySetting: "Paramètres de confidentialité"
   initialAccountSettingCompleted: "Configuration du profil terminée avec succès !"
@@ -1288,7 +1317,7 @@ _initialTutorial:
     doItToContinue: "Marquez le fichier joint comme sensible pour procéder."
   _done:
     title: "Le tutoriel est terminé ! 🎉"
-    description: "Les fonctionnalités introduites ici ne sont que quelques-unes. Pour savoir plus sur l'utilisation de Misskey, veuillez consulter {lien}."
+    description: "Les fonctionnalités introduites ici ne sont que quelques-unes. Pour savoir plus sur l'utilisation de Misskey, veuillez consulter {link}."
 _timelineDescription:
   home: "Sur le fil principal, vous pouvez voir les notes des utilisateurs auxquels vous êtes abonné·e."
   local: "Sur le fil local, vous pouvez voir les notes de tous les utilisateurs sur cette instance."
@@ -1449,7 +1478,7 @@ _role:
   edit: "Modifier le rôle"
   name: "Nom du rôle"
   description: "Description du rôle"
-  permission: "Rôle et autorisations"
+  permission: "Autorisations du rôle"
   assignTarget: "Attribuer"
   manual: "Manuel"
   manualRoles: "Rôles manuels"
@@ -1984,7 +2013,7 @@ _notification:
   unreadAntennaNote: "Antenne {name}"
   roleAssigned: "Rôle attribué"
   emptyPushNotificationMessage: "Les notifications push ont été mises à jour"
-  achievementEarned: "Accomplissement"
+  achievementEarned: "Accomplissement déverrouillé"
   testNotification: "Tester la notification"
   reactedBySomeUsers: "{n} utilisateur·rice·s ont réagi"
   renotedBySomeUsers: "{n} utilisateur·rice·s ont renoté"
@@ -2001,7 +2030,7 @@ _notification:
     receiveFollowRequest: "Demande d'abonnement reçue"
     followRequestAccepted: "Demande d'abonnement acceptée"
     roleAssigned: "Rôle reçu"
-    achievementEarned: "Accomplissement"
+    achievementEarned: "Déverrouillage d'accomplissement"
     app: "Notifications provenant des apps"
   _actions:
     followBack: "Suivre"
@@ -2139,5 +2168,5 @@ _dataSaver:
     title: "Mise en évidence du code"
     description: "Si la notation de mise en évidence du code est utilisée, par exemple dans la MFM, elle ne sera pas chargée tant qu'elle n'aura pas été tapée. La mise en évidence du code nécessite le chargement du fichier de définition de chaque langue à mettre en évidence, mais comme ces fichiers ne sont plus chargés automatiquement, on peut s'attendre à une réduction du trafic de données."
 _reversi:
+  waitingBoth: "Préparez-vous"
   total: "Total"
-
diff --git a/locales/hr-HR.yml b/locales/hr-HR.yml
index 881aa8464e06..9cfebdd01a61 100644
--- a/locales/hr-HR.yml
+++ b/locales/hr-HR.yml
@@ -3,4 +3,3 @@ _lang_: "japanski"
 ok: "OK"
 gotIt: "Razumijem"
 cancel: "otkazati"
-
diff --git a/locales/ht-HT.yml b/locales/ht-HT.yml
index 1698c9f28041..e3595c79b656 100644
--- a/locales/ht-HT.yml
+++ b/locales/ht-HT.yml
@@ -16,4 +16,3 @@ _2fa:
   renewTOTPCancel: "Sispann"
 _widgets:
   profile: "pwofil"
-
diff --git a/locales/hu-HU.yml b/locales/hu-HU.yml
index 2f7006484afe..023a91494d02 100644
--- a/locales/hu-HU.yml
+++ b/locales/hu-HU.yml
@@ -102,4 +102,3 @@ _deck:
   _columns:
     notifications: "Értesítések"
     tl: "Idővonal"
-
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 514a2866ca57..f8e645d63b57 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -400,6 +400,7 @@ name: "Nama"
 antennaSource: "Sumber Antenna"
 antennaKeywords: "Kata kunci yang diterima"
 antennaExcludeKeywords: "Kata kunci yang dikecualikan"
+antennaExcludeBots: "Kecualikan akun bot"
 antennaKeywordsDescription: "Pisahkan dengan spasi untuk kondisi AND. Pisahkan dengan baris baru untuk kondisi OR."
 notifyAntenna: "Beritahu untuk catatan baru"
 withFileAntenna: "Hanya tampilkan catatan dengan berkas yang dilampirkan"
@@ -494,6 +495,7 @@ emojiStyle: "Gaya emoji"
 native: "Native"
 disableDrawer: "Jangan gunakan menu bergaya laci"
 showNoteActionsOnlyHover: "Hanya tampilkan aksi catatan saat ditunjuk"
+showReactionsCount: "Lihat jumlah reaksi dalam catatan"
 noHistory: "Tidak ada riwayat"
 signinHistory: "Riwayat masuk"
 enableAdvancedMfm: "Nyalakan MFM tingkat lanjut"
@@ -991,6 +993,7 @@ neverShow: "Jangan tampilkan lagi"
 remindMeLater: "Mungkin nanti"
 didYouLikeMisskey: "Apakah kamu mulai menyukai Misskey?"
 pleaseDonate: "{host} menggunakan perangkat lunak bebas yaitu Misskey. Kami sangat mengapresiasi sekali donasi dari kamu agar pengembangan Misskey tetap dapat berlanjut!"
+correspondingSourceIsAvailable: "Sumber kode terkait tersedia di {anchor}"
 roles: "Peran"
 role: "Peran"
 noRole: "Peran tidak temukan"
@@ -1042,6 +1045,7 @@ sensitiveWords: "Kata sensitif"
 sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru."
 sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
 prohibitedWords: "Kata yang dilarang"
+prohibitedWordsDescription: "Menyalakan kesalahan ketika mencoba untuk memposting catatan dengan set kata-kata yang termasuk. Beberapa kata dapat diatur dan dipisahkan dengan baris baru."
 prohibitedWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
 hiddenTags: "Tagar tersembunyi"
 hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris."
@@ -1158,6 +1162,7 @@ showRenotes: "Tampilkan renote"
 edited: "Telah disunting"
 notificationRecieveConfig: "Pengaturan notifikasi"
 mutualFollow: "Saling mengikuti"
+followingOrFollower: "Mengikuti atau pengikut"
 fileAttachedOnly: "Hanya catatan dengan berkas"
 showRepliesToOthersInTimeline: "Tampilkan balasan ke pengguna lain dalam lini masa"
 hideRepliesToOthersInTimeline: "Sembunyikan balasan ke orang lain dari lini masa"
@@ -1167,6 +1172,12 @@ confirmShowRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk
 confirmHideRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menyembunyikan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
 externalServices: "Layanan eksternal"
 sourceCode: "Sumber kode"
+sourceCodeIsNotYetProvided: "Sumber kode belum tersedia. Hubungi admin untuk memperbaiki masalah ini."
+repositoryUrl: "URL Repositori"
+repositoryUrlDescription: "Jika kamu menggunakan Misskey begitu saja (tanpa ada perubahan dalam kode sumber), masukkan https://github.com/misskey-dev/misskey"
+repositoryUrlOrTarballRequired: "Apabila kamu masih mempublikasikan repositori, kamu setidaknya harus menyediakan berkas tarball. Lihat .config/example.yml untuk informasi lebih lanjut."
+feedback: "Umpan balik"
+feedbackUrl: "URL Umpan balik"
 impressum: "Impressum"
 impressumUrl: "Tautan Impressum"
 impressumDescription: "Pada beberapa negara seperti Jerman, inklusi dari informasi kontak operator (sebuah Impressum) diperlukan secara legal untuk situs web komersil."
@@ -1202,6 +1213,8 @@ soundWillBePlayed: "Suara yang akan dimainkan"
 showReplay: "Lihat tayangan ulang"
 replay: "Tayangan ulang"
 replaying: "Menayangkan Ulang"
+endReplay: "Keluat dari tayangan ulang"
+copyReplayData: "Salin data tayangan ulang"
 ranking: "Peringkat"
 lastNDays: "{n} hari terakhir"
 backToTitle: "Ke Judul"
@@ -1209,11 +1222,34 @@ hemisphere: "Letak kamu tinggal"
 withSensitive: "Lampirkan catatan dengan berkas sensitif"
 userSaysSomethingSensitive: "Postingan oleh {name} mengandung konten sensitif"
 enableHorizontalSwipe: "Geser untuk mengganti tab"
+loading: "Memuat..."
 surrender: "Batalkan"
+gameRetry: "Coba lagi"
+notUsePleaseLeaveBlank: "Kosongi bila tidak digunakan"
+useTotp: "Gunakan TOTP"
+useBackupCode: "Gunakan kode cadangan"
+launchApp: "Luncurkan Aplikasi"
+useNativeUIForVideoAudioPlayer: "Gunakan antarmuka peramban ketika memainkan video dan audio"
+keepOriginalFilename: "Simpan nama berkas asli"
+keepOriginalFilenameDescription: "Apabila pengaturan ini dimatikan, nama berkas akan diganti dengan string acak secara otomatis ketika kamu mengunggah berkas."
+noDescription: "Tidak ada deskripsi"
+alwaysConfirmFollow: "Selalu konfirmasi ketika mengikuti"
+inquiry: "Hubungi kami"
 _bubbleGame:
   howToPlay: "Cara bermain"
+  hold: "Tahan"
+  _score:
+    score: "Skor"
+    scoreYen: "Jumlah uang didapat"
+    highScore: "Skor tertinggi"
+    maxChain: "Jumlah skor berantai"
+    yen: "{yen} Yen"
+    estimatedQty: "{qty} buah"
+    scoreSweets: "{onigiriQtyWithUnit} onigiri"
   _howToPlay:
     section1: "Atur posisi dan jatuhkan obyek ke dalam kotak."
+    section2: "Ketika dua obyek menyentuh tipe yang sama satu sama lain, obyek tersebut akan berganti dan kamu mendapatkan poin skor."
+    section3: "Permainan berakhir jika obyek memenuhi kotak. Capai skor tertinggi dengan menggabungkan obyek bersama sambil menghindari obyek tersebut memenuhi kotak permainan!"
 _announcement:
   forExistingUsers: "Hanya pengguna yang telah ada"
   forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya."
@@ -1257,26 +1293,59 @@ _initialTutorial:
     reply: "Klik pada tombol ini untuk membalas ke sebuah pesan. Bisa juga untuk membalas ke sebuah balasan dan melanjutkannya seperti percakapan selayaknya utas."
     renote: "Kamu dapat membagikan catatan ke lini masa milikmu. Kamu juga dapat mengutipnya dengan komentarmu."
     reaction: "Kamu dapat menambahkan reaksi ke Catatan. Detil lebih lanjut akan dijelaskan di halaman berikutnya."
+    menu: "Kamu dapat melihat detil catatan, menyalin tautan, dan melakukan aksi lainnya."
   _reaction:
     title: "Apa itu Reaksi?"
+    description: "Catatan dapat direaksi dengan berbagai emoji. Reaksi memperbolehkan kamu untuk mengekspresikan nuansa yang tidak dapat disampaikan hanya dengan sebuah \"suka\"."
+    letsTryReacting: "Reaksi dapat ditambahkan dengan mengklik tombol '+' pada catatan. Coba lakukan mereaksi contoh catatan ini!"
+    reactToContinue: "Tambahkan reaksi untuk melanjutkan."
+    reactNotification: "Kamu akan menerima notifikasi real0time ketika seseorang mereaksi catatan kamu."
+    reactDone: "Kamu dapat mengurungkan reaksi dengan menekan tombol '-'."
   _timeline:
     title: "Konsep Lini Masa"
+    description1: "Misskey menyediakan berbagai lini masa sesuai dengan penggunaan (beberapa mungkin tidak tersedia karena bergantung dengan kebijakan peladen)."
+    home: "Kamu dapat melihat catatan dari akun yang kamu ikuti."
+    local: "Kamu dapat melihat catatan dari semua pengguna yang ada pada peladen ini."
+    social: "Catatan dari linimasa Beranda dan Lokal akan ditampilkan."
+    global: "Kamu dapat melihat catatan dari semua peladen yang terhubung."
+    description2: "Kamu dapat mengganti linimasa di bagian atas layar kamu kapan saja."
+    description3: "Sebagai tambahan, terdapat juga linimasa daftar dan linimasa kanal. Untuk detil lebih lanjut, silahkan melihat ke tautan berikut: {link}."
   _postNote:
     title: "Pengaturan posting Catatan"
+    description1: "Ketika memposting catatan ke Misskey, terdapat beberapa opsi yang tersedia. Form posting terlihat seperti ini."
     _visibility:
+      description: "Kamu dapat membatasi siapa yang dapat melihat catatan kamu."
       public: "Perlihatkan catatan ke semua pengguna."
       home: "Hanya publik ke lini masa Beranda. Pengguna yang mengunjungi profilmu melalui pengikut dan renote dapat melihatnya."
       followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun."
       direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung."
+      doNotSendConfidencialOnDirect1: "Hati-hati ketika mengirim informasi yang sensitif!"
+      doNotSendConfidencialOnDirect2: "Admin dari peladen dapat melihat apa yang kamu tulis. Hati-hati dengan informasi sensitif ketika mengirimkan catatan langsung kepada pengguna pada peladen yang tidak dipercaya."
+      localOnly: "Memposting dengan opsi ini tidak akan memfederasi catatan ke peladen lain. Pengguna pada peladen lain tidak akan dapat melihat catatan ini secara langsung, meskipun dengan pengaturan visibilitas yang sudah diatur di atas."
     _cw:
       title: "Peringatan Konten (CW)"
+      description: "Alih-alih isinya, konten yang ditulis dalam kolom 'komentar' akan ditampilkan. Menekan 'Selebihnya' akan menampilkan isi konten."
       _exampleNote:
         cw: "Peringatan: Bikin Lapar!"
         note: "Baru aja makan donat berlapis coklat 🍩😋"
+      useCases: "Fungsi ini digunakan ketika mengikutik panduan peladen untuk catatan yang dibutuhkan atau untuk membatasi diri dari teks sensitif atau spoiler."
   _howToMakeAttachmentsSensitive:
     title: "Bagaimana menandai lampiran sebagai sensitif?"
+    description: "Fungsi ini digunakan untuk lampiran yang dibutuhkan oleh panduan peladen atau sesuatu yang seharusnya tidak boleh dibiarkan begitu saja dengan cara menambahkan penanda \"sensitif\"."
+    tryThisFile: "Coba tandai gambar yang dilampirkan pada form ini sebagai sensitif!"
+    _exampleNote:
+      note: "Ups, kesalahan banget buka penutup wadah natto..."
+    method: "Untuk menandai lampiran sebagai sensitif, klik gambar pada berkas, buka menu, lalu klik \"Tandai sebagai sensitif\"."
+    sensitiveSucceeded: "Ketika melampirkan berkas, mohon atur sensitifitas sesuai dengan panduan peladen."
+    doItToContinue: "Tandai berkas terlampir sebagai sensitif untuk melanjutkan."
   _done:
     title: "Kamu telah menyelesaikan tutorial! 🎉"
+    description: "Fungsi yang diperkenalkan di sini merupakan sebagian kecil dari fitur yang ada. Untuk pemahaman lebih detil dalam menggunakan Misskey, kamu dapat merujuk ke {link}."
+_timelineDescription:
+  home: "Pada linimasa Beranda, kamu dapat melihat catatan dari akun yang kamu ikuti."
+  local: "Pada linimasa Lokal, kamu dapat melihat catatan dari semua pengguna yang ada pada peladen ini."
+  social: "Linimasa sosial menampilkan catatan dari kedua linimasa Beranda dan Lokal."
+  global: "Pada linimasa Global, kamu dapat melihat catatan dari semua peladen yang terhubung."
 _serverRules:
   description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan."
 _serverSettings:
@@ -1288,6 +1357,9 @@ _serverSettings:
   manifestJsonOverride: "Ambil alih manifest.json"
   shortName: "Nama pendek"
   shortNameDescription: "Inisial untuk nama instansi yang dapat ditampilkan apabila nama lengkap resmi terlalu panjang."
+  fanoutTimelineDescription: "Dapat meningkatkan performa dalam pengambilan data linimasa dan mengurangi beban pada database ketika dinyalakan. Sebagai gantinya, penggunaan memory pada Redis akan meningkan. Pertimbangkan untuk menonaktifkan fitur ini jika mengalami kekurangan memori pada server atau menyebabkan server tidak stabil."
+  fanoutTimelineDbFallback: "Fallback ke database"
+  fanoutTimelineDbFallbackDescription: "Ketika diaktifkan, lini masa akan fallback ke database untuk melakukan kueri tambahan apabila linimasa tidak disimpan dalam cache. Menonaktifkan ini dapat mengurangi beban server dengan mengeliminasi proses fallback, namun dapat berakibat membatasi jarak data dari lini masa yang dapat diambil."
 _accountMigration:
   moveFrom: "Pindahkan akun lain ke akun ini"
   moveFromSub: "Buat alias ke akun lain"
@@ -1545,6 +1617,16 @@ _achievements:
     _smashTestNotificationButton:
       title: "Tes overflow"
       description: "Picu tes notifikasi secara berulang dalam waktu yang sangat pendek"
+    _tutorialCompleted:
+      title: "Ijazah Sekolah Dasar Misskey"
+      description: "Tutorial selesai"
+    _bubbleGameExplodingHead:
+      title: "🤯"
+      description: "Obyek paling terbesar di permainan gelembung"
+    _bubbleGameDoubleExplodingHead:
+      title: "Ganda 🤯"
+      description: "Dua dari obyek paling terbesar pada permainan gelembung di waktu yang sama"
+      flavor: "Kamu dapat mengisi kotak makan siang seperti ini 🤯 🤯."
 _role:
   new: "Buat peran"
   edit: "Sunting peran"
@@ -1555,7 +1637,9 @@ _role:
   assignTarget: "Tipe tugas"
   descriptionOfAssignTarget: "<b>Manual</b> untuk mengganti secara manual siapa yang mendapatkan peran ini dan siapa yang tidak.\n<b>Kondisional</b> untuk pengguna secara otomatis dimasukkan atau dihapus dari peran berdasarkan kondisi yang ditentukan."
   manual: "Manual"
+  manualRoles: "Peran manual"
   conditional: "Kondisional"
+  conditionalRoles: "Peran kondisional"
   condition: "Kondisi"
   isConditionalRole: "Ini adalah peran kondisional"
   isPublic: "Publikkan Peran"
@@ -1583,6 +1667,7 @@ _role:
     gtlAvailable: "Dapat melihat lini masa global"
     ltlAvailable: "Dapat melihat lini masa lokal"
     canPublicNote: "Dapat mengirim catatan publik"
+    mentionMax: "Jumlah maksimum sebutan dalam sebuah catatan"
     canInvite: "Dapat membuat kode undangan instansi"
     inviteLimit: "Batas jumlah undangan"
     inviteLimitCycle: "Interval Penerbitan Kode Undangan"
@@ -1604,9 +1689,16 @@ _role:
     canHideAds: "Dapat menyembunyikan iklan"
     canSearchNotes: "Penggunaan pencarian catatan"
     canUseTranslator: "Penggunaan penerjemah"
+    avatarDecorationLimit: "Jumlah maksimum dekorasi avatar yang dapat diterapkan"
   _condition:
+    roleAssignedTo: "Ditugaskan ke peran manual"
     isLocal: "Pengguna lokal"
     isRemote: "Pengguna remote"
+    isCat: "Pengguna Kucing"
+    isBot: "Pengguna Bot"
+    isSuspended: "Pengguna yang ditangguhkan"
+    isLocked: "Akun privat"
+    isExplorable: "Pengguna efektif yang akunnya dapat dicari"
     createdLessThan: "Telah berlalu kurang dari X sejak pembuatan akun"
     createdMoreThan: "Telah berlalu lebih dari X sejak pembuatan akun"
     followersLessThanOrEq: "Memiliki pengikut X atau kurang dari tersebut"
@@ -1632,6 +1724,7 @@ _emailUnavailable:
   disposable: "Alamat surel temporer tidak dapat digunakan"
   mx: "Peladen alamat surel ini tidak valid"
   smtp: "Peladen alamat surel ini tidak merespon"
+  banned: "Kamu tidak dapat mendaftar dengan alamat surel ini"
 _ffVisibility:
   public: "Terbitkan"
   followers: "Tampil untuk pengikut saja"
@@ -1675,6 +1768,7 @@ _plugin:
   installWarn: "Mohon jangan memasang plugin yang tidak dapat dipercayai."
   manage: "Manajemen plugin"
   viewSource: "Lihat sumber"
+  viewLog: "Tampilkan log"
 _preferencesBackups:
   list: "Cadangan yang dibuat"
   saveNew: "Simpan cadangan baru"
@@ -1704,10 +1798,13 @@ _aboutMisskey:
   contributors: "Kontributor utama"
   allContributors: "Seluruh kontributor"
   source: "Sumber kode"
+  original: "Asli"
+  thisIsModifiedVersion: "{name} menggunakan versi modifikasi dari Misskey yang asli."
   translation: "Terjemahkan Misskey"
   donate: "Donasi ke Misskey"
   morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang tidak tercantum disini. Terima kasih! 🥰"
   patrons: "Pendukung"
+  projectMembers: "Anggota proyek"
 _displayOfSensitiveMedia:
   respect: "Sembunyikan media yang ditandai sensitif"
   ignore: "Tampilkan media yang ditandai sensitif"
@@ -1732,6 +1829,7 @@ _channel:
   notesCount: "terdapat {n} catatan"
   nameAndDescription: "Nama dan deskripsi"
   nameOnly: "Hanya nama"
+  allowRenoteToExternal: "Perbolehkan catat ulang dan kutipan di luar dari kanal"
 _menuDisplay:
   sideFull: "Horisontal"
   sideIcon: "Horisontal (Ikon)"
@@ -1860,7 +1958,6 @@ _2fa:
   registerTOTP: "Daftarkan aplikasi autentikator"
   step1: "Pertama, pasang aplikasi autentikasi (seperti {a} atau {b}) di perangkat kamu."
   step2: "Lalu, pindai kode QR yang ada di layar."
-  step2Click: "Mengeklik kode QR ini akan membolehkanmu untuk mendaftarkan 2FA ke security-key atau aplikasi autentikator ponsel."
   step2Uri: "Masukkan URI berikut jika kamu menggunakan program desktop"
   step3Title: "Masukkan kode autentikasi"
   step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan pemasangan."
@@ -1884,6 +1981,7 @@ _2fa:
   backupCodesDescription: "Kamu dapat menggunakan kode ini untuk mendapatkan akses ke akun kamu apabila berada dalam situasi tidak dapat menggunakan aplikasi autentikasi 2-faktor yang kamu miliki. Setiap kode hanya dapat digunakan satu kali. Mohon simpan kode ini di tempat yang aman."
   backupCodeUsedWarning: "Kode cadangan telah digunakan. Mohon mengatur ulang autentikasi 2-faktor secepatnya apabila kamu sudah tidak dapat menggunakannya lagi."
   backupCodesExhaustedWarning: "Semua kode cadangan telah digunakan. Apabila kamu kehilangan akses pada aplikasi autentikasi 2-faktor milikmu, kamu tidak dapat mengakses akun ini lagi. Mohon atur ulang autentikasi 2-faktor kamu."
+  moreDetailedGuideHere: "Berikut panduan detilnya"
 _permissions:
   "read:account": "Lihat informasi akun"
   "write:account": "Sunting informasi akun"
@@ -2145,6 +2243,7 @@ _play:
   title: "Judul"
   script: "Script"
   summary: "Deskripsi"
+  visibilityDescription: "Membuat catatan ini privat berarti tidak akan terlihat pada profil kamu, namun siapapun yang memiliki URL dari catatan ini akan dapat mengaksesnya."
 _pages:
   newPage: "Buat halaman baru"
   editPage: "Sunting halaman"
@@ -2189,6 +2288,8 @@ _pages:
     section: "Bagian"
     image: "Gambar"
     button: "Tombol"
+    dynamic: "Blok Dinamis"
+    dynamicDescription: "Blok ini telah dihapus. Mohon gunakan {play} dari sekarang."
     note: "Catatan yang ditanam"
     _note:
       id: "ID Catatan"
@@ -2218,8 +2319,10 @@ _notification:
   sendTestNotification: "Kirim tes notifikasi"
   notificationWillBeDisplayedLikeThis: "Notifikasi akan terlihat seperti ini"
   reactedBySomeUsers: "{n} orang memberikan reaksi"
+  likedBySomeUsers: "{n} pengguna menyukai catatan kamu"
   renotedBySomeUsers: "{n} orang telah merenote"
   followedBySomeUsers: "{n} orang telah mengikuti"
+  flushNotification: "Bersihkan notifikasi"
   _types:
     all: "Semua"
     note: "Catatan baru"
@@ -2317,6 +2420,7 @@ _moderationLogTypes:
   resetPassword: "Atur ulang kata sandi"
   suspendRemoteInstance: "Instansi luar telah ditangguhkan"
   unsuspendRemoteInstance: "Instansi luar batal ditangguhkan"
+  updateRemoteInstanceNote: "Catatan moderasi telah diperbaharui untuk peladen luar."
   markSensitiveDriveFile: "Berkas ditandai sensitif"
   unmarkSensitiveDriveFile: "Berkas batal ditandai sensitif"
   resolveAbuseReport: "Laporan terselesaikan"
@@ -2428,4 +2532,35 @@ _reversi:
   isLlotheo: "Pemain dengan batu yang sedikit menang (Llotheo)"
   loopedMap: "Peta melingkar"
   canPutEverywhere: "Keping dapat ditaruh dimana saja"
-
+  timeLimitForEachTurn: "Batas waktu untuk gantian"
+  freeMatch: "Pertandingan bebas"
+  lookingForPlayer: "Mencari lawan..."
+  gameCanceled: "Permainan ini telah dibatalkan."
+  shareToTlTheGameWhenStart: "Bagikan permainan ke lini masa ketika dimulai"
+  iStartedAGame: "Permainan telah dimulai! #MisskeyReversi"
+  opponentHasSettingsChanged: "Lawan telah mengganti pengaturan mereka."
+  allowIrregularRules: "Aturan non-reguler (bebas sepenuhnya)"
+  disallowIrregularRules: "Tanpa aturan non-reguler"
+  showBoardLabels: "Tampilkan penomoran baris dan kolom pada papan"
+  useAvatarAsStone: "Ubah batu menjadi avatar pengguna"
+_offlineScreen:
+  title: "Luring - tidak dapat terhubung ke peladen"
+  header: "Tidak dapat tersambung ke server"
+_urlPreviewSetting:
+  title: "Pengaturan pratinjau URL"
+  enable: "Aktifkan pratinjau URL"
+  timeout: "Waktu timeout pratinjau URL (ms)"
+  timeoutDescription: "Apabila ini memakan waktu lama dari nilai yang ditentukan untuk mendapatkan pratinjau, pratinjau tidak akan dibuat."
+  maximumContentLength: "Content-Length Maksimum (bytes)"
+  maximumContentLengthDescription: "Apabila Content-Length lebih besar dari nilai ini, pratinjau tidak akan dibuat."
+  requireContentLength: "Buat pratinjau hanya ketika Content-Length dapat didapatkan"
+  requireContentLengthDescription: "Apabila peladen lain tidak memberika Content-Length, pratinjau tidak akan dibuat."
+  userAgent: "User-Agent"
+  userAgentDescription: "Atur User-Agent yang digunakan untuk mengambil pratinjau. Apabila dibiarkan kosong, User-Agent bawaan akan digunakan."
+  summaryProxy: "Titik akhir proksi yang membuat pratinjau"
+  summaryProxyDescription: "Bukan untuk Misskey, namun untuk menghasilkan pratinjau menggunakan Summaly Proxy."
+  summaryProxyDescription2: "Parameter berikut tertautkan dengan proksi sebagai string kueri. Apabila proksi tidak mendukung tersebut, nilai di dalamnya diabaikan."
+_mediaControls:
+  pip: "Gambar dalam Gambar"
+  playbackRate: "Kecepatan Pemutaran"
+  loop: "Ulangi Pemutaran"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 480d11b6bad8..0a250a2e289e 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -85,7 +85,7 @@ note: "Nota"
 notes: "Note"
 following: "Follow"
 followers: "Follower"
-followsYou: "Segue"
+followsYou: "Follower"
 createList: "Aggiungi una nuova lista"
 manageLists: "Gestisci liste"
 error: "Errore"
@@ -134,12 +134,12 @@ deleteFile: "File da Drive eliminato"
 markAsSensitive: "Segna come esplicito"
 unmarkAsSensitive: "Non segnare come esplicito "
 enterFileName: "Nome del file"
-mute: "Silenzia"
+mute: "Silenziare"
 unmute: "Riattiva l'audio"
-renoteMute: "Silenzia le Rinota"
+renoteMute: "Silenziare le Rinota"
 renoteUnmute: "Non silenziare le Rinota"
-block: "Blocca"
-unblock: "Sblocca"
+block: "Bloccare"
+unblock: "Sbloccare"
 suspend: "Sospensione"
 unsuspend: "Revoca la sospensione"
 blockConfirm: "Vuoi davvero bloccare il profilo?"
@@ -200,8 +200,8 @@ charts: "Grafici"
 perHour: "orario"
 perDay: "giornaliero"
 stopActivityDelivery: "Interrompi la distribuzione di attività"
-blockThisInstance: "Blocca questa istanza"
-silenceThisInstance: "Silenzia l'istanza"
+blockThisInstance: "Bloccare l'istanza"
+silenceThisInstance: "Silenziare l'istanza"
 operations: "Operazioni"
 software: "Software"
 version: "Versione"
@@ -223,7 +223,7 @@ blockedInstances: "Istanze bloccate"
 blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse non potranno più interagire con la tua istanza."
 silencedInstances: "Istanze silenziate"
 silencedInstancesDescription: "Elenca i nomi host delle istanze che vuoi silenziare. Tutti i profili nelle istanze silenziate vengono trattati come tali. Possono solo inviare richieste di follow e menzionare soltanto i profili locali che seguono. Le istanze bloccate non sono interessate."
-muteAndBlock: "Silenziati / Bloccati"
+muteAndBlock: "Silenziare e bloccare"
 mutedUsers: "Profili silenziati"
 blockedUsers: "Profili bloccati"
 noUsers: "Non ci sono profili"
@@ -400,6 +400,7 @@ name: "Nome"
 antennaSource: "Fonte dell'antenna"
 antennaKeywords: "Parole chiavi da ricevere"
 antennaExcludeKeywords: "Parole chiavi da escludere"
+antennaExcludeBots: "Escludere i Bot"
 antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)."
 notifyAntenna: "Invia notifiche delle nuove note"
 withFileAntenna: "Solo note con file in allegato"
@@ -410,7 +411,7 @@ withReplies: "Includere le risposte"
 connectedTo: "Connessione ai seguenti profili:"
 notesAndReplies: "Note e risposte"
 withFiles: "Con allegati"
-silence: "Silenzia"
+silence: "Silenziare"
 silenceConfirm: "Vuoi davvero silenziare questo profilo?"
 unsilence: "Riattiva"
 unsilenceConfirm: "Vuoi davvero riattivare questo profilo?"
@@ -450,7 +451,7 @@ share: "Condividi"
 notFound: "Non trovato"
 notFoundDescription: "Nessuna pagina corrisponde all'URL indicata."
 uploadFolder: "Destinazione caricamento predefinita"
-markAsReadAllNotifications: "Segna tutte le notifiche come lette"
+markAsReadAllNotifications: "Segnare tutte le notifiche come lette"
 markAsReadAllUnreadNotes: "Segna tutte le note come lette"
 markAsReadAllTalkMessages: "Segna tutte le chat come lette"
 help: "Guida"
@@ -494,6 +495,7 @@ emojiStyle: "Stile emoji"
 native: "Nativo"
 disableDrawer: "Non mostrare il menù sul drawer"
 showNoteActionsOnlyHover: "Mostra le azioni delle Note solo al passaggio del mouse"
+showReactionsCount: "Visualizza il numero di reazioni su una nota"
 noHistory: "Nessuna cronologia"
 signinHistory: "Storico degli accessi al profilo"
 enableAdvancedMfm: "Attiva MFM avanzati"
@@ -577,7 +579,7 @@ scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScr
 output: "Uscita"
 script: "Script"
 disablePagesScript: "Disabilita AiScript nelle pagine"
-updateRemoteUser: "Aggiorna le informazioni dal profilo remoto"
+updateRemoteUser: "Aggiorna dati dal profilo remoto"
 unsetUserAvatar: "Rimozione foto profilo"
 unsetUserAvatarConfirm: "Vuoi davvero rimuovere la foto profilo?"
 unsetUserBanner: "Rimuovi intestazione profilo"
@@ -587,7 +589,7 @@ deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?"
 removeAllFollowing: "Annulla tutti i follow"
 removeAllFollowingDescription: "Cancella tutti i follows del server {host}. Per favore, esegui se, ad esempio, l'istanza non esiste più."
 userSuspended: "L'utente è in sospensione"
-userSilenced: "Profilo silente."
+userSilenced: "Profilo silenziato"
 yourAccountSuspendedTitle: "Questo profilo è sospeso"
 yourAccountSuspendedDescription: "Questo profilo è stato sospeso a causa di una violazione del regolamento. Per informazioni, contattare l'amministrazione. Si prega di non creare un nuovo account."
 tokenRevoked: "Il token non è valido"
@@ -657,7 +659,7 @@ wordMute: "Filtri parole"
 hardWordMute: "Filtro parole forte"
 regexpError: "errore regex"
 regexpErrorDescription: "Si è verificato un errore nell'espressione regolare alla riga {line} della parola muta {tab}:"
-instanceMute: "Silenzia l'istanza"
+instanceMute: "Silenziare l'istanza"
 userSaysSomething: "{name} ha parlato"
 makeActive: "Attiva"
 display: "Visualizza"
@@ -682,14 +684,14 @@ fileIdOrUrl: "ID o URL del file"
 behavior: "Comportamento"
 sample: "Esempio"
 abuseReports: "Segnalazioni"
-reportAbuse: "Segnala"
-reportAbuseRenote: "Segnala la Rinota"
-reportAbuseOf: "Segnala {name}"
+reportAbuse: "Segnalare"
+reportAbuseRenote: "Segnalare la Rinota"
+reportAbuseOf: "Segnalare {name}"
 fillAbuseReportDescription: "Per favore, spiegaci il motivo della segnalazione. Se riguarda una Nota precisa, indica anche l'indirizzo URL."
 abuseReported: "La segnalazione è stata inviata. Grazie."
 reporter: "il corrispondente"
-reporteeOrigin: "Origine del segnalato"
-reporterOrigin: "Origine del segnalatore"
+reporteeOrigin: "Segnalazione a"
+reporterOrigin: "Segnalazione da"
 forwardReport: "Inoltro di un report a un'istanza remota."
 forwardReportIsAnonymous: "L'istanza remota non vedrà le tue informazioni, apparirai come profilo di sistema, anonimo."
 send: "Inviare"
@@ -861,7 +863,7 @@ troubleshooting: "Risoluzione problemi"
 useBlurEffect: "Utilizza effetto sfocatura"
 learnMore: "Più dettagli"
 misskeyUpdated: "Misskey è stato aggiornato!"
-whatIsNew: "Visualizza le informazioni sull'aggiornamento"
+whatIsNew: "Informazioni sull'aggiornamento"
 translate: "Traduci"
 translatedFrom: "Traduzione da {x}"
 accountDeletionInProgress: "È in corso l'eliminazione del profilo"
@@ -887,7 +889,7 @@ manageAccounts: "Gestisci i profili"
 makeReactionsPublic: "Pubblicare la lista delle reazioni."
 makeReactionsPublicDescription: "La lista delle reazioni che avete fatto è a disposizione di tutti."
 classic: "Classico"
-muteThread: "Silenzia conversazione"
+muteThread: "Silenziare conversazione"
 unmuteThread: "Riattiva la conversazione"
 followingVisibility: "Visibilità dei profili seguiti"
 followersVisibility: "Visibilità dei profili che ti seguono"
@@ -969,11 +971,11 @@ shuffle: "Casuale"
 account: "Account"
 move: "Sposta"
 pushNotification: "Notifiche Push"
-subscribePushNotification: "Attiva le notifiche push"
-unsubscribePushNotification: "Disattiva le notifiche push"
+subscribePushNotification: "Attivare le notifiche push"
+unsubscribePushNotification: "Disattivare le notifiche push"
 pushNotificationAlreadySubscribed: "Le notifiche push sono già attivate"
 pushNotificationNotSupported: "Il client o il server non supporta le notifiche push"
-sendPushNotificationReadMessage: "Elimina le notifiche push dopo la relativa lettura"
+sendPushNotificationReadMessage: "Eliminare le notifiche push dopo la relativa lettura"
 sendPushNotificationReadMessageCaption: "Se possibile, verrà mostrata brevemente una notifica con il testo \"{emptyPushNotificationMessage}\". Potrebbe influire negativamente sulla durata della batteria."
 windowMaximize: "Ingrandisci"
 windowMinimize: "Contrai finestra"
@@ -1160,6 +1162,7 @@ showRenotes: "Includi le Rinota"
 edited: "Modificato"
 notificationRecieveConfig: "Preferenze di notifica"
 mutualFollow: "Follow reciproco"
+followingOrFollower: "Following o Follower"
 fileAttachedOnly: "Solo con allegati"
 showRepliesToOthersInTimeline: "Risposte altrui nella TL"
 hideRepliesToOthersInTimeline: "Nascondi Riposte altrui nella TL"
@@ -1210,6 +1213,8 @@ soundWillBePlayed: "Con musica ed effetti sonori"
 showReplay: "Vedi i replay"
 replay: "Replay"
 replaying: "Replay in corso"
+endReplay: "Termina replay"
+copyReplayData: "Copia replay"
 ranking: "Classifica"
 lastNDays: "Ultimi {n} giorni"
 backToTitle: "Torna al titolo"
@@ -1217,9 +1222,28 @@ hemisphere: "Geolocalizzazione"
 withSensitive: "Mostra le Note con allegati espliciti"
 userSaysSomethingSensitive: "Note da {name} con allegati espliciti"
 enableHorizontalSwipe: "Trascina per invertire i tab"
+loading: "Caricamento"
 surrender: "Annulla"
+gameRetry: "Riprova"
+notUsePleaseLeaveBlank: "Lasciare vuoto, se non in uso"
+useTotp: "Usare il codice OTP"
+useBackupCode: "Usare il codice usa-e-getta"
+launchApp: "Esegui l'App"
+useNativeUIForVideoAudioPlayer: "Riprodurre audio/video usando le funzionalità del browser"
+keepOriginalFilename: "Mantieni il nome file originale"
+keepOriginalFilenameDescription: "Disattivandola, i file verranno caricati usando nomi casuali."
+noDescription: "Manca la descrizione"
 _bubbleGame:
   howToPlay: "Come giocare"
+  hold: "Tieni"
+  _score:
+    score: "Punteggio"
+    scoreYen: "Capitale"
+    highScore: "Punteggio migliore"
+    maxChain: "Miglior combo"
+    yen: "{yen}¥"
+    estimatedQty: "{qty} punti"
+    scoreSweets: "Onigiri {onigiriQtyWithUnit}"
   _howToPlay:
     section1: "Scegli la posizione e rilascia l'oggetto nel contenitore."
     section2: "Se due oggetti dello stesso tipo si toccano, si trasformano in un oggetto diverso, aumentando il punteggio."
@@ -1235,7 +1259,7 @@ _announcement:
   readConfirmText: "Hai già letto \"{title}˝?"
   shouldNotBeUsedToPresentPermanentInfo: "Ti consigliamo di utilizzare gli annunci per pubblicare informazioni tempestive e limitate nel tempo, anziché informazioni importanti a lungo andare nel tempo, poiché potrebbero risultare difficili da ritrovare e peggiorare la fruibilità del servizio, specialmente alle nuove persone iscritte."
   dialogAnnouncementUxWarn: "Ti consigliamo di usarli con cautela, poiché è molto probabile che avere più di un annuncio in stile \"finestra di dialogo\" peggiori sensibilmente la fruibilità del servizio, specialmente alle nuove persone iscritte."
-  silence: "Silenzia gli annunci"
+  silence: "Silenziare gli annunci"
   silenceDescription: "Se attivi questa opzione, non riceverai notifiche sugli annunci, evitando di contrassegnarle come già lette."
 _initialAccountSetting:
   accountCreated: "Il tuo profilo è stato creato!"
@@ -1274,14 +1298,14 @@ _initialTutorial:
     letsTryReacting: "Puoi aggiungere una Reazione cliccando il bottone \"+\" (più) della relativa Nota. Prova ad aggiungerne una a questa Nota di esempio!"
     reactToContinue: "Aggiungere la Reazione ti consentirà di procedere col tutorial."
     reactNotification: "Quando qualcuno reagisce alle tue Note, ricevi una notifica in tempo reale."
-    reactDone: "Puoi annullare la tua Reazione premendo il bottone \"ー\" (meno)"
+    reactDone: "Annulla la tua Reazione premendo il bottone \"ー\" (meno)"
   _timeline:
     title: "Come funziona la Timeline"
     description1: "Misskey fornisce alcune Timeline (sequenze cronologiche di Note). Una di queste potrebbe essere stata disattivata dagli amministratori."
-    home: "Puoi vedere le Note provenienti dai profili che segui (follow)."
-    local: "Puoi vedere tutte le Note pubblicate dai profili di questa istanza."
-    social: "Puoi vedere sia le Note della Timeline Home che quelle della Timeline Locale, insieme!"
-    global: "Puoi vedere le Note da pubblicate da tutte le altre istanze federate con la nostra."
+    home: "le Note provenienti dai profili che segui (follow)."
+    local: "tutte le Note pubblicate dai profili di questa istanza."
+    social: "sia le Note della Timeline Home che quelle della Timeline Locale, insieme!"
+    global: "le Note da pubblicate da tutte le altre istanze federate con la nostra."
     description2: "Nella parte superiore dello schermo, puoi scegliere una Timeline o l'altra in qualsiasi momento."
     description3: "Ci sono anche sequenze temporali di elenchi, sequenze temporali di canali, ecc. Per ulteriori dettagli, consultare il {link}.\nPuoi vedere anche Timeline delle liste di profili (se ne hai create), canali, ecc... Per i dettagli, visita {link}."
   _postNote:
@@ -1305,13 +1329,13 @@ _initialTutorial:
       useCases: "Utilizzalo per chiarire il contenuto della Nota, prima che sia letta. Come richiesto dal regolamento del server o per autoregolamentare spoiler e testi troppo espliciti."
   _howToMakeAttachmentsSensitive:
     title: "Come indicare che gli allegati sono espliciti?"
-    description: "Contrassegnare gli allegati come espliciti, va fatto quando è richiesto dal regolamento del server o quando gli allegati non devono essere immediatamente visibili."
+    description: "Si fa quando è richiesto dal regolamento del server o quando non devono essere visibili immediatamente."
     tryThisFile: "Prova a rendere esplicite le immagini allegate a questo modulo!"
     _exampleNote:
-      note: "Ho fatto un errore aprendo il coperchio del natto... (fagioli di soia fermentati, particolarmente appiccicosi)"
-    method: "Per indicare che un allegato è esplicito, tocca il file per aprirne il menu e scegliere la voce \"Segna come esplicito\"."
-    sensitiveSucceeded: "Quando alleghi file, assicurati di indicare se è materiale esplicito, in modo appropriato, in base al regolamento del tuo server."
-    doItToContinue: "Impostando l'immagine come esplicita, potrai procedere col tutorial."
+      note: "AAA! Ho rotto il coperchio del natto... (fagioli di soia fermentati)"
+    method: "Tocca il file, si aprirà il menu, scegli la voce \"Segna come esplicito\""
+    sensitiveSucceeded: "Quando alleghi file, assicurati di indicare se è materiale esplicito in modo appropriato, decidi in base al regolamento dell'istanza."
+    doItToContinue: "Imposta l'immagine come esplicita per procedere col tutorial."
   _done:
     title: "Il tutorial è finito! 🎉"
     description: "Queste sono solamente alcune delle funzionalità principali di Misskey. Per ulteriori informazioni, {link}."
@@ -1337,7 +1361,7 @@ _serverSettings:
 _accountMigration:
   moveFrom: "Migra un altro profilo dentro a questo"
   moveFromSub: "Crea un alias verso un altro profilo remoto"
-  moveFromLabel: "Profilo da cui migrare:"
+  moveFromLabel: "Profilo da cui migrare #{n}"
   moveFromDescription: "Se desideri spostare i profili follower da un altro profilo a questo, devi prima creare un alias qui. Assicurati averlo creato PRIMA di eseguire l'attività! Inserisci l'indirizzo del profilo mittente in questo modo: @persona@istanza.it"
   moveTo: "Migrare questo profilo verso un un altro"
   moveToLabel: "Profilo verso cui migrare"
@@ -1641,6 +1665,7 @@ _role:
     gtlAvailable: "Disponibilità della Timeline Federata"
     ltlAvailable: "Disponibilità della Timeline Locale"
     canPublicNote: "Scrivere Note con Visibilità Pubblica"
+    mentionMax: "Numero massimo di menzioni in una nota"
     canInvite: "Generare codici di invito all'istanza"
     inviteLimit: "Limite di codici invito"
     inviteLimitCycle: "Intervallo di emissione del codice di invito"
@@ -1664,6 +1689,7 @@ _role:
     canUseTranslator: "Tradurre le Note"
     avatarDecorationLimit: "Numero massimo di decorazioni foto profilo installabili"
   _condition:
+    roleAssignedTo: "Assegnato a ruoli manualmente"
     isLocal: "Profilo locale"
     isRemote: "Profilo remoto"
     createdLessThan: "Profilo creato da meno di N"
@@ -1735,6 +1761,7 @@ _plugin:
   installWarn: "Si prega di installare soltanto estensioni che provengono da fonti affidabili."
   manage: "Gestisci estensioni"
   viewSource: "Visualizza sorgente"
+  viewLog: "Mostra log"
 _preferencesBackups:
   list: "Elenco di impostazioni salvate in precedenza"
   saveNew: "Nuovo salvataggio"
@@ -1809,7 +1836,7 @@ _instanceMute:
   instanceMuteDescription: "Disattiva tutte le note, le note di rinvio (condivisione) dell'istanza configurata, comprese le risposte agli utenti dell'istanza."
   instanceMuteDescription2: "Impostazione separata da una nuova riga"
   title: "Nasconde le note dell'istanza configurata."
-  heading: "Istanze da silenziare."
+  heading: "Istanze da silenziare"
 _theme:
   explore: "Esplora temi"
   install: "Installa un tema"
@@ -1924,7 +1951,6 @@ _2fa:
   registerTOTP: "Registra una App di autenticazione a due fattori (2FA/MFA)"
   step1: "Innanzitutto, installa sul dispositivo un'App di autenticazione come {a} o {b}."
   step2: "Quindi, tramite la App installata, scansiona questo codice QR."
-  step2Click: "Cliccando sul codice QR, puoi registrarlo con l'app di autenticazione o il portachiavi installato sul tuo dispositivo."
   step2Uri: "Inserisci il seguente URL se desideri utilizzare una App per PC"
   step3Title: "Inserisci il codice di verifica"
   step3: "Inserite il token visualizzato nell'app e il gioco è fatto."
@@ -1948,6 +1974,7 @@ _2fa:
   backupCodesDescription: "Puoi usare questi codici usa-e-getta per ottenere l'accesso al tuo profilo in caso sia impossibile usare l'App col codice OTP. Salvali in un posto sicuro."
   backupCodeUsedWarning: "È stato usato un codice usa-e-getta. Per favore, riconfigura l'autenticazione a due fattori il prima possibile, nel caso la configurazione precedente abbia smesso di funzionare."
   backupCodesExhaustedWarning: "Hai esaurito i codici usa-e-getta. Se l'App che genera il codice OTP non è più disponibile, non potrai più accedere al tuo profilo. Ripeti la configurazione per l'autenticazione a due fattori."
+  moreDetailedGuideHere: "Informazioni dettagliate sull'autenticazione multi fattore (2FA/MFA)"
 _permissions:
   "read:account": "Visualizza le informazioni sul profilo"
   "write:account": "Modifica le informazioni sul profilo"
@@ -1964,8 +1991,8 @@ _permissions:
   "read:mutes": "Vedi i profili silenziati"
   "write:mutes": "Gestisci i profili silenziati"
   "write:notes": "Creare / Eliminare note"
-  "read:notifications": "Visualizza notifiche"
-  "write:notifications": "Gerisci notifiche"
+  "read:notifications": "Visualizzare notifiche"
+  "write:notifications": "Gestire notifiche"
   "read:reactions": "Vedi reazioni"
   "write:reactions": "Gerisci reazioni"
   "write:votes": "Votare"
@@ -2209,6 +2236,7 @@ _play:
   title: "Titolo"
   script: "Script"
   summary: "Descrizione"
+  visibilityDescription: "Impostarlo su privato significa che non verrà visualizzato sul tuo profilo, ma chiunque ha l'URL potrà comunque accedervi."
 _pages:
   newPage: "Crea pagina"
   editPage: "Modifica pagina"
@@ -2253,6 +2281,8 @@ _pages:
     section: "Sezione"
     image: "Immagini"
     button: "Pulsante"
+    dynamic: "Riquadri dinamici"
+    dynamicDescription: "Questo riquadro è obsoleto. Utilizza {play} da ora in poi."
     note: "Nota integrata"
     _note:
       id: "ID nota"
@@ -2277,13 +2307,15 @@ _notification:
   roleAssigned: "Ruolo assegnato"
   emptyPushNotificationMessage: "Le notifiche push sono state aggiornate."
   achievementEarned: "Obiettivo raggiunto"
-  testNotification: "Prova la notifica"
-  checkNotificationBehavior: "Prova il comportamento della notifica"
+  testNotification: "Provare la notifica"
+  checkNotificationBehavior: "Provare il comportamento della notifica"
   sendTestNotification: "Spedisci una notifica di prova"
   notificationWillBeDisplayedLikeThis: "La notifica apparirà così"
   reactedBySomeUsers: "{n} reazioni"
+  likedBySomeUsers: "{n} apprezzamenti"
   renotedBySomeUsers: "{n} Rinota"
-  followedBySomeUsers: "{n} nuovi follower"
+  followedBySomeUsers: "{n} follower"
+  flushNotification: "Azzera le notifiche"
   _types:
     all: "Tutto"
     note: "Nuove Note"
@@ -2335,8 +2367,8 @@ _deck:
     direct: "Note Dirette"
     roleTimeline: "Timeline Ruolo"
 _dialog:
-  charactersExceeded: "Hai superato il limite di {max} caratteri! ({corrente})"
-  charactersBelow: "Sei al di sotto del minimo di {min} caratteri!  ({corrente})"
+  charactersExceeded: "Hai superato il limite di {max} caratteri! ({current})"
+  charactersBelow: "Sei al di sotto del minimo di {min} caratteri!  ({current})"
 _disabledTimeline:
   title: "Timeline disabilitata"
   description: "Il ruolo in cui sei non ti permette di leggere questa timeline"
@@ -2381,6 +2413,7 @@ _moderationLogTypes:
   resetPassword: "Password azzerata"
   suspendRemoteInstance: "Istanza remota sospesa"
   unsuspendRemoteInstance: "Istanza remota riattivata"
+  updateRemoteInstanceNote: "Aggiornamento del promemoria di moderazione per il server remoto"
   markSensitiveDriveFile: "File nel Drive segnato come esplicito"
   unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito"
   resolveAbuseReport: "Segnalazione risolta"
@@ -2501,7 +2534,24 @@ _reversi:
   opponentHasSettingsChanged: "L'avversario ha cambiato configurazione"
   allowIrregularRules: "Regole inconsuete (completamente libere)"
   disallowIrregularRules: "Impedire le regole inconsuete"
+  showBoardLabels: "Mostra le coordinate del gioco"
+  useAvatarAsStone: "Immagini profilo come pedine"
 _offlineScreen:
   title: "Scollegato. Impossibile connettersi al server"
   header: "Impossibile connettersi al server"
-
+_urlPreviewSetting:
+  title: "Impostazioni per l'anteprima delle URL"
+  enable: "Attiva l'anteprima delle URL"
+  timeout: "Timeout dell'anteprima in millisecondi"
+  timeoutDescription: "Impegna al massimo il tempo indicato, altrimenti ignora l'anteprima"
+  maximumContentLength: "Grandezza del contenuto (Content-Length in byte)"
+  maximumContentLengthDescription: "Se la grandezza supera il valore, l'anteprima verrà ignorata."
+  requireContentLength: "Genenerare l'anteprima solo quando è definito Content-Length"
+  requireContentLengthDescription: "In assenza di questo parametro dal server remoto, l'anteprima verrà ignorata."
+  userAgent: "User-Agent"
+  userAgentDescription: "Definire con quale User-Agent si intende identificarsi durante l'acquisizione di un'anteprima. Se è vuoto, useremo il valore predefinito."
+  summaryProxy: "Endpoint proxy che genera l'anteprima"
+_mediaControls:
+  pip: "Sovraimpressione"
+  playbackRate: "Velocità di riproduzione"
+  loop: "Ripetizione infinita"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 7ff26c757dd2..e6a23a34d7e2 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -400,6 +400,7 @@ name: "名前"
 antennaSource: "受信ソース(このソースは食われへん)"
 antennaKeywords: "受信キーワード"
 antennaExcludeKeywords: "除外キーワード"
+antennaExcludeBots: "Botアカウントを除外"
 antennaKeywordsDescription: "スペースで区切ったるとAND指定で、改行で区切ったるとOR指定や"
 notifyAntenna: "新しいノートを通知すんで"
 withFileAntenna: "なんか添付されたノートだけ"
@@ -494,6 +495,7 @@ emojiStyle: "絵文字のスタイル"
 native: "ネイティブ"
 disableDrawer: "メニューをドロワーで表示せえへん"
 showNoteActionsOnlyHover: "ノートの操作部をホバー時のみ表示するで"
+showReactionsCount: "ノートのリアクション数を表示する"
 noHistory: "履歴はないわ。"
 signinHistory: "ログイン履歴"
 enableAdvancedMfm: "ややこしいMFMもありにする"
@@ -1042,6 +1044,8 @@ resetPasswordConfirm: "パスワード作り直すんでええな?"
 sensitiveWords: "けったいな単語"
 sensitiveWordsDescription: "設定した単語が入っとるノートの公開範囲をホームにしたるわ。改行で区切ったら複数設定できるで。"
 sensitiveWordsDescription2: "スペースで区切るとAND指定、キーワードをスラッシュで囲んだら正規表現や。"
+prohibitedWords: "禁止ワード"
+prohibitedWordsDescription: "設定した言葉が含まれるノートを投稿しようとしたら、エラーが出るようにするで。改行で区切って複数設定できるで。"
 prohibitedWordsDescription2: "スペースで区切るとAND指定、キーワードをスラッシュで囲んだら正規表現や。"
 hiddenTags: "見えてへんハッシュタグ"
 hiddenTagsDescription: "設定したタグを最近流行りのとこに見えんようにすんで。複数設定するときは改行で区切ってな。"
@@ -1158,6 +1162,7 @@ showRenotes: "リノート出す"
 edited: "いじったやつ"
 notificationRecieveConfig: "通知もらうかの設定"
 mutualFollow: "お互いフォローしてんで"
+followingOrFollower: "フォロー中またはフォロワー"
 fileAttachedOnly: "ファイルのっけてあるやつだけ"
 showRepliesToOthersInTimeline: "タイムラインに他の人への返信とかも入れるで"
 hideRepliesToOthersInTimeline: "タイムラインに他の人への返信とかは入れへん"
@@ -1167,6 +1172,12 @@ confirmShowRepliesAll: "これは元に戻せへんから慎重に決めてや
 confirmHideRepliesAll: "これは元に戻せへんから慎重に決めてや。本当にタイムラインに今フォローしとる全員の返信を入れへんのか?"
 externalServices: "他のサイトのサービス"
 sourceCode: "ソースコード"
+sourceCodeIsNotYetProvided: "ソースコードはまだ提供されてへんで。問題の修正について管理者に問い合わせてみ。"
+repositoryUrl: "リポジトリURL"
+repositoryUrlDescription: "ソースコードが公開されているリポジトリがある場合、そのURLを記入するで。Misskeyをそのまんま(ソースコードにいかなる変更も加えずに)使っとる場合は https://github.com/misskey-dev/misskey と記入するで。"
+repositoryUrlOrTarballRequired: "リポジトリを公開してへんなら、代わりにtarballを提供する必要があるで。詳細は.config/example.ymlを参照してな。"
+feedback: "フィードバック"
+feedbackUrl: "フィードバックURL"
 impressum: "運営者の情報"
 impressumUrl: "運営者の情報URL"
 impressumDescription: "ドイツとかの一部んところではな、表示が義務付けられてんねん(Impressum)。"
@@ -1202,6 +1213,8 @@ soundWillBePlayed: "サウンドが再生されるで"
 showReplay: "リプレイ見る"
 replay: "リプレイ"
 replaying: "リプレイ中"
+endReplay: "リプレイを終了"
+copyReplayData: "リプレイデータをコピー"
 ranking: "ランキング"
 lastNDays: "直近{n}日"
 backToTitle: "タイトルへ"
@@ -1209,9 +1222,30 @@ hemisphere: "住んでる地域"
 withSensitive: "センシティブなファイルを含むノートを表示"
 userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿"
 enableHorizontalSwipe: "スワイプしてタブを切り替える"
+loading: "読み込み中"
 surrender: "やめとく"
+gameRetry: "もういっちょ"
+notUsePleaseLeaveBlank: "使用せえへん場合は空欄にしてや"
+useTotp: "ワンタイムパスワードを使う"
+useBackupCode: "バックアップコードを使う"
+launchApp: "アプリを起動"
+useNativeUIForVideoAudioPlayer: "動画・音声の再生にブラウザのUIを使用する"
+keepOriginalFilename: "オリジナルのファイル名を保持"
+keepOriginalFilenameDescription: "この設定をオフにすると、アップロード時にファイル名が自動でランダム文字列に置き換えられるで。"
+noDescription: "説明文はあらへんで"
+alwaysConfirmFollow: "フォローの際常に確認する"
+inquiry: "問い合わせ"
 _bubbleGame:
   howToPlay: "遊び方"
+  hold: "ホールド"
+  _score:
+    score: "スコア"
+    scoreYen: "稼いだ金額"
+    highScore: "ハイスコア"
+    maxChain: "最大チェーン数"
+    yen: "{yen}円"
+    estimatedQty: "{qty}個分"
+    scoreSweets: "おにぎり {onigiriQtyWithUnit}"
   _howToPlay:
     section1: "位置を調整してハコにモノを落とすで。"
     section2: "同じもんがくっついたら別のやつになって、スコアがもらえるで。"
@@ -1633,6 +1667,7 @@ _role:
     gtlAvailable: "グローバルタイムライン見る"
     ltlAvailable: "ローカルタイムライン見る"
     canPublicNote: "パブリック投稿できるか"
+    mentionMax: "ノート内の最大メンション数"
     canInvite: "サーバー招待コード作る"
     inviteLimit: "招待コード作れる数"
     inviteLimitCycle: "招待コードの作れる間隔"
@@ -1656,8 +1691,14 @@ _role:
     canUseTranslator: "翻訳使えるかどうか"
     avatarDecorationLimit: "アイコンデコのいっちばんつけれる数"
   _condition:
+    roleAssignedTo: "マニュアルロールにアサイン済み"
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
+    isCat: "猫ユーザー"
+    isBot: "botユーザー"
+    isSuspended: "サスペンド済みユーザー"
+    isLocked: "鍵アカウントユーザー"
+    isExplorable: "「アカウントを見つけやすくする」が有効なユーザー"
     createdLessThan: "アカウント作ってから~以内"
     createdMoreThan: "アカウント作ってから~経過"
     followersLessThanOrEq: "フォロワー数が~以下"
@@ -1727,6 +1768,7 @@ _plugin:
   installWarn: "信頼できへんプラグインはインストールせんとってな"
   manage: "プラグインの管理"
   viewSource: "ソース見る"
+  viewLog: "ログを表示"
 _preferencesBackups:
   list: "作ったバックアップ"
   saveNew: "新しく保存"
@@ -1741,8 +1783,8 @@ _preferencesBackups:
   deleteConfirm: "{name}を消すん?"
   renameConfirm: "「{old}」を「{new}」に変えるん?"
   noBackups: "バックアップはないで。「新しく保存」ってとこでこのクライアント設定を鯖に保存できるで。"
-  createdAt: "作った日時:{date}{time}"
-  updatedAt: "更新日時:{date}{time}"
+  createdAt: "作った日時: {date} {time}"
+  updatedAt: "更新日時: {date} {time}"
   cannotLoad: "読み込みできへん..."
   invalidFile: "ファイル形式が違うで?"
 _registry:
@@ -1756,6 +1798,8 @@ _aboutMisskey:
   contributors: "主な貢献者"
   allContributors: "全ての貢献者"
   source: "ソースコード"
+  original: "オリジナル"
+  thisIsModifiedVersion: "{name}はオリジナルのMisskeyをいじったバージョンをつこうてるで。"
   translation: "Misskeyを翻訳"
   donate: "Misskeyに寄付"
   morePatrons: "他にもぎょうさんの人からサポートしてもろてんねん。ほんまおおきに🥰"
@@ -1914,7 +1958,6 @@ _2fa:
   registerTOTP: "認証アプリの設定はじめる"
   step1: "ほんなら、{a}や{b}とかの認証アプリを使っとるデバイスにインストールしてな。"
   step2: "次に、ここにあるQRコードをアプリでスキャンしてな~。"
-  step2Click: "QRコード押したら、今使とる端末に入っとる認証アプリとかキーリングに登録できるで。"
   step2Uri: "デスクトップアプリを使う時は次のURIを入れるで"
   step3Title: "確認コードを入れてーや"
   step3: "アプリに映っとる確認コード(トークン)を入れて終わりや。"
@@ -1938,6 +1981,7 @@ _2fa:
   backupCodesDescription: "認証アプリが使用できんなった場合、以下のバックアップコードを使ってアカウントにアクセスできるで。これらのコードは必ず安全な場所に置いときや。各コードは一回だけ使用できるで。"
   backupCodeUsedWarning: "バックアップコードが使用されたで。認証アプリが使えなくなってるん場合、なるべく早く認証アプリを再設定しや。"
   backupCodesExhaustedWarning: "バックアップコードが全て使用されたで。認証アプリを利用できん場合、これ以上アカウントにアクセスできなくなるで。認証アプリを再登録しや。"
+  moreDetailedGuideHere: "詳細なガイドはこちら"
 _permissions:
   "read:account": "アカウントの情報を見るで"
   "write:account": "アカウントの情報を変更するで"
@@ -2199,6 +2243,7 @@ _play:
   title: "タイトル"
   script: "スクリプト"
   summary: "説明"
+  visibilityDescription: "非公開に設定するとプロフィールに表示されへんくなるけど、URLを知っとる人は引き続きアクセスできるで。"
 _pages:
   newPage: "ページを作る"
   editPage: "ページの編集"
@@ -2243,6 +2288,8 @@ _pages:
     section: "セクション"
     image: "画像"
     button: "ボタン"
+    dynamic: "動的ブロック"
+    dynamicDescription: "このブロックは廃止されとるで。今後は{play}を利用してや。"
     note: "ノート埋め込み"
     _note:
       id: "ノートID"
@@ -2272,8 +2319,10 @@ _notification:
   sendTestNotification: "テスト通知を送信するで"
   notificationWillBeDisplayedLikeThis: "通知はこのように表示されるで"
   reactedBySomeUsers: "{n}人がツッコんだで"
+  likedBySomeUsers: "{n}人がいいねしたで"
   renotedBySomeUsers: "{n}人がリノートしたで"
   followedBySomeUsers: "{n}人にフォローされたで"
+  flushNotification: "通知の履歴をリセットする"
   _types:
     all: "すべて"
     note: "あんたらの新規投稿"
@@ -2371,6 +2420,7 @@ _moderationLogTypes:
   resetPassword: "パスワードをリセット"
   suspendRemoteInstance: "リモートサーバーを止めんで"
   unsuspendRemoteInstance: "リモートサーバーを再開すんで"
+  updateRemoteInstanceNote: "リモートサーバーのモデレーションノート更新"
   markSensitiveDriveFile: "ファイルをセンシティブ付与"
   unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
   resolveAbuseReport: "苦情を解決"
@@ -2491,7 +2541,26 @@ _reversi:
   opponentHasSettingsChanged: "相手が設定変えたで"
   allowIrregularRules: "変則許可 (完全フリー)"
   disallowIrregularRules: "変則なし"
+  showBoardLabels: "盤面に行・列番号を表示"
+  useAvatarAsStone: "石をアイコンにする"
 _offlineScreen:
   title: "オフライン - サーバーに接続できひんで"
   header: "サーバーに接続できへんわ"
-
+_urlPreviewSetting:
+  title: "URLプレビューの設定"
+  enable: "URLプレビューを有効にする"
+  timeout: "プレビュー取得時のタイムアウト(ms)"
+  timeoutDescription: "プレビュー取得の所要時間がこの値を超えた場合、プレビューは生成されへんで。"
+  maximumContentLength: "Content-Lengthの最大値(byte)"
+  maximumContentLengthDescription: "Content-Lengthがこの値を超えた場合、プレビューは生成されへんで。"
+  requireContentLength: "Content-Lengthが取得できた場合のみプレビューを生成"
+  requireContentLengthDescription: "相手サーバがContent-Lengthを返さない場合、プレビューは生成されへんで。"
+  userAgent: "User-Agent"
+  userAgentDescription: "プレビュー取得時に使用されるUser-Agentを設定するで。空欄の場合、デフォルトのUser-Agentが使用されるで。"
+  summaryProxy: "プレビューを生成するプロキシのエンドポイント"
+  summaryProxyDescription: "Misskey本体やなく、サマリープロキシを使用してプレビューを生成するで。"
+  summaryProxyDescription2: "プロキシには下記パラメータがクエリ文字列として連携されるで。プロキシ側がこれらをサポートせえへんときは、設定値は無視されるで。"
+_mediaControls:
+  pip: "ピクチャインピクチャ"
+  playbackRate: "再生速度"
+  loop: "ループ再生"
diff --git a/locales/jbo-EN.yml b/locales/jbo-EN.yml
index 297ca53dd7d6..d4fea291d790 100644
--- a/locales/jbo-EN.yml
+++ b/locales/jbo-EN.yml
@@ -1,4 +1,3 @@
 ---
 _lang_: "la .lojban."
 headlineMisskey: "lo se tcana noi jorne fi loi notci"
-
diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml
index b976f028f098..22e24d3baac0 100644
--- a/locales/kab-KAB.yml
+++ b/locales/kab-KAB.yml
@@ -104,4 +104,3 @@ _deck:
   _columns:
     notifications: "Ilɣuyen"
     list: "Tibdarin"
-
diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml
index bb6d1ee24248..b3ad46f2b16a 100644
--- a/locales/kn-IN.yml
+++ b/locales/kn-IN.yml
@@ -84,4 +84,3 @@ _deck:
     notifications: "ಅಧಿಸೂಚನೆಗಳು"
     tl: "ಸಮಯಸಾಲು"
     mentions: "ಹೆಸರಿಸಿದ"
-
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index 39492d902ffe..c80a4d399752 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -16,8 +16,8 @@ cancel: "아이예"
 noThankYou: "뎃어예"
 enterUsername: "사용자 이럼 서기"
 renotedBy: "{user}님이 리노트햇어예"
-noNotes: "노트가 없십니다"
-noNotifications: "알림이 없십니다"
+noNotes: "노트가 어ᇝ십니다"
+noNotifications: "알림이 어ᇝ십니다"
 instance: "서버"
 settings: "설정"
 notificationSettings: "알림 설정"
@@ -26,7 +26,7 @@ otherSettings: "다린 설정"
 openInWindow: "창서 옐기"
 profile: "프로필"
 timeline: "타임라인"
-noAccountDescription: "자기소개가 없십니다"
+noAccountDescription: "자기소개가 어ᇝ십니다"
 login: "로그인"
 loggingIn: "로그인하고 잇어예"
 logout: "로그아웃"
@@ -80,7 +80,7 @@ unfollowConfirm: "{name}님얼 고마 팔로잉합니꺼?"
 exportRequested: "내가기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다. 요청이 껕나모 ‘드라이브’에 옇십니다."
 importRequested: "가오기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다."
 lists: "리스트"
-noLists: "리스트가 없십니다"
+noLists: "리스트가 어ᇝ십니다"
 note: "노트"
 notes: "노트"
 following: "팔로잉"
@@ -161,7 +161,7 @@ youCanCleanRemoteFilesCache: "파일 간리으 🗑️ 모냥얼 누질리모 
 cacheRemoteSensitiveFiles: "웬겍으 수ᇚ힌 파일얼 캐시하기"
 cacheRemoteSensitiveFilesDescription: "요 설정얼 꺼모 웬겍 수ᇚ힌 파일이 캐시하지 아이하고 바리 링크합니다."
 flagAsBot: "자동 게정입니다"
-flagAsBotDescription: "요 게정얼 프로그램서 설라먼 키야 합니다. 키모 다런 개발자가 반엉얼 끋없이 데풀이하지 몬 하게 도아 줄 수 잇고 Misskey으 시스템서 자동 게정이 뎁니다."
+flagAsBotDescription: "요 게정얼 프로그램서 설라먼 키야 합니다. 키모 다런 개발자가 반엉얼 끋어ᇝ이 데풀이하지 몬 하게 도아 줄 수 잇고 Misskey으 시스템서 자동 게정이 뎁니다."
 flagAsCat: "애웅애웅애웅애웅!"
 flagAsCatDescription: "애옹?"
 flagShowTimelineReplies: "타임라인서 노트으 답하기 보기"
@@ -176,7 +176,7 @@ wallpaper: "벡지"
 setWallpaper: "벡지 설정"
 removeWallpaper: "벡지 뭉캐기"
 searchWith: "찾기: {q}"
-youHaveNoLists: "리스트가 없십니다"
+youHaveNoLists: "리스트가 어ᇝ십니다"
 followConfirm: "{name}님얼 팔로잉합니꺼?"
 proxyAccount: "프락시 게정"
 proxyAccountDescription: "프락시 게정언 턱벨한 조겐서 웬겍 팔로잉얼 하넌 게정입니다. 사용자가 웬겍 사용자럴 리스트에 옇얼 때 리스트에 옇언 사용자럴 누도 팔로잉 아이하모 할동이 서버로 아이 오니께 요 게정이 아인 프락시 게정얼 팔로잉하게 합니다."
@@ -210,17 +210,17 @@ instanceInfo: "서버 정보"
 statistics: "통게"
 clearQueue: "대기옐 비우기"
 clearQueueConfirmTitle: "대기옐얼 비웁니꺼?"
-clearQueueConfirmText: "대기옐에 잇넌 걸얼 아이 보냅니다. 흐이 요 동작언 할 필요가 없십니다."
+clearQueueConfirmText: "대기옐에 잇넌 걸얼 아이 보냅니다. 흐이 요 동작언 할 필요가 어ᇝ십니다."
 clearCachedFiles: "캐시 비우기"
 clearCachedFilesConfirm: "캐시한 웬겍 파일얼 말캉 뭉캡니꺼?"
 blockedInstances: "차단한 서버"
 blockedInstancesDescription: "차단할라넌 서버으 호스트럴 줄 바꿈해서로 비이 줍니다. 차단한 서버넌 요 서버하고 교류 몬 합니다."
 silencedInstances: "수ᇚ훈 서버"
-silencedInstancesDescription: "수ᇚ훌라넌 서버으 호스트럴 줄 바꿈해서로 비이 줍니다. 수ᇚ훈 서버으 게정언 말캉 ‘수ᇚ후기’가 데서 팔로잉 요청만 데고 팔로워가 아인 로컬 게정서 멘션얼 몬 합니다. 차단한 서버넌 상간 없십니다."
+silencedInstancesDescription: "수ᇚ훌라넌 서버으 호스트럴 줄 바꿈해서로 비이 줍니다. 수ᇚ훈 서버으 게정언 말캉 ‘수ᇚ후기’가 데서 팔로잉 요청만 데고 팔로워가 아인 로컬 게정서 멘션얼 몬 합니다. 차단한 서버넌 상간 어ᇝ십니다."
 muteAndBlock: "수ᇚ훔하고 차단"
 mutedUsers: "수ᇚ훈 사용자"
 blockedUsers: "차단한 사용자"
-noUsers: "사용자가 없십니다"
+noUsers: "사용자가 어ᇝ십니다"
 editProfile: "프로필 적기"
 noteDeleteConfirm: "요 노트럴 뭉캡니꺼?"
 pinLimitExceeded: "더 몬 붙입니다"
@@ -230,15 +230,15 @@ processing: "처리하고 잇어예"
 preview: "미리보기"
 default: "기본값"
 defaultValueIs: "기본값: {value}"
-noCustomEmojis: "이모지가 없십니다"
-noJobs: "작업이 없십니다"
+noCustomEmojis: "이모지가 어ᇝ십니다"
+noJobs: "작업이 어ᇝ십니다"
 federating: "옌합하고 잇어예"
 blocked: "차단햇어예"
 suspended: "고만 보내예"
 all: "말캉"
 subscribing: "구독하고 잇어예"
 publishing: "보내고 잇어예"
-notResponding: "답이 없어예"
+notResponding: "답이 어ᇝ어예"
 instanceFollowing: "서버으 팔로잉"
 instanceFollowers: "서버으 팔로워"
 instanceUsers: "서버으 사용자"
@@ -275,7 +275,7 @@ uploadFromUrlRequested: "올리기럴 요청햇십니다"
 uploadFromUrlMayTakeTime: "올리기가 껕날라먼 시간이 쪼매 걸릴 깁니다."
 explore: "살펴보기"
 messageRead: "이럿어예"
-noMoreHistory: "요카마 엣날 기록이 없십니다"
+noMoreHistory: "요카마 옛날 기록이 어ᇝ십니다"
 startMessaging: "대화하기"
 nUsersRead: "{n}멩이 이럿십니다"
 agreeTo: "{0}에 동이하기"
@@ -432,28 +432,28 @@ securityKey: "보안키"
 lastUsed: "마지막 쓰임"
 lastUsedAt: "마지막 쓰임: {t}"
 unregister: "맨걸기 무루기"
-passwordLessLogin: "비밀번호 없시 로그인"
-passwordLessLoginDescription: "비밀번호 말고 보안키나 패스키 같은 것만 써 가 로그인합니다."
+passwordLessLogin: "비밀번호 어ᇝ이 로그인"
+passwordLessLoginDescription: "비밀번호 어ᇝ이 보안 키나 패스 키만 서서 로그인합니다."
 resetPassword: "비밀번호 재설정"
-newPasswordIs: "새 비밀번호는 \"{password}\" 입니다"
+newPasswordIs: "새 비밀번호넌 ‘{password}’입니다"
 reduceUiAnimation: "화면 움직임 효과들을 수ᇚ후기"
 share: "노누기"
 notFound: "몬 찾앗십니다"
-notFoundDescription: "고런 주소로 들어가는 하멘은 없십니다."
-uploadFolder: "기본 업로드 위치"
-markAsReadAllNotifications: "모든 알림 이럿다고 표시"
-markAsReadAllUnreadNotes: "모든 글 이럿다고 표시"
-markAsReadAllTalkMessages: "모든 대화 이럿다고 표시"
+notFoundDescription: "선 주소에 맞넌 페이지가 어ᇝ십니다."
+uploadFolder: "기본 올리기 위치"
+markAsReadAllNotifications: "모던 알림얼 읽엄 포시"
+markAsReadAllUnreadNotes: "모던 걸얼 읽엄 포시"
+markAsReadAllTalkMessages: "모던 대화 읽엄 포시"
 help: "도움말"
-inputMessageHere: "여따가 메시지를 입력해주이소"
-close: "닫기"
+inputMessageHere: "옇다 메시지럴 서이소"
+close: "꺼기"
 invites: "초대하기"
-members: "멤버"
-transfer: "양도"
+members: "구성원"
+transfer: "넘구기"
 title: "제목"
-text: "글"
+text: "걸"
 enable: "키기"
-next: "다음"
+next: "다엄"
 retype: "다시 서기"
 noteOf: "{user}님으 노트"
 quoteAttached: "따옴"
@@ -468,6 +468,7 @@ tooShort: "억수로 짜립니다"
 tooLong: "억수로 집니다"
 passwordMatched: "맞십니다"
 passwordNotMatched: "안 맞십니다"
+signinWith: "{n}서 로그인"
 signinFailed: "로그인 몬 했십니다. 고 이름이랑 비밀번호 제대로 썼는가 확인해 주이소."
 or: "아니면"
 language: "언어"
@@ -512,13 +513,13 @@ useObjectStorage: "오브젝트 스토리지 키기"
 objectStorageBaseUrl: "Base URL"
 objectStorageBaseUrlDesc: "오브젝트 (미디어) 참조 링크 만들 때 쓰는 URL임다. CDN 내지 프락시를 쓴다 카멘은 그 URL을 갖다 늫고, 아이면 써먹을 서비스네 가이드를 봐봐가 공개적으로 접근할 수 있는 주소를 여 넣어 주이소. 그니께, 내가 AWS S3을 쓴다 카면은 'https://<bucket>.s3.amazonaws.com', GCS를 쓴다 카면 'https://storage.googleapis.com/<bucket>' 처럼 쓰믄 되입니더."
 objectStorageBucket: "Bucket"
-objectStorageBucketDesc: "써먹을 서비스의 바께쓰 이름을 여 써 주이소."
+objectStorageBucketDesc: "설 서비스으 버킷 이럼얼 서 주이소."
 objectStoragePrefix: "Prefix"
 objectStoragePrefixDesc: "요 Prefix 디렉토리 안에다가 파일이 들어감다."
 objectStorageEndpoint: "Endpoint"
-objectStorageEndpointDesc: "AWS S3을 쓸라멘 요는 비워두고, 아이멘은 그 서비스 가이드에 맞게 endpoint를 넣어 주이소. '<host>' 내지 '<host>:<port>'처럼 넣십니다."
+objectStorageEndpointDesc: "AWS S3넌 비아 두고 다런 것언 거 서비스으 엔드포인트럴 서 주이소. ‘<host>’나 ‘<host>:<port>’맨치로 섭니다."
 objectStorageRegion: "Region"
-objectStorageRegionDesc: "'xx-east-1' 같은 region 이름을 옇어 주이소. 만약에 내 서비스엔 region 같은 개념이 읎다, 카면은 대신에 'us-east-1'라고 해 두이소. AWS 설정 파일이나 환경 변수를 끌어다 쓰겠다믄 요는 비워 두이소."
+objectStorageRegionDesc: "‘xx-east-1’맨치로 리전 이럼얼 서 주이소. 설 서비스에 리전 개넴이 어ᇝ어먼 ‘us-east-1’라고 해 두이소. 에이더블유에스 설정 파일이나 환겡 벤수가 이ᇇ어면 비아 두이소."
 objectStorageUseSSL: "SSL 쓰기"
 objectStorageUseSSLDesc: "API 호출할 때 HTTPS 안 쓸거면은 꺼 두이소"
 objectStorageUseProxy: "연결에 프락시 사용"
@@ -534,7 +535,7 @@ newNoteRecived: "새 노트 있어예"
 sounds: "소리"
 sound: "소리"
 listen: "듣기"
-none: "없음"
+none: "어ᇝ엄"
 showInPage: "바닥서 보기"
 popout: "새 창 열기"
 volume: "음량"
@@ -542,13 +543,13 @@ masterVolume: "대빵 음량"
 notUseSound: "음소거하기"
 useSoundOnlyWhenActive: "Misskey가 활성화되어 있을 때만 소리 내기"
 details: "자세히"
-chooseEmoji: "이모지 선택"
+chooseEmoji: "이모지 개리기"
 unableToProcess: "작업 다 몬 했십니다"
 recentUsed: "최근 쓴 놈"
 install: "설치"
 uninstall: "삭제"
 installedApps: "설치된 애플리케이션"
-nothing: "뭣도 없어예"
+nothing: "어ᇝ어예"
 installedDate: "설치한 날"
 lastUsedDate: "마지막 사용"
 state: "상태"
@@ -579,6 +580,7 @@ enableInfiniteScroll: "알아서 더 보기"
 useCw: "내용 수ᇚ후기"
 description: "설멩"
 describeFile: "캡션 옇기"
+enterFileDescription: "캡션 서기"
 author: "맨던 사람"
 manage: "간리"
 emailServer: "전자우펜 서버"
@@ -598,6 +600,7 @@ reporter: "신고한 사람"
 reporteeOrigin: "신고덴 사람"
 reporterOrigin: "신고한 곳"
 forwardReport: "웬겍 서버에 신고 보내기"
+waitingFor: "{x}(얼)럴 지달리고 잇십니다"
 random: "무작이"
 system: "시스템"
 clip: "클립 맨걸기"
@@ -610,10 +613,13 @@ followersCount: "팔로워 수"
 noteFavoritesCount: "질겨찾기한 노트 수"
 clips: "클립 맨걸기"
 clearCache: "캐시 비우기"
+typingUsers: "{users} 님이 서고 잇어예"
 unlikeConfirm: "좋네예럴 무룹니꺼?"
 info: "정보"
+selectAccount: "계정 개리기"
 user: "사용자"
 administration: "간리"
+translatedFrom: "{x}서 번옉"
 on: "킴"
 off: "껌"
 hide: "수ᇚ후기"
@@ -625,6 +631,8 @@ oneDay: "하리"
 oneWeek: "한 주"
 oneMonth: "한 달"
 file: "파일"
+typeToConfirm: "게속할라먼 {x}럴 누질라 주이소"
+pleaseSelect: "개리 주이소"
 tools: "도구"
 like: "좋네예!"
 unlike: "좋네예 무루기"
@@ -632,7 +640,7 @@ numberOfLikes: "좋네예 수"
 show: "보기"
 roles: "옉할"
 role: "옉할"
-noRole: "옉할이 없십니다"
+noRole: "옉할이 어ᇝ십니다"
 thisPostMayBeAnnoyingCancel: "아이예"
 likeOnly: "좋네예마"
 myClips: "내 클립"
@@ -720,6 +728,7 @@ _theme:
 _sfx:
   note: "새 노트"
   notification: "알림"
+  reaction: "리액션 개리기"
 _2fa:
   step3Title: "학인 기호럴 서기"
   renewTOTPCancel: "뎃어예"
@@ -744,6 +753,9 @@ _cw:
 _visibility:
   home: "덜머리"
   followers: "팔로워"
+_postForm:
+  _placeholders:
+    e: "옇다 서 주이소"
 _profile:
   name: "이럼"
   username: "사용자 이럼"
@@ -796,5 +808,8 @@ _moderationLogTypes:
   resetPassword: "비밀번호 재설정"
   resolveAbuseReport: "신고 해겔하기"
 _reversi:
-  total: "합계"
-
+  reversi: "리버시"
+  chooseBoard: "보드 개리기"
+  black: "꺼멍"
+  white: "허영"
+  total: "합게"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 877ae6b217be..fc3a64acab1d 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -38,9 +38,9 @@ addUser: "유저 추가"
 favorite: "즐겨찾기"
 favorites: "즐겨찾기"
 unfavorite: "즐겨찾기에서 제거"
-favorited: "즐겨찾기에 등록했습니다"
-alreadyFavorited: "이미 즐겨찾기에 등록되어 있습니다"
-cantFavorite: "즐겨찾기에 등록하지 못했습니다"
+favorited: "즐겨찾기에 등록했습니다."
+alreadyFavorited: "이미 즐겨찾기에 등록했습니다."
+cantFavorite: "즐겨찾기에 등록하지 못했습니다."
 pin: "프로필에 고정"
 unpin: "프로필에서 고정 해제"
 copyContent: "내용 복사"
@@ -93,7 +93,7 @@ somethingHappened: "오류가 발생했습니다"
 retry: "다시 시도"
 pageLoadError: "페이지를 불러오지 못했습니다."
 pageLoadErrorDescription: "네트워크 연결 또는 브라우저 캐시로 인해 발생했을 가능성이 높습니다. 캐시를 삭제하거나, 잠시 후 다시 시도해 주세요."
-serverIsDead: "서버로부터 응답이 없습니다. 잠시 후 다시 시도해주세요."
+serverIsDead: "서버가 응답하지 않습니다. 잠시 후 다시 시도해 주세요."
 youShouldUpgradeClient: "이 페이지를 표시하려면 새로고침하여 새로운 버전의 클라이언트를 이용해 주십시오."
 enterListName: "리스트 이름을 입력"
 privacy: "프라이버시"
@@ -119,8 +119,8 @@ you: "나"
 clickToShow: "클릭하여 보기"
 sensitive: "열람 주의"
 add: "추가"
-reaction: "리액션"
-reactions: "리액션"
+reaction: "반응"
+reactions: "반응"
 emojiPicker: "이모지 선택기"
 pinnedEmojisForReactionSettingDescription: "리액션을 할 때 프로필에 고정하여 표시할 이모지를 설정할 수 있습니다"
 pinnedEmojisSettingDescription: "이모지를 입력할 때 프로필에 고정하여 표시할 이모지를 설정할 수 있습니다"
@@ -169,7 +169,7 @@ cacheRemoteSensitiveFilesDescription: "이 설정을 비활성화하면 리모
 flagAsBot: "나는 봇입니다"
 flagAsBotDescription: "이 계정을 자동화된 수단으로 운용할 경우에 활성화해 주세요. 이 플래그를 활성화하면, 다른 봇이 이를 참고하여 봇 끼리의 무한 연쇄 반응을 회피하거나, 이 계정의 시스템 상에서의 취급이 Bot 운영에 최적화되는 등의 변화가 생깁니다."
 flagAsCat: "미야아아아오오오오오오오오오옹!!!!!!!"
-flagAsCatDescription: "야옹?"
+flagAsCatDescription: "야옹?(이 계정이 고양이라면 눌러 주세요.)"
 flagShowTimelineReplies: "타임라인에 노트의 답글을 표시하기"
 flagShowTimelineRepliesDescription: "이 설정을 활성화하면 타임라인에 다른 유저 간의 답글을 표시합니다."
 autoAcceptFollowed: "팔로우 중인 유저로부터의 팔로우 요청을 자동 수락"
@@ -187,7 +187,7 @@ followConfirm: "{name}님을 팔로우 하시겠습니까?"
 proxyAccount: "프록시 계정"
 proxyAccountDescription: "프록시 계정은 특정 조건 하에서 유저의 리모트 팔로우를 대행하는 계정입니다. 예를 들면, 유저가 리모트 유저를 리스트에 넣었을 때, 리스트에 들어간 유저를 아무도 팔로우한 적이 없다면 액티비티가 서버로 배달되지 않기 때문에, 대신 프록시 계정이 해당 유저를 팔로우하도록 합니다."
 host: "호스트"
-selectUser: "유저 선택"
+selectUser: "사용자 선택"
 recipient: "수신인"
 annotation: "내용에 대한 주석"
 federation: "연합"
@@ -230,7 +230,7 @@ noUsers: "아무도 없습니다"
 editProfile: "프로필 수정"
 noteDeleteConfirm: "이 노트를 삭제하시겠습니까?"
 pinLimitExceeded: "더 이상 고정할 수 없습니다."
-intro: "Misskey의 설치가 완료되었습니다! 관리자 계정을 생성해주세요."
+intro: "Misskey의 설치를 완료했습니다! 관리자 계정을 만들어 주세요."
 done: "완료"
 processing: "처리중"
 preview: "미리보기"
@@ -296,7 +296,7 @@ activity: "활동"
 images: "이미지"
 image: "이미지"
 birthday: "생일"
-yearsOld: "{age}세"
+yearsOld: "만 {age} 세"
 registeredDate: "등록일"
 location: "장소"
 theme: "테마"
@@ -400,6 +400,7 @@ name: "이름"
 antennaSource: "받을 소스"
 antennaKeywords: "받을 검색어"
 antennaExcludeKeywords: "제외할 검색어"
+antennaExcludeBots: "봇 계정 제외"
 antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다"
 notifyAntenna: "새로운 노트를 알림"
 withFileAntenna: "파일이 첨부된 노트만"
@@ -414,11 +415,11 @@ silence: "사일런스"
 silenceConfirm: "이 계정을 사일런스로 설정하시겠습니까?"
 unsilence: "사일런스 해제"
 unsilenceConfirm: "이 계정의 사일런스를 해제하시겠습니까?"
-popularUsers: "인기 유저"
-recentlyUpdatedUsers: "최근 활동한 유저"
-recentlyRegisteredUsers: "최근 가입한 유저"
-recentlyDiscoveredUsers: "최근 발견한 유저"
-exploreUsersCount: "{count}명의 유저가 있습니다"
+popularUsers: "인기 사용자"
+recentlyUpdatedUsers: "최근에 활동한 사용자"
+recentlyRegisteredUsers: "최근에 가입한 사용자"
+recentlyDiscoveredUsers: "최근에 발견한 사용자"
+exploreUsersCount: "{count}명의 사용자가 있습니다"
 exploreFediverse: "연합우주를 탐색"
 popularTags: "인기 태그"
 userList: "리스트"
@@ -470,7 +471,7 @@ quoteQuestion: "인용해서 작성하시겠습니까?"
 noMessagesYet: "아직 대화가 없습니다"
 newMessageExists: "새 메시지가 있습니다"
 onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
-signinRequired: "로그인 해주세요"
+signinRequired: "진행하기 전에 로그인을 해 주세요"
 invitations: "초대"
 invitationCode: "초대 코드"
 checking: "확인하는 중입니다"
@@ -494,6 +495,7 @@ emojiStyle: "이모지 스타일"
 native: "기본"
 disableDrawer: "드로어 메뉴를 사용하지 않기"
 showNoteActionsOnlyHover: "노트 액션 버튼을 마우스를 올렸을 때에만 표시"
+showReactionsCount: "노트의 반응 수를 표시하기"
 noHistory: "기록이 없습니다"
 signinHistory: "로그인 기록"
 enableAdvancedMfm: "고급 MFM을 활성화"
@@ -529,13 +531,13 @@ useObjectStorage: "오브젝트 스토리지를 사용"
 objectStorageBaseUrl: "Base URL"
 objectStorageBaseUrlDesc: "오브젝트 (미디어) 참조 URL 을 만들 때 사용되는 URL입니다. CDN 또는 프록시를 사용하는 경우 그 URL을 지정하고, 그 외의 경우 사용할 서비스의 가이드에 따라 공개적으로 액세스 할 수 있는 주소를 지정해 주세요. 예를 들어, AWS S3의 경우 'https://<bucket>.s3.amazonaws.com', GCS등의 경우 'https://storage.googleapis.com/<bucket>' 와 같이 지정합니다."
 objectStorageBucket: "Bucket"
-objectStorageBucketDesc: "사용 서비스의 bucket명을 지정해주세요."
+objectStorageBucketDesc: "사용하는 서비스의 bucket 이름을 지정해 주세요."
 objectStoragePrefix: "Prefix"
 objectStoragePrefixDesc: "이 Prefix 의 디렉토리 아래에 파일이 저장됩니다."
 objectStorageEndpoint: "Endpoint"
-objectStorageEndpointDesc: "AWS S3의 경우 공란, 다른 서비스의 경우 각 서비스의 가이드에 맞게 endpoint를 설정해주세요. '<host>' 혹은 '<host>:<port>' 와 같이 지정합니다."
+objectStorageEndpointDesc: "AWS S3는 비워 두고 다른 서비스는 각 서비스의 endpoint를 설정해 주세요. ‘<host>’ 혹은 ‘<host>:<port>’처럼 지정합니다."
 objectStorageRegion: "Region"
-objectStorageRegionDesc: "'xx-east-1'와 같이 region을 지정해 주세요. 사용하는 서비스에 region 개념이 없는 경우 'us-east-1'으로 설정해 주세요. AWS 설정 파일 또는 환경 변수를 참조할 경우에는 비워주세요."
+objectStorageRegionDesc: "‘xx-east-1’처럼 region을 지정해 주세요. 사용하는 서비스에 region 개념이 없으면 ‘us-east-1’처럼 설정해 주세요. AWS 설정 파일이나 환경 변수가 있으면 비워 주세요."
 objectStorageUseSSL: "SSL 사용"
 objectStorageUseSSLDesc: "API 호출시 HTTPS 를 사용하지 않는 경우 OFF 로 설정해 주세요"
 objectStorageUseProxy: "연결에 프록시를 사용"
@@ -577,7 +579,7 @@ scratchpadDescription: "스크래치 패드는 AiScript 의 테스트 환경을
 output: "출력"
 script: "스크립트"
 disablePagesScript: "Pages 에서 AiScript 를 사용하지 않음"
-updateRemoteUser: "리모트 유저 정보 갱신"
+updateRemoteUser: "원격 사용자 정보 갱신"
 unsetUserAvatar: "아바타 제거"
 unsetUserAvatarConfirm: "아바타를 제거할까요?"
 unsetUserBanner: "배너 제거"
@@ -660,7 +662,7 @@ regexpErrorDescription: "{tab}단어 뮤트 {line}행의 정규 표현식에 오
 instanceMute: "서버 뮤트"
 userSaysSomething: "{name}님이 무언가를 말했습니다"
 makeActive: "활성화"
-display: "표시"
+display: "보기"
 copy: "복사"
 metrics: "통계"
 overview: "요약"
@@ -684,7 +686,7 @@ sample: "예시"
 abuseReports: "신고"
 reportAbuse: "신고"
 reportAbuseRenote: "리노트 신고하기"
-reportAbuseOf: "{name}을 신고하기"
+reportAbuseOf: "{name} 신고하기"
 fillAbuseReportDescription: "신고하려는 이유를 자세히 알려주세요. 특정 게시물을 신고할 때에는 게시물의 URL도 포함해 주세요."
 abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다."
 reporter: "신고자"
@@ -709,7 +711,7 @@ createNew: "새로 만들기"
 optional: "옵션"
 createNewClip: "새 클립 만들기"
 unclip: "클립 해제"
-confirmToUnclipAlreadyClippedNote: "이 노트는 이미 \"{name}\" 클립에 포함되어 있습니다. 클립을 해제하시겠습니까?"
+confirmToUnclipAlreadyClippedNote: "이 노트는 ‘{name}’ 클립을 이미 포함합니다. 클립에서 제외하시겠습니까?"
 public: "공개"
 private: "비공개"
 i18nInfo: "Misskey는 자원봉사자들에 의해 다양한 언어로 번역되고 있습니다. {link}에서 번역에 참가할 수 있습니다."
@@ -722,13 +724,13 @@ repliedCount: "받은 답글 수"
 renotedCount: "받은 리노트 수"
 followingCount: "팔로우 수"
 followersCount: "팔로워 수"
-sentReactionsCount: "보낸 리액션 수"
-receivedReactionsCount: "받은 리액션 수"
-pollVotesCount: "투표한 횟수"
-pollVotedCount: "투표받은 횟수"
+sentReactionsCount: "반응 수"
+receivedReactionsCount: "받은 반응 수"
+pollVotesCount: "투표 수"
+pollVotedCount: "받은 투표 수"
 yes: "예"
 no: "아니오"
-driveFilesCount: "드라이브 파일 개수"
+driveFilesCount: "드라이브에 있는 파일 수"
 driveUsage: "드라이브 사용량"
 noCrawle: "검색엔진의 인덱싱 거부"
 noCrawleDescription: "검색엔진에 사용자 페이지, 노트, 페이지 등의 콘텐츠를 인덱싱되지 않게 합니다."
@@ -763,7 +765,7 @@ needReloadToApply: "변경 사항은 새로고침하면 적용됩니다."
 showTitlebar: "타이틀 바를 표시하기"
 clearCache: "캐시 비우기"
 onlineUsersCount: "{n}명이 접속 중"
-nUsers: "{n} 유저"
+nUsers: "{n} 사용자"
 nNotes: "{n} 노트"
 sendErrorReports: "오류 보고서 보내기"
 sendErrorReportsDescription: "이 설정을 활성화하면, 문제가 발생했을 때 오류에 대한 상세 정보를 Misskey에 보내어 더 나은 소프트웨어를 만드는 데에 도움을 줄 수 있습니다."
@@ -809,7 +811,7 @@ addDescription: "설명 추가"
 userPagePinTip: "각 노트의 메뉴에서 「프로필에 고정」을 선택하는 것으로, 여기에 노트를 표시해 둘 수 있어요."
 notSpecifiedMentionWarning: "수신자가 선택되지 않은 멘션이 있어요"
 info: "정보"
-userInfo: "유저 정보"
+userInfo: "사용자 정보"
 unknown: "알 수 없음"
 onlineStatus: "온라인 상태"
 hideOnlineStatus: "온라인 상태 숨기기"
@@ -897,7 +899,7 @@ incorrectPassword: "비밀번호가 올바르지 않습니다."
 voteConfirm: "\"{choice}\"에 투표하시겠습니까?"
 hide: "숨기기"
 useDrawerReactionPickerForMobile: "모바일에서 드로어 메뉴로 표시"
-welcomeBackWithName: "환영합니다, {name}님"
+welcomeBackWithName: "{name}님, 환영합니다."
 clickToFinishEmailVerification: "[{ok}]를 눌러 이메일 인증을 완료하세요."
 overridedDeviceKind: "장치 유형"
 smartphone: "스마트폰"
@@ -1092,9 +1094,9 @@ preservedUsernames: "예약된 사용자명"
 preservedUsernamesDescription: "예약할 사용자명을 한 줄에 하나씩 입력합니다. 여기에서 지정한 사용자명으로는 계정을 생성할 수 없게 됩니다. 단, 관리자 권한으로 계정을 생성할 때에는 해당되지 않으며, 이미 존재하는 계정도 영향을 받지 않습니다."
 createNoteFromTheFile: "이 파일로 노트를 작성"
 archive: "아카이브"
-channelArchiveConfirmTitle: "{name} 을(를) 아카이브하시겠습니까?"
-channelArchiveConfirmDescription: "아카이브한 채널은 채널 목록과 검색 결과에 표시되지 않으며, 채널에 새로운 노트를 작성할 수 없게 됩니다."
-thisChannelArchived: "이 채널은 아카이브되었습니다."
+channelArchiveConfirmTitle: "{name} 채널을 보존하시겠습니까?"
+channelArchiveConfirmDescription: "보존한 채널은 채널 목록과 검색 결과에 표시되지 않으며 새로운 노트도 작성할 수 없습니다."
+thisChannelArchived: "이 채널은 보존되었습니다."
 displayOfNote: "노트 표시"
 initialAccountSetting: "초기 설정"
 youFollowing: "팔로잉"
@@ -1156,10 +1158,11 @@ unnotifyNotes: "새 노트 알림 끄기"
 authentication: "인증"
 authenticationRequiredToContinue: "계속하려면 인증하십시오"
 dateAndTime: "일시"
-showRenotes: "리노트 표시"
+showRenotes: "리노트 보기"
 edited: "수정됨"
 notificationRecieveConfig: "알림 설정"
 mutualFollow: "맞팔로우"
+followingOrFollower: "팔로 중이거나 팔로워"
 fileAttachedOnly: "미디어를 포함한 노트만"
 showRepliesToOthersInTimeline: "타임라인에 다른 사람에게 보내는 답글을 포함"
 hideRepliesToOthersInTimeline: "타임라인에 다른 사람에게 보내는 답글을 포함하지 않음"
@@ -1210,16 +1213,34 @@ soundWillBePlayed: "소리가 재생됩니다"
 showReplay: "리플레이 보기"
 replay: "리플레이"
 replaying: "리플레이 중"
+endReplay: "리플레이 종료"
+copyReplayData: "리플레이 데이터를 복사"
 ranking: "랭킹"
 lastNDays: "최근 {n}일"
 backToTitle: "타이틀로 가기"
 hemisphere: "거주 지역"
 withSensitive: "민감한 파일이 포함된 노트 보기"
-userSaysSomethingSensitive: "{name}의 민감한 파일이 포함된 게시물"
+userSaysSomethingSensitive: "{name} 같은 민감한 파일이 포함된 글"
 enableHorizontalSwipe: "스와이프하여 탭 전환"
+loading: "불러오는 중"
 surrender: "그만두기"
+gameRetry: "다시 시도"
+notUsePleaseLeaveBlank: "사용하지 않는 경우 비워두세요."
+useTotp: "일회용 비밀번호 사용"
+useBackupCode: "백업 코드 사용"
+launchApp: "앱 실행"
+useNativeUIForVideoAudioPlayer: "브라우저 UI에서 미디어 재생"
 _bubbleGame:
   howToPlay: "설명"
+  hold: "홀드"
+  _score:
+    score: "점수"
+    scoreYen: "번 돈"
+    highScore: "최고 점수"
+    maxChain: "최대 콤보 수"
+    yen: "{yen}엔"
+    estimatedQty: "{qty}개"
+    scoreSweets: "오니기리 {onigiriQtyWithUnit}"
   _howToPlay:
     section1: "위치를 조정하여 상자에 물건을 떨어뜨립니다."
     section2: "같은 종류의 물건이 붙으면 다른 물건으로 바뀌면서 점수를 얻게 됩니다."
@@ -1232,7 +1253,7 @@ _announcement:
   end: "공지에서 내리기"
   tooManyActiveAnnouncementDescription: "공지사항이 너무 많을 경우, 사용자 경험에 영향을 끼칠 가능성이 있습니다. 오래된 공지사항은 아카이브하시는 것을 권장드립니다."
   readConfirmTitle: "읽음으로 표시합니까?"
-  readConfirmText: "\"{title}\"을(를) 읽음으로 표시합니다."
+  readConfirmText: "〈{title}〉의 내용을 읽음으로 표시합니다."
   shouldNotBeUsedToPresentPermanentInfo: "신규 유저의 이용 경험에 악영향을 끼칠 수 있으므로, 일시적인 알림 수단으로만 사용하고 고정된 정보에는 사용을 지양하는 것을 추천합니다."
   dialogAnnouncementUxWarn: "다이얼로그 형태의 알림이 동시에 2개 이상 존재하는 경우, 사용자 경험에 악영향을 끼칠 수 있으므로 신중히 결정하십시오."
   silence: "조용히 알림"
@@ -1513,7 +1534,7 @@ _achievements:
     _iLoveMisskey:
       title: "I Love Misskey"
       description: "\"I ❤ #Misskey\"를 포스트했습니다"
-      flavor: "Misskey를 이용해주셔서 감사합니다! - 개발팀 일동"
+      flavor: "Misskey를 이용해 주셔서 감사합니다! ― 개발 팀"
     _foundTreasure:
       title: "보물찾기"
       description: "숨겨진 보물을 발견했습니다"
@@ -1551,10 +1572,10 @@ _achievements:
       description: "3개 이상의 창을 열었습니다"
     _driveFolderCircularReference:
       title: "순환 참조"
-      description: "드라이브 폴더를 자신을 가리키도록 만드려 시도했습니다"
+      description: "드라이브 폴더에 스스로를 넣게 했습니다"
     _reactWithoutRead:
       title: "읽고 답하긴 하시는 건가요?"
-      description: "100자가 넘는 노트가 작성되고 3초 안에 반응했습니다"
+      description: "100자가 넘는 노트를 작성한 지 3초 안에 반응했어요"
     _clickedClickHere:
       title: "여기를 누르세요"
       description: "여기를 눌렀습니다"
@@ -1641,6 +1662,7 @@ _role:
     gtlAvailable: "글로벌 타임라인 보이기"
     ltlAvailable: "로컬 타임라인 보이기"
     canPublicNote: "공개 노트 허용"
+    mentionMax: "노트에 넣을 수 있는 멘션 수"
     canInvite: "서버 초대 코드 발행"
     inviteLimit: "초대 한도"
     inviteLimitCycle: "초대 발급 간격"
@@ -1650,13 +1672,13 @@ _role:
     driveCapacity: "드라이브 용량"
     alwaysMarkNsfw: "파일을 항상 NSFW로 지정"
     pinMax: "고정할 수 있는 노트 수"
-    antennaMax: "최대 안테나 생성 허용 수"
+    antennaMax: "만들 수 있는 안테나 수"
     wordMuteMax: "단어 뮤트할 수 있는 문자 수"
-    webhookMax: "생성할 수 있는 웹훅 수"
-    clipMax: "생성할 수 있는 클립 수"
-    noteEachClipsMax: "각 클립에 추가할 수 있는 노트 수"
-    userListMax: "생성할 수 있는 유저 리스트 수"
-    userEachUserListsMax: "유저 리스트당 최대 사용자 수"
+    webhookMax: "만들 수 있는 웹후크 수"
+    clipMax: "만들 수 있는 클립 수"
+    noteEachClipsMax: "클립에 넣을 수 있는 노트 수"
+    userListMax: "만들 수 있는 사용자 리스트 수"
+    userEachUserListsMax: "사용자 리스트에 넣을 수 있는 사용자 수"
     rateLimitFactor: "요청 빈도 제한"
     descriptionOfRateLimitFactor: "작을수록 제한이 완화되고, 클수록 제한이 강화됩니다."
     canHideAds: "광고 숨기기"
@@ -1664,16 +1686,17 @@ _role:
     canUseTranslator: "번역 기능의 사용"
     avatarDecorationLimit: "아바타 장식의 최대 붙임 개수"
   _condition:
+    roleAssignedTo: "수동 역할에 이미 할당됨"
     isLocal: "로컬 사용자"
     isRemote: "리모트 사용자"
     createdLessThan: "가입한 지 다음 일수 이내인 유저"
     createdMoreThan: "가입한 지 다음 일수 이상인 유저"
     followersLessThanOrEq: "팔로워 수가 다음 이하인 유저"
-    followersMoreThanOrEq: "팔로워 수가 다음 이상인 유저"
+    followersMoreThanOrEq: "팔로워 수가 다음보다 많은 사용자"
     followingLessThanOrEq: "팔로잉 수가 다음 이하인 유저"
-    followingMoreThanOrEq: "팔로잉 수가 다음 이상인 유저"
+    followingMoreThanOrEq: "팔로잉 수가 다음보다 많은 사용자"
     notesLessThanOrEq: "노트 수가 다음 이하인 유저"
-    notesMoreThanOrEq: "노트 수가 다음 이상인 유저"
+    notesMoreThanOrEq: "노트 수가 다음보다 많은 사용자"
     and: "다음을 모두 만족"
     or: "다음을 하나라도 만족"
     not: "다음을 만족하지 않음"
@@ -1735,6 +1758,7 @@ _plugin:
   installWarn: "신뢰할 수 없는 플러그인은 설치하지 않는 것이 좋습니다."
   manage: "플러그인 관리"
   viewSource: "소스 보기"
+  viewLog: "로그 보기"
 _preferencesBackups:
   list: "생성한 백업"
   saveNew: "새 백업 만들기"
@@ -1745,12 +1769,12 @@ _preferencesBackups:
   cannotSave: "저장하지 못했습니다"
   nameAlreadyExists: "\"{name}\" 백업이 이미 존재합니다. 다른 이름을 설정하여 주십시오."
   applyConfirm: "\"{name}\" 백업을 현재 기기에 적용하시겠습니까? 현재 설정은 덮어 씌워집니다."
-  saveConfirm: "{name} 을 덮어쓰시겠습니까?"
-  deleteConfirm: "{name} 을(를) 삭제하시겠습니까?"
-  renameConfirm: "\"{old}\" 백업을 \"{new}\"(으)로 바꾸시겠습니까?"
+  saveConfirm: "{name} 백업을 덮어쓰시겠습니까?"
+  deleteConfirm: "{name} 백업을 삭제하시겠습니까?"
+  renameConfirm: "‘{old}’ 백업을 ‘{new}’ 백업으로 바꾸시겠습니까?"
   noBackups: "저장된 백업이 없습니다. \"새 백업 만들기\"를 눌러 현재 클라이언트 설정을 서버에 백업할 수 있습니다."
-  createdAt: "생성 날짜: {date} {time}"
-  updatedAt: "갱신 날짜: {date} {time}"
+  createdAt: "만든 날짜: {date} {time}"
+  updatedAt: "고친 날짜: {date} {time}"
   cannotLoad: "가져오기에 실패했습니다"
   invalidFile: "파일 형식이 올바르지 않습니다."
 _registry:
@@ -1924,7 +1948,6 @@ _2fa:
   registerTOTP: "인증 앱 설정 시작"
   step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다."
   step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
-  step2Click: "QR 코드를 클릭하면 기기에 설치된 인증 앱에 등록할 수 있습니다."
   step2Uri: "데스크톱 앱을 사용하려면 다음 URI를 입력하십시오"
   step3Title: "인증 코드 입력"
   step3: "앱에 표시된 토큰을 입력하시면 완료됩니다."
@@ -1937,7 +1960,7 @@ _2fa:
   securityKeyName: "키 이름 입력"
   tapSecurityKey: "브라우저의 지시에 따라 보안 키 또는 패스키를 등록하여 주십시오"
   removeKey: "보안 키를 삭제"
-  removeKeyConfirm: "{name} 을(를) 삭제하시겠습니까?"
+  removeKeyConfirm: "{name} 앱을 삭제하시겠습니까?"
   whyTOTPOnlyRenew: "보안 키가 등록되어 있는 경우 인증 앱을 해제할 수 없습니다."
   renewTOTP: "인증 앱 재설정"
   renewTOTPConfirm: "기존에 등록되어 있던 인증 키는 사용하지 못하게 됩니다."
@@ -2078,7 +2101,7 @@ _widgets:
   postForm: "글 입력란"
   slideshow: "슬라이드 쇼"
   button: "버튼"
-  onlineUsers: "온라인 유저"
+  onlineUsers: "온라인 사용자"
   jobQueue: "작업 대기열"
   serverMetric: "서버 통계"
   aiscript: "AiScript 콘솔"
@@ -2137,10 +2160,10 @@ _postForm:
     c: "무엇을 생각하고 있나요?"
     d: "말하고 싶은 게 있나요?"
     e: "여기에 적어 주세요"
-    f: "작성해주시길 기다리고 있어요..."
+    f: "글 쓰기를 기다려요…"
 _profile:
   name: "이름"
-  username: "유저명"
+  username: "사용자 이름"
   description: "자기소개"
   youCanIncludeHashtags: "해시 태그를 포함할 수 있습니다."
   metadata: "추가 정보"
@@ -2168,7 +2191,7 @@ _charts:
   apRequest: "요청"
   usersIncDec: "유저 수 증감"
   usersTotal: "유저 수 합계"
-  activeUsers: "활성 유저 수"
+  activeUsers: "활동 사용자 수"
   notesIncDec: "노트 수 증감"
   localNotesIncDec: "로컬 노트 수 증감"
   remoteNotesIncDec: "리모트 노트 수 증감"
@@ -2179,8 +2202,8 @@ _charts:
   storageUsageTotal: "스토리지 사용량 합계"
 _instanceCharts:
   requests: "요청"
-  users: "유저 수 증감"
-  usersTotal: "누적 유저 수"
+  users: "사용자 수 차이"
+  usersTotal: "누적 사용자 수"
   notes: "노트 수 증감"
   notesTotal: "누적 노트 수"
   ff: "팔로잉/팔로워 증감"
@@ -2209,6 +2232,7 @@ _play:
   title: "제목"
   script: "스크립트"
   summary: "설명"
+  visibilityDescription: "비공개로 설정하면 프로필에 표시하지 않지만 URL을 아는 사람은 계속해서 접속할 수 있습니다."
 _pages:
   newPage: "페이지 만들기"
   editPage: "페이지 수정"
@@ -2219,7 +2243,7 @@ _pages:
   pageSetting: "페이지 설정"
   nameAlreadyExists: "지정한 페이지 URL이 이미 존재합니다"
   invalidNameTitle: "유효하지 않은 페이지 URL입니다"
-  invalidNameText: "비어있지 않은지 확인해주세요"
+  invalidNameText: "비어있는지 확인해 주세요"
   editThisPage: "이 페이지를 편집"
   viewSource: "소스 보기"
   viewPage: "페이지 보기"
@@ -2253,6 +2277,8 @@ _pages:
     section: "섹션"
     image: "이미지"
     button: "버튼"
+    dynamic: "동적 블록"
+    dynamicDescription: "이 블록은 폐지되었습니다. 이제부터 {play}에서 이용해 주세요."
     note: "노트필기"
     _note:
       id: "노트 ID"
@@ -2282,17 +2308,19 @@ _notification:
   sendTestNotification: "테스트 알림 보내기"
   notificationWillBeDisplayedLikeThis: "알림이 이렇게 표시됩니다"
   reactedBySomeUsers: "{n}명이 반응했습니다"
+  likedBySomeUsers: "{n}명이 좋아요를 했습니다"
   renotedBySomeUsers: "{n}명이 리노트했습니다"
   followedBySomeUsers: "{n}명에게 팔로우됨"
+  flushNotification: "알림 이력을 초기화"
   _types:
     all: "전부"
-    note: "유저의 새 게시물"
+    note: "사용자의 새 글"
     follow: "팔로잉"
     mention: "멘션"
     reply: "답글"
     renote: "리노트"
     quote: "인용"
-    reaction: "리액션"
+    reaction: "반응"
     pollEnded: "투표가 종료됨"
     receiveFollowRequest: "팔로우 요청을 받았을 때"
     followRequestAccepted: "팔로우 요청이 승인되었을 때"
@@ -2335,7 +2363,7 @@ _deck:
     direct: "다이렉트"
     roleTimeline: "역할 타임라인"
 _dialog:
-  charactersExceeded: "최대 글자수를 초과하였습니다! 현재 {current} / 최대 {min}"
+  charactersExceeded: "최대 글자수를 초과하였습니다! 현재 {current} / 최대 {max}"
   charactersBelow: "최소 글자수 미만입니다! 현재 {current} / 최소 {min}"
 _disabledTimeline:
   title: "비활성화된 타임라인"
@@ -2459,7 +2487,7 @@ _dataSaver:
 _hemisphere:
   N: "북반구"
   S: "남반구"
-  caption: "일부 클라이언트 설정에서 계절을 판단하기 위해 사용합니다."
+  caption: "일부 클라이언트 설정에서 계절을 판단하려고 사용합니다."
 _reversi:
   reversi: "리버시"
   gameSettings: "대국 설정"
@@ -2467,42 +2495,61 @@ _reversi:
   blackOrWhite: "선공/후공"
   blackIs: "{name}님이 흑(선공)"
   rules: "규칙"
-  thisGameIsStartedSoon: "대국이 곧 시작됩니다"
-  waitingForOther: "상대방의 준비가 완료되기를 기다리고 있습니다."
-  waitingForMe: "당신의 준비가 완료되기를 기다리고 있습니다."
+  thisGameIsStartedSoon: "대국을 곧 시작합니다"
+  waitingForOther: "상대의 준비가 끝나기를 기다리고 있습니다."
+  waitingForMe: "나의 준비가 끝나기를 기다리고 있습니다."
   waitingBoth: "준비하세요"
   ready: "준비 완료"
-  cancelReady: "준비 다시 시작"
+  cancelReady: "준비되지 않음"
   opponentTurn: "상대의 차례입니다"
-  myTurn: "당신의 차례입니다"
-  turnOf: "{name}의 차례입니다"
-  pastTurnOf: "{name}의 차례"
+  myTurn: "나의 차례입니다"
+  turnOf: "{name}님의 차례입니다"
+  pastTurnOf: "{name}님의 차례"
   surrender: "기권"
-  surrendered: "기권에 의해"
+  surrendered: "상대의 기권"
   timeout: "시간 초과"
   drawn: "무승부"
-  won: "{name}의 승리"
+  won: "{name}님의 승리"
   black: "흑"
   white: "백"
   total: "합계"
-  turnCount: "{count}턴 째"
+  turnCount: "{count}번째 수"
   myGames: "내 대국"
-  allGames: "모두의 대국"
+  allGames: "모든 대국"
   ended: "종료"
   playing: "대국 중"
-  isLlotheo: "돌이 적은 사람이 승리 (로세오)"
-  loopedMap: "루프 지도"
-  canPutEverywhere: "어디에도 둘 수 있는 모드"
-  timeLimitForEachTurn: "1턴의 시간 제한"
-  freeMatch: "프리매치"
-  lookingForPlayer: "상대를 찾고 있습니다"
+  isLlotheo: "돌이 적은 쪽이 승리(로세오)"
+  loopedMap: "순환 지도"
+  canPutEverywhere: "어디든 둘 수 있는 모드"
+  timeLimitForEachTurn: "각 수의 시간 제한"
+  freeMatch: "자유 대국"
+  lookingForPlayer: "대국 상대를 찾고 있습니다"
   gameCanceled: "대국이 취소되었습니다"
-  shareToTlTheGameWhenStart: "대국 시작 시 타임라인에 대국을 게시"
-  iStartedAGame: "대국이 시작되었습니다! #MisskeyReversi"
-  opponentHasSettingsChanged: "상대방이 설정을 변경했습니다"
-  allowIrregularRules: "규칙변경 허가 (완전 자유)"
-  disallowIrregularRules: "규칙변경 없음"
+  shareToTlTheGameWhenStart: "대국이 시작할 때 타임라인에 공유"
+  iStartedAGame: "대국을 시작하였습니다! #MisskeyReversi"
+  opponentHasSettingsChanged: "상대가 설정을 변경했습니다"
+  allowIrregularRules: "규칙 변경 허용(완전 자유)"
+  disallowIrregularRules: "규칙 변경 없음"
+  showBoardLabels: "판에 행·열 번호 표시"
+  useAvatarAsStone: "돌을 아이콘으로 표시"
 _offlineScreen:
   title: "오프라인 - 서버에 접속할 수 없습니다"
   header: "서버에 접속할 수 없습니다"
-
+_urlPreviewSetting:
+  title: "URL 미리보기 설정"
+  enable: "URL 미리보기 활성화"
+  timeout: "미리보기를 불러올 때의 타임아웃 (ms)"
+  timeoutDescription: "미리보기를 로딩하는데 걸리는 시간이 정한 시간보다 오래 걸리는 경우, 미리보기를 생성하지 않습니다."
+  maximumContentLength: "Content-Length의 최대치 (byte)"
+  maximumContentLengthDescription: "Content-Length가 이 값을 넘어서면 미리보기를 생성하지 않습니다."
+  requireContentLength: "Content-Length를 얻었을 때만 미리보기 만들기"
+  requireContentLengthDescription: "상대 서버가 Content-Length를 되돌려주지 않는다면 미리보기를 만들지 않습니다."
+  userAgent: "User-Agent"
+  userAgentDescription: "미리보기를 얻을 때 사용한 User-Agent를 설정합니다. 비어 있다면 기본값의 User-Agent를 사용합니다."
+  summaryProxy: "미리보기를 만든 프록시의 엔드포인트"
+  summaryProxyDescription: "Misskey 본체를 사용하지 않고 서머리 프록시로 미리보기를 만듭니다."
+  summaryProxyDescription2: "프록시는 아래의 파라미터를 쿼리 문자열로 연동합니다. 프록시 측이 이를 지원하지 않으면 설정값을 무시합니다."
+_mediaControls:
+  pip: "화면 속 화면"
+  playbackRate: "재생 속도"
+  loop: "반복 재생"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index 6f03c914fd25..fa4b3b6f9a95 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -466,4 +466,3 @@ _webhookSettings:
   name: "ຊື່"
 _moderationLogTypes:
   suspend: "ລະງັບ"
-
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index e3ff4261775c..e33b978bc857 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -497,4 +497,3 @@ _webhookSettings:
 _moderationLogTypes:
   suspend: "Opschorten"
   resetPassword: "Wachtwoord terugzetten"
-
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 098faa8addd7..475f93267be8 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -721,4 +721,3 @@ _webhookSettings:
   name: "Navn"
 _moderationLogTypes:
   suspend: "Suspender"
-
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 99eb1f302800..b7eb4683ebf7 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -20,6 +20,7 @@ noNotes: "Brak wpisów"
 noNotifications: "Brak powiadomień"
 instance: "Instancja"
 settings: "Ustawienia"
+notificationSettings: "Powiadomienia"
 basicSettings: "Podstawowe ustawienia"
 otherSettings: "Pozostałe ustawienia"
 openInWindow: "Otwórz w oknie"
@@ -44,13 +45,20 @@ pin: "Przypnij do profilu"
 unpin: "Odepnij z profilu"
 copyContent: "Skopiuj zawartość"
 copyLink: "Skopiuj odnośnik"
+copyLinkRenote: "Skopiuj link renote'a"
 delete: "Usuń"
 deleteAndEdit: "Usuń i edytuj"
 deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz wszystkie reakcje, udostępnienia i odpowiedzi do tego wpisu."
 addToList: "Dodaj do listy"
+addToAntenna: "Dodaj do anteny"
 sendMessage: "Wyślij wiadomość"
 copyRSS: "Kopiuj RSS"
 copyUsername: "Kopiuj nazwę użytkownika"
+copyUserId: "Kopiuj ID użytkownika"
+copyNoteId: "Kopiuj ID notatki"
+copyFileId: "Kopiuj ID pliku"
+copyFolderId: "Kopiuj ID folderu"
+copyProfileUrl: "Kopiuj URL profilu"
 searchUser: "Wyszukiwanie użytkowników"
 reply: "Odpowiedz"
 loadMore: "Załaduj więcej"
@@ -103,6 +111,8 @@ renoted: "Udostępniono."
 cantRenote: "Ten wpis nie może zostać udostępniony."
 cantReRenote: "Udostępnienie nie może zostać udostępnione."
 quote: "Cytuj"
+inChannelRenote: "Renote tylko na kanale"
+inChannelQuote: "Cytat tylko na kanale"
 pinnedNote: "Przypięty wpis"
 pinned: "Przypnij do profilu"
 you: "Ty"
@@ -111,14 +121,23 @@ sensitive: "NSFW"
 add: "Dodaj"
 reaction: "Reakcja"
 reactions: "Reakcja"
+emojiPicker: "Selektor Emoji"
+pinnedEmojisForReactionSettingDescription: "Ustaw emotikony które powinny być przypięte i od razu wyświetlone podczas reagowania."
+pinnedEmojisSettingDescription: "Ustaw emotikony które powinny być przypięte i wyświetlone podczas przeglądania selektora Emoji"
+emojiPickerDisplay: "Wyświetlanie selektora Emoji"
+overwriteFromPinnedEmojisForReaction: "Zastąp z ustawień reakcji"
+overwriteFromPinnedEmojis: "Zastąp z ogólnych ustawień"
 reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+” aby dodać"
 rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu"
 attachCancel: "Usuń załącznik"
+deleteFile: "Usuń plik"
 markAsSensitive: "Oznacz jako NSFW"
 unmarkAsSensitive: "Cofnij NSFW"
 enterFileName: "Wprowadź nazwę pliku"
 mute: "Wycisz"
 unmute: "Cofnij wyciszenie"
+renoteMute: "Wycisz renote'y"
+renoteUnmute: "Wyłącz wyciszenie renote'ów"
 block: "Zablokuj"
 unblock: "Odblokuj"
 suspend: "Zawieś"
@@ -128,8 +147,10 @@ unblockConfirm: "Czy na pewno chcesz odblokować to konto?"
 suspendConfirm: "Czy na pewno chcesz zawiesić to konto?"
 unsuspendConfirm: "Czy na pewno chcesz cofnąć zawieszenie tego konta?"
 selectList: "Wybierz listę"
+editList: "Edytuj listę"
 selectChannel: "Wybierz kanał"
 selectAntenna: "Wybierz Antennę"
+editAntenna: "Edytuj antenę"
 selectWidget: "Wybierz widżet"
 editWidgets: "Edytuj widżety"
 editWidgetsExit: "Gotowe"
@@ -142,11 +163,15 @@ addEmoji: "Dodaj emoji"
 settingGuide: "Proponowana konfiguracja"
 cacheRemoteFiles: "Przechowuj zdalne pliki w pamięci podręcznej"
 cacheRemoteFilesDescription: "Gdy ta opcja jest wyłączona, zdalne pliki są ładowane bezpośrednio ze zdalnych instancji. Wyłączenie the opcji zmniejszy użycie powierzchni dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane."
+youCanCleanRemoteFilesCache: "Możesz wyczyścić cache poprzez kliknięcie przycisku 🗑️ w widoku menedżera plików."
+cacheRemoteSensitiveFiles: "Przechowuj wrażliwe zdalne pliki w pamięci podręcznej"
+cacheRemoteSensitiveFilesDescription: "Gdy ta opcja jest wyłączona, wrażliwe pliki zdalne są wczytywane bezpośrednio ze zdalnej instancji bez cacheowania."
 flagAsBot: "To konto jest botem"
 flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów, aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne systemy Misskey, traktując konto jako bota."
 flagAsCat: "To konto jest kotem"
 flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot."
 flagShowTimelineReplies: "Pokazuj odpowiedzi na osi czasu"
+flagShowTimelineRepliesDescription: "Gdy włączone, pokazuje odpowiedzi użytkowników na notatki innych użytkowników w osi czasu."
 autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz"
 addAccount: "Dodaj konto"
 reloadAccountsList: "Odśwież listę kont"
@@ -176,6 +201,7 @@ perHour: "co godzinę"
 perDay: "co dzień"
 stopActivityDelivery: "Przestań przesyłać aktywności"
 blockThisInstance: "Zablokuj tę instancję"
+silenceThisInstance: "Wycisz tę instancję"
 operations: "Działania"
 software: "Oprogramowanie"
 version: "Wersja"
@@ -195,6 +221,8 @@ clearCachedFiles: "Wyczyść pamięć podręczną"
 clearCachedFilesConfirm: "Czy na pewno chcesz usunąć wszystkie zdalne pliki z pamięci podręcznej?"
 blockedInstances: "Zablokowane instancje"
 blockedInstancesDescription: "Wypisz nazwy hostów instancji, które powinny zostać zablokowane. Wypisane instancje nie będą mogły dłużej komunikować się z tą instancją."
+silencedInstances: "Wyciszone instancje"
+silencedInstancesDescription: "Wypisz nazwy hostów instancji, które chcesz wyciszyć. Wszystkie konta wymienionych instancji będą traktowane jako wyciszone, będą mogły jedynie wysyłać prośby o obserwację i nie będą mogły wspominać kont lokalnych, jeśli nie będą obserwowane. Nie będzie to miało wpływu na zablokowane instancje."
 muteAndBlock: "Wycisz / Zablokuj"
 mutedUsers: "Wyciszeni użytkownicy"
 blockedUsers: "Zablokowani użytkownicy"
@@ -239,10 +267,12 @@ removed: "Pomyślnie usunięto"
 removeAreYouSure: "Czy na pewno chcesz usunąć „{x}”?"
 deleteAreYouSure: "Czy na pewno chcesz usunąć „{x}”?"
 resetAreYouSure: "Czy na pewno chcesz zresetować?"
+areYouSure: "Na pewno?"
 saved: "Zapisano"
 messaging: "Wiadomości"
 upload: "Wyślij"
 keepOriginalUploading: "Zachowaj oryginalny obraz"
+keepOriginalUploadingDescription: "Zapisuje oryginalnie przesłany obraz w niezmienionej postaci. Jeśli ta opcja jest wyłączona, po przesłaniu zostanie wygenerowana wersja do wyświetlenia w Internecie."
 fromDrive: "Z dysku"
 fromUrl: "Z adresu URL"
 uploadFromUrl: "Wyślij z adresu URL"
@@ -255,7 +285,10 @@ noMoreHistory: "Nie ma dalszej historii"
 startMessaging: "Rozpocznij czat"
 nUsersRead: "przeczytano przez {n}"
 agreeTo: "Wyrażam zgodę na {0}"
+agree: "Zatwierdź"
 agreeBelow: "Zaakceptuj poniżej"
+basicNotesBeforeCreateAccount: "Ważne notatki"
+termsOfService: "Warunki usługi"
 start: "Rozpocznij"
 home: "Strona główna"
 remoteUserCaution: "Te informacje mogą nie być aktualne, ponieważ użytkownik pochodzi ze zdalnej instancji."
@@ -285,6 +318,7 @@ folderName: "Nazwa katalogu"
 createFolder: "Utwórz katalog"
 renameFolder: "Zmień nazwę katalogu"
 deleteFolder: "Usuń ten katalog"
+folder: "Folder"
 addFile: "Dodaj plik"
 emptyDrive: "Dysk jest pusty"
 emptyFolder: "Ten katalog jest pusty"
@@ -298,6 +332,7 @@ copyUrl: "Skopiuj adres URL"
 rename: "Zmień nazwę"
 avatar: "Awatar"
 banner: "Baner"
+displayOfSensitiveMedia: "Wyświetlanie wrażliwej zawartości"
 whenServerDisconnected: "Po utracie połączenia z serwerem"
 disconnectedFromServer: "Utracono połączenie z serwerem."
 reload: "Odśwież"
@@ -345,8 +380,11 @@ hcaptcha: "hCaptcha"
 enableHcaptcha: "Włącz hCaptcha"
 hcaptchaSiteKey: "Klucz strony"
 hcaptchaSecretKey: "Tajny klucz"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "Włącz mCaptcha"
 mcaptchaSiteKey: "Klucz strony"
 mcaptchaSecretKey: "Tajny klucz"
+mcaptchaInstanceUrl: "URL instancji mCaptcha"
 recaptcha: "reCAPTCHA"
 enableRecaptcha: "Włącz reCAPTCHA"
 recaptchaSiteKey: "Klucz strony"
@@ -389,15 +427,19 @@ aboutMisskey: "O Misskey"
 administrator: "Admin"
 token: "Token"
 2fa: "Klucz 2FA "
+setupOf2fa: "Skonfiguruj dwuetapową autentykację"
 totp: "Klucz aplikacji uwierzytelniającej (totp)"
 totpDescription: "Opis klucza czasowego"
 moderator: "Moderator"
 moderation: "Moderacja"
+moderationNote: "Notka moderacyjna"
+addModerationNote: "Dodaj notkę moderacyjną"
+moderationLogs: "Logi moderacyjne"
 nUsersMentioned: "{n} wspomnianych użytkowników"
 securityKeyAndPasskey: "Klucz bezpieczeństwa i klucze Passkey"
 securityKey: "Klucz bezpieczeństwa"
 lastUsed: "Ostatnio używane"
-lastUsedAt: "Ostatnio używane w"
+lastUsedAt: "Ostatnio używane: {t}"
 unregister: "Cofnij rejestrację"
 passwordLessLogin: "Skonfiguruj logowanie bez użycia hasła"
 passwordLessLoginDescription: "Opis logowania bez użycia hasła"
@@ -451,8 +493,12 @@ aboutX: "O {x}"
 emojiStyle: "Styl emoji"
 native: "Natywny"
 disableDrawer: "Nie używaj menu w stylu szuflady"
+showNoteActionsOnlyHover: "Pokazuj akcje notatek tylko po najechaniu myszką"
+showReactionsCount: "Wyświetl liczbę reakcji na notatkę"
 noHistory: "Brak historii"
 signinHistory: "Historia logowania"
+enableAdvancedMfm: "Włącz zaawansowane MFM"
+enableAnimatedMfm: "Włącz animowane MFM"
 doing: "Przetwarzanie..."
 category: "Kategoria"
 tags: "Tagi"
@@ -461,6 +507,8 @@ createAccount: "Utwórz konto"
 existingAccount: "Istniejące konto"
 regenerate: "Wygeneruj ponownie"
 fontSize: "Rozmiar czcionki"
+mediaListWithOneImageAppearance: "Wysokość list multimediów z tylko jednym obrazem"
+limitTo: "Limituj do {x}"
 noFollowRequests: "Nie masz żadnych oczekujących próśb o możliwość obserwacji"
 openImageInNewTab: "Otwórz obraz w nowej karcie"
 dashboard: "Kokpit"
@@ -480,6 +528,7 @@ showFeaturedNotesInTimeline: "Pokazuj wyróżnione wpisy w osi czasu"
 objectStorage: "Pamięć obiektowa"
 useObjectStorage: "Używaj pamięci obiektowej"
 objectStorageBaseUrl: "Podstawowy URL"
+objectStorageBaseUrlDesc: "Adres URL używany jako odniesienie. Podaj adres URL swojego CDN lub Proxy, gdy używasz któregokolwiek z nich.\nDla S3 użyj 'https://<bucket>.s3.amazonaws.com' a dla GCS lub równej usługi użyj 'https://storage.googleapis.com/<bucket>', itd."
 objectStorageBucket: "Bucket"
 objectStorageBucketDesc: "Podaj nazwę „wiadra” używaną przez konfigurowaną usługę."
 objectStoragePrefix: "Prefiks"
@@ -492,9 +541,13 @@ objectStorageUseSSL: "Użyj SSL"
 objectStorageUseSSLDesc: "Wyłącz, jeżeli nie zamierzasz używać HTTPS dla połączenia z API"
 objectStorageUseProxy: "Połącz przez proxy"
 objectStorageUseProxyDesc: "Wyłącz, jeżeli nie zamierzasz używać proxy dla połączenia z pamięcią blokową"
+objectStorageSetPublicRead: "Ustaw opcję \"public-read\" przy przesyłaniu"
+s3ForcePathStyleDesc: "Jeśli opcja s3ForcePathStyle jest włączona, nazwa Bucket'u musi być zawarta w ścieżce adresu URL, a nie w nazwie hosta adresu URL. Włączenie tego ustawienia może być konieczne w przypadku użycia usług takich jak self-hosted instancja Minio."
 serverLogs: "Dziennik zdarzeń"
 deleteAll: "Usuń wszystkie"
 showFixedPostForm: "Wyświetlaj formularz tworzenia wpisu w górnej części osi czasu"
+showFixedPostFormInChannel: "Wyświetl formularz postowania w górnej części osi czasu (Kanały)"
+withRepliesByDefaultForNewlyFollowed: "Domyślnie uwzględnij odpowiedzi nowo obserwowanych użytkowników w osi czasu"
 newNoteRecived: "Masz nowy wpis"
 sounds: "Dźwięk"
 sound: "Dźwięki"
@@ -504,6 +557,8 @@ showInPage: "Pokaż na stronie"
 popout: "Popout"
 volume: "Głośność"
 masterVolume: "Głośność główna"
+notUseSound: "Wyłącz dźwięk"
+useSoundOnlyWhenActive: "Puszczaj dźwięki tylko, gdy Misskey jest aktywne."
 details: "Szczegóły"
 chooseEmoji: "Wybierz emoji"
 unableToProcess: "Nie udało się dokończyć działania."
@@ -524,6 +579,10 @@ output: "Wyjście"
 script: "Skrypt"
 disablePagesScript: "Wyłącz AiScript na Stronach"
 updateRemoteUser: "Aktualizuj zdalne dane o użytkowniku"
+unsetUserAvatar: "Usuń awatar"
+unsetUserAvatarConfirm: "Czy na pewno chcesz usunąć awatar tego użytkownika?"
+unsetUserBanner: "Usuń baner"
+unsetUserBannerConfirm: "Czy na pewno chcesz usunąć baner?"
 deleteAllFiles: "Usuń wszystkie pliki"
 deleteAllFilesConfirm: "Czy na pewno chcesz usunąć wszystkie pliki?"
 removeAllFollowing: "Przestań obserwować"
@@ -539,6 +598,7 @@ accountDeletedDescription: "Opis konta usuniętego"
 menu: "Menu"
 divider: "Rozdzielacz"
 addItem: "Dodaj element"
+rearrange: "Posortuj"
 relays: "Przekaźniki"
 addRelay: "Dodaj przekaźnik"
 inboxUrl: "Adres URL skrzynki nadawczej"
@@ -573,6 +633,7 @@ medium: "Średnie"
 small: "Małe"
 generateAccessToken: "Generuj token dostępu"
 permission: "Uprawnienia"
+adminPermission: "Uprawnienia administracyjne"
 enableAll: "Włącz wszystko"
 disableAll: "Wyłącz wszystko"
 tokenRequested: "Przydziel dostęp do konta"
@@ -590,9 +651,12 @@ smtpPort: "Port"
 smtpUser: "Nazwa użytkownika"
 smtpPass: "Hasło"
 emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć weryfikację SMTP"
+smtpSecure: "Użyj niejawnego SSL/TLS dla połączeń SMTP"
 smtpSecureInfo: "Wyłącz, jeżeli używasz STARTTLS"
 testEmail: "Przetestuj dostarczanie wiadomości e-mail"
 wordMute: "Wyciszenie słowa"
+regexpError: "Błąd wyrażenia regularnego"
+regexpErrorDescription: "Wystąpił błąd w wyrażeniu regularnym w linii {line} twoich {tab} wyciszeń:"
 instanceMute: "Wyciszone instancje"
 userSaysSomething: "{name} powiedział(-a) coś"
 makeActive: "Aktywuj"
@@ -612,18 +676,22 @@ useGlobalSettingDesc: "Jeżeli włączone, zostaną wykorzystane ustawienia powi
 other: "Inne"
 regenerateLoginToken: "Generuj token logowania ponownie"
 regenerateLoginTokenDescription: "Regeneruje token używany wewnętrznie podczas logowania. Zazwyczaj nie jest to konieczne. Po regeneracji wszystkie urządzenia zostaną wylogowane."
+theKeywordWhenSearchingForCustomEmoji: "To jest słowo kluczowe używane podczas wyszukiwania customowych Emoji."
 setMultipleBySeparatingWithSpace: "Możesz ustawić wiele, oddzielając je spacjami."
 fileIdOrUrl: "ID pliku albo URL"
 behavior: "Zachowanie"
 sample: "Przykład"
 abuseReports: "Zgłoszenia"
 reportAbuse: "Zgłoś"
+reportAbuseRenote: "Zgłoś renote"
 reportAbuseOf: "Zgłoś {name}"
 fillAbuseReportDescription: "Wypełnij szczegóły zgłoszenia. Jeżeli dotyczy ono określonego wpisu, uwzględnij jego adres URL."
 abuseReported: "Twoje zgłoszenie zostało wysłane. Dziękujemy."
+reporter: "Zgłaszający"
 reporteeOrigin: "Pochodzenie zgłoszonego"
 reporterOrigin: "Pochodzenie zgłaszającego"
 forwardReport: "Przekaż zgłoszenie do innej instancji"
+forwardReportIsAnonymous: "Zamiast twojego konta, anonimowe konto systemowe będzie wyświetlone jako zgłaszający na instancji zdalnej."
 send: "Wyślij"
 abuseMarkAsResolved: "Oznacz zgłoszenie jako rozwiązane"
 openInNewTab: "Otwórz w nowej karcie"
@@ -668,6 +736,7 @@ lockedAccountInfo: "Dopóki nie ustawisz widoczności wpisu na \"Obserwujący\",
 alwaysMarkSensitive: "Oznacz domyślnie jako NSFW"
 loadRawImages: "Wyświetlaj zdjęcia w załącznikach w całości zamiast miniatur"
 disableShowingAnimatedImages: "Nie odtwarzaj animowanych obrazów"
+highlightSensitiveMedia: "Podkreśl wrażliwą zawartość"
 verificationEmailSent: "Wiadomość weryfikacyjna została wysłana. Odwiedź uwzględniony odnośnik, aby ukończyć weryfikację."
 notSet: "Nie ustawiono"
 emailVerified: "Adres e-mail został potwierdzony"
@@ -678,6 +747,8 @@ contact: "Kontakt"
 useSystemFont: "Używaj domyślnej czcionki systemu"
 clips: "Klipy"
 experimentalFeatures: "Eksperymentalne funkcje"
+experimental: "Eksperymentalne"
+thisIsExperimentalFeature: "Ta funkcja jest eksperymentalna. Jej funkcjonalność może ulec zmianie, i może ona nie funkcjonować tak, jak zamierzono."
 developer: "Programista"
 makeExplorable: "Pokazuj konto na stronie „Eksploruj”"
 makeExplorableDescription: "Jeżeli wyłączysz tę opcję, Twoje konto nie będzie wyświetlać się w sekcji „Eksploruj”."
@@ -695,12 +766,14 @@ onlineUsersCount: "{n} osób jest online"
 nUsers: "{n} użytkowników"
 nNotes: "{n} wpisów"
 sendErrorReports: "Wyślij raporty o błędach"
+sendErrorReportsDescription: "Gdy włączone, jeśli wystąpi problem, szczegółowe informacje o błędach będą udostępniane Misskey, pomagając ulepszyć jakość Misskey.\nBędzie to zawierało informacje takie jak wersja twojego systemu operacyjnego, jakiej przeglądarki używasz, twoja aktywność w Misskey, itd."
 myTheme: "Mój motyw"
 backgroundColor: "Tło"
 accentColor: "Akcent"
 textColor: "Tekst"
 saveAs: "Zapisz jako…"
 advanced: "Zaawansowane"
+advancedSettings: "Zaawansowane ustawienia"
 value: "Wartość"
 createdAt: "Utworzono"
 updatedAt: "Zaktualizowano"
@@ -760,12 +833,14 @@ noMaintainerInformationWarning: "Informacje o administratorze nie są skonfiguro
 noBotProtectionWarning: "Zabezpieczenie przed botami nie jest skonfigurowane."
 configure: "Skonfiguruj"
 postToGallery: "Opublikuj w galerii"
+postToHashtag: "Postuj do tego hashtagu"
 gallery: "Galeria"
 recentPosts: "Ostatnie wpisy"
 popularPosts: "Popularne wpisy"
 shareWithNote: "Udostępnij z wpisem"
 ads: "Reklamy"
 expiration: "Ankieta kończy się"
+startingperiod: "Początek"
 memo: "Notatki"
 priority: "Priorytet"
 high: "Wysoki"
@@ -792,13 +867,19 @@ translatedFrom: "Przetłumaczone z {x}"
 accountDeletionInProgress: "Trwa usuwanie konta"
 usernameInfo: "Nazwa, która identyfikuje Twoje konto spośród innych na tym serwerze.  Możesz użyć alfabetu (a~z, A~Z), cyfr (0~9) lub podkreślników (_). Nazwy użytkownika nie mogą być później zmieniane."
 aiChanMode: "Tryb Ai"
+devMode: "Tryb programisty"
 keepCw: "Zostaw ostrzeżenia o zawartości"
 pubSub: "Konta Pub/Sub"
+lastCommunication: "Ostatnia komunikacja"
 resolved: "Rozwiązane"
 unresolved: "Nierozwiązane"
 breakFollow: "Usuń obserwującego"
+breakFollowConfirm: "Czy na pewno usunąć tego obserwującego?"
 itsOn: "Włączone"
 itsOff: "Wyłączone"
+on: "Włączone"
+off: "Wyłączone"
+emailRequiredForSignup: "Wymagaj adresu e-mail do rejestracji"
 unread: "Nieodczytane"
 filter: "Filtr"
 controlPanel: "Panel sterowania"
@@ -808,6 +889,8 @@ makeReactionsPublicDescription: "To spowoduje, że lista wszystkich Twoich dotyc
 classic: "Klasyczny"
 muteThread: "Wycisz wątek"
 unmuteThread: "Wyłącz wyciszenie wątku"
+followingVisibility: "Widoczność obserwacji"
+followersVisibility: "Widoczność obserwujących"
 continueThread: "Pokaż kontynuację wątku"
 deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie Twojego konta. Kontynuować?"
 incorrectPassword: "Nieprawidłowe hasło."
@@ -820,9 +903,14 @@ overridedDeviceKind: "Typ urządzenia"
 smartphone: "Smartfon"
 tablet: "Tablet"
 auto: "Automatycznie"
+themeColor: "Motyw kolorystyczny"
 size: "Rozmiar"
 numberOfColumn: "Liczba kolumn"
 searchByGoogle: "Szukaj"
+instanceDefaultLightTheme: "Domyślny motyw dla trybu jasnego"
+instanceDefaultDarkTheme: "Domyślny motyw dla trybu ciemnego"
+instanceDefaultThemeDescription: "Opis domyślnego motywu instancji"
+mutePeriod: "Okres wyciszenia"
 period: "Ankieta kończy się"
 indefinitely: "Nigdy"
 tenMinutes: "10 minut"
@@ -831,29 +919,50 @@ oneDay: "1 dzień"
 oneWeek: "1 tydzień"
 oneMonth: "jeden miesiąc"
 failedToFetchAccountInformation: "Nie udało się uzyskać informacji o koncie"
+rateLimitExceeded: "Limit szybkości przekroczony"
+cropImage: "Przytnij obraz"
+cropImageAsk: "Czy chcesz przyciąć obrazek?"
+cropYes: "Tak, przytnij"
+cropNo: "Nie chce przycinać"
 file: "Pliki"
+recentNHours: "W ciągu ostatnich {n} godzin"
+recentNDays: "W ciągu ostatnich {n} dni"
+noEmailServerWarning: "Serwer Email nie jest skonfigurowany"
 recommended: "Zalecane"
 check: "Zweryfikuj"
+driveCapOverrideLabel: "Zmień limit pojemności dysku użytkownika"
+requireAdminForView: "Aby to zobaczyć, musisz być administratorem"
+isSystemAccount: "To jest konto stworzone i zarządzane przez system"
+typeToConfirm: "Wielki chuj "
 deleteAccount: "Usuń konto"
 document: "Dokumentacja"
 numberOfPageCache: "Ilość stron w cache"
+numberOfPageCacheDescription: "Zwiększenie tej liczby polepszy wygodę, ale spowoduje większe obciążenie jako użycie pamięci na urządzeniu użytkownika."
 logoutConfirm: "Czy na pewno chcesz się wylogować?"
 lastActiveDate: "Ostatnio użyte w"
 statusbar: "Pasek stanu"
 pleaseSelect: "Wybierz opcję"
 reverse: "Odwróć"
 colored: "Kolorowe"
+refreshInterval: "Okres aktualizacji"
 label: "Etykieta"
 type: "Typ"
 speed: "Prędkość"
+slow: "Wolny"
+fast: "Szybki"
+sensitiveMediaDetection: "Detekcja wrażliwej zawartości"
 localOnly: "Lokalne tylko"
+remoteOnly: "Tylko zdalne instancje"
 failedToUpload: "Przesyłanie nie powiodło się"
 cannotUploadBecauseInappropriate: "Nie można przesłać tego pliku, ponieważ jego części zostały wykryte jako potencjalnie nieodpowiednie."
 cannotUploadBecauseNoFreeSpace: "Przesyłanie nie powiodło się z powodu braku miejsca na dysku."
+cannotUploadBecauseExceedsFileSizeLimit: "Nie można przesłać pliku, ponieważ wykracza on poza limit wielkości pliku."
 beta: "Beta"
 enableAutoSensitive: "Automatyczne oznaczanie NSFW"
 enableAutoSensitiveDescription: "Umożliwia automatyczne wykrywanie i oznaczanie zawartości NSFW za pomocą uczenia maszynowego. Nawet jeśli ta opcja jest wyłączona, może być włączona w całej instancji."
+activeEmailValidationDescription: "Włącza bardziej restrykcyjną walidację adresów e-mail, co obejmuje sprawdzanie adresów jednorazowych i czy komunikacja z tym adresem jest możliwa. Gdy wyłączone, tylko format adresu e-mail jest sprawdzany."
 navbar: "Pasek nawigacyjny"
+shuffle: "Mieszaj"
 account: "Konta"
 move: "Przenieś"
 pushNotification: "Powiadomienia"
@@ -863,22 +972,70 @@ pushNotificationAlreadySubscribed: "Powiadomienia push są włączone"
 pushNotificationNotSupported: "Przeglądarka lub instancja nie obsługuje powiadomień push"
 sendPushNotificationReadMessage: "Usuń powiadomienia push po przeczytaniu powiadomień i wiadomości."
 sendPushNotificationReadMessageCaption: "Chwilowo pojawi się powiadomienie \"{emptyPushNotificationMessage}\". Może wzrosnąć zużycie baterii urządzenia."
+windowMaximize: "Maksymalizuj"
+windowMinimize: "Minimalizuj"
+windowRestore: "Przywróć"
+caption: "Legenda"
 loggedInAsBot: "Jesteś obecnie zalogowany/a jako bot"
+tools: "Narzędzia"
+cannotLoad: "Nie można wczytać"
+numberOfProfileView: "Wyświetlenia profilu"
 like: "Polub"
+unlike: "Usuń polubienie"
+numberOfLikes: "Liczba polubień"
 show: "Wyświetlanie"
+neverShow: "Nie pokazuj ponownie"
+remindMeLater: "Przypomnij później"
+didYouLikeMisskey: "Czy Misskey się tobie spodobało?"
+pleaseDonate: "{host} używa darmowego oprogramowania — Misskey. Bylibyśmy bardzo wdzięczni za datki, które pozwolą na kontynuację rozwoju Misskey!"
+correspondingSourceIsAvailable: "Odpowiedni kod źródłowy jest dostępny pod {anchor}."
+roles: "Role"
+role: "Rola"
+noRole: "Rola nie znaleziona"
+normalUser: "Normalny użytkownik"
+undefined: "Niezdefiniowane"
+assign: "Przydziel"
+unassign: "Cofnij przydzielenie"
 color: "Kolor"
+manageCustomEmojis: "Zarządzaj niestandardowymi Emoji"
+manageAvatarDecorations: "Zarządzaj dekoracjami awatara"
+invalidParamError: "Błąd parametrów"
+permissionDeniedError: "Odrzucono operacje"
+permissionDeniedErrorDescription: "Konto nie posiada uprawnień"
+preset: "Konfiguracja"
+selectFromPresets: "Wybierz konfiguracje"
+achievements: "Osiągnięcia"
+thisPostMayBeAnnoyingCancel: "Odrzuć"
+internalServerError: "Wewnętrzny błąd serwera"
+internalServerErrorDescription: "Niespodziewany błąd po stronie serwera"
+copyErrorInfo: "Kopiuj informacje o błędzie"
+joinThisServer: "Dołącz do chaty"
+disableFederationOk: "Wyłącz federacje"
+invitationRequiredToRegister: "Ten serwer wymaga zaproszenia. Tylko osoby z zaproszeniem mogą się zarejestrować"
+emailNotSupported: "Wysyłanie wiadomości E-mail nie jest obsługiwane na tym serwerze"
+postToTheChannel: "Publikuj na kanale"
 youFollowing: "Śledzeni"
 icon: "Awatar"
 replies: "Odpowiedz"
 renotes: "Udostępnij"
 sourceCode: "Kod źródłowy"
 flip: "Odwróć"
+lastNDays: "W ciągu ostatnich {n} dni"
+surrender: "Odrzuć"
+gameRetry: "Spróbuj ponownie"
+_bubbleGame:
+  _score:
+    score: "Wynik"
 _role:
+  assignTarget: "Przydziel"
   priority: "Priorytet"
   _priority:
     low: "Niski"
     middle: "Średnie"
     high: "Wysoki"
+  _options:
+    canManageCustomEmojis: "Zarządzaj niestandardowymi Emoji"
+    canManageAvatarDecorations: "Zarządzaj dekoracjami awatara"
 _sensitiveMediaDetection:
   description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera."
   setSensitiveFlagAutomatically: "Oznacz jako NSFW"
@@ -1400,4 +1557,3 @@ _moderationLogTypes:
   resetPassword: "Zresetuj hasło"
 _reversi:
   total: "Łącznie"
-
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index f62557fb23bb..e00f5750ddb5 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -733,9 +733,9 @@ reloadToApplySetting: "As configurações serão refletidas após recarregar a p
 needReloadToApply: "É necessário recarregar a página para refletir as alterações."
 showTitlebar: "Exibir barra de título"
 clearCache: "Limpar o cache"
-onlineUsersCount: "Pessoas Online"
-nUsers: "Usuários"
-nNotes: "Notas"
+onlineUsersCount: "{n} Pessoas Online"
+nUsers: "{n} Usuários"
+nNotes: "{n} Notas"
 sendErrorReports: "Enviar relatórios de erro"
 sendErrorReportsDescription: "Ao ativar essa opção, informações detalhadas de erro serão compartilhadas com o Misskey em caso de problemas, o que pode ajudar a melhorar a qualidade do software. As informações de erro podem incluir a versão do sistema operacional, o tipo de navegador e o sua atividade no Misskey."
 myTheme: "Meu tema"
@@ -767,7 +767,7 @@ emailNotification: "Notificações por e-mail"
 publish: "Publicar"
 inChannelSearch: "Pesquisar no canal"
 useReactionPickerForContextMenu: "Clique com o botão direito do mouse para abrir o seletor de reações."
-typingUsers: "digitando"
+typingUsers: "{users} pessoas digitando"
 jumpToSpecifiedDate: "Pular para uma data específica"
 showingPastTimeline: "Visualizar linha de tempo anterior"
 clear: "Limpar"
@@ -834,7 +834,7 @@ learnMore: "Saiba mais"
 misskeyUpdated: "Misskey foi atualizado!"
 whatIsNew: "Ver atualizações"
 translate: "Traduzir"
-translatedFrom: "Traduzido de"
+translatedFrom: "Traduzido de {x}"
 accountDeletionInProgress: "Encerramento de conta em andamento"
 usernameInfo: "O nome para identificar exclusivamente a sua conta no servidor. Pode conter letras (az, AZ), números (0~9) e sublinhados (_). O nome de usuário não pode ser alterado posteriormente."
 aiChanMode: "Modo AI-chan"
@@ -1301,8 +1301,8 @@ _preferencesBackups:
 _channel:
   featured: "Destaques"
   following: "Seguindo"
-  usersCount: "usuários ativos"
-  notesCount: "notas"
+  usersCount: "{n} usuários ativos"
+  notesCount: "{n} notas"
   nameAndDescription: "Nome e descrição"
 _menuDisplay:
   sideFull: "Exibir painel lateral inteiro"
@@ -1501,4 +1501,3 @@ _moderationLogTypes:
   resetPassword: "Redefinir senha"
 _reversi:
   total: "Total"
-
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index c1158e47b7fa..695eb2501fe6 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -729,4 +729,3 @@ _moderationLogTypes:
   resetPassword: "Resetează parola"
 _reversi:
   total: "Total"
-
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index d666b694905d..66e032f16f82 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -17,7 +17,7 @@ noThankYou: "Нет, спасибо"
 enterUsername: "Введите имя пользователя"
 renotedBy: "{user} делится"
 noNotes: "Нет ни одной заметки"
-noNotifications: "Нет ни одного уведомления"
+noNotifications: "Нет уведомлений"
 instance: "Инстанс"
 settings: "Настройки"
 notificationSettings: "Настройки уведомлений"
@@ -129,6 +129,7 @@ overwriteFromPinnedEmojis: "Заменить на эмодзи из общего
 reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте кнопкой «+»."
 rememberNoteVisibility: "Запоминать видимость заметок"
 attachCancel: "Удалить вложение"
+deleteFile: "Удалить файл"
 markAsSensitive: "Отметить как «не для всех»"
 unmarkAsSensitive: "Снять отметку «не для всех»"
 enterFileName: "Введите имя файла"
@@ -312,6 +313,7 @@ folderName: "Имя папки"
 createFolder: "Создать папку"
 renameFolder: "Переименовать папку"
 deleteFolder: "Удалить папку"
+folder: "Папка"
 addFile: "Добавить файл"
 emptyDrive: "Диск пуст"
 emptyFolder: "Папка пуста"
@@ -373,6 +375,8 @@ hcaptcha: "hCaptcha"
 enableHcaptcha: "Включить hCaptcha"
 hcaptchaSiteKey: "Ключ сайта"
 hcaptchaSecretKey: "Секретный ключ"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "Включить mCaptcha"
 mcaptchaSiteKey: "Ключ сайта"
 mcaptchaSecretKey: "Секретный ключ"
 recaptcha: "reCAPTCHA"
@@ -542,6 +546,8 @@ showInPage: "Показать страницу"
 popout: "Развернуть"
 volume: "Громкость"
 masterVolume: "Основная регулировка громкости"
+notUseSound: "Выключить звук"
+useSoundOnlyWhenActive: "Использовать звук, когда Misskey активен."
 details: "Подробнее"
 chooseEmoji: "Выберите эмодзи"
 unableToProcess: "Не удаётся завершить операцию"
@@ -562,6 +568,10 @@ output: "Выходы"
 script: "Скрипт"
 disablePagesScript: "Отключить скрипты на «Страницах»"
 updateRemoteUser: "Обновить данные пользователя с его сервера"
+unsetUserAvatar: "Убрать аватар"
+unsetUserAvatarConfirm: "Вы точно хотите убрать аватар?"
+unsetUserBanner: "Убрать баннер"
+unsetUserBannerConfirm: "Вы точно хотите убрать баннер?"
 deleteAllFiles: "Удалить все файлы"
 deleteAllFilesConfirm: "Вы хотите удалить все файлы?"
 removeAllFollowing: "Удалить всех подписчиков"
@@ -612,6 +622,7 @@ medium: "Средне"
 small: "Мелко"
 generateAccessToken: "Создать токен доступа"
 permission: "Разрешения"
+adminPermission: "Доступ администратора"
 enableAll: "Включить все"
 disableAll: "Выключить всё"
 tokenRequested: "Открыть доступ к учётной записи"
@@ -633,6 +644,7 @@ smtpSecure: "Использовать SSL/TLS для SMTP-соединений"
 smtpSecureInfo: "Выключите при использовании STARTTLS."
 testEmail: "Проверка доставки электронной почты"
 wordMute: "Скрытие слов"
+hardWordMute: ""
 regexpError: "Ошибка в регулярном выражении"
 regexpErrorDescription: "В списке {tab} скрытых слов, в строке {line} обнаружена синтаксическая ошибка:"
 instanceMute: "Глушение инстансов"
@@ -1084,6 +1096,7 @@ renotes: "Репост"
 loadReplies: "Показать ответы"
 sourceCode: "Исходный код"
 flip: "Переворот"
+code: "Код"
 lastNDays: "Последние {n} сут"
 surrender: "Этот пост не может быть отменен."
 _initialAccountSetting:
@@ -1626,7 +1639,6 @@ _2fa:
   registerTOTP: "Начните настраивать приложение-аутентификатор"
   step1: "Прежде всего, установите на устройство приложение для аутентификации, например, {a} или {b}."
   step2: "Далее отсканируйте отображаемый QR-код при помощи приложения."
-  step2Click: "Нажав на QR-код, вы можете зарегистрироваться с помощью приложения для аутентификации или брелка для ключей, установленного на вашем устройстве."
   step3Title: "Введите проверочный код"
   step3: "И наконец, введите код, который покажет приложение."
   step4: "Теперь при каждом входе на сайт вам нужно будет вводить код из приложения аналогичным образом."
@@ -1975,4 +1987,3 @@ _moderationLogTypes:
   resetPassword: "Сброс пароля:"
 _reversi:
   total: "Всего"
-
diff --git a/locales/si-LK.yml b/locales/si-LK.yml
index cd21505a47e5..e130d68ed850 100644
--- a/locales/si-LK.yml
+++ b/locales/si-LK.yml
@@ -1,2 +1,19 @@
 ---
-
+_lang_: "සිංහල"
+monthAndDay: "{month}-{day}"
+username: "පරිශීලක නාමය"
+password: "මුරපදය"
+cancel: "අවලංගු කරන්න"
+instance: "සර්වර්"
+login: "පිවිසෙන්න"
+users: "පරිශීලක"
+note: "නෝට්"
+notes: "නෝට්"
+instances: "සර්වර්"
+smtpUser: "පරිශීලක නාමය"
+smtpPass: "මුරපදය"
+user: "පරිශීලක"
+_sfx:
+  note: "නෝට්"
+_profile:
+  username: "පරිශීලක නාමය"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 251496b10b68..0978701e5557 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -1448,4 +1448,3 @@ _moderationLogTypes:
   resetPassword: "Resetovať heslo"
 _reversi:
   total: "Celkom"
-
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 07d5509a6a4b..62bc71a13df2 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -576,4 +576,3 @@ _webhookSettings:
 _moderationLogTypes:
   suspend: "Suspendera"
   resetPassword: "Återställ Lösenord"
-
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index c0e79d5e1649..020b95485475 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -33,7 +33,7 @@ logout: "ออกจากระบบ"
 signup: "สร้างบัญชีผู้ใช้"
 uploading: "กำลังอัปโหลด"
 save: "บันทึก"
-users: "ผู้ใช้งาน"
+users: "ผู้ใช้"
 addUser: "เพิ่มผู้ใช้"
 favorite: "รายการโปรด"
 favorites: "รายการโปรด"
@@ -400,6 +400,7 @@ name: "ชื่อ"
 antennaSource: "แหล่งเสาอากาศ"
 antennaKeywords: "คีย์เวิร์ดที่ควรฟัง"
 antennaExcludeKeywords: "คีย์เวิร์ดที่จะยกเว้น"
+antennaExcludeBots: "ยกเว้นบัญชีบอต"
 antennaKeywordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR"
 notifyAntenna: "แจ้งเตือนเกี่ยวกับโน้ตใหม่"
 withFileAntenna: "เฉพาะโน้ตที่มีไฟล์"
@@ -494,6 +495,7 @@ emojiStyle: "สไตล์เอโมจิ"
 native: "ภาษาแม่"
 disableDrawer: "อย่าใช้ลิ้นชักสไตล์เมนู"
 showNoteActionsOnlyHover: "แสดงการดำเนินการเฉพาะโน้ตเมื่อโฮเวอร์"
+showReactionsCount: "แสดงจำนวนรีแอกชั่นในโน้ต"
 noHistory: "ไม่มีประวัติ"
 signinHistory: "ประวัติการเข้าสู่ระบบ"
 enableAdvancedMfm: "เปิดใช้งาน MFM ขั้นสูง"
@@ -825,7 +827,7 @@ switchAccount: "สลับบัญชีผู้ใช้"
 enabled: "เปิดใช้งาน"
 disabled: "ปิดการใช้งาน"
 quickAction: "ปุ่มลัด"
-user: "ผู้ใช้งาน"
+user: "ผู้ใช้"
 administration: "การจัดการ"
 accounts: "บัญชีผู้ใช้"
 switch: "สลับ"
@@ -1223,6 +1225,16 @@ enableHorizontalSwipe: "ปัดเพื่อสลับแท็บ"
 loading: "กำลังโหลด"
 surrender: "ยอมแพ้"
 gameRetry: "เริ่มเกมใหม่"
+notUsePleaseLeaveBlank: "หากไม่ได้ใช้กรุณาเว้นว่างไว้"
+useTotp: "ใช้รหัสผ่านแบบใช้ครั้งเดียว (TOTP)"
+useBackupCode: "ใช้รหัสสำรอง"
+launchApp: "เริ่มแอป"
+useNativeUIForVideoAudioPlayer: "ใช้ UI ของเบราว์เซอร์เพื่อเล่นวิดีโอ/เสียง"
+keepOriginalFilename: "คงชื่อไฟล์เดิมไว้"
+keepOriginalFilenameDescription: "หากปิดการตั้งค่านี้ ในระหว่างการอัปโหลดชื่อไฟล์จะถูกแทนที่ด้วยสตริงแบบสุ่มโดยอัตโนมัติ"
+noDescription: "ไม่มีข้อความอธิบาย"
+alwaysConfirmFollow: "แสดงข้อความยืนยันเมื่อกดติดตาม"
+inquiry: "ติดต่อเรา"
 _bubbleGame:
   howToPlay: "วิธีเล่น"
   hold: "หยุดชั่วคราว"
@@ -1351,7 +1363,7 @@ _serverSettings:
 _accountMigration:
   moveFrom: "ย้ายข้อมูลบัญชีอื่นไปยังอีกบัญชีนี้หนึ่ง"
   moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น"
-  moveFromLabel: "บัญชีที่จะย้ายจาก:"
+  moveFromLabel: "บัญชีที่จะย้ายจาก #{n}"
   moveFromDescription: "ถ้าหากคุณต้องการโอนข้อมูล คุณจำเป็นต้องสร้างบัญชีสำรองสำหรับการย้ายบัญชี  หลังจากนั้นป้อนบัญชีที่จะย้ายไปในรูปแบบต่อไปนี้: @person@instance.com"
   moveTo: "ย้ายข้อมูลบัญชีนี้ไปยังบัญชีอีกหนึ่ง"
   moveToLabel: "บัญชีที่จะย้ายไปที่:"
@@ -1682,6 +1694,11 @@ _role:
     roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ"
     isLocal: "ผู้ใช้ในพื้นที่"
     isRemote: "ผู้ใช้ระยะไกล"
+    isCat: "ผู้ใช้ที่เป็นแมว"
+    isBot: "ผู้ใช้ที่เป็นบอต"
+    isSuspended: "ผู้ใช้ที่ถูกระงับ"
+    isLocked: "ผู้ใช้บัญชีไม่เปิดเผยสาธารณะ"
+    isExplorable: "ผู้ใช้ที่เปิดใช้งาน “ทำให้บัญชีของฉันค้นหาได้ง่ายขึ้น”"
     createdLessThan: "สร้างน้อยกว่า"
     createdMoreThan: "สร้างมากกว่า"
     followersLessThanOrEq: "จำนวนผู้ติดตามน้อยกว่าหรือเท่ากับ\n"
@@ -1751,6 +1768,7 @@ _plugin:
   installWarn: "กรุณาอย่าติดตั้งปลั๊กอินที่ไม่น่าเชื่อถือนะคะ"
   manage: "จัดการปลั๊กอิน"
   viewSource: "ดูต้นฉบับ"
+  viewLog: "แสดงปูม"
 _preferencesBackups:
   list: "สร้างการสำรองข้อมูล"
   saveNew: "บันทึกข้อมูลสำรองใหม่"
@@ -1940,7 +1958,6 @@ _2fa:
   registerTOTP: "ลงทะเบียนแอพตัวตรวจสอบสิทธิ์"
   step1: "ขั้นตอนแรก ติดตั้งแอปยืนยันตัวตน (เช่น {a} หรือ {b}) บนอุปกรณ์ของคุณ"
   step2: "จากนั้นสแกนรหัส QR ที่แสดงบนหน้าจอนี้"
-  step2Click: "การคลิกที่รหัส QR นี้จะช่วยให้คุณนั้นสามารถลงทะเบียน 2FA กับคีย์ความปลอดภัยหรือแอปตรวจสอบความถูกต้องของโทรศัพท์ได้"
   step2Uri: "ป้อนใส่ URL ดังต่อไปนี้ถ้าหากคุณใช้โปรแกรมเดสก์ท็อป"
   step3Title: "ป้อนรหัสยืนยัน"
   step3: "ป้อนโทเค็นที่แอปของคุณให้มาเพื่อเสร็จสิ้นการตั้งค่า"
@@ -1964,6 +1981,7 @@ _2fa:
   backupCodesDescription: "หากแอปยืนยันตัวตนของคุณไม่พร้อมใช้งาน คุณสามารถใช้รหัสสำรองด้านล่างเพื่อเข้าถึงบัญชีของคุณได้ อย่าลืมเก็บรหัสเหล่านี้ไว้ในที่ปลอดภัย แต่ละรหัสสามารถใช้ได้เพียงครั้งเดียวเท่านั้น"
   backupCodeUsedWarning: "มีการใช้รหัสสำรองแล้ว โปรดกรุณากำหนดค่าการตรวจสอบสิทธิ์แบบสองปัจจัยโดยเร็วที่สุดถ้าหากคุณยังไม่สามารถใช้งานได้อีก"
   backupCodesExhaustedWarning: "รหัสสำรองทั้งหมดถูกใช้แล้ว ถ้าหากคุณยังสูญเสียการเข้าถึงแอปการตรวจสอบสิทธิ์แบบสองปัจจัยคุณจะยังไม่สามารถเข้าถึงบัญชีนี้ได้ กรุณากำหนดค่าการรับรองความถูกต้องด้วยการยืนยันสองชั้น"
+  moreDetailedGuideHere: "คลิกที่นี่เพื่อดูคำแนะนำโดยละเอียด"
 _permissions:
   "read:account": "ดูข้อมูลบัญชีของคุณ"
   "write:account": "แก้ไขข้อมูลบัญชีของคุณ"
@@ -2225,6 +2243,7 @@ _play:
   title: "หัวข้อ"
   script: "สคริปต์"
   summary: "รายละเอียด"
+  visibilityDescription: "หากตั้งค่าเป็นส่วนตัว มันจะไม่ปรากฏในโปรไฟล์อีกต่อไป แต่ผู้ที่ทราบ URL ของมันจะยังสามารถเข้าถึงได้"
 _pages:
   newPage: "สร้างหน้าเพจใหม่"
   editPage: "แก้ไขหน้าเพจ"
@@ -2269,6 +2288,8 @@ _pages:
     section: "ประเภท"
     image: "รูปภาพ"
     button: "ปุ่ม"
+    dynamic: "บล็อกแบบไดนามิก"
+    dynamicDescription: "บล็อกนี้ล้าสมัยแล้ว โปรดใช้ {play} แทน นับจากนี้เป็นต้นไป"
     note: "โน้ตที่ฝังตัว"
     _note:
       id: "โน้ต ID"
@@ -2298,6 +2319,7 @@ _notification:
   sendTestNotification: "ส่งทดสอบการแจ้งเตือน"
   notificationWillBeDisplayedLikeThis: "การแจ้งเตือนมีลักษณะแบบนี้"
   reactedBySomeUsers: "ถูกรีแอคชั่นโดยผู้ใช้ {n} ราย"
+  likedBySomeUsers: "{n} คนถูกใจ"
   renotedBySomeUsers: "รีโน้ตจากผู้ใช้ {n} ราย"
   followedBySomeUsers: "มีผู้ติดตาม {n} ราย"
   flushNotification: "ล้างประวัติการแจ้งเตือน"
@@ -2524,4 +2546,21 @@ _reversi:
 _offlineScreen:
   title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
   header: "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
-
+_urlPreviewSetting:
+  title: "การตั้งค่าการแสดงตัวอย่าง URL"
+  enable: "เปิดใช้งานการแสดงตัวอย่าง URL"
+  timeout: "เวลาจำกัดในการโหลดตัวอย่าง URL (ms)"
+  timeoutDescription: "หากเวลาที่ใช้ในการโหลดเกินค่านี้ จะไม่มีการสร้างการแสดงตัวอย่าง"
+  maximumContentLength: "ค่าสูงสุดของ Content-Length (byte)"
+  maximumContentLengthDescription: "หาก Content-Length เกินค่านี้ จะไม่มีการสร้างการแสดงตัวอย่าง"
+  requireContentLength: "สร้างการแสดงตัวอย่างเฉพาะในกรณีที่รับ Content-Length ไหว"
+  requireContentLengthDescription: "หากเซิร์ฟเวอร์อื่นไม่ส่งคืน Content-Length จะไม่มีการสร้างการแสดงตัวอย่าง"
+  userAgent: "User-Agent"
+  userAgentDescription: "ตั้งค่า User-Agent ที่ใช้ในการรับการแสดงตัวอย่าง หากเว้นว่างไว้ ระบบจะใช้ User-Agent เริ่มต้น"
+  summaryProxy: "endpoint ของพร็อกซีที่สร้างการแสดงตัวอย่าง"
+  summaryProxyDescription: "สร้างการแสดงตัวอย่างด้วย summary Proxy แทนที่จะใช้เนื้อหา Misskey"
+  summaryProxyDescription2: "พารามิเตอร์ต่อไปนี้จะถูกใช้เป็นสตริงการสืบค้นเพื่อเชื่อมต่อกับพร็อกซี หากฝั่งพร็อกซีไม่รองรับการตั้งค่าเหล่านี้จะถูกละเว้น"
+_mediaControls:
+  pip: "รูปภาพในรูปภาม"
+  playbackRate: "ความเร็วในการเล่น"
+  loop: "เล่นวนซ้ำ"
diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml
index e93a6e43e129..0793592d3402 100644
--- a/locales/tr-TR.yml
+++ b/locales/tr-TR.yml
@@ -455,4 +455,3 @@ _deck:
 _moderationLogTypes:
   suspend: "askıya al"
   resetPassword: "Şifre sıfırlama"
-
diff --git a/locales/ug-CN.yml b/locales/ug-CN.yml
index e06cee11a2c2..e48f64511cf5 100644
--- a/locales/ug-CN.yml
+++ b/locales/ug-CN.yml
@@ -17,4 +17,3 @@ _2fa:
   renewTOTPCancel: "ئۇنى توختىتىڭ"
 _widgets:
   profile: "profile"
-
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index df36f43c06c9..0ce5dc12028d 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -1623,4 +1623,3 @@ _moderationLogTypes:
   resetPassword: "Скинути пароль"
 _reversi:
   total: "Всього"
-
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index b87b59692533..809e78549295 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -1090,4 +1090,3 @@ _moderationLogTypes:
   resetPassword: "Parolni tiklash"
 _reversi:
   total: "Jami"
-
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 59883f4a6cc0..d9c21d29ad4c 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -121,9 +121,11 @@ sensitive: "Nhạy cảm"
 add: "Thêm"
 reaction: "Biểu cảm"
 reactions: "Biểu cảm"
+emojiPicker: "Bộ chọn biểu tượng cảm xúc"
 reactionSettingDescription2: "Kéo để sắp xếp, nhấn để xóa, nhấn \"+\" để thêm."
 rememberNoteVisibility: "Lưu kiểu tút mặc định"
 attachCancel: "Gỡ tập tin đính kèm"
+deleteFile: "Xoá tệp tin"
 markAsSensitive: "Đánh dấu là nhạy cảm"
 unmarkAsSensitive: "Bỏ đánh dấu nhạy cảm"
 enterFileName: "Nhập tên tập tin"
@@ -257,6 +259,7 @@ removed: "Đã xóa"
 removeAreYouSure: "Bạn có chắc muốn gỡ \"{x}\"?"
 deleteAreYouSure: "Bạn có chắc muốn xóa \"{x}\"?"
 resetAreYouSure: "Bạn có chắc muốn đặt lại?"
+areYouSure: "Bạn chắc chứ?"
 saved: "Đã lưu"
 messaging: "Trò chuyện"
 upload: "Tải lên"
@@ -307,6 +310,7 @@ folderName: "Tên thư mục"
 createFolder: "Tạo thư mục"
 renameFolder: "Đổi tên thư mục"
 deleteFolder: "Xóa thư mục"
+folder: "Thư mục"
 addFile: "Thêm tập tin"
 emptyDrive: "Ổ đĩa của bạn trống trơn"
 emptyFolder: "Thư mục trống"
@@ -368,6 +372,8 @@ hcaptcha: "hCaptcha"
 enableHcaptcha: "Bật hCaptcha"
 hcaptchaSiteKey: "Khóa của trang"
 hcaptchaSecretKey: "Khóa bí mật"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "Bật mCaptcha"
 mcaptchaSiteKey: "Khóa của trang"
 mcaptchaSecretKey: "Khóa bí mật"
 recaptcha: "reCAPTCHA"
@@ -385,6 +391,7 @@ name: "Tên"
 antennaSource: "Nguồn trạm phát sóng"
 antennaKeywords: "Từ khóa để nghe"
 antennaExcludeKeywords: "Từ khóa để lọc ra"
+antennaExcludeBots: "Loại trừ các tài khoản bot"
 antennaKeywordsDescription: "Phân cách bằng dấu cách cho điều kiện AND hoặc bằng xuống dòng cho điều kiện OR."
 notifyAntenna: "Thông báo có tút mới"
 withFileAntenna: "Chỉ những tút có media"
@@ -537,6 +544,7 @@ showInPage: "Hiện trong trang"
 popout: "Pop-out"
 volume: "Âm lượng"
 masterVolume: "Âm thanh chung"
+notUseSound: "Tắt tiếng"
 details: "Chi tiết"
 chooseEmoji: "Chọn emoji"
 unableToProcess: "Không thể hoàn tất hành động"
@@ -557,6 +565,10 @@ output: "Nguồn ra"
 script: "Kịch bản"
 disablePagesScript: "Tắt AiScript trên Trang"
 updateRemoteUser: "Cập nhật thông tin người dùng ở máy chủ khác"
+unsetUserAvatar: "Gỡ ảnh đại diện"
+unsetUserAvatarConfirm: "Bạn có chắc muốn gỡ ảnh đại diện?"
+unsetUserBanner: "Gỡ ảnh bìa"
+unsetUserBannerConfirm: "Bạn có chắc muốn gỡ ảnh bìa?"
 deleteAllFiles: "Xóa toàn bộ tập tin"
 deleteAllFilesConfirm: "Bạn có chắc xóa toàn bộ tập tin?"
 removeAllFollowing: "Ngưng theo dõi tất cả mọi người"
@@ -859,6 +871,8 @@ makeReactionsPublicDescription: "Điều này sẽ hiển thị công khai danh
 classic: "Cổ điển"
 muteThread: "Không quan tâm nữa"
 unmuteThread: "Quan tâm tút này"
+followingVisibility: "Hiển thị lượt theo dõi"
+followersVisibility: "Hiển thị người theo dõi"
 continueThread: "Tiếp tục xem chuỗi tút"
 deleteAccountConfirm: "Điều này sẽ khiến tài khoản bị xóa vĩnh viễn. Vẫn tiếp tục?"
 incorrectPassword: "Sai mật khẩu."
@@ -968,6 +982,7 @@ assign: "Phân công"
 unassign: "Hủy phân công"
 color: "Màu sắc"
 manageCustomEmojis: "Quản lý CustomEmoji"
+manageAvatarDecorations: "Quản lý trang trí ảnh đại diện"
 youCannotCreateAnymore: "Bạn đã tới giới hạn tạo."
 cannotPerformTemporary: "Tạm thời không sử dụng được"
 cannotPerformTemporaryDescription: "Tạm thời không sử dụng được vì lần số điều kiện quá giới hạn. Thử lại sau mọt lát nữa."
@@ -991,18 +1006,24 @@ copyErrorInfo: "Sao chép thông tin lỗi"
 joinThisServer: "Đăng ký trên chủ máy này"
 exploreOtherServers: "Tìm chủ máy khác"
 letsLookAtTimeline: "Thử xem Timeline"
+disableFederationOk: "Vô hiệu hoá"
 emailNotSupported: "Máy chủ này không hỗ trợ gửi email"
 postToTheChannel: "Đăng lên kênh"
 cannotBeChangedLater: "Không thể thay đổi sau này."
+likeOnly: "Chỉ lượt thích"
 rolesAssignedToMe: "Vai trò được giao cho tôi"
 resetPasswordConfirm: "Bạn thực sự muốn đặt lại mật khẩu?"
 sensitiveWords: "Các từ nhạy cảm"
+prohibitedWords: "Các từ bị cấm"
 license: "Giấy phép"
 unfavoriteConfirm: "Bạn thực sự muốn xoá khỏi mục yêu thích?"
+retryAllQueuesConfirmTitle: "Bạn có muốn thử lại?"
 retryAllQueuesConfirmText: "Điều này sẽ tạm thời làm tăng mức độ tải của máy chủ."
 enableChartsForRemoteUser: "Tạo biểu đồ người dùng từ xa"
 video: "Video"
 videos: "Các video"
+audio: "Âm thanh"
+audioFiles: "Âm thanh"
 dataSaver: "Tiết kiệm dung lượng"
 accountMigration: "Chuyển tài khoản"
 accountMoved: "Người dùng này đã chuyển sang một tài khoản mới:"
@@ -1019,34 +1040,82 @@ vertical: "Dọc"
 horizontal: "Thanh bên"
 position: "Vị trí"
 serverRules: "Luật của máy chủ"
+pleaseConfirmBelowBeforeSignup: "Để đăng ký trên máy chủ này, bạn phải xem xét và đồng ý với những điều sau."
+pleaseAgreeAllToContinue: "Bạn phải đồng ý tất cả điều trên để tiếp tục."
+continue: "Tiếp tục"
+archive: "Lưu trữ"
+thisChannelArchived: "Kênh này đã được lưu trữ."
+initialAccountSetting: "Thiết lập hồ sơ"
 youFollowing: "Đang theo dõi"
+preventAiLearning: "Từ chối sử dụng công nghệ Máy Học (AI Sáng Tạo)"
+options: "Tùy chọn"
+specifyUser: "Người dùng chỉ định"
+failedToPreviewUrl: "Không thể xem trước"
+update: "Cập nhật"
 later: "Để sau"
 goToMisskey: "Tới Misskey"
 installed: "Đã tải xuống"
 branding: "Thương hiệu"
 turnOffToImprovePerformance: "Tắt mục này có thể cải thiện hiệu năng."
+createInviteCode: "Tạo lời mời"
+createWithOptions: "Tạo cùng tùy chọn"
+createCount: "Số lượng mời"
+inviteCodeCreated: "Lời mời đã được tạo"
+inviteLimitExceeded: "Bạn đã vượt quá số lượng mời mà bạn có thể tạo."
+createLimitRemaining: "Giới hạn lượt mời: Còn lại {limit}"
+inviteLimitResetCycle: "Giới hạn này sẽ được đặt lại về {limit} lúc {time}."
 expirationDate: "Ngày hết hạn"
 noExpirationDate: "Vô thời hạn"
+inviteCodeUsedAt: "Mã mời đã được sử dụng lúc"
+registeredUserUsingInviteCode: "Lời mời đã được sử dụng bởi"
 waitingForMailAuth: "Đang chờ xác nhận email"
+inviteCodeCreator: "Lời mời đã được tạo bởi"
+usedAt: "Sử dụng vào lúc"
 unused: "Chưa được sử dụng"
 used: "Đã được sử dụng"
 expired: "Đã hết hạn"
 doYouAgree: "Đồng ý?"
-iHaveReadXCarefullyAndAgree: "Tôi đã đọc và đồng ý với \"x\"."
+beSureToReadThisAsItIsImportant: "Hãy đọc kỹ vì nó rất quan trọng."
+iHaveReadXCarefullyAndAgree: "Tôi đã đọc và đồng ý với \"{x}\"."
 dialog: "Hộp thoại"
 icon: "Ảnh đại diện"
 forYou: "Dành cho bạn"
 currentAnnouncements: "Thông báo hiện tại"
 pastAnnouncements: "Thông báo trước đó"
 youHaveUnreadAnnouncements: "Có thông báo chưa đọc."
+useSecurityKey: "Làm theo hướng dẫn trên trình duyệt hoặc thiết bị của bạn để sử dụng khóa bảo mật hoặc mật mã."
 replies: "Trả lời"
 renotes: "Đăng lại"
 loadReplies: "Hiển thị các trả lời"
+loadConversation: "Xem cuộc trò chuyện"
 pinnedList: "Các mục đã được ghim"
 keepScreenOn: "Giữ màn hình luôn bật"
 verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đường dẫn này"
+authentication: "Xác thực"
+authenticationRequiredToContinue: "Vui lòng xác thực để tiếp tục"
+dateAndTime: "Ngày và giờ"
+edited: "Đã chỉnh sửa"
+notificationRecieveConfig: "Cài đặt thông báo"
+mutualFollow: "Theo dõi lẫn nhau"
+followingOrFollower: "Đang theo dõi hoặc người theo dõi"
+externalServices: "Các dịch vụ bên ngoài"
 sourceCode: "Mã nguồn"
+feedback: "Phản hồi"
+feedbackUrl: "URL phản hồi"
+privacyPolicy: "Chính sách bảo mật"
+privacyPolicyUrl: "URL Chính sách bảo mật"
+tosAndPrivacyPolicy: "Điều khoản sử dụng và Chính sách bảo mật"
+avatarDecorations: "Trang trí ảnh đại diện"
+attach: "Mặc"
+detach: "Bỏ"
+detachAll: "Bỏ tất cả"
+angle: "Góc"
 flip: "Lật"
+showAvatarDecorations: "Hiển thị trang trí ảnh đại diện"
+releaseToRefresh: "Thả để làm mới"
+refreshing: "Đang làm mới"
+pullDownToRefresh: "Kéo xuống để làm mới"
+cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
 lastNDays: "{n} ngày trước"
 surrender: "Từ chối"
 _announcement:
@@ -1280,6 +1349,7 @@ _role:
     ltlAvailable: "Xem Timeline trong máy chủ này"
     canPublicNote: "Cho phép đăng bài công khai"
     canManageCustomEmojis: "Quản lý CustomEmoji"
+    canManageAvatarDecorations: "Quản lý trang trí ảnh đại diện"
     driveCapacity: "Dữ liệu Drive"
     pinMax: "Giới hạn ghim bài viết"
     antennaMax: "Giới hạn tạo ăng ten"
@@ -1508,7 +1578,6 @@ _2fa:
   registerTOTP: "Đăng ký ứng dụng xác thực"
   step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn."
   step2: "Sau đó, quét mã QR hiển thị trên màn hình này."
-  step2Click: "Quét mã QR trên ứng dụng xác thực (Authy, Google authenticator, v.v.)"
   step3Title: "Nhập mã xác thực"
   step3: "Nhập mã token do ứng dụng của bạn cung cấp để hoàn tất thiết lập."
   step4: "Kể từ bây giờ, những lần đăng nhập trong tương lai sẽ yêu cầu mã token đăng nhập đó."
@@ -1790,7 +1859,7 @@ _notification:
   youReceivedFollowRequest: "Bạn vừa có một yêu cầu theo dõi"
   yourFollowRequestAccepted: "Yêu cầu theo dõi của bạn đã được chấp nhận"
   pollEnded: "Cuộc bình chọn đã kết thúc"
-  unreadAntennaNote: "Ăng ten"
+  unreadAntennaNote: "Ăng ten {name}"
   emptyPushNotificationMessage: "Đã cập nhật thông báo đẩy"
   achievementEarned: "Hoàn thành Achievement"
   _types:
@@ -1852,6 +1921,6 @@ _webhookSettings:
 _moderationLogTypes:
   suspend: "Vô hiệu hóa"
   resetPassword: "Đặt lại mật khẩu"
+  createInvitation: "Tạo lời mời"
 _reversi:
   total: "Tổng cộng"
-
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 17ad6e715061..fb1ffc5a99d3 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -58,7 +58,7 @@ copyUserId: "复制用户 ID"
 copyNoteId: "复制帖子 ID"
 copyFileId: "复制文件ID"
 copyFolderId: "复制文件夹ID"
-copyProfileUrl: "复制配置文件URL"
+copyProfileUrl: "复制个人资料URL"
 searchUser: "搜索用户"
 reply: "回复"
 loadMore: "查看更多"
@@ -400,6 +400,7 @@ name: "名称"
 antennaSource: "接收来源"
 antennaKeywords: "包含关键字"
 antennaExcludeKeywords: "排除关键字"
+antennaExcludeBots: "排除机器人账户"
 antennaKeywordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。"
 notifyAntenna: "开启通知"
 withFileAntenna: "仅带有附件的帖子"
@@ -494,6 +495,7 @@ emojiStyle: "表情符号的样式"
 native: "原生"
 disableDrawer: "不显示抽屉菜单"
 showNoteActionsOnlyHover: "仅在悬停时显示帖子操作"
+showReactionsCount: "显示帖子的回应数"
 noHistory: "没有历史记录"
 signinHistory: "登录历史"
 enableAdvancedMfm: "启用扩展 MFM"
@@ -614,7 +616,7 @@ disablePlayer: "关闭播放器"
 expandTweet: "展开帖子"
 themeEditor: "主题编辑器"
 description: "描述"
-describeFile: "添加标题"
+describeFile: "添加描述"
 enterFileDescription: "输入标题"
 author: "作者"
 leaveConfirm: "存在未保存的更改。要放弃更改吗?"
@@ -1201,7 +1203,7 @@ code: "代码"
 reloadRequiredToApplySettings: "需要重新载入来使设置生效"
 remainingN: "剩余:{n}"
 overwriteContentConfirm: "将覆盖现有内容。确定吗?"
-seasonalScreenEffect: "应景的画面效果"
+seasonalScreenEffect: "符合当前季节的画面效果"
 decorate: "装饰"
 addMfmFunction: "添加装饰"
 enableQuickAddMfmFunction: "显示高级 MFM 选择器"
@@ -1223,6 +1225,16 @@ enableHorizontalSwipe: "滑动切换标签页"
 loading: "读取中"
 surrender: "取消"
 gameRetry: "重试"
+notUsePleaseLeaveBlank: "如不使用请留空"
+useTotp: "使用一次性代码"
+useBackupCode: "使用备用代码"
+launchApp: "启动应用"
+useNativeUIForVideoAudioPlayer: "使用浏览器的 UI 播放动画及音频"
+keepOriginalFilename: "保持原文件名"
+keepOriginalFilenameDescription: "若关闭此设置,上传文件时文件名将被替换为随机字符。"
+noDescription: "没有描述"
+alwaysConfirmFollow: "总是确认关注"
+inquiry: "联系我们"
 _bubbleGame:
   howToPlay: "游戏说明"
   hold: "抓住"
@@ -1233,6 +1245,7 @@ _bubbleGame:
     maxChain: "最高连击数"
     yen: "{yen} 日元"
     estimatedQty: "约 {qty} 个"
+    scoreSweets: "相当于 {onigiriQtyWithUnit} 饭团"
   _howToPlay:
     section1: "对准位置将Emoji投入盒子。"
     section2: "相同的Emoji相互接触合成后会得到新的Emoji,以此获得分数。"
@@ -1350,7 +1363,7 @@ _serverSettings:
 _accountMigration:
   moveFrom: "从别的账号迁移到此账户"
   moveFromSub: "为另一个账户建立别名"
-  moveFromLabel: "迁移前的账户"
+  moveFromLabel: "迁移前的账户 #{n}"
   moveFromDescription: "如果迁移时需要继承其他账户的关注者,你需要创建一个别名。此操作需要在迁移前完成!\n请像这样输入要迁移的账户:@username@server.example.com\n如果要删除,请将输入字段留空,并保存(不推荐)。"
   moveTo: "把这个账户迁移到新的账户"
   moveToLabel: "迁移后的账户"
@@ -1680,6 +1693,9 @@ _role:
     roleAssignedTo: "已分配给手动角色"
     isLocal: "是本地用户"
     isRemote: "是远程用户"
+    isBot: "机器人用户"
+    isSuspended: "停用的用户"
+    isExplorable: "启用“使账号可见”的用户"
     createdLessThan: "账户创建时间少于"
     createdMoreThan: "账户创建时间超过"
     followersLessThanOrEq: "关注者不多于"
@@ -1749,6 +1765,7 @@ _plugin:
   installWarn: "请不要安装不可信的插件。"
   manage: "管理插件..."
   viewSource: "查看源代码"
+  viewLog: "显示日志"
 _preferencesBackups:
   list: "已创建的备份"
   saveNew: "另存为"
@@ -1938,7 +1955,6 @@ _2fa:
   registerTOTP: "开始设置认证应用"
   step1: "首先,在您的设备上安装验证应用,例如 {a} 或 {b}。"
   step2: "然后,扫描屏幕上显示的二维码。"
-  step2Click: "通过点击二维码,您可以使用设备上安装的身份验证器应用程序或密钥环进行注册"
   step2Uri: "如果使用桌面应用程序的话,请输入下面的 URI"
   step3Title: "输入验证码"
   step3: "输入您的应用提供的动态口令以完成设置。"
@@ -1962,6 +1978,7 @@ _2fa:
   backupCodesDescription: "如果无法使用认证应用,可以使用以下的备用代码来访问账户。请务必将这些代码保存在安全的地方。每个代码仅可使用一次。"
   backupCodeUsedWarning: "已使用备用代码。如果无法使用认证应用,请尽快重新设定。"
   backupCodesExhaustedWarning: "已使用完所有的备用代码。如果无法使用认证应用,将无法再访问您的账户。请再次设定认证应用。"
+  moreDetailedGuideHere: "此处为详细指南"
 _permissions:
   "read:account": "查看账户信息"
   "write:account": "更改帐户信息"
@@ -2223,6 +2240,7 @@ _play:
   title: "标题"
   script: "脚本"
   summary: "描述"
+  visibilityDescription: "设置为不公开后资料将不再显示,但知道 URL 的人仍可继续访问。"
 _pages:
   newPage: "创建页面"
   editPage: "编辑页面"
@@ -2267,6 +2285,8 @@ _pages:
     section: "章节"
     image: "图片"
     button: "按钮"
+    dynamic: "动态区块"
+    dynamicDescription: "这个区块已经废弃。以后请使用{play}。"
     note: "嵌入的帖子"
     _note:
       id: "帖子 ID"
@@ -2296,6 +2316,7 @@ _notification:
   sendTestNotification: "发送测试通知"
   notificationWillBeDisplayedLikeThis: "通知将会这样表示"
   reactedBySomeUsers: "{n} 人回应了"
+  likedBySomeUsers: "{n}人赞了你的帖子"
   renotedBySomeUsers: "{n} 人转发了"
   followedBySomeUsers: "被 {n} 人关注"
   flushNotification: "重置通知历史"
@@ -2478,6 +2499,7 @@ _hemisphere:
 _reversi:
   reversi: "黑白棋"
   gameSettings: "对局设置"
+  chooseBoard: "选择棋盘"
   blackOrWhite: "先手/后手"
   blackIs: "{name}执黑(先手)"
   rules: "规则"
@@ -2504,6 +2526,8 @@ _reversi:
   allGames: "所有对局"
   ended: "结束"
   playing: "对局中"
+  isLlotheo: "落子少的一方获胜(又名奥赛罗)"
+  loopedMap: "循环棋盘"
   canPutEverywhere: "无限制放置模式"
   timeLimitForEachTurn: "1回合的时间限制"
   freeMatch: "自由匹配"
@@ -2519,4 +2543,21 @@ _reversi:
 _offlineScreen:
   title: "离线——无法连接到服务器"
   header: "无法连接到服务器"
-
+_urlPreviewSetting:
+  title: "设置 URL 预览"
+  enable: "启用 URL 预览"
+  timeout: "超时阈值(ms)"
+  timeoutDescription: "如果获取预览所用时间超过这个值,则不生成预览。"
+  maximumContentLength: "Content-Length 的最大值(byte)"
+  maximumContentLengthDescription: "如果 Content-Length 超过这个值,则不生成预览。"
+  requireContentLength: "仅在能取得 Content-Length 时生成预览"
+  requireContentLengthDescription: "如果目标服务器不返回 Content-Length,则不生成预览。"
+  userAgent: "User-Agent"
+  userAgentDescription: "设定获取预览时使用的 User-Agent。留空时将使用默认的 User-Agent。"
+  summaryProxy: "用来生成预览的代理的 endpoint。"
+  summaryProxyDescription: "不使用 Misskey 本体,而是通过 Summaly Proxy 生成预览。"
+  summaryProxyDescription2: "下面的参数将作为查询字符串发送至代理。代理侧如果不支持此设置,则忽略设定值。"
+_mediaControls:
+  pip: "画中画"
+  playbackRate: "播放速度"
+  loop: "循环播放"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 5cdecc10ac97..8cde13052f35 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -205,7 +205,7 @@ silenceThisInstance: "禁言此伺服器"
 operations: "操作"
 software: "軟體"
 version: "版本"
-metadata: "元資料"
+metadata: "詮釋資料"
 withNFiles: "{n} 個檔案"
 monitor: "監視器"
 jobQueue: "佇列"
@@ -400,6 +400,7 @@ name: "名稱"
 antennaSource: "接收來源"
 antennaKeywords: "包含關鍵字"
 antennaExcludeKeywords: "排除關鍵字"
+antennaExcludeBots: "排除機器人帳戶"
 antennaKeywordsDescription: "空格代表「以及」(AND),換行代表「或者」(OR)"
 notifyAntenna: "通知有新貼文"
 withFileAntenna: "僅帶有附件的貼文"
@@ -494,6 +495,7 @@ emojiStyle: "表情符號的風格"
 native: "原生"
 disableDrawer: "不顯示下拉式選單"
 showNoteActionsOnlyHover: "僅在游標停留時顯示貼文的操作選項"
+showReactionsCount: "顯示貼文的反應數目"
 noHistory: "沒有歷史紀錄"
 signinHistory: "登入歷史"
 enableAdvancedMfm: "啟用進階 MFM"
@@ -750,7 +752,7 @@ experimentalFeatures: "實驗中的功能"
 experimental: "實驗性"
 thisIsExperimentalFeature: "這是實驗性的功能。可能會有變更規格和不能正常動作的可能性。"
 developer: "開發者"
-makeExplorable: "使自己的帳戶能夠在「探索」頁面中顯示"
+makeExplorable: "使自己的帳戶更容易被找到"
 makeExplorableDescription: "如果關閉,帳戶將不會被顯示在「探索」頁面中。"
 showGapBetweenNotesInTimeline: "分開顯示時間軸上的貼文"
 duplicate: "複製"
@@ -1223,6 +1225,16 @@ enableHorizontalSwipe: "滑動切換時間軸"
 loading: "載入中"
 surrender: "退出"
 gameRetry: "再試一次"
+notUsePleaseLeaveBlank: "如不使用,請留空"
+useTotp: "使用一次性密碼"
+useBackupCode: "使用備用驗證碼"
+launchApp: "啟動 App"
+useNativeUIForVideoAudioPlayer: "使用瀏覽器的 UI 播放影片與音訊"
+keepOriginalFilename: "保留原始檔名"
+keepOriginalFilenameDescription: "如果關閉此設置,上傳時檔案名稱會自動替換為隨機字串。"
+noDescription: "沒有說明文字"
+alwaysConfirmFollow: "點擊追隨時總是顯示確認訊息"
+inquiry: "聯絡我們"
 _bubbleGame:
   howToPlay: "玩法說明"
   hold: "保留"
@@ -1351,7 +1363,7 @@ _serverSettings:
 _accountMigration:
   moveFrom: "從其他帳戶遷移到這個帳戶"
   moveFromSub: "為另一個帳戶建立別名"
-  moveFromLabel: "要遷移過來的帳戶:"
+  moveFromLabel: "要遷移過來的帳戶 #{n}"
   moveFromDescription: "如果你想把追隨者從別的帳戶遷移過來,必須先在這裡建立別名。請務必在執行遷移之前建立別名!請像這樣輸入要遷移的帳戶:@person@instance.com"
   moveTo: "將這個帳戶遷移至新的帳戶"
   moveToLabel: "要遷移到的帳戶:"
@@ -1682,6 +1694,11 @@ _role:
     roleAssignedTo: "手動指派角色完成"
     isLocal: "本地使用者"
     isRemote: "遠端使用者"
+    isCat: "使用者是貓"
+    isBot: "使用者是機器人"
+    isSuspended: "被停權的使用者"
+    isLocked: "上鎖的使用者"
+    isExplorable: "開啟了「使您的帳戶更容易被找到」功能的使用者"
     createdLessThan: "帳戶加入時間不超過"
     createdMoreThan: "帳戶加入時間已超過"
     followersLessThanOrEq: "追隨者人數在~以下"
@@ -1751,6 +1768,7 @@ _plugin:
   installWarn: "請不要安裝來源不明的外掛。"
   manage: "管理外掛"
   viewSource: "檢視原始碼"
+  viewLog: "顯示記錄 "
 _preferencesBackups:
   list: "已備份的設定檔"
   saveNew: "另存新檔"
@@ -1940,7 +1958,6 @@ _2fa:
   registerTOTP: "開始設定驗證應用程式"
   step1: "首先,在您的裝置上安裝驗證程式,例如 {a} 或 {b}。"
   step2: "然後,掃描螢幕上的 QR 碼。"
-  step2Click: "您可以點擊 QR 碼,以使用裝置上的驗證應用程式或金鑰環註冊。"
   step2Uri: "使用桌面版應用程式時,請輸入以下的 URI"
   step3Title: "輸入驗證碼"
   step3: "輸入應用程式所提供的權杖以完成設定。"
@@ -1964,6 +1981,7 @@ _2fa:
   backupCodesDescription: "如果驗證應用程式不能用了,可以使用以下的備用驗證碼存取您的帳戶。請務必妥善保管這個驗證碼。每個驗證碼只能使用一次。"
   backupCodeUsedWarning: "已使用備用驗證碼。如果無法使用驗證應用程式,請盡快重新設定。"
   backupCodesExhaustedWarning: "已使用所有備用驗證碼。如果無法使用驗證應用程式,則將無法再存取您的帳戶。請重新設定您的驗證應用程式。"
+  moreDetailedGuideHere: "請點擊此處查看詳細說明。"
 _permissions:
   "read:account": "查看我的帳戶資訊"
   "write:account": "更改我的帳戶資訊"
@@ -2007,7 +2025,7 @@ _permissions:
   "read:admin:index-stats": "查看資料庫索引的相關資訊"
   "read:admin:table-stats": "查看資料庫表格的相關資訊"
   "read:admin:user-ips": "查看使用者的 IP 位址"
-  "read:admin:meta": "查看實例的元資料"
+  "read:admin:meta": "查看實例的詮釋資料"
   "write:admin:reset-password": "重設使用者的密碼"
   "write:admin:resolve-abuse-user-report": "解決來自使用者的檢舉"
   "write:admin:send-email": "發送郵件"
@@ -2019,7 +2037,7 @@ _permissions:
   "write:admin:unset-user-avatar": "刪除使用者的頭像"
   "write:admin:unset-user-banner": "刪除使用者的橫幅"
   "write:admin:unsuspend-user": "解除凍結使用者"
-  "write:admin:meta": "編輯實例的元資料"
+  "write:admin:meta": "編輯實例的詮釋資料"
   "write:admin:user-note": "編輯審查筆記"
   "write:admin:roles": "編輯角色"
   "read:admin:roles": "查看角色"
@@ -2188,7 +2206,7 @@ _charts:
   notesIncDec: "貼文増減"
   localNotesIncDec: "本地貼文増減"
   remoteNotesIncDec: "遠端貼文數目增减"
-  notesTotal: "貼文合共"
+  notesTotal: "貼文總數"
   filesIncDec: "檔案增減"
   filesTotal: "檔案總數"
   storageUsageIncDec: "儲存空間增減"
@@ -2225,6 +2243,7 @@ _play:
   title: "標題"
   script: "腳本"
   summary: "描述"
+  visibilityDescription: "如果您將其設為私密,它將不再顯示在您的個人資料中,但知道該 URL 的人仍然可以存取它。"
 _pages:
   newPage: "建立頁面"
   editPage: "編輯頁面"
@@ -2269,6 +2288,8 @@ _pages:
     section: "區段"
     image: "圖片"
     button: "按鈕"
+    dynamic: "動態方塊"
+    dynamicDescription: "這個方塊已經廢止,現在開始請使用 {play}。"
     note: "嵌式貼文"
     _note:
       id: "貼文ID"
@@ -2298,6 +2319,7 @@ _notification:
   sendTestNotification: "發送測試通知"
   notificationWillBeDisplayedLikeThis: "通知會以這樣的方式顯示"
   reactedBySomeUsers: "{n}人做出了反應"
+  likedBySomeUsers: "{n} 人按了讚"
   renotedBySomeUsers: "{n}人做了轉發"
   followedBySomeUsers: "被{n}人追隨了"
   flushNotification: "重置通知歷史紀錄"
@@ -2524,4 +2546,21 @@ _reversi:
 _offlineScreen:
   title: "離線-無法連接伺服器"
   header: "無法連接伺服器"
-
+_urlPreviewSetting:
+  title: "URL 預覽設定"
+  enable: "啟用 URL 預覽"
+  timeout: "取得預覽的逾時時間 (ms)"
+  timeoutDescription: "若取得預覽所需的時間超過這個值,則不會產生預覽。"
+  maximumContentLength: "Content-Length 的最大値 (byte)"
+  maximumContentLengthDescription: "若 Content-Length 超過這個值,則不會產生預覽。"
+  requireContentLength: "僅在能夠取得 Content-Length 時,才產生預覽。"
+  requireContentLengthDescription: "若對方的伺服器未回傳 Content -Length,則不會產生預覽。"
+  userAgent: "User-Agent"
+  userAgentDescription: "設定獲取預覽時使用的 User-Agent 。如果留空,將使用預設的 User-Agent 。"
+  summaryProxy: "產生預覽的代理端點"
+  summaryProxyDescription: "使用摘要代理程式而不是 Misskey 本身產生預覽。"
+  summaryProxyDescription2: "以下參數會作為查詢字串連結到代理。如果代理端不支援,這些設定將被忽略。"
+_mediaControls:
+  pip: "畫中畫"
+  playbackRate: "播放速度"
+  loop: "循環播放"

From 7bde630820a9270e47f14fcfe5752c98d4716634 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 21 May 2024 11:19:33 +0900
Subject: [PATCH 208/266] =?UTF-8?q?`/tags`=20=E3=81=A8=20`/user-tags`=20?=
 =?UTF-8?q?=E3=81=8C=E6=A4=9C=E7=B4=A2=E3=82=A8=E3=83=B3=E3=82=B8=E3=83=B3?=
 =?UTF-8?q?=E3=81=AB=E3=82=A4=E3=83=B3=E3=83=87=E3=83=83=E3=82=AF=E3=82=B9?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#13847)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore(backend): noindex for tag search pages

* docs(changelog): `/tags` と `/user-tags` が検索エンジンにインデックスされないように

* chore: base.pug内でフラグでコントロールするように
---
 CHANGELOG.md                                      |  1 +
 .../backend/src/server/web/ClientServerService.ts | 15 ++++++++++++++-
 packages/backend/src/server/web/views/base.pug    |  3 +++
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d1b0a010a3f..ecacefe84eba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -85,6 +85,7 @@
 - Fix: FTTが有効かつsinceIdのみを指定した場合に帰って来るレスポンスが逆順である問題を修正
 - Fix: `/i/notifications`に `includeTypes`か`excludeTypes`を指定しているとき、通知が存在するのに空配列を返すことがある問題を修正
 - Fix: 複数idを指定する`users/show`が関係ないユーザを返すことがある問題を修正
+- Fix: `/tags` と `/user-tags` が検索エンジンにインデックスされないように
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index 1394616752bb..f35ec8ba31ca 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -438,7 +438,7 @@ export class ClientServerService {
 
 		//#endregion
 
-		const renderBase = async (reply: FastifyReply) => {
+		const renderBase = async (reply: FastifyReply, data: { [key: string]: any } = {}) => {
 			const meta = await this.metaService.fetch();
 			reply.header('Cache-Control', 'public, max-age=30');
 			return await reply.view('base', {
@@ -447,6 +447,7 @@ export class ClientServerService {
 				title: meta.name ?? 'Misskey',
 				desc: meta.description,
 				...await this.generateCommonPugData(meta),
+				...data,
 			});
 		};
 
@@ -744,6 +745,18 @@ export class ClientServerService {
 		});
 		//#endregion
 
+		//region noindex pages
+		// Tags
+		fastify.get<{ Params: { clip: string; } }>('/tags/:tag', async (request, reply) => {
+			return await renderBase(reply, { noindex: true });
+		});
+
+		// User with Tags
+		fastify.get<{ Params: { clip: string; } }>('/user-tags/:tag', async (request, reply) => {
+			return await renderBase(reply, { noindex: true });
+		});
+		//endregion
+
 		fastify.get('/_info_card_', async (request, reply) => {
 			const meta = await this.metaService.fetch(true);
 
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 1d9146e22a37..ec1325e4e97f 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -50,6 +50,9 @@ html
 			block title
 				= title || 'Misskey'
 
+		if noindex
+			meta(name='robots' content='noindex')
+
 		block desc
 			meta(name='description' content= desc || '✨🌎✨ A interplanetary communication platform ✨🚀✨')
 

From 37f2952af9ca417549ce8024bd3045a409347138 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 21 May 2024 13:33:43 +0900
Subject: [PATCH 209/266] Update about-misskey.vue

---
 packages/frontend/src/pages/about-misskey.vue | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 92accd0117c5..a83510d7f7e8 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -231,6 +231,12 @@ const patronsWithIcon = [{
 }, {
 	name: 'Takeno',
 	icon: 'https://assets.misskey-hub.net/patrons/6fba81536aea48fe94a30909c502dfa1.jpg',
+}, {
+	name: 'くびすじ',
+	icon: 'https://assets.misskey-hub.net/patrons/aa5789850b2149aeb5b89ebe2e9083db.jpg',
+}, {
+	name: '古道京紗@ぷらいべったー',
+	icon: 'https://assets.misskey-hub.net/patrons/18346d0519704963a4beabe6abc170af.jpg',
 }];
 
 const patrons = [

From 3340631d434c48ecbc519b26f0e7888156f3c835 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 21 May 2024 13:35:32 +0900
Subject: [PATCH 210/266] Update about-misskey.vue

---
 packages/frontend/src/pages/about-misskey.vue | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index a83510d7f7e8..b55ae220d8ef 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -237,6 +237,9 @@ const patronsWithIcon = [{
 }, {
 	name: '古道京紗@ぷらいべったー',
 	icon: 'https://assets.misskey-hub.net/patrons/18346d0519704963a4beabe6abc170af.jpg',
+}, {
+	name: '越貝鯛丸',
+	icon: 'https://assets.misskey-hub.net/patrons/86c7374de37849b882d8ebbc833dc968.jpg',
 }];
 
 const patrons = [

From 126383dca2a80e35c2606dd5d3dcd62dad5869cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 21 May 2024 15:07:37 +0900
Subject: [PATCH 211/266] =?UTF-8?q?deps:=20AiScript=20VSCode=E3=81=AE?=
 =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92=E4=B8=8A?=
 =?UTF-8?q?=E3=81=92=E3=82=8B=20(#13851)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/package.json |  2 +-
 pnpm-lock.yaml                 | 50 ++++++++++++----------------------
 2 files changed, 18 insertions(+), 34 deletions(-)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index a482373200ef..530d077a4e4c 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -29,7 +29,7 @@
 		"@twemoji/parser": "15.1.1",
 		"@vitejs/plugin-vue": "5.0.4",
 		"@vue/compiler-sfc": "3.4.26",
-		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.4",
+		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.9",
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
 		"buraha": "0.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9ddc1f11604d..f77aefc067ee 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -722,8 +722,8 @@ importers:
         specifier: 3.4.26
         version: 3.4.26
       aiscript-vscode:
-        specifier: github:aiscript-dev/aiscript-vscode#v0.1.4
-        version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424
+        specifier: github:aiscript-dev/aiscript-vscode#v0.1.9
+        version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02
       astring:
         specifier: 1.8.6
         version: 1.8.6
@@ -919,7 +919,7 @@ importers:
         version: 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))
       '@testing-library/vue':
         specifier: 8.0.3
-        version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
+        version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -4819,9 +4819,6 @@ packages:
   '@vue/compiler-sfc@3.4.26':
     resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==}
 
-  '@vue/compiler-ssr@3.4.25':
-    resolution: {integrity: sha512-H2ohvM/Pf6LelGxDBnfbbXFPyM4NE3hrw0e/EpwuSiYu8c819wx+SVGdJ65p/sFrYDd6OnSDxN1MB2mN07hRSQ==}
-
   '@vue/compiler-ssr@3.4.26':
     resolution: {integrity: sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==}
 
@@ -4845,11 +4842,6 @@ packages:
   '@vue/runtime-dom@3.4.26':
     resolution: {integrity: sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==}
 
-  '@vue/server-renderer@3.4.25':
-    resolution: {integrity: sha512-8VTwq0Zcu3K4dWV0jOwIVINESE/gha3ifYCOKEhxOj6MEl5K5y8J8clQncTcDhKF+9U765nRw4UdUEXvrGhyVQ==}
-    peerDependencies:
-      vue: 3.4.25
-
   '@vue/server-renderer@3.4.26':
     resolution: {integrity: sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==}
     peerDependencies:
@@ -4959,9 +4951,9 @@ packages:
     resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==}
     engines: {node: '>=18'}
 
-  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424:
-    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424}
-    version: 0.1.4
+  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02:
+    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02}
+    version: 0.1.9
     engines: {vscode: ^1.83.0}
 
   ajv-draft-04@1.0.0:
@@ -10875,6 +10867,9 @@ packages:
   vue-component-type-helpers@2.0.16:
     resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
 
+  vue-component-type-helpers@2.0.19:
+    resolution: {integrity: sha512-cN3f1aTxxKo4lzNeQAkVopswuImUrb5Iurll9Gaw5cqpnbTAxtEMM1mgi6ou4X79OCyqYv1U1mzBHJkzmiK82w==}
+
   vue-demi@0.14.7:
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
     engines: {node: '>=12'}
@@ -14956,7 +14951,7 @@ snapshots:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.26(typescript@5.4.5)
-      vue-component-type-helpers: 2.0.16
+      vue-component-type-helpers: 2.0.19
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -15217,11 +15212,11 @@ snapshots:
     dependencies:
       '@testing-library/dom': 9.3.4
 
-  '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
+  '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
     dependencies:
       '@babel/runtime': 7.23.4
       '@testing-library/dom': 9.3.3
-      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
+      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
       vue: 3.4.26(typescript@5.4.5)
     optionalDependencies:
       '@vue/compiler-sfc': 3.4.26
@@ -16012,12 +16007,6 @@ snapshots:
       postcss: 8.4.38
       source-map-js: 1.2.0
 
-  '@vue/compiler-ssr@3.4.25':
-    dependencies:
-      '@vue/compiler-dom': 3.4.25
-      '@vue/shared': 3.4.25
-    optional: true
-
   '@vue/compiler-ssr@3.4.26':
     dependencies:
       '@vue/compiler-dom': 3.4.26
@@ -16052,13 +16041,6 @@ snapshots:
       '@vue/shared': 3.4.26
       csstype: 3.1.3
 
-  '@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5))':
-    dependencies:
-      '@vue/compiler-ssr': 3.4.25
-      '@vue/shared': 3.4.25
-      vue: 3.4.26(typescript@5.4.5)
-    optional: true
-
   '@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5))':
     dependencies:
       '@vue/compiler-ssr': 3.4.26
@@ -16071,13 +16053,13 @@ snapshots:
 
   '@vue/shared@3.4.26': {}
 
-  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
+  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
     dependencies:
       js-beautify: 1.14.9
       vue: 3.4.26(typescript@5.4.5)
       vue-component-type-helpers: 1.8.4
     optionalDependencies:
-      '@vue/server-renderer': 3.4.25(vue@3.4.26(typescript@5.4.5))
+      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.4.5))
 
   '@webgpu/types@0.1.30': {}
 
@@ -16159,7 +16141,7 @@ snapshots:
       clean-stack: 5.2.0
       indent-string: 5.0.0
 
-  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424:
+  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02:
     dependencies:
       '@aiscript-dev/aiscript-languageserver': https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz
       vscode-languageclient: 9.0.1
@@ -23223,6 +23205,8 @@ snapshots:
 
   vue-component-type-helpers@2.0.16: {}
 
+  vue-component-type-helpers@2.0.19: {}
+
   vue-demi@0.14.7(vue@3.4.26(typescript@5.4.5)):
     dependencies:
       vue: 3.4.26(typescript@5.4.5)

From 6a637db36b8a0c32774b5da5e40236c5f14a59e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 21 May 2024 17:23:20 +0900
Subject: [PATCH 212/266] =?UTF-8?q?enhance(frontend):=20=E9=80=9A=E5=B8=B8?=
 =?UTF-8?q?=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E3=81=A7=E3=82=82=E3=80=81?=
 =?UTF-8?q?=E3=81=8A=E6=B0=97=E3=81=AB=E5=85=A5=E3=82=8A=E3=81=AB=E7=99=BB?=
 =?UTF-8?q?=E9=8C=B2=E3=81=97=E3=81=9F=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D?=
 =?UTF-8?q?=E3=83=AB=E3=81=AB=E3=83=AA=E3=83=8E=E3=83=BC=E3=83=88=E3=81=A7?=
 =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13855)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): チャンネルにリノートできるように

* Update Changelog
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            | 12 ++++++
 locales/ja-JP.yml                             |  3 ++
 .../frontend/src/scripts/get-note-menu.ts     | 38 +++++++++++++++++++
 4 files changed, 54 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ecacefe84eba..9bdc1d135a68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@
 - Enhance: 通報のコメント内のリンクをクリックした際、ウィンドウで開くように
 - Enhance: `Ui:C:postForm` および `Ui:C:postFormButton` に `localOnly` と `visibility` を設定できるように
 - Enhance: AiScriptを0.18.0にバージョンアップ
+- Enhance: 通常のノートでも、お気に入りに登録したチャンネルにリノートできるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 779a5d2c3fbe..70741b6460cd 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -448,6 +448,10 @@ export interface Locale extends ILocale {
      * リノートしました。
      */
     "renoted": string;
+    /**
+     * {name} にリノートしました。
+     */
+    "renotedToX": ParameterizedString<"name">;
     /**
      * この投稿はリノートできません。
      */
@@ -468,6 +472,14 @@ export interface Locale extends ILocale {
      * チャンネル内引用
      */
     "inChannelQuote": string;
+    /**
+     * チャンネルにリノート
+     */
+    "renoteToChannel": string;
+    /**
+     * 他のチャンネルにリノート
+     */
+    "renoteToOtherChannel": string;
     /**
      * ピン留めされたノート
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 8f17215802c3..b5808f754168 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -108,11 +108,14 @@ enterEmoji: "絵文字を入力"
 renote: "リノート"
 unrenote: "リノート解除"
 renoted: "リノートしました。"
+renotedToX: "{name} にリノートしました。"
 cantRenote: "この投稿はリノートできません。"
 cantReRenote: "リノートをリノートすることはできません。"
 quote: "引用"
 inChannelRenote: "チャンネル内リノート"
 inChannelQuote: "チャンネル内引用"
+renoteToChannel: "チャンネルにリノート"
+renoteToOtherChannel: "他のチャンネルにリノート"
 pinnedNote: "ピン留めされたノート"
 pinned: "ピン留め"
 you: "あなた"
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 2cd21c1edc2e..e7c9a848e0f4 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -518,6 +518,7 @@ export function getRenoteMenu(props: {
 
 	const channelRenoteItems: MenuItem[] = [];
 	const normalRenoteItems: MenuItem[] = [];
+	const normalExternalChannelRenoteItems: MenuItem[] = [];
 
 	if (appearNote.channel) {
 		channelRenoteItems.push(...[{
@@ -596,12 +597,49 @@ export function getRenoteMenu(props: {
 				});
 			},
 		}]);
+
+		normalExternalChannelRenoteItems.push({
+			type: 'parent',
+			icon: 'ti ti-repeat',
+			text: appearNote.channel ? i18n.ts.renoteToOtherChannel : i18n.ts.renoteToChannel,
+			children: async () => {
+				const channels = await misskeyApi('channels/my-favorites', {
+					limit: 30,
+				});
+				return channels.filter((channel) => {
+					if (!appearNote.channelId) return true;
+					return channel.id !== appearNote.channelId;
+				}).map((channel) => ({
+					text: channel.name,
+					action: () => {
+						const el = props.renoteButton.value;
+						if (el) {
+							const rect = el.getBoundingClientRect();
+							const x = rect.left + (el.offsetWidth / 2);
+							const y = rect.top + (el.offsetHeight / 2);
+							os.popup(MkRippleEffect, { x, y }, {}, 'end');
+						}
+
+						if (!props.mock) {
+							misskeyApi('notes/create', {
+								renoteId: appearNote.id,
+								channelId: channel.id,
+							}).then(() => {
+								os.toast(i18n.tsx.renotedToX({ name: channel.name }));
+							});
+						}
+					},
+				}));
+			},
+		});
 	}
 
 	const renoteItems = [
 		...normalRenoteItems,
 		...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] as MenuItem[] : [],
 		...channelRenoteItems,
+		...(normalExternalChannelRenoteItems.length > 0 && (normalRenoteItems.length > 0 || channelRenoteItems.length > 0)) ? [{ type: 'divider' }] as MenuItem[] : [],
+		...normalExternalChannelRenoteItems,
 	];
 
 	return {

From 20c0bd9ddb86cd194be52d3f7c297ad5fd148a12 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 21 May 2024 17:29:02 +0900
Subject: [PATCH 213/266] =?UTF-8?q?happy-dom=E3=81=AB=E3=83=A1=E3=83=A2?=
 =?UTF-8?q?=E3=83=AA=E3=83=AA=E3=83=BC=E3=82=AF=E3=81=8C=E3=81=82=E3=82=8A?=
 =?UTF-8?q?=E3=81=9D=E3=81=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/package.json  |  2 +-
 packages/frontend/package.json |  2 +-
 pnpm-lock.yaml                 | 60 ++++++++++++++++++++--------------
 3 files changed, 37 insertions(+), 27 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 23b3bfdb8bfe..8e29252d759c 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -117,7 +117,7 @@
 		"fluent-ffmpeg": "2.1.2",
 		"form-data": "4.0.0",
 		"got": "14.2.1",
-		"happy-dom": "14.7.1",
+		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
 		"htmlescape": "1.1.1",
 		"http-link-header": "1.1.3",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 530d077a4e4c..56b824c0c5c0 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -119,7 +119,7 @@
 		"eslint-plugin-import": "2.29.1",
 		"eslint-plugin-vue": "9.25.0",
 		"fast-glob": "3.3.2",
-		"happy-dom": "14.7.1",
+		"happy-dom": "10.0.3",
 		"intersection-observer": "0.12.2",
 		"micromatch": "4.0.5",
 		"msw": "2.2.14",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f77aefc067ee..465539b83481 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -234,8 +234,8 @@ importers:
         specifier: 14.2.1
         version: 14.2.1
       happy-dom:
-        specifier: 14.7.1
-        version: 14.7.1
+        specifier: 10.0.3
+        version: 10.0.3
       hpagent:
         specifier: 1.2.0
         version: 1.2.0
@@ -871,7 +871,7 @@ importers:
         version: 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/addon-interactions':
         specifier: 8.0.9
-        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@storybook/addon-links':
         specifier: 8.0.9
         version: 8.0.9(react@18.3.1)
@@ -904,7 +904,7 @@ importers:
         version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
       '@storybook/test':
         specifier: 8.0.9
-        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@storybook/theming':
         specifier: 8.0.9
         version: 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -961,7 +961,7 @@ importers:
         version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       '@vitest/coverage-v8':
         specifier: 0.34.6
-        version: 0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        version: 0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@vue/runtime-core':
         specifier: 3.4.26
         version: 3.4.26
@@ -987,8 +987,8 @@ importers:
         specifier: 3.3.2
         version: 3.3.2
       happy-dom:
-        specifier: 14.7.1
-        version: 14.7.1
+        specifier: 10.0.3
+        version: 10.0.3
       intersection-observer:
         specifier: 0.12.2
         version: 0.12.2
@@ -1027,10 +1027,10 @@ importers:
         version: 1.0.3
       vitest:
         specifier: 0.34.6
-        version: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+        version: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
       vitest-fetch-mock:
         specifier: 0.2.2
-        version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       vue-component-type-helpers:
         specifier: 2.0.16
         version: 2.0.16
@@ -6898,9 +6898,8 @@ packages:
     engines: {node: '>=0.4.7'}
     hasBin: true
 
-  happy-dom@14.7.1:
-    resolution: {integrity: sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==}
-    engines: {node: '>=16.0.0'}
+  happy-dom@10.0.3:
+    resolution: {integrity: sha512-WkCP+Z5fX6U5PY+yHP3ElV5D9PoxRAHRWPFq3pG9rg/6Hjf5ak7dozAgSCywsTRUq2qfa8vV8OQvUy5pRXy8EQ==}
 
   har-schema@2.0.0:
     resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
@@ -10967,6 +10966,10 @@ packages:
   webpack-virtual-modules@0.5.0:
     resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
 
+  whatwg-encoding@2.0.0:
+    resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
+    engines: {node: '>=12'}
+
   whatwg-encoding@3.1.1:
     resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
     engines: {node: '>=18'}
@@ -14375,11 +14378,11 @@ snapshots:
     dependencies:
       '@storybook/global': 5.0.0
 
-  '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@storybook/global': 5.0.0
       '@storybook/instrumenter': 8.0.9
-      '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+      '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@storybook/types': 8.0.9
       polished: 4.2.2
       ts-dedent: 2.2.0
@@ -14883,14 +14886,14 @@ snapshots:
       - encoding
       - supports-color
 
-  '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@storybook/client-logger': 8.0.9
       '@storybook/core-events': 8.0.9
       '@storybook/instrumenter': 8.0.9
       '@storybook/preview-api': 8.0.9
       '@testing-library/dom': 9.3.4
-      '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+      '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
       '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4)
       '@vitest/expect': 1.3.1
       '@vitest/spy': 1.6.0
@@ -15192,7 +15195,7 @@ snapshots:
       lz-string: 1.5.0
       pretty-format: 27.5.1
 
-  '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@adobe/css-tools': 4.3.3
       '@babel/runtime': 7.23.4
@@ -15206,7 +15209,7 @@ snapshots:
       '@jest/globals': 29.7.0
       '@types/jest': 29.5.12
       jest: 29.7.0(@types/node@20.12.7)
-      vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+      vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
 
   '@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4)':
     dependencies:
@@ -15870,7 +15873,7 @@ snapshots:
       vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
       vue: 3.4.26(typescript@5.4.5)
 
-  '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@bcoe/v8-coverage': 0.2.3
@@ -15883,7 +15886,7 @@ snapshots:
       std-env: 3.7.0
       test-exclude: 6.0.0
       v8-to-istanbul: 9.2.0
-      vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+      vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
     transitivePeerDependencies:
       - supports-color
 
@@ -18798,10 +18801,13 @@ snapshots:
     optionalDependencies:
       uglify-js: 3.17.4
 
-  happy-dom@14.7.1:
+  happy-dom@10.0.3:
     dependencies:
+      css.escape: 1.5.1
       entities: 4.5.0
+      iconv-lite: 0.6.3
       webidl-conversions: 7.0.0
+      whatwg-encoding: 2.0.0
       whatwg-mimetype: 3.0.0
 
   har-schema@2.0.0: {}
@@ -23124,14 +23130,14 @@ snapshots:
       sass: 1.76.0
       terser: 5.30.3
 
-  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)):
+  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)):
     dependencies:
       cross-fetch: 3.1.6(encoding@0.1.13)
-      vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+      vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
     transitivePeerDependencies:
       - encoding
 
-  vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3):
+  vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3):
     dependencies:
       '@types/chai': 4.3.11
       '@types/chai-subset': 1.3.5
@@ -23158,7 +23164,7 @@ snapshots:
       vite-node: 0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
       why-is-node-running: 2.2.2
     optionalDependencies:
-      happy-dom: 14.7.1
+      happy-dom: 10.0.3
       jsdom: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - less
@@ -23324,6 +23330,10 @@ snapshots:
 
   webpack-virtual-modules@0.5.0: {}
 
+  whatwg-encoding@2.0.0:
+    dependencies:
+      iconv-lite: 0.6.3
+
   whatwg-encoding@3.1.1:
     dependencies:
       iconv-lite: 0.6.3

From c69de6b48cad0fb9f4af7a9b88f24aef9c3aeb08 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Tue, 21 May 2024 20:43:00 +0900
Subject: [PATCH 214/266] fix(l10n): fix wrong description of server silence
 (#13857)

---
 locales/ja-JP.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b5808f754168..626e3f30f890 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -225,7 +225,7 @@ clearCachedFilesConfirm: "キャッシュされたリモートファイルをす
 blockedInstances: "ブロックしたサーバー"
 blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。"
 silencedInstances: "サイレンスしたサーバー"
-silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたインスタンスには影響しません。"
+silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。"
 muteAndBlock: "ミュートとブロック"
 mutedUsers: "ミュートしたユーザー"
 blockedUsers: "ブロックしたユーザー"

From ed432d06d76c2cfc3d46b2d8f7931ec3fb0235d0 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 22 May 2024 06:40:05 +0900
Subject: [PATCH 215/266] New Crowdin updates (#13850)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Simplified)
---
 locales/de-DE.yml | 2 +-
 locales/pl-PL.yml | 2 +-
 locales/zh-CN.yml | 3 +++
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 3b3993825569..3e1c40512e77 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -654,7 +654,7 @@ smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest."
 testEmail: "Emailversand testen"
 wordMute: "Wortstummschaltung"
 regexpError: "Fehler in einem regulären Ausdruck"
-regexpErrorDescription: "Im regulären Ausdruck deiner {tab}en Wortstummschaltungen ist ein Fehler aufgetreten:"
+regexpErrorDescription: "Im regulären Ausdruck deiner in Zeile {line} von {tab}en Wortstummschaltungen ist ein Fehler aufgetreten:"
 instanceMute: "Instanzstummschaltungen"
 userSaysSomething: "{name} hat etwas gesagt"
 makeActive: "Aktivieren"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index b7eb4683ebf7..2183aa3022bc 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -933,7 +933,7 @@ check: "Zweryfikuj"
 driveCapOverrideLabel: "Zmień limit pojemności dysku użytkownika"
 requireAdminForView: "Aby to zobaczyć, musisz być administratorem"
 isSystemAccount: "To jest konto stworzone i zarządzane przez system"
-typeToConfirm: "Wielki chuj "
+typeToConfirm: "Wprowadź {x}, aby potwierdzić"
 deleteAccount: "Usuń konto"
 document: "Dokumentacja"
 numberOfPageCache: "Ilość stron w cache"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index fb1ffc5a99d3..17164dfe988c 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -108,11 +108,14 @@ enterEmoji: "输入表情符号"
 renote: "转发"
 unrenote: "取消转发"
 renoted: "已转发。"
+renotedToX: "转帖给 {name}"
 cantRenote: "该帖无法转发。"
 cantReRenote: "转发无法被再次转发。"
 quote: "引用"
 inChannelRenote: "在频道内转发"
 inChannelQuote: "在频道内引用"
+renoteToChannel: "转帖至频道"
+renoteToOtherChannel: "转帖至其它频道"
 pinnedNote: "已置顶的帖子"
 pinned: "置顶"
 you: "您"

From aafa669cf59778ed695632b45af0408cc9c3f038 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 23 May 2024 13:15:22 +0900
Subject: [PATCH 216/266] =?UTF-8?q?feat(frontend):=20=E9=95=B7=E3=81=84?=
 =?UTF-8?q?=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=82=92=E3=83=9A=E3=83=BC?=
 =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=97=E3=81=9F=E9=9A=9B=E3=81=AB=E3=83=86?=
 =?UTF-8?q?=E3=82=AD=E3=82=B9=E3=83=88=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?=
 =?UTF-8?q?=E3=81=A8=E3=81=97=E3=81=A6=E6=B7=BB=E4=BB=98=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=81=8B=E3=81=A9=E3=81=86=E3=81=8B=E3=82=92=E9=81=B8=E6=8A=9E?=
 =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1386?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(frontend): ask if attach as file if clipboard text is very long

* docs(changelog): 長いテキストをペーストした際にテキストファイルとして添付するかどうかを選択できるように
---
 CHANGELOG.md                                    |  1 +
 locales/index.d.ts                              |  6 +++++-
 locales/ja-JP.yml                               |  1 +
 packages/frontend/src/components/MkPostForm.vue | 17 +++++++++++++++++
 4 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bdc1d135a68..ce66d779a313 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,7 @@
 - Enhance: `Ui:C:postForm` および `Ui:C:postFormButton` に `localOnly` と `visibility` を設定できるように
 - Enhance: AiScriptを0.18.0にバージョンアップ
 - Enhance: 通常のノートでも、お気に入りに登録したチャンネルにリノートできるように
+- Enhance: 長いテキストをペーストした際にテキストファイルとして添付するかどうかを選択できるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 70741b6460cd..d5d6ef0f3497 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -917,7 +917,7 @@ export interface Locale extends ILocale {
      */
     "silencedInstances": string;
     /**
-     * サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたインスタンスには影響しません。
+     * サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。
      */
     "silencedInstancesDescription": string;
     /**
@@ -1900,6 +1900,10 @@ export interface Locale extends ILocale {
      * 引用として添付しますか?
      */
     "quoteQuestion": string;
+    /**
+     * クリップボードのテキストが長いです。テキストファイルとして添付しますか?
+     */
+    "attachAsFileQuestion": string;
     /**
      * まだチャットはありません
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 626e3f30f890..9aa1e6e6a072 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -471,6 +471,7 @@ retype: "再入力"
 noteOf: "{user}のノート"
 quoteAttached: "引用付き"
 quoteQuestion: "引用として添付しますか?"
+attachAsFileQuestion: "クリップボードのテキストが長いです。テキストファイルとして添付しますか?"
 noMessagesYet: "まだチャットはありません"
 newMessageExists: "新しいメッセージがあります"
 onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 41d603e40ff7..1df90076818e 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -612,6 +612,23 @@ async function onPaste(ev: ClipboardEvent) {
 			quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null;
 		});
 	}
+
+	if (paste.length > 1000) {
+		ev.preventDefault();
+		os.confirm({
+			type: 'info',
+			text: i18n.ts.attachAsFileQuestion,
+		}).then(({ canceled }) => {
+			if (canceled) {
+				insertTextAtCursor(textareaEl.value, paste);
+				return;
+			}
+
+			const fileName = formatTimeString(new Date(), defaultStore.state.pastedFileName).replace(/{{number}}/g, "0");
+			const file = new File([paste], `${fileName}.txt`, { type: "text/plain" });
+			upload(file, `${fileName}.txt`);
+		});
+	}
 }
 
 function onDragover(ev) {

From 8489d39372c571736881329d92a0dabeba3f3e69 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Thu, 23 May 2024 05:25:01 +0000
Subject: [PATCH 217/266] Bump version to 2024.5.0-beta.2

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index e05fbdcca74d..b9ac4fc2a16f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-beta.1",
+	"version": "2024.5.0-beta.2",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index cc5d5bdbf4fa..80ae84796aaa 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-beta.1",
+	"version": "2024.5.0-beta.2",
 	"description": "Misskey SDK for JavaScript",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",

From 611e303bab1ace64c7ab1611e35d850a96f0bace Mon Sep 17 00:00:00 2001
From: Acid Chicken <root@acid-chicken.com>
Date: Thu, 23 May 2024 15:19:52 +0900
Subject: [PATCH 218/266] feat(backend): add /healthz endpoint (#13834)

* feat(backend): add /healthz endpoint

* feat(backend): also check meilisearch status if available

* style: header

* chore: no-store

* chore: healthcheck.sh

* style: format
---
 healthcheck.sh                                |  2 +-
 packages/backend/src/boot/entry.ts            |  3 ++
 packages/backend/src/boot/ready.ts            |  6 +++
 .../backend/src/server/HealthServerService.ts | 54 +++++++++++++++++++
 packages/backend/src/server/ServerModule.ts   |  2 +
 packages/backend/src/server/ServerService.ts  |  3 ++
 6 files changed, 69 insertions(+), 1 deletion(-)
 create mode 100644 packages/backend/src/boot/ready.ts
 create mode 100644 packages/backend/src/server/HealthServerService.ts

diff --git a/healthcheck.sh b/healthcheck.sh
index d6d416c7a14a..dcfcf7678618 100644
--- a/healthcheck.sh
+++ b/healthcheck.sh
@@ -4,4 +4,4 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 PORT=$(grep '^port:' /misskey/.config/default.yml | awk 'NR==1{print $2; exit}')
-curl -s -S -o /dev/null "http://localhost:${PORT}"
+curl -Sfso/dev/null "http://localhost:${PORT}/healthz"
diff --git a/packages/backend/src/boot/entry.ts b/packages/backend/src/boot/entry.ts
index 6b8e83d4f9ef..04c6ca9723cb 100644
--- a/packages/backend/src/boot/entry.ts
+++ b/packages/backend/src/boot/entry.ts
@@ -15,6 +15,7 @@ import Logger from '@/logger.js';
 import { envOption } from '../env.js';
 import { masterMain } from './master.js';
 import { workerMain } from './worker.js';
+import { readyRef } from './ready.js';
 
 import 'reflect-metadata';
 
@@ -79,6 +80,8 @@ if (cluster.isWorker || envOption.disableClustering) {
 	await workerMain();
 }
 
+readyRef.value = true;
+
 // ユニットテスト時にMisskeyが子プロセスで起動された時のため
 // それ以外のときは process.send は使えないので弾く
 if (process.send) {
diff --git a/packages/backend/src/boot/ready.ts b/packages/backend/src/boot/ready.ts
new file mode 100644
index 000000000000..591ae5cb58a9
--- /dev/null
+++ b/packages/backend/src/boot/ready.ts
@@ -0,0 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const readyRef = { value: false };
diff --git a/packages/backend/src/server/HealthServerService.ts b/packages/backend/src/server/HealthServerService.ts
new file mode 100644
index 000000000000..2c3ed85925c9
--- /dev/null
+++ b/packages/backend/src/server/HealthServerService.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import * as Redis from 'ioredis';
+import { DataSource } from 'typeorm';
+import { bindThis } from '@/decorators.js';
+import { DI } from '@/di-symbols.js';
+import { readyRef } from '@/boot/ready.js';
+import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
+import type { MeiliSearch } from 'meilisearch';
+
+@Injectable()
+export class HealthServerService {
+	constructor(
+		@Inject(DI.redis)
+		private redis: Redis.Redis,
+
+		@Inject(DI.redisForPub)
+		private redisForPub: Redis.Redis,
+
+		@Inject(DI.redisForSub)
+		private redisForSub: Redis.Redis,
+
+		@Inject(DI.redisForTimelines)
+		private redisForTimelines: Redis.Redis,
+
+		@Inject(DI.db)
+		private db: DataSource,
+
+		@Inject(DI.meilisearch)
+		private meilisearch: MeiliSearch | null,
+	) {}
+
+	@bindThis
+	public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
+		fastify.get('/', async (request, reply) => {
+			reply.code(await Promise.all([
+				new Promise<void>((resolve, reject) => readyRef.value ? resolve() : reject()),
+				this.redis.ping(),
+				this.redisForPub.ping(),
+				this.redisForSub.ping(),
+				this.redisForTimelines.ping(),
+				this.db.query('SELECT 1'),
+				...(this.meilisearch ? [this.meilisearch.health()] : []),
+			]).then(() => 200, () => 503));
+			reply.header('Cache-Control', 'no-store');
+		});
+
+		done();
+	}
+}
diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts
index f43968d236df..12d50619856c 100644
--- a/packages/backend/src/server/ServerModule.ts
+++ b/packages/backend/src/server/ServerModule.ts
@@ -8,6 +8,7 @@ import { EndpointsModule } from '@/server/api/EndpointsModule.js';
 import { CoreModule } from '@/core/CoreModule.js';
 import { ApiCallService } from './api/ApiCallService.js';
 import { FileServerService } from './FileServerService.js';
+import { HealthServerService } from './HealthServerService.js';
 import { NodeinfoServerService } from './NodeinfoServerService.js';
 import { ServerService } from './ServerService.js';
 import { WellKnownServerService } from './WellKnownServerService.js';
@@ -55,6 +56,7 @@ import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js
 		ClientServerService,
 		ClientLoggerService,
 		FeedService,
+		HealthServerService,
 		UrlPreviewService,
 		ActivityPubServerService,
 		FileServerService,
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index da17a88e0351..3572f16627cc 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -28,6 +28,7 @@ import { ApiServerService } from './api/ApiServerService.js';
 import { StreamingApiServerService } from './api/StreamingApiServerService.js';
 import { WellKnownServerService } from './WellKnownServerService.js';
 import { FileServerService } from './FileServerService.js';
+import { HealthServerService } from './HealthServerService.js';
 import { ClientServerService } from './web/ClientServerService.js';
 import { OpenApiServerService } from './api/openapi/OpenApiServerService.js';
 import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
@@ -61,6 +62,7 @@ export class ServerService implements OnApplicationShutdown {
 		private wellKnownServerService: WellKnownServerService,
 		private nodeinfoServerService: NodeinfoServerService,
 		private fileServerService: FileServerService,
+		private healthServerService: HealthServerService,
 		private clientServerService: ClientServerService,
 		private globalEventService: GlobalEventService,
 		private loggerService: LoggerService,
@@ -108,6 +110,7 @@ export class ServerService implements OnApplicationShutdown {
 		fastify.register(this.wellKnownServerService.createServer);
 		fastify.register(this.oauth2ProviderService.createServer, { prefix: '/oauth' });
 		fastify.register(this.oauth2ProviderService.createTokenServer, { prefix: '/oauth/token' });
+		fastify.register(this.healthServerService.createServer, { prefix: '/healthz' });
 
 		fastify.get<{ Params: { path: string }; Querystring: { static?: any; badge?: any; }; }>('/emoji/:path(.*)', async (request, reply) => {
 			const path = request.params.path;

From 83a9aa4533912c685a74a107be3894c4a85a338c Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 23 May 2024 15:55:47 +0900
Subject: [PATCH 219/266] feat: suspend instance improvements (#13861)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(backend): dead instance detection

* feat(backend): suspend type detection

* feat(frontend): show suspend reason on frontend

* feat(backend): resume federation automatically if the server is automatically suspended

* docs(changelog): 配信停止まわりの改善

* lint: fix lint errors

* Update packages/frontend/src/pages/instance-info.vue

* lint: fix lint error

* chore: suspendedState => suspensionState

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  3 ++
 locales/index.d.ts                            | 32 ++++++++++++
 locales/ja-JP.yml                             | 10 ++++
 .../1716345015347-NotRespondingSince.js       | 16 ++++++
 ...8870-SuspensionStateInsteadOfIsSspended.js | 50 +++++++++++++++++++
 .../core/entities/InstanceEntityService.ts    |  3 +-
 packages/backend/src/models/Instance.ts       | 17 +++++--
 .../models/json-schema/federation-instance.ts |  5 ++
 .../processors/DeliverProcessorService.ts     | 14 +++++-
 .../queue/processors/InboxProcessorService.ts |  2 +
 .../src/server/api/ApiServerService.ts        |  2 +-
 .../admin/federation/update-instance.ts       | 11 +++-
 .../frontend/src/pages/admin/federation.vue   | 14 +++++-
 packages/frontend/src/pages/instance-info.vue | 29 +++++++++--
 packages/misskey-js/src/autogen/types.ts      |  2 +
 15 files changed, 193 insertions(+), 17 deletions(-)
 create mode 100644 packages/backend/migration/1716345015347-NotRespondingSince.js
 create mode 100644 packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce66d779a313..21bb3b2e8eee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,9 @@
   - サスペンド済みユーザーか
   - 鍵アカウントユーザーか
   - 「アカウントを見つけやすくする」が有効なユーザーか
+- Enhance: Goneを出さずに終了したサーバーへの配信停止を自動的に行うように
+  - もしそのようなサーバーからから配信が届いた場合には自動的に配信を再開します
+- Enhance: 配信停止の理由を表示するように
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 - Fix: 正規化されていない状態のhashtagが連合されてきたhtmlに含まれているとhashtagが正しくhashtagに復元されない問題を修正
 - Fix: みつけるのアンケート欄にてチャンネルのアンケートが含まれてしまう問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index d5d6ef0f3497..991ec1ac1d9e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4972,6 +4972,38 @@ export interface Locale extends ILocale {
      * お問い合わせ
      */
     "inquiry": string;
+    "_delivery": {
+        /**
+         * 配信状態
+         */
+        "status": string;
+        /**
+         * 配信停止
+         */
+        "stop": string;
+        /**
+         * 配信再開
+         */
+        "resume": string;
+        "_type": {
+            /**
+             * 配信中
+             */
+            "none": string;
+            /**
+             * 手動停止中
+             */
+            "manuallySuspended": string;
+            /**
+             * サーバー削除のため停止中
+             */
+            "goneSuspended": string;
+            /**
+             * サーバー応答なしのため停止中
+             */
+            "autoSuspendedForNotResponding": string;
+        };
+    };
     "_bubbleGame": {
         /**
          * 遊び方
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 9aa1e6e6a072..d7635acc2ee2 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1240,6 +1240,16 @@ noDescription: "説明文はありません"
 alwaysConfirmFollow: "フォローの際常に確認する"
 inquiry: "お問い合わせ"
 
+_delivery:
+  status: "配信状態"
+  stop: "配信停止"
+  resume: "配信再開"
+  _type:
+    none: "配信中"
+    manuallySuspended: "手動停止中"
+    goneSuspended: "サーバー削除のため停止中"
+    autoSuspendedForNotResponding: "サーバー応答なしのため停止中"
+
 _bubbleGame:
   howToPlay: "遊び方"
   hold: "ホールド"
diff --git a/packages/backend/migration/1716345015347-NotRespondingSince.js b/packages/backend/migration/1716345015347-NotRespondingSince.js
new file mode 100644
index 000000000000..fc4ee6639ae1
--- /dev/null
+++ b/packages/backend/migration/1716345015347-NotRespondingSince.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class NotRespondingSince1716345015347 {
+    name = 'NotRespondingSince1716345015347'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" ADD "notRespondingSince" TIMESTAMP WITH TIME ZONE`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "notRespondingSince"`);
+    }
+}
diff --git a/packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js b/packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js
new file mode 100644
index 000000000000..4808a9a3db43
--- /dev/null
+++ b/packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class SuspensionStateInsteadOfIsSspended1716345771510 {
+    name = 'SuspensionStateInsteadOfIsSspended1716345771510'
+
+    async up(queryRunner) {
+        await queryRunner.query(`CREATE TYPE "public"."instance_suspensionstate_enum" AS ENUM('none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding')`);
+
+        await queryRunner.query(`DROP INDEX "public"."IDX_34500da2e38ac393f7bb6b299c"`);
+
+        await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "isSuspended" TO "suspensionState"`);
+
+        await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" DROP DEFAULT`);
+
+        await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" TYPE "public"."instance_suspensionstate_enum" USING (
+            CASE "suspensionState"
+               WHEN TRUE THEN 'manuallySuspended'::instance_suspensionstate_enum
+               ELSE 'none'::instance_suspensionstate_enum
+            END
+        )`);
+
+        await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" SET DEFAULT 'none'`);
+
+        await queryRunner.query(`CREATE INDEX "IDX_3ede46f507c87ad698051d56a8" ON "instance" ("suspensionState") `);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`DROP INDEX "public"."IDX_3ede46f507c87ad698051d56a8"`);
+
+        await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" DROP DEFAULT`);
+
+        await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" TYPE boolean USING (
+            CASE "suspensionState"
+               WHEN 'none'::instance_suspensionstate_enum THEN FALSE
+               ELSE TRUE
+            END
+        )`);
+
+        await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" SET DEFAULT false`);
+
+        await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "suspensionState" TO "isSuspended"`);
+
+        await queryRunner.query(`CREATE INDEX "IDX_34500da2e38ac393f7bb6b299c" ON "instance" ("isSuspended") `);
+
+        await queryRunner.query(`DROP TYPE "public"."instance_suspensionstate_enum"`);
+    }
+}
diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts
index e46bd8b9637e..9117b1391481 100644
--- a/packages/backend/src/core/entities/InstanceEntityService.ts
+++ b/packages/backend/src/core/entities/InstanceEntityService.ts
@@ -39,7 +39,8 @@ export class InstanceEntityService {
 			followingCount: instance.followingCount,
 			followersCount: instance.followersCount,
 			isNotResponding: instance.isNotResponding,
-			isSuspended: instance.isSuspended,
+			isSuspended: instance.suspensionState !== 'none',
+			suspensionState: instance.suspensionState,
 			isBlocked: this.utilityService.isBlockedHost(meta.blockedHosts, instance.host),
 			softwareName: instance.softwareName,
 			softwareVersion: instance.softwareVersion,
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index 9863c9d75da7..17cd5c666557 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -81,13 +81,22 @@ export class MiInstance {
 	public isNotResponding: boolean;
 
 	/**
-	 * このインスタンスへの配信を停止するか
+	 * このインスタンスと不通になった日時
+	 */
+	@Column('timestamp with time zone', {
+		nullable: true,
+	})
+	public notRespondingSince: Date | null;
+
+	/**
+	 * このインスタンスへの配信状態
 	 */
 	@Index()
-	@Column('boolean', {
-		default: false,
+	@Column('enum', {
+		default: 'none',
+		enum: ['none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding'],
 	})
-	public isSuspended: boolean;
+	public suspensionState: 'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding';
 
 	@Column('varchar', {
 		length: 64, nullable: true,
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index 42d98fe5238e..ed40d405c66d 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -45,6 +45,11 @@ export const packedFederationInstanceSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		suspensionState: {
+			type: 'string',
+			nullable: false, optional: false,
+			enum: ['none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding'],
+		},
 		isBlocked: {
 			type: 'boolean',
 			optional: false, nullable: false,
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index 5fed070929e8..b73195afc3f8 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -5,6 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import * as Bull from 'bullmq';
+import { Not } from 'typeorm';
 import { DI } from '@/di-symbols.js';
 import type { InstancesRepository } from '@/models/_.js';
 import type Logger from '@/logger.js';
@@ -62,7 +63,7 @@ export class DeliverProcessorService {
 		if (suspendedHosts == null) {
 			suspendedHosts = await this.instancesRepository.find({
 				where: {
-					isSuspended: true,
+					suspensionState: Not('none'),
 				},
 			});
 			this.suspendedHostsCache.set(suspendedHosts);
@@ -79,6 +80,7 @@ export class DeliverProcessorService {
 				if (i.isNotResponding) {
 					this.federatedInstanceService.update(i.id, {
 						isNotResponding: false,
+						notRespondingSince: null,
 					});
 				}
 
@@ -98,7 +100,15 @@ export class DeliverProcessorService {
 				if (!i.isNotResponding) {
 					this.federatedInstanceService.update(i.id, {
 						isNotResponding: true,
+						notRespondingSince: new Date(),
 					});
+				} else if (i.notRespondingSince) {
+					// 1週間以上不通ならサスペンド
+					if (i.suspensionState === 'none' && i.notRespondingSince.getTime() <= Date.now() - 1000 * 60 * 60 * 24 * 7) {
+						this.federatedInstanceService.update(i.id, {
+							suspensionState: 'autoSuspendedForNotResponding',
+						});
+					}
 				}
 
 				this.apRequestChart.deliverFail();
@@ -116,7 +126,7 @@ export class DeliverProcessorService {
 					if (job.data.isSharedInbox && res.statusCode === 410) {
 						this.federatedInstanceService.fetch(host).then(i => {
 							this.federatedInstanceService.update(i.id, {
-								isSuspended: true,
+								suspensionState: 'goneSuspended',
 							});
 						});
 						throw new Bull.UnrecoverableError(`${host} is gone`);
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 1d05f4ade10c..f465339075b9 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -188,6 +188,8 @@ export class InboxProcessorService {
 			this.federatedInstanceService.update(i.id, {
 				latestRequestReceivedAt: new Date(),
 				isNotResponding: false,
+				// もしサーバーが死んでるために配信が止まっていた場合には自動的に復活させてあげる
+				suspensionState: i.suspensionState === 'autoSuspendedForNotResponding' ? 'none' : undefined,
 			});
 
 			this.fetchInstanceMetadataService.fetchInstanceMetadata(i);
diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts
index e99244cdd02d..4a5935f93092 100644
--- a/packages/backend/src/server/api/ApiServerService.ts
+++ b/packages/backend/src/server/api/ApiServerService.ts
@@ -137,7 +137,7 @@ export class ApiServerService {
 			const instances = await this.instancesRepository.find({
 				select: ['host'],
 				where: {
-					isSuspended: false,
+					suspensionState: 'none',
 				},
 			});
 
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
index 0bcdc2a4b81e..fed7bfbbde66 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
@@ -46,12 +46,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new Error('instance not found');
 			}
 
+			const isSuspendedBefore = instance.suspensionState !== 'none';
+			let suspensionState: undefined | 'manuallySuspended' | 'none';
+
+			if (ps.isSuspended != null && isSuspendedBefore !== ps.isSuspended) {
+				suspensionState = ps.isSuspended ? 'manuallySuspended' : 'none';
+			}
+
 			await this.federatedInstanceService.update(instance.id, {
-				isSuspended: ps.isSuspended,
+				suspensionState,
 				moderationNote: ps.moderationNote,
 			});
 
-			if (ps.isSuspended != null && instance.isSuspended !== ps.isSuspended) {
+			if (ps.isSuspended != null && isSuspendedBefore !== ps.isSuspended) {
 				if (ps.isSuspended) {
 					this.moderationLogService.log(me, 'suspendRemoteInstance', {
 						id: instance.id,
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index de27e1f67a75..0aaa39858402 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -58,6 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import * as Misskey from 'misskey-js';
 import { computed, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -90,8 +91,17 @@ const pagination = {
 	})),
 };
 
-function getStatus(instance) {
-	if (instance.isSuspended) return 'Suspended';
+function getStatus(instance: Misskey.entities.FederationInstance) {
+	switch (instance.suspensionState) {
+		case 'manuallySuspended':
+			return 'Manually Suspended';
+		case 'goneSuspended':
+			return 'Automatically Suspended (Gone)';
+		case 'autoSuspendedForNotResponding':
+			return 'Automatically Suspended (Not Responding)';
+		case 'none':
+			break;
+	}
 	if (instance.isBlocked) return 'Blocked';
 	if (instance.isSilenced) return 'Silenced';
 	if (instance.isNotResponding) return 'Error';
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index cb7fe2866c2a..26797ba85e30 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -35,7 +35,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<FormSection v-if="iAmModerator">
 					<template #label>Moderation</template>
 					<div class="_gaps_s">
-						<MkSwitch v-model="suspended" :disabled="!instance" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</MkSwitch>
+						<MkKeyValue>
+							<template #key>
+								{{ i18n.ts._delivery.status }}
+							</template>
+							<template #value>
+								{{ i18n.ts._delivery._type[suspensionState] }}
+							</template>
+						</MkKeyValue>
+						<MkButton v-if="suspensionState === 'none'" :disabled="!instance" danger @click="stopDelivery">{{ i18n.ts._delivery.stop }}</MkButton>
+						<MkButton v-if="suspensionState !== 'none'" :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton>
 						<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
 						<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
 						<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
@@ -155,7 +164,7 @@ const tab = ref('overview');
 const chartSrc = ref('instance-requests');
 const meta = ref<Misskey.entities.AdminMetaResponse | null>(null);
 const instance = ref<Misskey.entities.FederationInstance | null>(null);
-const suspended = ref(false);
+const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding'>('none');
 const isBlocked = ref(false);
 const isSilenced = ref(false);
 const faviconUrl = ref<string | null>(null);
@@ -183,7 +192,7 @@ async function fetch(): Promise<void> {
 	instance.value = await misskeyApi('federation/show-instance', {
 		host: props.host,
 	});
-	suspended.value = instance.value?.isSuspended ?? false;
+	suspensionState.value = instance.value?.suspensionState ?? 'none';
 	isBlocked.value = instance.value?.isBlocked ?? false;
 	isSilenced.value = instance.value?.isSilenced ?? false;
 	faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
@@ -209,11 +218,21 @@ async function toggleSilenced(): Promise<void> {
 	});
 }
 
-async function toggleSuspend(): Promise<void> {
+async function stopDelivery(): Promise<void> {
 	if (!instance.value) throw new Error('No instance?');
+	suspensionState.value = 'manuallySuspended';
 	await misskeyApi('admin/federation/update-instance', {
 		host: instance.value.host,
-		isSuspended: suspended.value,
+		isSuspended: true,
+	});
+}
+
+async function resumeDelivery(): Promise<void> {
+	if (!instance.value) throw new Error('No instance?');
+	suspensionState.value = 'none';
+	await misskeyApi('admin/federation/update-instance', {
+		host: instance.value.host,
+		isSuspended: false,
 	});
 }
 
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 302587ccfa6b..5bd68af010ab 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4475,6 +4475,8 @@ export type components = {
       followersCount: number;
       isNotResponding: boolean;
       isSuspended: boolean;
+      /** @enum {string} */
+      suspensionState: 'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding';
       isBlocked: boolean;
       /** @example misskey */
       softwareName: string | null;

From e0b47999faf2536f7998e8ddf3558998b31aef82 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 23 May 2024 17:29:59 +0900
Subject: [PATCH 220/266] =?UTF-8?q?fix(backend):=20`read:admin:show-user`?=
 =?UTF-8?q?=20=E3=81=A8=20`read:admin:show-users`=20=E3=82=92=E7=B5=B1?=
 =?UTF-8?q?=E5=90=88=20(#13798)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 同じdisplayNameの権限があるのを修正

* read:admin:show-user と read:admin:show-users を統合

* Update Changelog
---
 CHANGELOG.md                                                  | 1 +
 locales/index.d.ts                                            | 4 ----
 locales/ja-JP.yml                                             | 1 -
 packages/backend/src/server/api/endpoints/admin/show-users.ts | 2 +-
 packages/misskey-js/etc/misskey-js.api.md                     | 2 +-
 packages/misskey-js/src/autogen/apiClientJSDoc.ts             | 2 +-
 packages/misskey-js/src/autogen/types.ts                      | 4 ++--
 packages/misskey-js/src/consts.ts                             | 1 -
 8 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21bb3b2e8eee..681903a1f2ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 ### Note
 - コントロールパネル内にあるサマリープロキシの設定個所がセキュリティから全般へ変更となります。
 - 悪意のある第三者がリモートユーザーになりすましたアクティビティを受け取れてしまう問題を修正しました。詳しくは[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-2vxv-pv3m-3wvj)をご覧ください。
+- 管理者向け権限 `read:admin:show-users` は `read:admin:show-user` に統合されました。必要に応じてAPIトークンを再発行してください。
 
 ### General
 - Enhance: URLプレビューの有効化・無効化を設定できるように #13569
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 991ec1ac1d9e..18d8eee18fa2 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -7935,10 +7935,6 @@ export interface Locale extends ILocale {
          * ユーザーのプライベートな情報を見る
          */
         "read:admin:show-user": string;
-        /**
-         * ユーザーのプライベートな情報を見る
-         */
-        "read:admin:show-users": string;
         /**
          * ユーザーを凍結する
          */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index d7635acc2ee2..8b1738aebe19 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2085,7 +2085,6 @@ _permissions:
   "read:admin:server-info": "サーバーの情報を見る"
   "read:admin:show-moderation-log": "モデレーションログを見る"
   "read:admin:show-user": "ユーザーのプライベートな情報を見る"
-  "read:admin:show-users": "ユーザーのプライベートな情報を見る"
   "write:admin:suspend-user": "ユーザーを凍結する"
   "write:admin:unset-user-avatar": "ユーザーのアバターを削除する"
   "write:admin:unset-user-banner": "ユーザーのバーナーを削除する"
diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts
index 424212ba24c1..2fef9abbf966 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts
@@ -16,7 +16,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
-	kind: 'read:admin:show-users',
+	kind: 'read:admin:show-user',
 
 	res: {
 		type: 'array',
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 9720b04e3963..9cdb61da8770 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2626,7 +2626,7 @@ type PagesUpdateRequest = operations['pages___update']['requestBody']['content']
 function parse(acct: string): Acct;
 
 // @public (undocumented)
-export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
+export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
 
 // @public (undocumented)
 type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 53093501008c..729107a78dbf 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -678,7 +678,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
      */
     request<E extends 'admin/show-users', P extends Endpoints[E]['req']>(
       endpoint: E,
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 5bd68af010ab..1e9c190ca56f 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -567,7 +567,7 @@ export type paths = {
      * admin/show-users
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
      */
     post: operations['admin___show-users'];
   };
@@ -8647,7 +8647,7 @@ export type operations = {
    * admin/show-users
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
    */
   'admin___show-users': {
     requestBody: {
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index b690621e98d9..fd6ef4d68ded 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -58,7 +58,6 @@ export const permissions = [
 	'read:admin:server-info',
 	'read:admin:show-moderation-log',
 	'read:admin:show-user',
-	'read:admin:show-users',
 	'write:admin:suspend-user',
 	'write:admin:unset-user-avatar',
 	'write:admin:unset-user-banner',

From 3ffbf6296f44c6f8837f0b8533a3b60b64403bf9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 27 May 2024 17:15:11 +0900
Subject: [PATCH 221/266] =?UTF-8?q?feat:=20=E5=80=8B=E5=88=A5=E3=81=AE?=
 =?UTF-8?q?=E3=81=8A=E7=9F=A5=E3=82=89=E3=81=9B=E3=81=AB=E3=83=AA=E3=83=B3?=
 =?UTF-8?q?=E3=82=AF=E3=81=A7=E9=A3=9B=E3=81=B9=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#13885)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(announcement): 個別のお知らせにリンクで飛べるように (MisskeyIO#639)

(cherry picked from commit f6bf7f992a78e54d86a4701dbd1e4fda7ef4eb27)

* fix

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>

* fix

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>

* 一覧ページではお知らせpanel全体を押せるように

* お知らせバーは個別ページに飛ばすように

* Update Changelog

* spdx

* attempt to fox test

* remove unnecessary thong

* `announcement` → `announcements/show`

* リンクを押せる場所をタイトルと日付部分のみに変更

---------

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 CHANGELOG.md                                  |   2 +
 .../backend/src/core/AnnouncementService.ts   |  49 +++---
 packages/backend/src/core/CoreModule.ts       |   6 +
 .../entities/AnnouncementEntityService.ts     |  71 +++++++++
 .../backend/src/server/api/EndpointsModule.ts |   4 +
 packages/backend/src/server/api/endpoints.ts  |   2 +
 .../src/server/api/endpoints/announcements.ts |  11 +-
 .../api/endpoints/announcements/show.ts       |  54 +++++++
 .../backend/test/unit/AnnouncementService.ts  |   2 +
 packages/frontend/src/pages/announcement.vue  | 142 ++++++++++++++++++
 packages/frontend/src/pages/announcements.vue |  25 +--
 packages/frontend/src/router/definition.ts    |   3 +
 .../src/ui/_common_/announcements.vue         |   2 +-
 packages/misskey-js/etc/misskey-js.api.md     |   8 +
 .../misskey-js/src/autogen/apiClientJSDoc.ts  |  11 ++
 packages/misskey-js/src/autogen/endpoint.ts   |   3 +
 packages/misskey-js/src/autogen/entities.ts   |   2 +
 packages/misskey-js/src/autogen/types.ts      |  63 ++++++++
 18 files changed, 415 insertions(+), 45 deletions(-)
 create mode 100644 packages/backend/src/core/entities/AnnouncementEntityService.ts
 create mode 100644 packages/backend/src/server/api/endpoints/announcements/show.ts
 create mode 100644 packages/frontend/src/pages/announcement.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 681903a1f2ba..7e23fa8f7273 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@
 
 ### Client
 - Feat: アップロードするファイルの名前をランダム文字列にできるように
+- Feat: 個別のお知らせにリンクで飛べるように  
+  (Cherry-picked from https://github.com/MisskeyIO/misskey)
 - Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
 - Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
 - Enhance: リアクション・いいねの総数を表示するように
diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts
index b298a7092983..9b60df2cae99 100644
--- a/packages/backend/src/core/AnnouncementService.ts
+++ b/packages/backend/src/core/AnnouncementService.ts
@@ -4,13 +4,14 @@
  */
 
 import { Inject, Injectable } from '@nestjs/common';
-import { Brackets } from 'typeorm';
+import { Brackets, EntityNotFoundError } from 'typeorm';
 import { DI } from '@/di-symbols.js';
 import type { MiUser } from '@/models/User.js';
 import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead, UsersRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import { Packed } from '@/misc/json-schema.js';
 import { IdService } from '@/core/IdService.js';
+import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { ModerationLogService } from '@/core/ModerationLogService.js';
 
@@ -29,6 +30,7 @@ export class AnnouncementService {
 		private idService: IdService,
 		private globalEventService: GlobalEventService,
 		private moderationLogService: ModerationLogService,
+		private announcementEntityService: AnnouncementEntityService,
 	) {
 	}
 
@@ -79,7 +81,7 @@ export class AnnouncementService {
 			userId: values.userId,
 		}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
 
-		const packed = (await this.packMany([announcement]))[0];
+		const packed = await this.announcementEntityService.pack(announcement);
 
 		if (values.userId) {
 			this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
@@ -177,6 +179,24 @@ export class AnnouncementService {
 		}
 	}
 
+	@bindThis
+	public async getAnnouncement(announcementId: MiAnnouncement['id'], me: MiUser | null): Promise<Packed<'Announcement'>> {
+		const announcement = await this.announcementsRepository.findOneByOrFail({ id: announcementId });
+		if (me) {
+			if (announcement.userId && announcement.userId !== me.id) {
+				throw new EntityNotFoundError(this.announcementsRepository.metadata.target, { id: announcementId });
+			}
+
+			const read = await this.announcementReadsRepository.findOneBy({
+				announcementId: announcement.id,
+				userId: me.id,
+			});
+			return this.announcementEntityService.pack({ ...announcement, isRead: read !== null }, me);
+		} else {
+			return this.announcementEntityService.pack(announcement, null);
+		}
+	}
+
 	@bindThis
 	public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
 		try {
@@ -193,29 +213,4 @@ export class AnnouncementService {
 			this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements');
 		}
 	}
-
-	@bindThis
-	public async packMany(
-		announcements: MiAnnouncement[],
-		me?: { id: MiUser['id'] } | null | undefined,
-		options?: {
-			reads?: MiAnnouncementRead[];
-		},
-	): Promise<Packed<'Announcement'>[]> {
-		const reads = me ? (options?.reads ?? await this.getReads(me.id)) : [];
-		return announcements.map(announcement => ({
-			id: announcement.id,
-			createdAt: this.idService.parse(announcement.id).date.toISOString(),
-			updatedAt: announcement.updatedAt?.toISOString() ?? null,
-			text: announcement.text,
-			title: announcement.title,
-			imageUrl: announcement.imageUrl,
-			icon: announcement.icon,
-			display: announcement.display,
-			needConfirmationToRead: announcement.needConfirmationToRead,
-			silence: announcement.silence,
-			forYou: announcement.userId === me?.id,
-			isRead: reads.some(read => read.announcementId === announcement.id),
-		}));
-	}
 }
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 595315587241..be80df6f1cf1 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -84,6 +84,7 @@ import ApRequestChart from './chart/charts/ap-request.js';
 import { ChartManagementService } from './chart/ChartManagementService.js';
 
 import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js';
+import { AnnouncementEntityService } from './entities/AnnouncementEntityService.js';
 import { AntennaEntityService } from './entities/AntennaEntityService.js';
 import { AppEntityService } from './entities/AppEntityService.js';
 import { AuthSessionEntityService } from './entities/AuthSessionEntityService.js';
@@ -223,6 +224,7 @@ const $ApRequestChart: Provider = { provide: 'ApRequestChart', useExisting: ApRe
 const $ChartManagementService: Provider = { provide: 'ChartManagementService', useExisting: ChartManagementService };
 
 const $AbuseUserReportEntityService: Provider = { provide: 'AbuseUserReportEntityService', useExisting: AbuseUserReportEntityService };
+const $AnnouncementEntityService: Provider = { provide: 'AnnouncementEntityService', useExisting: AnnouncementEntityService };
 const $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useExisting: AntennaEntityService };
 const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting: AppEntityService };
 const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService };
@@ -363,6 +365,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		ChartManagementService,
 
 		AbuseUserReportEntityService,
+		AnnouncementEntityService,
 		AntennaEntityService,
 		AppEntityService,
 		AuthSessionEntityService,
@@ -499,6 +502,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$ChartManagementService,
 
 		$AbuseUserReportEntityService,
+		$AnnouncementEntityService,
 		$AntennaEntityService,
 		$AppEntityService,
 		$AuthSessionEntityService,
@@ -635,6 +639,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		ChartManagementService,
 
 		AbuseUserReportEntityService,
+		AnnouncementEntityService,
 		AntennaEntityService,
 		AppEntityService,
 		AuthSessionEntityService,
@@ -770,6 +775,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$ChartManagementService,
 
 		$AbuseUserReportEntityService,
+		$AnnouncementEntityService,
 		$AntennaEntityService,
 		$AppEntityService,
 		$AuthSessionEntityService,
diff --git a/packages/backend/src/core/entities/AnnouncementEntityService.ts b/packages/backend/src/core/entities/AnnouncementEntityService.ts
new file mode 100644
index 000000000000..90b04d0229a6
--- /dev/null
+++ b/packages/backend/src/core/entities/AnnouncementEntityService.ts
@@ -0,0 +1,71 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { DI } from '@/di-symbols.js';
+import type { AnnouncementsRepository, AnnouncementReadsRepository, MiAnnouncement, MiUser } from '@/models/_.js';
+import type { Packed } from '@/misc/json-schema.js';
+import { bindThis } from '@/decorators.js';
+import { IdService } from '@/core/IdService.js';
+
+@Injectable()
+export class AnnouncementEntityService {
+	constructor(
+		@Inject(DI.announcementsRepository)
+		private announcementsRepository: AnnouncementsRepository,
+
+		@Inject(DI.announcementReadsRepository)
+		private announcementReadsRepository: AnnouncementReadsRepository,
+
+		private idService: IdService,
+	) {
+	}
+
+	@bindThis
+	public async pack(
+		src: MiAnnouncement['id'] | MiAnnouncement & { isRead?: boolean | null },
+		me?: { id: MiUser['id'] } | null | undefined,
+	): Promise<Packed<'Announcement'>> {
+		const announcement = typeof src === 'object'
+			? src
+			: await this.announcementsRepository.findOneByOrFail({
+				id: src,
+			}) as MiAnnouncement & { isRead?: boolean | null };
+
+		if (me && announcement.isRead === undefined) {
+			announcement.isRead = await this.announcementReadsRepository
+				.countBy({
+					announcementId: announcement.id,
+					userId: me.id,
+				})
+				.then((count: number) => count > 0);
+		}
+
+		return {
+			id: announcement.id,
+			createdAt: this.idService.parse(announcement.id).date.toISOString(),
+			updatedAt: announcement.updatedAt?.toISOString() ?? null,
+			title: announcement.title,
+			text: announcement.text,
+			imageUrl: announcement.imageUrl,
+			icon: announcement.icon,
+			display: announcement.display,
+			forYou: announcement.userId === me?.id,
+			needConfirmationToRead: announcement.needConfirmationToRead,
+			silence: announcement.silence,
+			isRead: announcement.isRead !== null ? announcement.isRead : undefined,
+		};
+	}
+
+	@bindThis
+	public async packMany(
+		announcements: (MiAnnouncement['id'] | MiAnnouncement & { isRead?: boolean | null } | MiAnnouncement)[],
+		me?: { id: MiUser['id'] } | null | undefined,
+	) : Promise<Packed<'Announcement'>[]> {
+		return (await Promise.allSettled(announcements.map(x => this.pack(x, me))))
+			.filter(result => result.status === 'fulfilled')
+			.map(result => (result as PromiseFulfilledResult<Packed<'Announcement'>>).value);
+	}
+}
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 88d3999eb0b1..c645f4bcc61c 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -83,6 +83,7 @@ import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'
 import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
 import * as ep___admin_roles_users from './endpoints/admin/roles/users.js';
 import * as ep___announcements from './endpoints/announcements.js';
+import * as ep___announcements_show from './endpoints/announcements/show.js';
 import * as ep___antennas_create from './endpoints/antennas/create.js';
 import * as ep___antennas_delete from './endpoints/antennas/delete.js';
 import * as ep___antennas_list from './endpoints/antennas/list.js';
@@ -455,6 +456,7 @@ const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', us
 const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default };
 const $admin_roles_users: Provider = { provide: 'ep:admin/roles/users', useClass: ep___admin_roles_users.default };
 const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default };
+const $announcements_show: Provider = { provide: 'ep:announcements/show', useClass: ep___announcements_show.default };
 const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default };
 const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default };
 const $antennas_list: Provider = { provide: 'ep:antennas/list', useClass: ep___antennas_list.default };
@@ -831,6 +833,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$admin_roles_updateDefaultPolicies,
 		$admin_roles_users,
 		$announcements,
+		$announcements_show,
 		$antennas_create,
 		$antennas_delete,
 		$antennas_list,
@@ -1201,6 +1204,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$admin_roles_updateDefaultPolicies,
 		$admin_roles_users,
 		$announcements,
+		$announcements_show,
 		$antennas_create,
 		$antennas_delete,
 		$antennas_list,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index f7e64a7356d4..a38c62f35a9d 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -83,6 +83,7 @@ import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'
 import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
 import * as ep___admin_roles_users from './endpoints/admin/roles/users.js';
 import * as ep___announcements from './endpoints/announcements.js';
+import * as ep___announcements_show from './endpoints/announcements/show.js';
 import * as ep___antennas_create from './endpoints/antennas/create.js';
 import * as ep___antennas_delete from './endpoints/antennas/delete.js';
 import * as ep___antennas_list from './endpoints/antennas/list.js';
@@ -453,6 +454,7 @@ const eps = [
 	['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies],
 	['admin/roles/users', ep___admin_roles_users],
 	['announcements', ep___announcements],
+	['announcements/show', ep___announcements_show],
 	['antennas/create', ep___antennas_create],
 	['antennas/delete', ep___antennas_delete],
 	['antennas/list', ep___antennas_list],
diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts
index 3b12f5b62ccb..ff8dd73605c5 100644
--- a/packages/backend/src/server/api/endpoints/announcements.ts
+++ b/packages/backend/src/server/api/endpoints/announcements.ts
@@ -7,9 +7,9 @@ import { Inject, Injectable } from '@nestjs/common';
 import { Brackets } from 'typeorm';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { QueryService } from '@/core/QueryService.js';
-import { AnnouncementService } from '@/core/AnnouncementService.js';
+import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
 import { DI } from '@/di-symbols.js';
-import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/_.js';
+import type { AnnouncementsRepository } from '@/models/_.js';
 
 export const meta = {
 	tags: ['meta'],
@@ -44,11 +44,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.announcementsRepository)
 		private announcementsRepository: AnnouncementsRepository,
 
-		@Inject(DI.announcementReadsRepository)
-		private announcementReadsRepository: AnnouncementReadsRepository,
-
 		private queryService: QueryService,
-		private announcementService: AnnouncementService,
+		private announcementEntityService: AnnouncementEntityService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId)
@@ -60,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const announcements = await query.limit(ps.limit).getMany();
 
-			return this.announcementService.packMany(announcements, me);
+			return this.announcementEntityService.packMany(announcements, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/announcements/show.ts b/packages/backend/src/server/api/endpoints/announcements/show.ts
new file mode 100644
index 000000000000..6312a0a54cb8
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/announcements/show.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { EntityNotFoundError } from 'typeorm';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { AnnouncementService } from '@/core/AnnouncementService.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+	tags: ['meta'],
+
+	requireCredential: false,
+
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		ref: 'Announcement',
+	},
+
+	errors: {
+		noSuchAnnouncement: {
+			message: 'No such announcement.',
+			code: 'NO_SUCH_ANNOUNCEMENT',
+			id: 'b57b5e1d-4f49-404a-9edb-46b00268f121',
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		announcementId: { type: 'string', format: 'misskey:id' },
+	},
+	required: ['announcementId'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private announcementService: AnnouncementService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			try {
+				return await this.announcementService.getAnnouncement(ps.announcementId, me);
+			} catch (err) {
+				if (err instanceof EntityNotFoundError) throw new ApiError(meta.errors.noSuchAnnouncement);
+				throw err;
+			}
+		});
+	}
+}
diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts
index aa082ff2f280..81da0fac3126 100644
--- a/packages/backend/test/unit/AnnouncementService.ts
+++ b/packages/backend/test/unit/AnnouncementService.ts
@@ -10,6 +10,7 @@ import { ModuleMocker } from 'jest-mock';
 import { Test } from '@nestjs/testing';
 import { GlobalModule } from '@/GlobalModule.js';
 import { AnnouncementService } from '@/core/AnnouncementService.js';
+import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
 import type {
 	AnnouncementReadsRepository,
 	AnnouncementsRepository,
@@ -67,6 +68,7 @@ describe('AnnouncementService', () => {
 			],
 			providers: [
 				AnnouncementService,
+				AnnouncementEntityService,
 				CacheService,
 				IdService,
 			],
diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue
new file mode 100644
index 000000000000..85ae9062d47e
--- /dev/null
+++ b/packages/frontend/src/pages/announcement.vue
@@ -0,0 +1,142 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
+	<MkSpacer :contentMax="800">
+		<Transition
+			:enterActiveClass="defaultStore.state.animation ? $style.fadeEnterActive : ''"
+			:leaveActiveClass="defaultStore.state.animation ? $style.fadeLeaveActive : ''"
+			:enterFromClass="defaultStore.state.animation ? $style.fadeEnterFrom : ''"
+			:leaveToClass="defaultStore.state.animation ? $style.fadeLeaveTo : ''"
+			mode="out-in"
+		>
+			<div v-if="announcement" :key="announcement.id" class="_panel" :class="$style.announcement">
+				<div v-if="announcement.forYou" :class="$style.forYou"><i class="ti ti-pin"></i> {{ i18n.ts.forYou }}</div>
+				<div :class="$style.header">
+					<span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span>
+					<span style="margin-right: 0.5em;">
+						<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
+						<i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
+						<i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
+						<i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+					</span>
+					<Mfm :text="announcement.title"/>
+				</div>
+				<div :class="$style.content">
+					<Mfm :text="announcement.text"/>
+					<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
+					<div style="margin-top: 8px; opacity: 0.7; font-size: 85%;">
+						{{ i18n.ts.createdAt }}: <MkTime :time="announcement.createdAt" mode="detail"/>
+					</div>
+					<div v-if="announcement.updatedAt" style="opacity: 0.7; font-size: 85%;">
+						{{ i18n.ts.updatedAt }}: <MkTime :time="announcement.updatedAt" mode="detail"/>
+					</div>
+				</div>
+				<div v-if="$i && !announcement.silence && !announcement.isRead" :class="$style.footer">
+					<MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
+				</div>
+			</div>
+			<MkError v-else-if="error" @retry="fetch()"/>
+			<MkLoading v-else/>
+		</Transition>
+	</MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed, watch } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkButton from '@/components/MkButton.vue';
+import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i, updateAccount } from '@/account.js';
+import { defaultStore } from '@/store.js';
+
+const props = defineProps<{
+	announcementId: string;
+}>();
+
+const announcement = ref<Misskey.entities.Announcement | null>(null);
+const error = ref<any>(null);
+const path = computed(() => props.announcementId);
+
+function fetch() {
+	announcement.value = null;
+	misskeyApi('announcements/show', {
+		announcementId: props.announcementId,
+	}).then(async _announcement => {
+		announcement.value = _announcement;
+	}).catch(err => {
+		error.value = err;
+	});
+}
+
+async function read(target: Misskey.entities.Announcement): Promise<void> {
+	if (target.needConfirmationToRead) {
+		const confirm = await os.confirm({
+			type: 'question',
+			title: i18n.ts._announcement.readConfirmTitle,
+			text: i18n.tsx._announcement.readConfirmText({ title: target.title }),
+		});
+		if (confirm.canceled) return;
+	}
+
+	target.isRead = true;
+	await misskeyApi('i/read-announcement', { announcementId: target.id });
+	if ($i) {
+		updateAccount({
+			unreadAnnouncements: $i.unreadAnnouncements.filter((a: { id: string; }) => a.id !== target.id),
+		});
+	}
+}
+
+watch(() => path.value, fetch, { immediate: true });
+
+const headerActions = computed(() => []);
+
+const headerTabs = computed(() => []);
+
+definePageMetadata(() => ({
+	title: announcement.value ? `${i18n.ts.announcements}: ${announcement.value.title}` : i18n.ts.announcements,
+	icon: 'ti ti-speakerphone',
+}));
+</script>
+
+<style lang="scss" module>
+.announcement {
+	padding: 16px;
+}
+
+.forYou {
+	display: flex;
+	align-items: center;
+	line-height: 24px;
+	font-size: 90%;
+	white-space: pre;
+	color: #d28a3f;
+}
+
+.header {
+	margin-bottom: 16px;
+	font-weight: bold;
+	font-size: 120%;
+}
+
+.content {
+	> img {
+		display: block;
+		max-height: 300px;
+		max-width: 100%;
+	}
+}
+
+.footer {
+	margin-top: 16px;
+}
+</style>
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index bcd6eb7c0ff6..e50b208775c7 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -21,14 +21,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 								<i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
 								<i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
 							</span>
-							<span>{{ announcement.title }}</span>
+							<MkA :to="`/announcements/${announcement.id}`"><span>{{ announcement.title }}</span></MkA>
 						</div>
 						<div :class="$style.content">
 							<Mfm :text="announcement.text"/>
 							<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
-							<div style="opacity: 0.7; font-size: 85%;">
-								<MkTime :time="announcement.updatedAt ?? announcement.createdAt" mode="detail"/>
-							</div>
+							<MkA :to="`/announcements/${announcement.id}`">
+								<div style="margin-top: 8px; opacity: 0.7; font-size: 85%;">
+									{{ i18n.ts.createdAt }}: <MkTime :time="announcement.createdAt" mode="detail"/>
+								</div>
+								<div v-if="announcement.updatedAt" style="opacity: 0.7; font-size: 85%;">
+									{{ i18n.ts.updatedAt }}: <MkTime :time="announcement.updatedAt" mode="detail"/>
+								</div>
+							</MkA>
 						</div>
 						<div v-if="tab !== 'past' && $i && !announcement.silence && !announcement.isRead" :class="$style.footer">
 							<MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
@@ -73,24 +78,24 @@ const paginationEl = ref<InstanceType<typeof MkPagination>>();
 
 const tab = ref('current');
 
-async function read(announcement) {
-	if (announcement.needConfirmationToRead) {
+async function read(target) {
+	if (target.needConfirmationToRead) {
 		const confirm = await os.confirm({
 			type: 'question',
 			title: i18n.ts._announcement.readConfirmTitle,
-			text: i18n.tsx._announcement.readConfirmText({ title: announcement.title }),
+			text: i18n.tsx._announcement.readConfirmText({ title: target.title }),
 		});
 		if (confirm.canceled) return;
 	}
 
 	if (!paginationEl.value) return;
-	paginationEl.value.updateItem(announcement.id, a => {
+	paginationEl.value.updateItem(target.id, a => {
 		a.isRead = true;
 		return a;
 	});
-	misskeyApi('i/read-announcement', { announcementId: announcement.id });
+	misskeyApi('i/read-announcement', { announcementId: target.id });
 	updateAccount({
-		unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== announcement.id),
+		unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== target.id),
 	});
 }
 
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index c5b576f505c9..c12ae0fa572b 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -193,6 +193,9 @@ const routes: RouteDef[] = [{
 }, {
 	path: '/announcements',
 	component: page(() => import('@/pages/announcements.vue')),
+}, {
+	path: '/announcements/:announcementId',
+	component: page(() => import('@/pages/announcement.vue')),
 }, {
 	path: '/about',
 	component: page(() => import('@/pages/about.vue')),
diff --git a/packages/frontend/src/ui/_common_/announcements.vue b/packages/frontend/src/ui/_common_/announcements.vue
index 362c29e6c250..374bc20b5450 100644
--- a/packages/frontend/src/ui/_common_/announcements.vue
+++ b/packages/frontend/src/ui/_common_/announcements.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
 		:key="announcement.id"
 		:class="$style.item"
-		to="/announcements"
+		:to="`/announcements/${announcement.id}`"
 	>
 		<span :class="$style.icon">
 			<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 9cdb61da8770..6ff711cabbb5 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -336,6 +336,12 @@ type AnnouncementsRequest = operations['announcements']['requestBody']['content'
 // @public (undocumented)
 type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type AnnouncementsShowRequest = operations['announcements___show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AnnouncementsShowResponse = operations['announcements___show']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type Antenna = components['schemas']['Antenna'];
 
@@ -1224,6 +1230,8 @@ declare namespace entities {
         AdminRolesUsersResponse,
         AnnouncementsRequest,
         AnnouncementsResponse,
+        AnnouncementsShowRequest,
+        AnnouncementsShowResponse,
         AntennasCreateRequest,
         AntennasCreateResponse,
         AntennasDeleteRequest,
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 729107a78dbf..181f7274b7b6 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -851,6 +851,17 @@ declare module '../api.js' {
       credential?: string | null,
     ): Promise<SwitchCaseResponseType<E, P>>;
 
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'announcements/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
     /**
      * No description provided.
      * 
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index b0982e1e55d0..ab3baf16700d 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -101,6 +101,8 @@ import type {
 	AdminRolesUsersResponse,
 	AnnouncementsRequest,
 	AnnouncementsResponse,
+	AnnouncementsShowRequest,
+	AnnouncementsShowResponse,
 	AntennasCreateRequest,
 	AntennasCreateResponse,
 	AntennasDeleteRequest,
@@ -631,6 +633,7 @@ export type Endpoints = {
 	'admin/roles/update-default-policies': { req: AdminRolesUpdateDefaultPoliciesRequest; res: EmptyResponse };
 	'admin/roles/users': { req: AdminRolesUsersRequest; res: AdminRolesUsersResponse };
 	'announcements': { req: AnnouncementsRequest; res: AnnouncementsResponse };
+	'announcements/show': { req: AnnouncementsShowRequest; res: AnnouncementsShowResponse };
 	'antennas/create': { req: AntennasCreateRequest; res: AntennasCreateResponse };
 	'antennas/delete': { req: AntennasDeleteRequest; res: EmptyResponse };
 	'antennas/list': { req: EmptyRequest; res: AntennasListResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 60bf6659c0d9..02ca932d8a38 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -104,6 +104,8 @@ export type AdminRolesUsersRequest = operations['admin___roles___users']['reques
 export type AdminRolesUsersResponse = operations['admin___roles___users']['responses']['200']['content']['application/json'];
 export type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
 export type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
+export type AnnouncementsShowRequest = operations['announcements___show']['requestBody']['content']['application/json'];
+export type AnnouncementsShowResponse = operations['announcements___show']['responses']['200']['content']['application/json'];
 export type AntennasCreateRequest = operations['antennas___create']['requestBody']['content']['application/json'];
 export type AntennasCreateResponse = operations['antennas___create']['responses']['200']['content']['application/json'];
 export type AntennasDeleteRequest = operations['antennas___delete']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 1e9c190ca56f..208f03dc3efc 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -706,6 +706,15 @@ export type paths = {
      */
     post: operations['announcements'];
   };
+  '/announcements/show': {
+    /**
+     * announcements/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['announcements___show'];
+  };
   '/antennas/create': {
     /**
      * antennas/create
@@ -9662,6 +9671,60 @@ export type operations = {
       };
     };
   };
+  /**
+   * announcements/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  announcements___show: {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          announcementId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Announcement'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * antennas/create
    * @description No description provided.

From 1df8ea824e5dace883f0d6855d7342984c8032d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 27 May 2024 17:15:42 +0900
Subject: [PATCH 222/266] =?UTF-8?q?fix(backend):=20`/@`=20=E3=81=AB?=
 =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=BB=E3=82=B9=E3=81=99=E3=82=8B=E3=81=A8?=
 =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=82=A8=E3=83=A9=E3=83=BC?=
 =?UTF-8?q?=E3=81=8C=E7=99=BA=E7=94=9F=E3=81=99=E3=82=8B=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13884)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../backend/src/server/web/ClientServerService.ts    | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index f35ec8ba31ca..ab03489c0d76 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -466,7 +466,9 @@ export class ClientServerService {
 		};
 
 		// Atom
-		fastify.get<{ Params: { user: string; } }>('/@:user.atom', async (request, reply) => {
+		fastify.get<{ Params: { user?: string; } }>('/@:user.atom', async (request, reply) => {
+			if (request.params.user == null) return await renderBase(reply);
+
 			const feed = await getFeed(request.params.user);
 
 			if (feed) {
@@ -479,7 +481,9 @@ export class ClientServerService {
 		});
 
 		// RSS
-		fastify.get<{ Params: { user: string; } }>('/@:user.rss', async (request, reply) => {
+		fastify.get<{ Params: { user?: string; } }>('/@:user.rss', async (request, reply) => {
+			if (request.params.user == null) return await renderBase(reply);
+
 			const feed = await getFeed(request.params.user);
 
 			if (feed) {
@@ -492,7 +496,9 @@ export class ClientServerService {
 		});
 
 		// JSON
-		fastify.get<{ Params: { user: string; } }>('/@:user.json', async (request, reply) => {
+		fastify.get<{ Params: { user?: string; } }>('/@:user.json', async (request, reply) => {
+			if (request.params.user == null) return await renderBase(reply);
+
 			const feed = await getFeed(request.params.user);
 
 			if (feed) {

From 1b81ca45636db21166753e0aa00d91ab23e46ac5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 27 May 2024 17:16:47 +0900
Subject: [PATCH 223/266] =?UTF-8?q?enhance(frontend):=20=E3=80=8C=E8=A6=8B?=
 =?UTF-8?q?=E3=81=9F=E3=81=93=E3=81=A8=E3=81=AE=E3=81=82=E3=82=8B=E3=83=AA?=
 =?UTF-8?q?=E3=83=8E=E3=83=BC=E3=83=88=E3=82=92=E7=9C=81=E7=95=A5=E3=81=97?=
 =?UTF-8?q?=E3=81=A6=E8=A1=A8=E7=A4=BA=E3=80=8D=E3=81=AE=E5=90=8D=E7=A7=B0?=
 =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4=20(#13883)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 「見たことのあるリノートを省略して表示」の名称を変更

* ひとつだけcaptionが入ってるやつが真ん中にいると不格好だったので場所変更
---
 locales/index.d.ts                               | 6 +++++-
 locales/ja-JP.yml                                | 3 ++-
 packages/frontend/src/pages/settings/general.vue | 5 ++++-
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 18d8eee18fa2..eb7e297aa325 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4113,9 +4113,13 @@ export interface Locale extends ILocale {
      */
     "thisPostMayBeAnnoyingIgnore": string;
     /**
-     * 見たことのあるリノートを省略して表示
+     * リノートのスマート省略
      */
     "collapseRenotes": string;
+    /**
+     * リアクションやリノートをしたことがあるノートをたたんで表示します。
+     */
+    "collapseRenotesDescription": string;
     /**
      * サーバー内部エラー
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 8b1738aebe19..ebaf16745c01 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1024,7 +1024,8 @@ thisPostMayBeAnnoying: "この投稿は迷惑になる可能性があります
 thisPostMayBeAnnoyingHome: "ホームに投稿"
 thisPostMayBeAnnoyingCancel: "やめる"
 thisPostMayBeAnnoyingIgnore: "このまま投稿"
-collapseRenotes: "見たことのあるリノートを省略して表示"
+collapseRenotes: "リノートのスマート省略"
+collapseRenotesDescription: "リアクションやリノートをしたことがあるノートをたたんで表示します。"
 internalServerError: "サーバー内部エラー"
 internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。"
 copyErrorInfo: "エラー情報をコピー"
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 55d514ddf9d6..cfc63f2a08d9 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -50,9 +50,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 		<div class="_gaps_m">
 			<div class="_gaps_s">
+				<MkSwitch v-model="collapseRenotes">
+					<template #label>{{ i18n.ts.collapseRenotes }}</template>
+					<template #caption>{{ i18n.ts.collapseRenotesDescription }}</template>
+				</MkSwitch>
 				<MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
 				<MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
-				<MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
 				<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
 				<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
 				<MkSwitch v-if="advancedMfm" v-model="enableQuickAddMfmFunction">{{ i18n.ts.enableQuickAddMfmFunction }}</MkSwitch>

From 805a11aadbbc0f0a32531fd86443de514df74466 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 27 May 2024 17:18:12 +0900
Subject: [PATCH 224/266] =?UTF-8?q?enhance(backend):=20=E3=83=97=E3=83=AD?=
 =?UTF-8?q?=E3=83=95=E3=82=A3=E3=83=BC=E3=83=AB=E3=81=AE=E3=83=AA=E3=83=B3?=
 =?UTF-8?q?=E3=82=AF=E6=A4=9C=E8=A8=BC=E3=81=ABtry-catch=E3=82=92=E8=BF=BD?=
 =?UTF-8?q?=E5=8A=A0=20(#13882)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(backend): プロフィールのリンク検証にtry-catchを追加

* :v:
---
 .../src/server/api/endpoints/i/update.ts      | 36 +++++++++++--------
 1 file changed, 21 insertions(+), 15 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index 84a1931a3df5..a8e702f328e6 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -498,26 +498,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	private async verifyLink(url: string, user: MiLocalUser) {
 		if (!safeForSql(url)) return;
 
-		const html = await this.httpRequestService.getHtml(url);
+		try {
+			const html = await this.httpRequestService.getHtml(url);
 
-		const { window } = new JSDOM(html);
-		const doc = window.document;
+			const { window } = new JSDOM(html);
+			const doc = window.document;
 
-		const myLink = `${this.config.url}/@${user.username}`;
+			const myLink = `${this.config.url}/@${user.username}`;
 
-		const aEls = Array.from(doc.getElementsByTagName('a'));
-		const linkEls = Array.from(doc.getElementsByTagName('link'));
+			const aEls = Array.from(doc.getElementsByTagName('a'));
+			const linkEls = Array.from(doc.getElementsByTagName('link'));
 
-		const includesMyLink = aEls.some(a => a.href === myLink);
-		const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.rel === 'me' && link.href === myLink);
+			const includesMyLink = aEls.some(a => a.href === myLink);
+			const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.rel === 'me' && link.href === myLink);
 
-		if (includesMyLink || includesRelMeLinks) {
-			await this.userProfilesRepository.createQueryBuilder('profile').update()
-				.where('userId = :userId', { userId: user.id })
-				.set({
-					verifiedLinks: () => `array_append("verifiedLinks", '${url}')`, // ここでSQLインジェクションされそうなのでとりあえず safeForSql で弾いている
-				})
-				.execute();
+			if (includesMyLink || includesRelMeLinks) {
+				await this.userProfilesRepository.createQueryBuilder('profile').update()
+					.where('userId = :userId', { userId: user.id })
+					.set({
+						verifiedLinks: () => `array_append("verifiedLinks", '${url}')`, // ここでSQLインジェクションされそうなのでとりあえず safeForSql で弾いている
+					})
+					.execute();
+			}
+
+			window.close();
+		} catch (err) {
+			// なにもしない
 		}
 	}
 }

From d013e4516d7afb6ed4362467f69df2d79b9f0f9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 27 May 2024 17:19:09 +0900
Subject: [PATCH 225/266] =?UTF-8?q?enhance(frontend):=20=E3=81=8A=E6=B0=97?=
 =?UTF-8?q?=E3=81=AB=E5=85=A5=E3=82=8A=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D?=
 =?UTF-8?q?=E3=83=AB=E3=82=92=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13881)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/cache.ts                   |  1 +
 packages/frontend/src/pages/channel.vue          |  3 +++
 packages/frontend/src/pages/timeline.vue         |  6 ++----
 packages/frontend/src/scripts/get-note-menu.ts   |  6 ++----
 packages/frontend/src/ui/deck/channel-column.vue | 13 ++++++-------
 5 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts
index b286528de6c9..bfe8fbe0e40a 100644
--- a/packages/frontend/src/cache.ts
+++ b/packages/frontend/src/cache.ts
@@ -11,3 +11,4 @@ export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, ()
 export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/roles/list'));
 export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list'));
 export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list'));
+export const favoritedChannelsCache = new Cache<Misskey.entities.Channel[]>(1000 * 60 * 30, () => misskeyApi('channels/my-favorites', { limit: 100 }));
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 611ae6feca0f..a895df76e8e9 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -83,6 +83,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { deviceKind } from '@/scripts/device-kind.js';
 import MkNotes from '@/components/MkNotes.vue';
 import { url } from '@/config.js';
+import { favoritedChannelsCache } from '@/cache.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import { defaultStore } from '@/store.js';
@@ -153,6 +154,7 @@ function favorite() {
 		channelId: channel.value.id,
 	}).then(() => {
 		favorited.value = true;
+		favoritedChannelsCache.delete();
 	});
 }
 
@@ -168,6 +170,7 @@ async function unfavorite() {
 		channelId: channel.value.id,
 	}).then(() => {
 		favorited.value = false;
+		favoritedChannelsCache.delete();
 	});
 }
 
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 48dfc1fd44fd..98744c631855 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -48,7 +48,7 @@ import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import { $i } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { antennasCache, userListsCache } from '@/cache.js';
+import { antennasCache, userListsCache, favoritedChannelsCache } from '@/cache.js';
 import { deviceKind } from '@/scripts/device-kind.js';
 import { deepMerge } from '@/scripts/merge.js';
 import { MenuItem } from '@/types/menu.js';
@@ -173,9 +173,7 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> {
 }
 
 async function chooseChannel(ev: MouseEvent): Promise<void> {
-	const channels = await misskeyApi('channels/my-favorites', {
-		limit: 100,
-	});
+	const channels = await favoritedChannelsCache.fetch();
 	const items: MenuItem[] = [
 		...channels.map(channel => {
 			const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null;
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index e7c9a848e0f4..71ad299f507f 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -16,7 +16,7 @@ import { url } from '@/config.js';
 import { defaultStore, noteActions } from '@/store.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { getUserMenu } from '@/scripts/get-user-menu.js';
-import { clipsCache } from '@/cache.js';
+import { clipsCache, favoritedChannelsCache } from '@/cache.js';
 import { MenuItem } from '@/types/menu.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { isSupportShare } from '@/scripts/navigator.js';
@@ -603,9 +603,7 @@ export function getRenoteMenu(props: {
 			icon: 'ti ti-repeat',
 			text: appearNote.channel ? i18n.ts.renoteToOtherChannel : i18n.ts.renoteToChannel,
 			children: async () => {
-				const channels = await misskeyApi('channels/my-favorites', {
-					limit: 30,
-				});
+				const channels = await favoritedChannelsCache.fetch();
 				return channels.filter((channel) => {
 					if (!appearNote.channelId) return true;
 					return channel.id !== appearNote.channelId;
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index bd3b059497b1..28c741bba238 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -26,6 +26,7 @@ import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { favoritedChannelsCache } from '@/cache.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
@@ -42,20 +43,18 @@ if (props.column.channelId == null) {
 }
 
 async function setChannel() {
-	const channels = await misskeyApi('channels/my-favorites', {
-		limit: 100,
-	});
-	const { canceled, result: channel } = await os.select({
+	const channels = await favoritedChannelsCache.fetch();
+	const { canceled, result: chosenChannel } = await os.select({
 		title: i18n.ts.selectChannel,
 		items: channels.map(x => ({
 			value: x, text: x.name,
 		})),
 		default: props.column.channelId,
 	});
-	if (canceled) return;
+	if (canceled || chosenChannel == null) return;
 	updateColumn(props.column.id, {
-		channelId: channel.id,
-		name: channel.name,
+		channelId: chosenChannel.id,
+		name: chosenChannel.name,
 	});
 }
 

From 6af9492ea5492c02a11302afe7c6a6e83c00de1b Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Mon, 27 May 2024 17:21:05 +0900
Subject: [PATCH 226/266] Quick action implement (#13878)

* enhance(frontend): quick action for file admin-lookup

* docs(changelog): update changelog

* enhance(frontend): quick action for general admin-lookup, remove unimplemented note, instance admin-lookup

* docs(changelog): update changelog

* chore: fix lint
---
 CHANGELOG.md                                  |  2 ++
 packages/frontend/src/pages/admin/files.vue   | 27 ++-----------------
 packages/frontend/src/pages/admin/index.vue   | 21 ++++++---------
 packages/frontend/src/pages/admin/users.vue   |  2 +-
 .../{lookup-user.ts => admin-lookup.ts}       | 23 ++++++++++++++++
 5 files changed, 36 insertions(+), 39 deletions(-)
 rename packages/frontend/src/scripts/{lookup-user.ts => admin-lookup.ts} (72%)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e23fa8f7273..d23f512e3ded 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,8 @@
 - Enhance: AiScriptを0.18.0にバージョンアップ
 - Enhance: 通常のノートでも、お気に入りに登録したチャンネルにリノートできるように
 - Enhance: 長いテキストをペーストした際にテキストファイルとして添付するかどうかを選択できるように
+- Enhance: コントロールパネルのクイックアクションからファイルを照会できるように
+- Enhance: コントロールパネルのクイックアクションから通常の照会を行えるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
 - Fix: 周年の実績が閏年を考慮しない問題を修正
 - Fix: ローカルURLのプレビューポップアップが左上に表示される
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 3fe021e77102..5132b85c64ed 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -42,7 +42,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
 import * as os from '@/os.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
+import { lookupFile } from '@/scripts/admin-lookup.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -73,33 +73,10 @@ function clear() {
 	});
 }
 
-function show(file) {
-	os.pageWindow(`/admin/file/${file.id}`);
-}
-
-async function find() {
-	const { canceled, result: q } = await os.inputText({
-		title: i18n.ts.fileIdOrUrl,
-		minLength: 1,
-	});
-	if (canceled) return;
-
-	misskeyApi('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => {
-		show(file);
-	}).catch(err => {
-		if (err.code === 'NO_SUCH_FILE') {
-			os.alert({
-				type: 'error',
-				text: i18n.ts.notFound,
-			});
-		}
-	});
-}
-
 const headerActions = computed(() => [{
 	text: i18n.ts.lookup,
 	icon: 'ti ti-search',
-	handler: find,
+	handler: lookupFile,
 }, {
 	text: i18n.ts.clearCachedFiles,
 	icon: 'ti ti-trash',
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index d4a41c66ccb6..eef1c8afa944 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -33,9 +33,10 @@ import { i18n } from '@/i18n.js';
 import MkSuperMenu from '@/components/MkSuperMenu.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import { instance } from '@/instance.js';
+import { lookup } from '@/scripts/lookup.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
-import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js';
+import { lookupUser, lookupUserByEmail, lookupFile } from '@/scripts/admin-lookup.js';
 import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
 import { useRouter } from '@/router/supplier.js';
 
@@ -82,7 +83,7 @@ const menuDef = computed(() => [{
 		type: 'button',
 		icon: 'ti ti-search',
 		text: i18n.ts.lookup,
-		action: lookup,
+		action: adminLookup,
 	}, ...(instance.disableRegistration ? [{
 		type: 'button',
 		icon: 'ti ti-user-plus',
@@ -282,7 +283,7 @@ function invite() {
 	});
 }
 
-function lookup(ev: MouseEvent) {
+function adminLookup(ev: MouseEvent) {
 	os.popupMenu([{
 		text: i18n.ts.user,
 		icon: 'ti ti-user',
@@ -295,23 +296,17 @@ function lookup(ev: MouseEvent) {
 		action: () => {
 			lookupUserByEmail();
 		},
-	}, {
-		text: i18n.ts.note,
-		icon: 'ti ti-pencil',
-		action: () => {
-			alert('TODO');
-		},
 	}, {
 		text: i18n.ts.file,
 		icon: 'ti ti-cloud',
 		action: () => {
-			alert('TODO');
+			lookupFile();
 		},
 	}, {
-		text: i18n.ts.instance,
-		icon: 'ti ti-planet',
+		text: i18n.ts.lookup,
+		icon: 'ti ti-world-search',
 		action: () => {
-			alert('TODO');
+			lookup();
 		},
 	}], ev.currentTarget ?? ev.target);
 }
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index 06317760d2ac..7d87b97a3615 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -63,7 +63,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
-import { lookupUser } from '@/scripts/lookup-user.js';
+import { lookupUser } from '@/scripts/admin-lookup.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
diff --git a/packages/frontend/src/scripts/lookup-user.ts b/packages/frontend/src/scripts/admin-lookup.ts
similarity index 72%
rename from packages/frontend/src/scripts/lookup-user.ts
rename to packages/frontend/src/scripts/admin-lookup.ts
index efc9132e757c..1b57b853c97b 100644
--- a/packages/frontend/src/scripts/lookup-user.ts
+++ b/packages/frontend/src/scripts/admin-lookup.ts
@@ -63,3 +63,26 @@ export async function lookupUserByEmail() {
 		}
 	}
 }
+
+export async function lookupFile() {
+	const { canceled, result: q } = await os.inputText({
+		title: i18n.ts.fileIdOrUrl,
+		minLength: 1,
+	});
+	if (canceled) return;
+
+	const show = (file) => {
+		os.pageWindow(`/admin/file/${file.id}`);
+	};
+
+	misskeyApi('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => {
+		show(file);
+	}).catch(err => {
+		if (err.code === 'NO_SUCH_FILE') {
+			os.alert({
+				type: 'error',
+				text: i18n.ts.notFound,
+			});
+		}
+	});
+}

From 140df4b5e050f1c2b55e08f9c5b511588b0370d2 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 27 May 2024 08:27:39 +0000
Subject: [PATCH 227/266] Bump version to 2024.5.0-beta.3

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index b9ac4fc2a16f..22e5217ea287 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-beta.2",
+	"version": "2024.5.0-beta.3",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 80ae84796aaa..d72004862c72 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-beta.2",
+	"version": "2024.5.0-beta.3",
 	"description": "Misskey SDK for JavaScript",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",

From e50107792c870098ac78a64d8a92e69d5f11893a Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 27 May 2024 08:37:07 +0000
Subject: [PATCH 228/266] Bump version to 2024.5.0-beta.4

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 22e5217ea287..ca3883b804b5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-beta.3",
+	"version": "2024.5.0-beta.4",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index d72004862c72..bad0142899b5 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-beta.3",
+	"version": "2024.5.0-beta.4",
 	"description": "Misskey SDK for JavaScript",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",

From 28e0e20879d2b2834b5f3f47fdf8663afa8a07f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Mon, 27 May 2024 19:22:46 +0900
Subject: [PATCH 229/266] [ci skip] Delete .github/FUNDING.yml

use misskey-dev/.github repository
---
 .github/FUNDING.yml | 4 ----
 1 file changed, 4 deletions(-)
 delete mode 100644 .github/FUNDING.yml

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index d42b58abc09c..000000000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-# These are supported funding model platforms
-
-github: [misskey-dev]
-patreon: syuilo

From cf2256cf4162f0f58fea3afbe08d9805451a9efc Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Mon, 27 May 2024 20:11:39 +0900
Subject: [PATCH 230/266] fix: CHANGELOG not reflecting correctly (#13888)

* fix: CHANGELOG not reflecting correctly

* Update .github/workflows/release-edit-with-push.yml

Co-authored-by: anatawa12 <anatawa12@icloud.com>

---------

Co-authored-by: anatawa12 <anatawa12@icloud.com>
---
 .github/workflows/release-edit-with-push.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml
index 944b98eb7cc2..890cb047bd05 100644
--- a/.github/workflows/release-edit-with-push.yml
+++ b/.github/workflows/release-edit-with-push.yml
@@ -37,4 +37,7 @@ jobs:
       # PRのnotesを更新
       - name: Update PR
         run: |
-          gh pr edit ${{ steps.get_pr.outputs.pr_number }} --body "${{ steps.changelog.outputs.changelog }}"
+          gh pr edit "$PR_NUMBER" --body "$CHANGELOG"
+        env:
+          CHANGELOG: ${{ steps.changelog.outputs.changelog }}
+          PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}

From a7a8dc4dbbab075cdee140f468fd7e3559cde475 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 27 May 2024 20:12:25 +0900
Subject: [PATCH 231/266] =?UTF-8?q?=E3=82=82=E3=81=A8=E3=82=82=E3=81=A8?=
 =?UTF-8?q?=E3=82=BB=E3=83=B3=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=81=A7?=
 =?UTF-8?q?=E3=81=AF=E3=81=AA=E3=81=84=E3=81=A8=E9=80=A3=E5=90=88=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=81=A6=E3=81=84=E3=81=9F=E3=83=95=E3=82=A1=E3=82=A4?=
 =?UTF-8?q?=E3=83=AB=E3=81=8C=E3=82=BB=E3=83=B3=E3=82=B7=E3=83=86=E3=82=A3?=
 =?UTF-8?q?=E3=83=96=E3=81=A8=E3=81=97=E3=81=A6=E9=80=A3=E5=90=88=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AB=E3=82=BB=E3=83=B3?=
 =?UTF-8?q?=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=81=A8=E3=81=97=E3=81=A6?=
 =?UTF-8?q?=E3=81=9D=E3=81=AE=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92?=
 =?UTF-8?q?=E6=89=B1=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=20(#13879)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(backend): mark an file as sensitive if the file was newly federated as sensitive

* docs(changelog): もともとセンシティブではないと連合されていたファイルがセンシティブとして連合された場合にセンシティブとしてそのファイルを扱うように

* fix: change way to update federated image

* Update packages/backend/src/core/DriveService.ts

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>

* update isSensitive of existing record object

---------

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
---
 CHANGELOG.md                              | 2 ++
 packages/backend/src/core/DriveService.ts | 6 ++++++
 2 files changed, 8 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d23f512e3ded..f8463f8cbf60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -96,6 +96,8 @@
 - Fix: `/i/notifications`に `includeTypes`か`excludeTypes`を指定しているとき、通知が存在するのに空配列を返すことがある問題を修正
 - Fix: 複数idを指定する`users/show`が関係ないユーザを返すことがある問題を修正
 - Fix: `/tags` と `/user-tags` が検索エンジンにインデックスされないように
+- Fix: もともとセンシティブではないと連合されていたファイルがセンシティブとして連合された場合にセンシティブとしてそのファイルを扱うように
+  - センシティブとして連合したファイルは非センシティブとして連合されてもセンシティブとして扱われます
 
 ## 2024.3.1
 
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 1bc1df1dda4c..63fa26f69d5c 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -504,6 +504,12 @@ export class DriveService {
 
 			if (much) {
 				this.registerLogger.info(`file with same hash is found: ${much.id}`);
+				if (sensitive && !much.isSensitive) {
+					// The file is federated as sensitive for this time, but was federated as non-sensitive before.
+					// Therefore, update the file to sensitive.
+					await this.driveFilesRepository.update({ id: much.id }, { isSensitive: true });
+					much.isSensitive = true;
+				}
 				return much;
 			}
 		}

From d7982e471c11d0656fa1266b2e4747ca5179647d Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Mon, 27 May 2024 20:24:15 +0900
Subject: [PATCH 232/266] New Crowdin updates (#13860)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Romanian)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Dutch)

* New translations ja-jp.yml (Norwegian)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Turkish)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Uzbek)

* New translations ja-jp.yml (Lao)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Danish)

* New translations ja-jp.yml (Chinese Simplified)
---
 locales/ar-SA.yml |   2 +
 locales/bn-BD.yml |   4 ++
 locales/ca-ES.yml |   5 +-
 locales/cs-CZ.yml |   4 ++
 locales/da-DK.yml |   2 +
 locales/de-DE.yml |   4 ++
 locales/en-US.yml |  14 +++++-
 locales/es-ES.yml |   5 +-
 locales/fr-FR.yml |   4 ++
 locales/id-ID.yml |  14 +++++-
 locales/it-IT.yml |   5 +-
 locales/ja-KS.yml |   5 +-
 locales/ko-GS.yml |   4 ++
 locales/ko-KR.yml |   5 +-
 locales/lo-LA.yml |   4 ++
 locales/nl-NL.yml |   4 ++
 locales/no-NO.yml |   2 +
 locales/pl-PL.yml |   4 ++
 locales/pt-PT.yml |   4 ++
 locales/ro-RO.yml |   4 ++
 locales/ru-RU.yml |   4 ++
 locales/sk-SK.yml |   4 ++
 locales/sv-SE.yml |   4 ++
 locales/th-TH.yml |   5 +-
 locales/tr-TR.yml |   4 ++
 locales/uk-UA.yml |   4 ++
 locales/uz-UZ.yml |   4 ++
 locales/vi-VN.yml |   4 ++
 locales/zh-CN.yml |  14 +++++-
 locales/zh-TW.yml | 124 +++++++++++++++++++++++++---------------------
 30 files changed, 205 insertions(+), 65 deletions(-)

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 88707fe1118e..955d672c1d05 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -1016,6 +1016,8 @@ sourceCode: "الشفرة المصدرية"
 flip: "اقلب"
 lastNDays: "آخر {n} أيام"
 surrender: "ألغِ"
+_delivery:
+  stop: "مُعلّق"
 _initialAccountSetting:
   accountCreated: "نجح إنشاء حسابك!"
   letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي."
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index dc5d315aed9f..abcf07da831e 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -857,6 +857,10 @@ replies: "জবাব"
 renotes: "রিনোট"
 sourceCode: "সোর্স কোড"
 flip: "উল্টান"
+_delivery:
+  stop: "স্থগিত করা হয়েছে"
+  _type:
+    none: "প্রকাশ করা হচ্ছে"
 _role:
   priority: "অগ্রাধিকার"
   _priority:
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index d035555c73af..0345ee0326e9 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -1224,6 +1224,10 @@ gameRetry: "Torna a provar"
 notUsePleaseLeaveBlank: "Si no voleu usar-ho, deixeu-ho en blanc"
 useTotp: "Usa una contrasenya d'un sol ús"
 useBackupCode: "Usa un codi de recuperació"
+_delivery:
+  stop: "Suspés"
+  _type:
+    none: "S'està publicant"
 _bubbleGame:
   howToPlay: "Com es juga"
   _howToPlay:
@@ -2001,7 +2005,6 @@ _permissions:
   "read:admin:server-info": "Veure informació del servidor"
   "read:admin:show-moderation-log": "Veure registre de moderació "
   "read:admin:show-user": "Veure informació privada de l'usuari "
-  "read:admin:show-users": "Veure informació privada de l'usuari "
   "write:admin:suspend-user": "Suspendre usuari"
   "write:admin:unset-user-avatar": "Esborrar avatar d'usuari "
   "write:admin:unset-user-banner": "Esborrar bàner de l'usuari "
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index cff533976ec6..c8a0b0cb2804 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1099,6 +1099,10 @@ sourceCode: "Zdrojový kód"
 flip: "Otočit"
 lastNDays: "Posledních {n} dnů"
 surrender: "Zrušit"
+_delivery:
+  stop: "Suspendováno"
+  _type:
+    none: "Publikuji"
 _initialAccountSetting:
   accountCreated: "Váš účet byl úspěšně vytvořen!"
   letsStartAccountSetup: "Pro začátek si nastavte svůj profil."
diff --git a/locales/da-DK.yml b/locales/da-DK.yml
index 08c15ed092fc..5eb7a5a5f417 100644
--- a/locales/da-DK.yml
+++ b/locales/da-DK.yml
@@ -1,2 +1,4 @@
 ---
 _lang_: "Dansk"
+headlineMisskey: ""
+introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスです。\n「ノート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 3e1c40512e77..9e42e0125257 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1185,6 +1185,10 @@ addMfmFunction: "MFM hinzufügen"
 sfx: "Soundeffekte"
 lastNDays: "Letzten {n} Tage"
 surrender: "Abbrechen"
+_delivery:
+  stop: "Gesperrt"
+  _type:
+    none: "Wird veröffentlicht"
 _announcement:
   forExistingUsers: "Nur für existierende Nutzer"
   forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 10e9fd778e70..c20a1ac7d8ef 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -108,11 +108,14 @@ enterEmoji: "Enter an emoji"
 renote: "Renote"
 unrenote: "Remove renote"
 renoted: "Renoted."
+renotedToX: "Renote from {name} users。"
 cantRenote: "This post can't be renoted."
 cantReRenote: "A renote can't be renoted."
 quote: "Quote"
 inChannelRenote: "Channel-only Renote"
 inChannelQuote: "Channel-only Quote"
+renoteToChannel: "Renote to channel"
+renoteToOtherChannel: "Renote to other channel"
 pinnedNote: "Pinned note"
 pinned: "Pin to profile"
 you: "You"
@@ -468,6 +471,7 @@ retype: "Enter again"
 noteOf: "Note by {user}"
 quoteAttached: "Quote"
 quoteQuestion: "Append as quote?"
+attachAsFileQuestion: "The text in clipboard is long. Would you want to attach it as text file?"
 noMessagesYet: "No messages yet"
 newMessageExists: "There are new messages"
 onlyOneFileCanBeAttached: "You can only attach one file to a message"
@@ -1235,6 +1239,15 @@ keepOriginalFilenameDescription: "If you turn off this setting, files names will
 noDescription: "There is not the explanation"
 alwaysConfirmFollow: "Always confirm when following"
 inquiry: "Contact"
+_delivery:
+  status: "Delivery status"
+  stop: "Suspended"
+  resume: "Delivery resume"
+  _type:
+    none: "Publishing"
+    manuallySuspended: "Manually suspended"
+    goneSuspended: "Server is suspended due to server deletion"
+    autoSuspendedForNotResponding: "Server is suspended due to no responding"
 _bubbleGame:
   howToPlay: "How to play"
   hold: "Hold"
@@ -2032,7 +2045,6 @@ _permissions:
   "read:admin:server-info": "View server info"
   "read:admin:show-moderation-log": "View moderation log"
   "read:admin:show-user": "View private user info"
-  "read:admin:show-users": "View private user info"
   "write:admin:suspend-user": "Suspend user"
   "write:admin:unset-user-avatar": "Remove user avatar"
   "write:admin:unset-user-banner": "Remove user banner"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 2e05364c312f..5c8249ded50f 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -1233,6 +1233,10 @@ useNativeUIForVideoAudioPlayer: "Usar la interfaz del navegador cuando se reprod
 keepOriginalFilename: "Mantener el nombre original del archivo"
 noDescription: "No hay descripción"
 alwaysConfirmFollow: "Confirmar siempre cuando se sigue a alguien"
+_delivery:
+  stop: "Suspendido"
+  _type:
+    none: "Publicando"
 _bubbleGame:
   howToPlay: "Cómo jugar"
   hold: "Mantener"
@@ -2029,7 +2033,6 @@ _permissions:
   "read:admin:server-info": "Ver información del servidor"
   "read:admin:show-moderation-log": "Ver log de moderación"
   "read:admin:show-user": "Ver información privada de usuario"
-  "read:admin:show-users": "Ver información privada de usuario"
   "write:admin:suspend-user": "Suspender cuentas de usuario"
   "write:admin:unset-user-avatar": "Quitar avatares de usuario"
   "write:admin:unset-user-banner": "Quitar banner de usuarios"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 58a11a5cc4ea..8d66c3d37572 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1224,6 +1224,10 @@ enableHorizontalSwipe: "Glisser pour changer d'onglet"
 loading: "Chargement en cours"
 surrender: "Annuler"
 gameRetry: "Réessayer"
+_delivery:
+  stop: "Suspendu·e"
+  _type:
+    none: "Publié"
 _bubbleGame:
   howToPlay: "Comment jouer"
   hold: "Réserver"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index f8e645d63b57..7f509afa501a 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -108,11 +108,14 @@ enterEmoji: "Masukkan emoji"
 renote: "Renote"
 unrenote: "Hapus renote"
 renoted: "Telah direnote"
+renotedToX: "{name} telah merenote"
 cantRenote: "Postingan ini tidak dapat direnote"
 cantReRenote: "Renote tidak dapat direnote"
 quote: "Kutip"
 inChannelRenote: "Hanya renote dalam kanal"
 inChannelQuote: "Hanya kutip dalam kanal"
+renoteToChannel: "Renote ke kanal"
+renoteToOtherChannel: "Renote ke kanal lainnya"
 pinnedNote: "Catatan yang disematkan"
 pinned: "Sematkan ke profil"
 you: "Kamu"
@@ -468,6 +471,7 @@ retype: "Masukkan ulang"
 noteOf: "Catatan milik {user}"
 quoteAttached: "Dikutip"
 quoteQuestion: "Apakah kamu ingin menambahkan kutipan?"
+attachAsFileQuestion: "Teks dalam papan klip terlalu panjang. Apakah kamu ingin melampirkannya sebagai berkas teks?"
 noMessagesYet: "Tidak ada pesan"
 newMessageExists: "Kamu mendapatkan pesan baru"
 onlyOneFileCanBeAttached: "Kamu hanya dapat melampirkan satu berkas ke dalam pesan"
@@ -1235,6 +1239,15 @@ keepOriginalFilenameDescription: "Apabila pengaturan ini dimatikan, nama berkas
 noDescription: "Tidak ada deskripsi"
 alwaysConfirmFollow: "Selalu konfirmasi ketika mengikuti"
 inquiry: "Hubungi kami"
+_delivery:
+  status: "Status pengiriman"
+  stop: "Ditangguhkan"
+  resume: "Lanjutkan pengiriman"
+  _type:
+    none: "Sedang menyiarkan langsung"
+    manuallySuspended: "Ditangguhkan manual"
+    goneSuspended: "Sedang ditangguhkan untuk penghapusan peladen"
+    autoSuspendedForNotResponding: "Sedang ditangguhkan karena peladen tidak menjawab"
 _bubbleGame:
   howToPlay: "Cara bermain"
   hold: "Tahan"
@@ -2032,7 +2045,6 @@ _permissions:
   "read:admin:server-info": "Lihat informasi peladen"
   "read:admin:show-moderation-log": "Lihat log moderasi"
   "read:admin:show-user": "Lihat informasi pengguna privat"
-  "read:admin:show-users": "Lihat informasi pengguna privat"
   "write:admin:suspend-user": "Tangguhkan pengguna"
   "write:admin:unset-user-avatar": "Hapus avatar pengguna"
   "write:admin:unset-user-banner": "Hapus banner pengguna"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 0a250a2e289e..1d12a62ccadc 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -1233,6 +1233,10 @@ useNativeUIForVideoAudioPlayer: "Riprodurre audio/video usando le funzionalità
 keepOriginalFilename: "Mantieni il nome file originale"
 keepOriginalFilenameDescription: "Disattivandola, i file verranno caricati usando nomi casuali."
 noDescription: "Manca la descrizione"
+_delivery:
+  stop: "Sospensione"
+  _type:
+    none: "Pubblicazione"
 _bubbleGame:
   howToPlay: "Come giocare"
   hold: "Tieni"
@@ -2025,7 +2029,6 @@ _permissions:
   "read:admin:server-info": "Vedere le informazioni sul server"
   "read:admin:show-moderation-log": "Vedere lo storico di moderazione"
   "read:admin:show-user": "Vedere le informazioni private degli account utente"
-  "read:admin:show-users": "Vedere le informazioni private degli account utente"
   "write:admin:suspend-user": "Sospendere i profili"
   "write:admin:unset-user-avatar": "Rimuovere la foto profilo dai profili"
   "write:admin:unset-user-banner": "Rimuovere l'immagine testata dai profili"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index e6a23a34d7e2..7a33968e9e37 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -1235,6 +1235,10 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ
 noDescription: "説明文はあらへんで"
 alwaysConfirmFollow: "フォローの際常に確認する"
 inquiry: "問い合わせ"
+_delivery:
+  stop: "配信せぇへん"
+  _type:
+    none: "配信しとる"
 _bubbleGame:
   howToPlay: "遊び方"
   hold: "ホールド"
@@ -2032,7 +2036,6 @@ _permissions:
   "read:admin:server-info": "サーバーの情報見る"
   "read:admin:show-moderation-log": "モデレーションログ見る"
   "read:admin:show-user": "ユーザーのプライベートな情報見る"
-  "read:admin:show-users": "ユーザーのプライベートな情報見る"
   "write:admin:suspend-user": "ユーザーを凍結"
   "write:admin:unset-user-avatar": "ユーザーのアバターを削除"
   "write:admin:unset-user-banner": "ユーザーのバナーを削除"
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index c80a4d399752..9466aff01f41 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -649,6 +649,10 @@ replies: "답하기"
 renotes: "리노트"
 attach: "옇기"
 surrender: "아이예"
+_delivery:
+  stop: "고만 보내예"
+  _type:
+    none: "보내고 잇어예"
 _initialAccountSetting:
   startTutorial: "길라잡이 하기"
 _initialTutorial:
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index fc3a64acab1d..294a5a1520cd 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -1230,6 +1230,10 @@ useTotp: "일회용 비밀번호 사용"
 useBackupCode: "백업 코드 사용"
 launchApp: "앱 실행"
 useNativeUIForVideoAudioPlayer: "브라우저 UI에서 미디어 재생"
+_delivery:
+  stop: "정지됨"
+  _type:
+    none: "배포 중"
 _bubbleGame:
   howToPlay: "설명"
   hold: "홀드"
@@ -2021,7 +2025,6 @@ _permissions:
   "read:admin:server-info": "서버 정보 보기"
   "read:admin:show-moderation-log": "조정 기록 보기"
   "read:admin:show-user": "사용자 개인정보 보기"
-  "read:admin:show-users": "사용자 개인정보 보기"
   "write:admin:suspend-user": "사용자 정지하기"
   "write:admin:unset-user-avatar": "사용자 아바타 삭제하기"
   "write:admin:unset-user-banner": "사용자 배너 삭제하기"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index fa4b3b6f9a95..087bac374524 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -395,6 +395,10 @@ searchByGoogle: "ຄົ້ນຫາ"
 file: "ໄຟລ໌"
 replies: "ຕອບ​ໄປ​ທີ"
 renotes: "Renote"
+_delivery:
+  stop: "ໂຈະ"
+  _type:
+    none: "ການ​ພິມ​ເຜີຍ​ແຜ່"
 _role:
   _priority:
     middle: "ປານກາງ"
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index e33b978bc857..eb48cf72da02 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -429,6 +429,10 @@ loggedInAsBot: "Momenteel als bot ingelogd"
 icon: "Avatar"
 replies: "Antwoord"
 renotes: "Herdelen"
+_delivery:
+  stop: "Opgeschort"
+  _type:
+    none: "Publiceren"
 _email:
   _follow:
     title: "volgde jou"
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 475f93267be8..2b4c9b77761f 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -464,6 +464,8 @@ icon: "Avatar"
 replies: "Svar"
 renotes: "Renote"
 surrender: "Avbryt"
+_delivery:
+  stop: "Suspendert"
 _initialAccountSetting:
   theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
 _achievements:
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 2183aa3022bc..9d75f7a9d76a 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -1023,6 +1023,10 @@ flip: "Odwróć"
 lastNDays: "W ciągu ostatnich {n} dni"
 surrender: "Odrzuć"
 gameRetry: "Spróbuj ponownie"
+_delivery:
+  stop: "Zawieszono"
+  _type:
+    none: "Publikowanie"
 _bubbleGame:
   _score:
     score: "Wynik"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index e00f5750ddb5..cfc576b6e119 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -1012,6 +1012,10 @@ keepScreenOn: "Manter a tela do dispositivo sempre ligada"
 flip: "Inversão"
 lastNDays: "Últimos {n} dias"
 surrender: "Cancelar"
+_delivery:
+  stop: "Suspenso"
+  _type:
+    none: "Publicando"
 _initialAccountSetting:
   followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
 _serverSettings:
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index 695eb2501fe6..328d34405e30 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -651,6 +651,10 @@ show: "Arată"
 icon: "Avatar"
 replies: "Răspunde"
 renotes: "Re-notează"
+_delivery:
+  stop: "Suspendat"
+  _type:
+    none: "Publicare"
 _role:
   _priority:
     middle: "Mediu"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 66e032f16f82..71f5cad601b5 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -1099,6 +1099,10 @@ flip: "Переворот"
 code: "Код"
 lastNDays: "Последние {n} сут"
 surrender: "Этот пост не может быть отменен."
+_delivery:
+  stop: "Заморожено"
+  _type:
+    none: "Публикация"
 _initialAccountSetting:
   accountCreated: "Аккаунт успешно создан!"
   letsStartAccountSetup: "Давайте настроим вашу учётную запись."
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 0978701e5557..52f6bf142cac 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -922,6 +922,10 @@ renotes: "Preposlať"
 sourceCode: "Zdrojový kód"
 flip: "Preklopiť"
 lastNDays: "Posledných {n} dní"
+_delivery:
+  stop: "Zmrazené"
+  _type:
+    none: "Zverejňovanie"
 _role:
   priority: "Priorita"
   _priority:
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 62bc71a13df2..089dc3949f8a 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -488,6 +488,10 @@ dataSaver: "Databesparing"
 icon: "Profilbild"
 replies: "Svara"
 renotes: "Omnotera"
+_delivery:
+  stop: "Suspenderad"
+  _type:
+    none: "Publiceras"
 _achievements:
   _types:
     _open3windows:
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index 020b95485475..ab09ac4d5a45 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -1235,6 +1235,10 @@ keepOriginalFilenameDescription: "หากปิดการตั้งค่
 noDescription: "ไม่มีข้อความอธิบาย"
 alwaysConfirmFollow: "แสดงข้อความยืนยันเมื่อกดติดตาม"
 inquiry: "ติดต่อเรา"
+_delivery:
+  stop: "ถูกระงับ"
+  _type:
+    none: "กำลังเผยแพร่"
 _bubbleGame:
   howToPlay: "วิธีเล่น"
   hold: "หยุดชั่วคราว"
@@ -2032,7 +2036,6 @@ _permissions:
   "read:admin:server-info": "ดูข้อมูลเซิร์ฟเวอร์"
   "read:admin:show-moderation-log": "ดูปูมการแก้ไข"
   "read:admin:show-user": "ดูข้อมูลส่วนตัวของผู้ใช้"
-  "read:admin:show-users": "ดูข้อมูลส่วนตัวของผู้ใช้"
   "write:admin:suspend-user": "ระงับผู้ใช้"
   "write:admin:unset-user-avatar": "ลบอวตารผู้ใช้"
   "write:admin:unset-user-banner": "ลบแบนเนอร์ผู้ใช้"
diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml
index 0793592d3402..cf6729a81d8e 100644
--- a/locales/tr-TR.yml
+++ b/locales/tr-TR.yml
@@ -378,6 +378,10 @@ addMemo: "Kısa not ekle"
 icon: "Avatar"
 replies: "yanıt"
 renotes: "vazgeçme"
+_delivery:
+  stop: "Askıya alınmış"
+  _type:
+    none: "Paylaşım"
 _accountDelete:
   started: "Silme işlemi başlatıldı"
 _email:
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 0ce5dc12028d..661ecf19d7d9 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -914,6 +914,10 @@ renotes: "Поширити"
 sourceCode: "Вихідний код"
 flip: "Перевернути"
 lastNDays: "Останні {n} днів"
+_delivery:
+  stop: "Призупинено"
+  _type:
+    none: "Публікація"
 _achievements:
   earnedAt: "Відкрито"
   _types:
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index 809e78549295..4a930626f4f8 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -846,6 +846,10 @@ icon: "Avatar"
 replies: "Javob berish"
 renotes: "Qayta qayd etish"
 flip: "Teskari"
+_delivery:
+  stop: "To'xtatilgan"
+  _type:
+    none: "Yuborilmoqda"
 _achievements:
   _types:
     _viewInstanceChart:
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index d9c21d29ad4c..acc2e0c6a99e 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1118,6 +1118,10 @@ pullDownToRefresh: "Kéo xuống để làm mới"
 cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
 lastNDays: "{n} ngày trước"
 surrender: "Từ chối"
+_delivery:
+  stop: "Đã vô hiệu hóa"
+  _type:
+    none: "Đang đăng"
 _announcement:
   forExistingUsers: "Chỉ những người dùng đã tồn tại"
   forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó."
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 17164dfe988c..3e500f8642b8 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -471,6 +471,7 @@ retype: "重新输入"
 noteOf: "{user} 的帖子"
 quoteAttached: "已引用"
 quoteQuestion: "是否引用此链接内容?"
+attachAsFileQuestion: "剪贴板内的文字过长。要转换为文本文件并添加吗?"
 noMessagesYet: "现在没有新的聊天"
 newMessageExists: "新信息"
 onlyOneFileCanBeAttached: "只能添加一个附件"
@@ -1024,6 +1025,7 @@ thisPostMayBeAnnoyingHome: "发到首页"
 thisPostMayBeAnnoyingCancel: "取消"
 thisPostMayBeAnnoyingIgnore: "就这样发布"
 collapseRenotes: "省略显示已经看过的转发内容"
+collapseRenotesDescription: "将回应过或转贴过的贴子折叠表示。"
 internalServerError: "内部服务器错误"
 internalServerErrorDescription: "内部服务器发生了预期外的错误"
 copyErrorInfo: "复制错误信息"
@@ -1238,6 +1240,15 @@ keepOriginalFilenameDescription: "若关闭此设置,上传文件时文件名
 noDescription: "没有描述"
 alwaysConfirmFollow: "总是确认关注"
 inquiry: "联系我们"
+_delivery:
+  status: "投递状态"
+  stop: "停止投递"
+  resume: "继续投递"
+  _type:
+    none: "投递中"
+    manuallySuspended: "手动停止中"
+    goneSuspended: "因服务器被删除而停止"
+    autoSuspendedForNotResponding: "因服务器无应答而停止"
 _bubbleGame:
   howToPlay: "游戏说明"
   hold: "抓住"
@@ -1696,8 +1707,10 @@ _role:
     roleAssignedTo: "已分配给手动角色"
     isLocal: "是本地用户"
     isRemote: "是远程用户"
+    isCat: "猫猫用户"
     isBot: "机器人用户"
     isSuspended: "停用的用户"
+    isLocked: "锁推用户"
     isExplorable: "启用“使账号可见”的用户"
     createdLessThan: "账户创建时间少于"
     createdMoreThan: "账户创建时间超过"
@@ -2032,7 +2045,6 @@ _permissions:
   "read:admin:server-info": "查看服务器信息"
   "read:admin:show-moderation-log": "查看管理日志"
   "read:admin:show-user": "查看用户的非公开信息"
-  "read:admin:show-users": "查看用户的非公开信息"
   "write:admin:suspend-user": "冻结用户"
   "write:admin:unset-user-avatar": "删除用户头像"
   "write:admin:unset-user-banner": "删除用户横幅"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 8cde13052f35..fed7b642dcee 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -108,11 +108,14 @@ enterEmoji: "輸入表情符號"
 renote: "轉發"
 unrenote: "取消轉發"
 renoted: "轉發成功。"
+renotedToX: "轉發給 {name} 了。"
 cantRenote: "無法轉發此貼文。"
 cantReRenote: "無法轉發之前已經轉發過的內容。"
 quote: "引用"
 inChannelRenote: "在頻道內轉發"
 inChannelQuote: "在頻道內引用"
+renoteToChannel: "轉發至頻道"
+renoteToOtherChannel: "轉發至其他頻道"
 pinnedNote: "已置頂的貼文"
 pinned: "置頂"
 you: "您"
@@ -169,7 +172,7 @@ cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取
 flagAsBot: "此使用者是機器人"
 flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整 Misskey 內部系統將本帳戶識別為機器人。"
 flagAsCat: "此帳戶是一隻貓,喵~~~!!!"
-flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
+flagAsCatDescription: "喵喵喵??"
 flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
 flagShowTimelineRepliesDescription: "啟用後,時間軸除了顯示使用者的貼文以外,還會顯示使用者對其他貼文的回覆。"
 autoAcceptFollowed: "自動允許來自追隨中使用者的追隨請求"
@@ -366,7 +369,7 @@ enableRegistration: "開放新使用者註冊"
 invite: "邀請"
 driveCapacityPerLocalAccount: "每個本地使用者的雲端硬碟容量"
 driveCapacityPerRemoteAccount: "每個非本地用戶的雲端空間大小"
-inMb: "以Mbps為單位"
+inMb: "以 MB 為單位"
 bannerUrl: "橫幅圖片URL"
 backgroundImageUrl: "背景圖片的來源網址 "
 basicInfo: "基本資訊"
@@ -378,12 +381,12 @@ pinnedClipId: "置頂的摘錄ID"
 pinnedNotes: "已置頂的貼文"
 hcaptcha: "hCaptcha"
 enableHcaptcha: "啟用 hCaptcha"
-hcaptchaSiteKey: "網站金鑰"
-hcaptchaSecretKey: "金鑰"
+hcaptchaSiteKey: "hcaptchaSiteKey"
+hcaptchaSecretKey: "hcaptchaSecretKey"
 mcaptcha: "mCaptcha"
 enableMcaptcha: "啟用 mCaptcha"
 mcaptchaSiteKey: "網站金鑰"
-mcaptchaSecretKey: "金鑰"
+mcaptchaSecretKey: "私密金鑰"
 mcaptchaInstanceUrl: "mCaptcha 的實例網址"
 recaptcha: "reCAPTCHA"
 enableRecaptcha: "啟用 reCAPTCHA"
@@ -391,8 +394,8 @@ recaptchaSiteKey: "網站金鑰"
 recaptchaSecretKey: "金鑰"
 turnstile: "Turnstile"
 enableTurnstile: "啟用 Turnstile"
-turnstileSiteKey: "網站金鑰"
-turnstileSecretKey: "金鑰"
+turnstileSiteKey: "turnstileSiteKey"
+turnstileSecretKey: "turnstileSecretKey"
 avoidMultiCaptchaConfirm: "使用多種驗證方式可能會造成干擾,您要關閉其他驗證方式嗎?您可以按「取消」保留多種驗證方式。"
 antennas: "天線"
 manageAntennas: "管理天線"
@@ -464,10 +467,11 @@ title: "標題"
 text: "文字"
 enable: "啟用"
 next: "下一步"
-retype: "再次輸入"
+retype: "重新輸入"
 noteOf: "{user}的貼文"
 quoteAttached: "引用"
 quoteQuestion: "是否要引用?"
+attachAsFileQuestion: "剪貼簿的文字較長。請問是否要改成附加檔案呢?"
 noMessagesYet: "沒有訊息"
 newMessageExists: "有新的訊息"
 onlyOneFileCanBeAttached: "只能加入一個附件"
@@ -602,7 +606,7 @@ addItem: "新增項目"
 rearrange: "排序方式"
 relays: "中繼器"
 addRelay: "新增中繼器"
-inboxUrl: "收件夾URL"
+inboxUrl: "收件夾 URL"
 addedRelays: "已加入的中繼器"
 serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。"
 deletedNote: "已刪除的貼文"
@@ -791,7 +795,7 @@ newVersionOfClientAvailable: "新版本的客戶端可用。"
 usageAmount: "使用量"
 capacity: "容量"
 inUse: "已使用"
-editCode: "編輯代碼"
+editCode: "編輯程式碼"
 apply: "套用"
 receiveAnnouncementFromInstance: "接收來自伺服器的通知"
 emailNotification: "郵件通知"
@@ -1062,7 +1066,7 @@ enableChartsForFederatedInstances: "生成遠端伺服器的圖表"
 showClipButtonInNoteFooter: "新增摘錄按鈕至貼文"
 reactionsDisplaySize: "反應的顯示尺寸"
 limitWidthOfReaction: "限制反應的最大寬度,並縮小顯示尺寸。"
-noteIdOrUrl: "貼文ID或URL"
+noteIdOrUrl: "貼文 ID 或 URL"
 video: "影片"
 videos: "影片"
 audio: "音效"
@@ -1077,7 +1081,7 @@ addMemo: "新增備註"
 editMemo: "編輯備註"
 reactionsList: "反應列表"
 renotesList: "轉發貼文列表"
-notificationDisplay: "通知的顯示"
+notificationDisplay: "通知"
 leftTop: "左上"
 rightTop: "右上"
 leftBottom: "左下"
@@ -1179,15 +1183,15 @@ repositoryUrlOrTarballRequired: "如果儲存庫不是公開的,則必須提
 feedback: "意見回饋"
 feedbackUrl: "意見回饋 URL"
 impressum: "營運者資訊"
-impressumUrl: "營運者資訊網址"
+impressumUrl: "營運者資訊 URL"
 impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。"
 privacyPolicy: "隱私政策"
-privacyPolicyUrl: "隱私政策網址"
+privacyPolicyUrl: "隱私政策 URL"
 tosAndPrivacyPolicy: "服務條款和隱私政策"
 avatarDecorations: "頭像裝飾"
 attach: "裝上"
 detach: "取下"
-detachAll: "移除所有裝飾"
+detachAll: "全部移除"
 angle: "角度"
 flip: "翻轉"
 showAvatarDecorations: "顯示頭像裝飾"
@@ -1205,7 +1209,7 @@ remainingN: "剩餘:{n}"
 overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
 seasonalScreenEffect: "隨季節變換畫面的呈現"
 decorate: "設置頭像裝飾"
-addMfmFunction: "插入MFM功能語法"
+addMfmFunction: "插入 MFM 功能語法"
 enableQuickAddMfmFunction: "顯示高級 MFM 選擇器"
 bubbleGame: "氣泡遊戲"
 sfx: "音效"
@@ -1225,16 +1229,25 @@ enableHorizontalSwipe: "滑動切換時間軸"
 loading: "載入中"
 surrender: "退出"
 gameRetry: "再試一次"
-notUsePleaseLeaveBlank: "如不使用,請留空"
+notUsePleaseLeaveBlank: "如果不使用的話請留白"
 useTotp: "使用一次性密碼"
 useBackupCode: "使用備用驗證碼"
-launchApp: "啟動 App"
+launchApp: "啟動 APP"
 useNativeUIForVideoAudioPlayer: "使用瀏覽器的 UI 播放影片與音訊"
 keepOriginalFilename: "保留原始檔名"
 keepOriginalFilenameDescription: "如果關閉此設置,上傳時檔案名稱會自動替換為隨機字串。"
 noDescription: "沒有說明文字"
 alwaysConfirmFollow: "點擊追隨時總是顯示確認訊息"
 inquiry: "聯絡我們"
+_delivery:
+  status: "傳送狀態"
+  stop: "已凍結"
+  resume: "繼續傳送"
+  _type:
+    none: "直播中"
+    manuallySuspended: "手動暫停中"
+    goneSuspended: "因為伺服器刪除所以暫停中"
+    autoSuspendedForNotResponding: "因為伺服器沒有回應所以暫停中"
 _bubbleGame:
   howToPlay: "玩法說明"
   hold: "保留"
@@ -1243,7 +1256,7 @@ _bubbleGame:
     scoreYen: "賺取的金額"
     highScore: "最高分"
     maxChain: "最大結合數"
-    yen: "{yen} 日圓"
+    yen: "{yen}円"
     estimatedQty: "{qty}個"
     scoreSweets: "飯糰 {onigiriQtyWithUnit}"
   _howToPlay:
@@ -1271,7 +1284,7 @@ _initialAccountSetting:
   privacySetting: "隱私設定"
   theseSettingsCanEditLater: "這裡的設定可以在之後變更。"
   youCanEditMoreSettingsInSettingsPageLater: "除此之外,還可以在「設定」頁面進行各種設定。之後請確認看看。"
-  followUsers: "為了構築時間軸,試著追蹤您感興趣的使用者吧。"
+  followUsers: "為了構築時間軸,試著追隨您感興趣的使用者吧。"
   pushNotificationDescription: "啟用推送通知,就可以在設備上接收{name}的通知。"
   initialAccountSettingCompleted: "初始設定完成了!"
   haveFun: "盡情享受{name}吧!"
@@ -1326,7 +1339,7 @@ _initialTutorial:
       title: "隱藏內容(CW)"
       description: "將顯示「註釋」中寫入的內容而不是本文。按一下「顯示內容」以顯示本文。"
       _exampleNote:
-        cw: "美食恐怖主義注意"
+        cw: "注意消夜文"
         note: "我吃了一個巧克力甜甜圈🍩😋"
       useCases: "伺服器的服務條款可能會規範特定的貼文需要使用隱藏內容,除此之外也會用在隱藏劇情洩漏與敏感內容的貼文。"
   _howToMakeAttachmentsSensitive:
@@ -1351,7 +1364,7 @@ _serverRules:
 _serverSettings:
   iconUrl: "圖示的 URL"
   appIconDescription: "指定顯示 {host} 為應用程式時的圖示。"
-  appIconUsageExample: "例如:漸進式網路應用程式(PWA)、於手機桌面新增書籤"
+  appIconUsageExample: "例如:PWA 或是在手機桌面作為書籤等"
   appIconStyleRecommendation: "因為可能會裁剪成圓形或圓角,所以建議用單色填滿邊框及背景。"
   appIconResolutionMustBe: "解析度必須為 {resolution}。"
   manifestJsonOverride: "覆寫 manifest.json"
@@ -1559,7 +1572,7 @@ _achievements:
     _postedAt0min0sec:
       title: "報時"
       description: "在零分零秒發佈貼文"
-      flavor: "啵、啵、啵、嗶ーー"
+      flavor: "啵.啵.啵.嗶ー"
     _selfQuote:
       title: "自我引用"
       description: "引用了自己的貼文"
@@ -1694,8 +1707,8 @@ _role:
     roleAssignedTo: "手動指派角色完成"
     isLocal: "本地使用者"
     isRemote: "遠端使用者"
-    isCat: "使用者是貓"
-    isBot: "使用者是機器人"
+    isCat: "貓使用者"
+    isBot: "機器人使用者"
     isSuspended: "被停權的使用者"
     isLocked: "上鎖的使用者"
     isExplorable: "開啟了「使您的帳戶更容易被找到」功能的使用者"
@@ -1857,7 +1870,7 @@ _theme:
   invalid: "佈景主題格式錯誤"
   make: "製作佈景主題"
   base: "基於"
-  addConstant: "添加常數"
+  addConstant: "新增常數"
   constant: "常數"
   defaultValue: "預設值"
   color: "顏色"
@@ -1932,22 +1945,22 @@ _soundSettings:
 _ago:
   future: "未來"
   justNow: "剛剛"
-  secondsAgo: "{n} 秒前"
-  minutesAgo: "{n} 分鐘前 "
-  hoursAgo: "{n} 小時前"
-  daysAgo: "{n} 天前"
-  weeksAgo: "{n} 週前"
-  monthsAgo: "{n} 個月前"
-  yearsAgo: "{n} 年前"
+  secondsAgo: "{n}秒前"
+  minutesAgo: "{n}分鐘前"
+  hoursAgo: "{n}小時前"
+  daysAgo: "{n}天前"
+  weeksAgo: "{n}周前"
+  monthsAgo: "{n}個月前"
+  yearsAgo: "{n}年前"
   invalid: "無"
 _timeIn:
-  seconds: "{n} 秒後"
-  minutes: "{n} 分後"
-  hours: "{n} 小時後"
-  days: "{n} 日後"
-  weeks: "{n} 週後"
-  months: "{n} 個月後"
-  years: "{n} 年後"
+  seconds: "{n}秒後"
+  minutes: "{n}分鐘後"
+  hours: "{n}小時後"
+  days: "{n}天後"
+  weeks: "{n}周後"
+  months: "{n}個月後"
+  years: "{n}年後"
 _time:
   second: "秒"
   minute: "分鐘"
@@ -2032,7 +2045,6 @@ _permissions:
   "read:admin:server-info": "查看伺服器的資訊"
   "read:admin:show-moderation-log": "查看審查紀錄"
   "read:admin:show-user": "查看使用者的私密資訊"
-  "read:admin:show-users": "查看使用者的私密資訊"
   "write:admin:suspend-user": "凍結使用者"
   "write:admin:unset-user-avatar": "刪除使用者的頭像"
   "write:admin:unset-user-banner": "刪除使用者的橫幅"
@@ -2085,13 +2097,13 @@ _antennaSources:
   userList: "來自特定清單中的貼文"
   userBlacklist: "除指定使用者外的所有貼文"
 _weekday:
-  sunday: "週日"
-  monday: "週一"
-  tuesday: "週二"
-  wednesday: "週三"
-  thursday: "週四"
-  friday: "週五"
-  saturday: "週六"
+  sunday: "星期天"
+  monday: "星期一"
+  tuesday: "星期二"
+  wednesday: "星期三"
+  thursday: "星期四"
+  friday: "星期五"
+  saturday: "星期六"
 _widgets:
   profile: "個人檔案"
   instanceInfo: "伺服器資訊"
@@ -2140,7 +2152,7 @@ _poll:
   deadlineDate: "截止日期"
   deadlineTime: "小時"
   duration: "時長"
-  votesCount: "{n} 票"
+  votesCount: "{n}票"
   totalVotes: "合計 {n} 票"
   vote: "投票"
   showResult: "顯示結果"
@@ -2173,7 +2185,7 @@ _postForm:
     e: "寫些什麼吧……"
     f: "靜待發文……"
 _profile:
-  name: "名稱"
+  name: "名字"
   username: "使用者名稱"
   description: "關於我"
   youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag"
@@ -2231,10 +2243,10 @@ _timelines:
 _play:
   new: "新增 Play"
   edit: "編輯 Play"
-  created: "已新增Play "
-  updated: "已更新Play "
+  created: "已新增 Play "
+  updated: "已更新 Play "
   deleted: "已刪除 Play"
-  pageSetting: "Play設定"
+  pageSetting: "Play 設定"
   editThisPage: "編輯此 Play"
   viewSource: "檢視原始碼"
   my: "自己的 Play"
@@ -2247,7 +2259,7 @@ _play:
 _pages:
   newPage: "建立頁面"
   editPage: "編輯頁面"
-  readPage: "正檢視原始碼"
+  readPage: "正在檢視原始碼"
   created: "頁面已建立"
   updated: "頁面已更新"
   deleted: "頁面已被刪除"
@@ -2274,7 +2286,7 @@ _pages:
   hideTitleWhenPinned: "被置頂於個人資料時隱藏頁面標題"
   font: "字型"
   fontSerif: "襯線體"
-  fontSansSerif: "無襯線體"
+  fontSansSerif: "黑體"
   eyeCatchingImageSet: "設定封面影像"
   eyeCatchingImageRemove: "刪除封面影像"
   chooseBlock: "新增方塊"
@@ -2384,7 +2396,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "按新增日期降序排列"
 _webhookSettings:
   createWebhook: "建立 Webhook"
-  name: "名稱"
+  name: "名字"
   secret: "密鑰"
   events: "何時運行 Webhook"
   active: "已啟用"

From 4579be0f5401001bcfc27c4d56133cc910f3f581 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 27 May 2024 20:54:53 +0900
Subject: [PATCH 233/266] =?UTF-8?q?=E6=96=B0=E7=9D=80=E3=83=8E=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=82=92=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89=E3=81=A7?=
 =?UTF-8?q?=E9=80=9A=E7=9F=A5=E3=81=99=E3=82=8B=E6=A9=9F=E8=83=BD=E3=82=92?=
 =?UTF-8?q?deck=20UI=E3=81=AB=E8=BF=BD=E5=8A=A0=20(#13867)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(deck-ui): implement note notification

* chore: remove notify in antenna

* docs(changelog): 新着ノートをサウンドで通知する機能をdeck UIに追加

* fix: type error in test

* lint: key order

* fix: remove notify column

* test: remove test for notify

* chore: make sound selectable

* fix: add license header

* fix: add license header again

* Unnecessary await

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

* ファイルを選択してください -> ファイルが選択されていません

* fix: i18n忘れ

* fix: i18n忘れ

* pleaseSelectFile > fileNotSelected

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |   8 ++
 locales/ja-JP.yml                             |   2 +
 .../1716450883149-RemoveAntennaNotify.js      |  16 +++
 .../src/core/entities/AntennaEntityService.ts |   1 -
 packages/backend/src/models/Antenna.ts        |   3 -
 .../backend/src/models/json-schema/antenna.ts |   4 -
 .../ExportAntennasProcessorService.ts         |   1 -
 .../ImportAntennasProcessorService.ts         |   4 +-
 .../server/api/endpoints/antennas/create.ts   |   4 +-
 .../server/api/endpoints/antennas/update.ts   |   2 -
 packages/backend/test/e2e/antennas.ts         |   4 -
 packages/backend/test/e2e/move.ts             |   2 -
 .../src/components/MkFormDialog.file.vue      |  71 ++++++++++++
 .../frontend/src/components/MkFormDialog.vue  |  12 +-
 packages/frontend/src/os.ts                   |   2 +-
 .../frontend/src/pages/my-antennas/editor.vue |   3 -
 packages/frontend/src/scripts/form.ts         |  30 +++--
 .../frontend/src/ui/deck/antenna-column.vue   |  24 +++-
 .../frontend/src/ui/deck/channel-column.vue   |  23 +++-
 packages/frontend/src/ui/deck/deck-store.ts   |   2 +
 packages/frontend/src/ui/deck/list-column.vue |  22 +++-
 .../src/ui/deck/role-timeline-column.vue      |  23 +++-
 packages/frontend/src/ui/deck/tl-column.vue   |  20 +++-
 .../src/ui/deck/tl-note-notification.ts       | 107 ++++++++++++++++++
 packages/misskey-js/src/autogen/types.ts      |   3 -
 26 files changed, 341 insertions(+), 53 deletions(-)
 create mode 100644 packages/backend/migration/1716450883149-RemoveAntennaNotify.js
 create mode 100644 packages/frontend/src/components/MkFormDialog.file.vue
 create mode 100644 packages/frontend/src/ui/deck/tl-note-notification.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8463f8cbf60..0a70fc7a8a0f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@
 - Enhance: AiScriptを0.18.0にバージョンアップ
 - Enhance: 通常のノートでも、お気に入りに登録したチャンネルにリノートできるように
 - Enhance: 長いテキストをペーストした際にテキストファイルとして添付するかどうかを選択できるように
+- Enhance: 新着ノートをサウンドで通知する機能をdeck UIに追加しました
 - Enhance: コントロールパネルのクイックアクションからファイルを照会できるように
 - Enhance: コントロールパネルのクイックアクションから通常の照会を行えるように
 - Fix: 一部のページ内リンクが正しく動作しない問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index eb7e297aa325..d4ded0bb5b8e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1280,6 +1280,10 @@ export interface Locale extends ILocale {
      * フォルダーを選択
      */
     "selectFolders": string;
+    /**
+     * ファイルが選択されていません
+     */
+    "fileNotSelected": string;
     /**
      * ファイル名を変更
      */
@@ -9143,6 +9147,10 @@ export interface Locale extends ILocale {
          * カラムを追加
          */
         "addColumn": string;
+        /**
+         * 新着ノート通知の設定
+         */
+        "newNoteNotificationSettings": string;
         /**
          * カラムの設定
          */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ebaf16745c01..d7ceb971afed 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -316,6 +316,7 @@ selectFile: "ファイルを選択"
 selectFiles: "ファイルを選択"
 selectFolder: "フォルダーを選択"
 selectFolders: "フォルダーを選択"
+fileNotSelected: "ファイルが選択されていません"
 renameFile: "ファイル名を変更"
 folderName: "フォルダー名"
 createFolder: "フォルダーを作成"
@@ -2420,6 +2421,7 @@ _deck:
   alwaysShowMainColumn: "常にメインカラムを表示"
   columnAlign: "カラムの寄せ"
   addColumn: "カラムを追加"
+  newNoteNotificationSettings: "新着ノート通知の設定"
   configureColumn: "カラムの設定"
   swapLeft: "左に移動"
   swapRight: "右に移動"
diff --git a/packages/backend/migration/1716450883149-RemoveAntennaNotify.js b/packages/backend/migration/1716450883149-RemoveAntennaNotify.js
new file mode 100644
index 000000000000..b5a2441855d2
--- /dev/null
+++ b/packages/backend/migration/1716450883149-RemoveAntennaNotify.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class RemoveAntennaNotify1716450883149 {
+    name = 'RemoveAntennaNotify1716450883149'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "notify"`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "antenna" ADD "notify" boolean NOT NULL`);
+    }
+}
diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts
index 3ec8efa6bfd0..4a17a3d80fba 100644
--- a/packages/backend/src/core/entities/AntennaEntityService.ts
+++ b/packages/backend/src/core/entities/AntennaEntityService.ts
@@ -38,7 +38,6 @@ export class AntennaEntityService {
 			users: antenna.users,
 			caseSensitive: antenna.caseSensitive,
 			localOnly: antenna.localOnly,
-			notify: antenna.notify,
 			excludeBots: antenna.excludeBots,
 			withReplies: antenna.withReplies,
 			withFile: antenna.withFile,
diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts
index f5e819059e03..33e6f4818952 100644
--- a/packages/backend/src/models/Antenna.ts
+++ b/packages/backend/src/models/Antenna.ts
@@ -90,9 +90,6 @@ export class MiAntenna {
 	})
 	public expression: string | null;
 
-	@Column('boolean')
-	public notify: boolean;
-
 	@Index()
 	@Column('boolean', {
 		default: true,
diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts
index 78cf6d3ba27f..c4ac358fa60a 100644
--- a/packages/backend/src/models/json-schema/antenna.ts
+++ b/packages/backend/src/models/json-schema/antenna.ts
@@ -72,10 +72,6 @@ export const packedAntennaSchema = {
 			optional: false, nullable: false,
 			default: false,
 		},
-		notify: {
-			type: 'boolean',
-			optional: false, nullable: false,
-		},
 		excludeBots: {
 			type: 'boolean',
 			optional: false, nullable: false,
diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
index 1d8e90f367f0..88c4ea29c0e6 100644
--- a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
@@ -84,7 +84,6 @@ export class ExportAntennasProcessorService {
 					excludeBots: antenna.excludeBots,
 					withReplies: antenna.withReplies,
 					withFile: antenna.withFile,
-					notify: antenna.notify,
 				}));
 				if (antennas.length - 1 !== index) {
 					write(', ');
diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
index ff1c04de06c5..e5b7c5ac5213 100644
--- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
@@ -47,9 +47,8 @@ const validate = new Ajv().compile({
 		excludeBots: { type: 'boolean' },
 		withReplies: { type: 'boolean' },
 		withFile: { type: 'boolean' },
-		notify: { type: 'boolean' },
 	},
-	required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'],
+	required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile'],
 });
 
 @Injectable()
@@ -92,7 +91,6 @@ export class ImportAntennasProcessorService {
 					excludeBots: antenna.excludeBots,
 					withReplies: antenna.withReplies,
 					withFile: antenna.withFile,
-					notify: antenna.notify,
 				}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
 				this.logger.succ('Antenna created: ' + result.id);
 				this.globalEventService.publishInternalEvent('antennaCreated', result);
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index 57c8eb495850..6b7bacb05499 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -67,9 +67,8 @@ export const paramDef = {
 		excludeBots: { type: 'boolean' },
 		withReplies: { type: 'boolean' },
 		withFile: { type: 'boolean' },
-		notify: { type: 'boolean' },
 	},
-	required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'],
+	required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile'],
 } as const;
 
 @Injectable()
@@ -128,7 +127,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				excludeBots: ps.excludeBots,
 				withReplies: ps.withReplies,
 				withFile: ps.withFile,
-				notify: ps.notify,
 			}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
 
 			this.globalEventService.publishInternalEvent('antennaCreated', antenna);
diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts
index e6720aacf82e..0c30bca9e0bf 100644
--- a/packages/backend/src/server/api/endpoints/antennas/update.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/update.ts
@@ -66,7 +66,6 @@ export const paramDef = {
 		excludeBots: { type: 'boolean' },
 		withReplies: { type: 'boolean' },
 		withFile: { type: 'boolean' },
-		notify: { type: 'boolean' },
 	},
 	required: ['antennaId'],
 } as const;
@@ -124,7 +123,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				excludeBots: ps.excludeBots,
 				withReplies: ps.withReplies,
 				withFile: ps.withFile,
-				notify: ps.notify,
 				isActive: true,
 				lastUsedAt: new Date(),
 			});
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index cf5c7dd130ea..4f78cc999d43 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -38,7 +38,6 @@ describe('アンテナ', () => {
 		excludeKeywords: [['']],
 		keywords: [['keyword']],
 		name: 'test',
-		notify: false,
 		src: 'all' as const,
 		userListId: null,
 		users: [''],
@@ -151,7 +150,6 @@ describe('アンテナ', () => {
 			isActive: true,
 			keywords: [['keyword']],
 			name: 'test',
-			notify: false,
 			src: 'all',
 			userListId: null,
 			users: [''],
@@ -219,8 +217,6 @@ describe('アンテナ', () => {
 		{ parameters: () => ({ withReplies: true }) },
 		{ parameters: () => ({ withFile: false }) },
 		{ parameters: () => ({ withFile: true }) },
-		{ parameters: () => ({ notify: false }) },
-		{ parameters: () => ({ notify: true }) },
 	];
 	test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => {
 		const response = await successfulApiCall({
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index 4e5306da97da..35050130dc1b 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -191,7 +191,6 @@ describe('Account Move', () => {
 				localOnly: false,
 				withReplies: false,
 				withFile: false,
-				notify: false,
 			}, alice);
 			antennaId = antenna.body.id;
 
@@ -435,7 +434,6 @@ describe('Account Move', () => {
 				localOnly: false,
 				withReplies: false,
 				withFile: false,
-				notify: false,
 			}, alice);
 
 			assert.strictEqual(res.status, 403);
diff --git a/packages/frontend/src/components/MkFormDialog.file.vue b/packages/frontend/src/components/MkFormDialog.file.vue
new file mode 100644
index 000000000000..936059423657
--- /dev/null
+++ b/packages/frontend/src/components/MkFormDialog.file.vue
@@ -0,0 +1,71 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div>
+	<MkButton inline rounded primary @click="selectButton($event)">{{ i18n.ts.selectFile }}</MkButton>
+	<div :class="['_nowrap', !fileName && $style.fileNotSelected]">{{ friendlyFileName }}</div>
+</div>
+</template>
+
+<script setup lang="ts">
+import * as Misskey from 'misskey-js';
+import { computed, ref } from 'vue';
+import { i18n } from '@/i18n.js';
+import MkButton from '@/components/MkButton.vue';
+import { selectFile } from '@/scripts/select-file.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+
+const props = defineProps<{
+	fileId?: string | null;
+	validate?: (file: Misskey.entities.DriveFile) => Promise<boolean>;
+}>();
+
+const emit = defineEmits<{
+	(ev: 'update', result: Misskey.entities.DriveFile): void;
+}>();
+
+const fileUrl = ref('');
+const fileName = ref<string>('');
+
+const friendlyFileName = computed<string>(() => {
+	if (fileName.value) {
+		return fileName.value;
+	}
+	if (fileUrl.value) {
+		return fileUrl.value;
+	}
+
+	return i18n.ts.fileNotSelected;
+});
+
+if (props.fileId) {
+	misskeyApi('drive/files/show', {
+		fileId: props.fileId,
+	}).then((apiRes) => {
+		fileName.value = apiRes.name;
+		fileUrl.value = apiRes.url;
+	});
+}
+
+function selectButton(ev: MouseEvent) {
+	selectFile(ev.currentTarget ?? ev.target).then(async (file) => {
+		if (!file) return;
+		if (props.validate && !await props.validate(file)) return;
+
+		emit('update', file);
+		fileName.value = file.name;
+		fileUrl.value = file.url;
+	});
+}
+
+</script>
+
+<style module>
+.fileNotSelected {
+	font-weight: 700;
+	color: var(--infoWarnFg);
+}
+</style>
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index deedc5badb1b..124f114111c2 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -21,8 +21,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<MkSpacer :marginMin="20" :marginMax="32">
 		<div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
-			<template v-for="(v, k) in Object.fromEntries(Object.entries(form).filter(([_, v]) => !('hidden' in v) || 'hidden' in v && !v.hidden))">
-				<MkInput v-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1">
+			<template v-for="(v, k) in Object.fromEntries(Object.entries(form))">
+				<template v-if="typeof v.hidden == 'function' ? v.hidden(values) : v.hidden"></template>
+				<MkInput v-else-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1">
 					<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="v.description" #caption>{{ v.description }}</template>
 				</MkInput>
@@ -53,6 +54,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)">
 					<span v-text="v.content || k"></span>
 				</MkButton>
+				<XFile
+					v-else-if="v.type === 'drive-file'"
+					:fileId="v.defaultFileId"
+					:validate="async f => !v.validate || await v.validate(f)"
+					@update="f => values[k] = f"
+				/>
 			</template>
 		</div>
 		<div v-else class="_fullinfo">
@@ -72,6 +79,7 @@ import MkSelect from './MkSelect.vue';
 import MkRange from './MkRange.vue';
 import MkButton from './MkButton.vue';
 import MkRadios from './MkRadios.vue';
+import XFile from './MkFormDialog.file.vue';
 import type { Form } from '@/scripts/form.js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index c561e84a23b5..f656a5237165 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -518,7 +518,7 @@ export function waiting(): Promise<void> {
 	});
 }
 
-export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true } | { result: GetFormResultType<F> }> {
+export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType<F> }> {
 	return new Promise(resolve => {
 		popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, {
 			done: result => {
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 97edbc44cefa..2949bfc02cec 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -39,7 +39,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
 			<MkSwitch v-model="caseSensitive">{{ i18n.ts.caseSensitive }}</MkSwitch>
 			<MkSwitch v-model="withFile">{{ i18n.ts.withFileAntenna }}</MkSwitch>
-			<MkSwitch v-model="notify">{{ i18n.ts.notifyAntenna }}</MkSwitch>
 		</div>
 		<div :class="$style.actions">
 			<MkButton inline primary @click="saveAntenna()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
@@ -82,7 +81,6 @@ const localOnly = ref<boolean>(props.antenna.localOnly);
 const excludeBots = ref<boolean>(props.antenna.excludeBots);
 const withReplies = ref<boolean>(props.antenna.withReplies);
 const withFile = ref<boolean>(props.antenna.withFile);
-const notify = ref<boolean>(props.antenna.notify);
 const userLists = ref<Misskey.entities.UserList[] | null>(null);
 
 watch(() => src.value, async () => {
@@ -99,7 +97,6 @@ async function saveAntenna() {
 		excludeBots: excludeBots.value,
 		withReplies: withReplies.value,
 		withFile: withFile.value,
-		notify: notify.value,
 		caseSensitive: caseSensitive.value,
 		localOnly: localOnly.value,
 		users: users.value.trim().split('\n').map(x => x.trim()),
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index b0db404f2818..242a504c3b5f 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -3,18 +3,22 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import * as Misskey from 'misskey-js';
+
 type EnumItem = string | {
 	label: string;
 	value: string;
 };
 
+type Hidden = boolean | ((v: any) => boolean);
+
 export type FormItem = {
 	label?: string;
 	type: 'string';
 	default: string | null;
 	description?: string;
 	required?: boolean;
-	hidden?: boolean;
+	hidden?: Hidden;
 	multiline?: boolean;
 	treatAsMfm?: boolean;
 } | {
@@ -23,27 +27,27 @@ export type FormItem = {
 	default: number | null;
 	description?: string;
 	required?: boolean;
-	hidden?: boolean;
+	hidden?: Hidden;
 	step?: number;
 } | {
 	label?: string;
 	type: 'boolean';
 	default: boolean | null;
 	description?: string;
-	hidden?: boolean;
+	hidden?: Hidden;
 } | {
 	label?: string;
 	type: 'enum';
 	default: string | null;
 	required?: boolean;
-	hidden?: boolean;
+	hidden?: Hidden;
 	enum: EnumItem[];
 } | {
 	label?: string;
 	type: 'radio';
 	default: unknown | null;
 	required?: boolean;
-	hidden?: boolean;
+	hidden?: Hidden;
 	options: {
 		label: string;
 		value: unknown;
@@ -58,20 +62,27 @@ export type FormItem = {
 	min: number;
 	max: number;
 	textConverter?: (value: number) => string;
+	hidden?: Hidden;
 } | {
 	label?: string;
 	type: 'object';
 	default: Record<string, unknown> | null;
-	hidden: boolean;
+	hidden: Hidden;
 } | {
 	label?: string;
 	type: 'array';
 	default: unknown[] | null;
-	hidden: boolean;
+	hidden: Hidden;
 } | {
 	type: 'button';
 	content?: string;
+	hidden?: Hidden;
 	action: (ev: MouseEvent, v: any) => void;
+} | {
+	type: 'drive-file';
+	defaultFileId?: string | null;
+	hidden?: Hidden;
+	validate?: (v: Misskey.entities.DriveFile) => Promise<boolean>;
 };
 
 export type Form = Record<string, FormItem>;
@@ -84,8 +95,9 @@ type GetItemType<Item extends FormItem> =
 	Item['type'] extends 'range' ? number :
 	Item['type'] extends 'enum' ? string :
 	Item['type'] extends 'array' ? unknown[] :
-	Item['type'] extends 'object' ? Record<string, unknown>
-	: never;
+	Item['type'] extends 'object' ? Record<string, unknown> :
+	Item['type'] extends 'drive-file' ? Misskey.entities.DriveFile | undefined :
+	never;
 
 export type GetFormResultType<F extends Form> = {
 	[P in keyof F]: GetItemType<F[P]>;
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index b42a21bf6fde..c3dc1e4fcec3 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -9,18 +9,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
 
-	<MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId"/>
+	<MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @note="onNote"/>
 </XColumn>
 </template>
 
 <script lang="ts" setup>
-import { onMounted, shallowRef } from 'vue';
+import { onMounted, ref, shallowRef, watch } from 'vue';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
 
 const props = defineProps<{
 	column: Column;
@@ -28,6 +32,7 @@ const props = defineProps<{
 }>();
 
 const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
 
 onMounted(() => {
 	if (props.column.antennaId == null) {
@@ -35,6 +40,10 @@ onMounted(() => {
 	}
 });
 
+watch(soundSetting, v => {
+	updateColumn(props.column.id, { soundSetting: v });
+});
+
 async function setAntenna() {
 	const antennas = await misskeyApi('antennas/list');
 	const { canceled, result: antenna } = await os.select({
@@ -54,7 +63,11 @@ function editAntenna() {
 	os.pageWindow('my/antennas/' + props.column.antennaId);
 }
 
-const menu = [
+function onNote() {
+	sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [
 	{
 		icon: 'ti ti-pencil',
 		text: i18n.ts.selectAntenna,
@@ -65,6 +78,11 @@ const menu = [
 		text: i18n.ts.editAntenna,
 		action: editAntenna,
 	},
+	{
+		icon: 'ti ti-bell',
+		text: i18n.ts._deck.newNoteNotificationSettings,
+		action: () => soundSettingsButton(soundSetting),
+	},
 ];
 
 /*
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index 28c741bba238..7c5b13eaf1e7 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -13,13 +13,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div style="padding: 8px; text-align: center;">
 			<MkButton primary gradate rounded inline small @click="post"><i class="ti ti-pencil"></i></MkButton>
 		</div>
-		<MkTimeline ref="timeline" src="channel" :channel="column.channelId"/>
+		<MkTimeline ref="timeline" src="channel" :channel="column.channelId" @note="onNote"/>
 	</template>
 </XColumn>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue';
+import { ref, shallowRef, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
@@ -29,6 +29,10 @@ import * as os from '@/os.js';
 import { favoritedChannelsCache } from '@/cache.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
 
 const props = defineProps<{
 	column: Column;
@@ -37,11 +41,16 @@ const props = defineProps<{
 
 const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
 const channel = shallowRef<Misskey.entities.Channel>();
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
 
 if (props.column.channelId == null) {
 	setChannel();
 }
 
+watch(soundSetting, v => {
+	updateColumn(props.column.id, { soundSetting: v });
+});
+
 async function setChannel() {
 	const channels = await favoritedChannelsCache.fetch();
 	const { canceled, result: chosenChannel } = await os.select({
@@ -70,9 +79,17 @@ async function post() {
 	});
 }
 
-const menu = [{
+function onNote() {
+	sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [{
 	icon: 'ti ti-pencil',
 	text: i18n.ts.selectChannel,
 	action: setChannel,
+}, {
+	icon: 'ti ti-bell',
+	text: i18n.ts._deck.newNoteNotificationSettings,
+	action: () => soundSettingsButton(soundSetting),
 }];
 </script>
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 70b55e8172a4..bb3c04cd5c8f 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -9,6 +9,7 @@ import { notificationTypes } from 'misskey-js';
 import { Storage } from '@/pizzax.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { deepClone } from '@/scripts/clone.js';
+import { SoundStore } from '@/store.js';
 
 type ColumnWidget = {
 	name: string;
@@ -33,6 +34,7 @@ export type Column = {
 	withRenotes?: boolean;
 	withReplies?: boolean;
 	onlyFiles?: boolean;
+	soundSetting: SoundStore;
 };
 
 export const deckStore = markRaw(new Storage('deck', {
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 70ea54326f34..5369112494c6 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
 
-	<MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :withRenotes="withRenotes"/>
+	<MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :withRenotes="withRenotes" @note="onNote"/>
 </XColumn>
 </template>
 
@@ -21,6 +21,10 @@ import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
 
 const props = defineProps<{
 	column: Column;
@@ -29,6 +33,7 @@ const props = defineProps<{
 
 const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
 const withRenotes = ref(props.column.withRenotes ?? true);
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
 
 if (props.column.listId == null) {
 	setList();
@@ -40,6 +45,10 @@ watch(withRenotes, v => {
 	});
 });
 
+watch(soundSetting, v => {
+	updateColumn(props.column.id, { soundSetting: v });
+});
+
 async function setList() {
 	const lists = await misskeyApi('users/lists/list');
 	const { canceled, result: list } = await os.select({
@@ -59,7 +68,11 @@ function editList() {
 	os.pageWindow('my/lists/' + props.column.listId);
 }
 
-const menu = [
+function onNote() {
+	sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [
 	{
 		icon: 'ti ti-pencil',
 		text: i18n.ts.selectList,
@@ -75,5 +88,10 @@ const menu = [
 		text: i18n.ts.showRenotes,
 		ref: withRenotes,
 	},
+	{
+		icon: 'ti ti-bell',
+		text: i18n.ts._deck.newNoteNotificationSettings,
+		action: () => soundSettingsButton(soundSetting),
+	},
 ];
 </script>
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index eae2ee13f344..32ab7527b471 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -9,18 +9,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<i class="ti ti-badge"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
 
-	<MkTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId"/>
+	<MkTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId" @note="onNote"/>
 </XColumn>
 </template>
 
 <script lang="ts" setup>
-import { onMounted, shallowRef } from 'vue';
+import { onMounted, ref, shallowRef, watch } from 'vue';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
 
 const props = defineProps<{
 	column: Column;
@@ -28,6 +32,7 @@ const props = defineProps<{
 }>();
 
 const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
 
 onMounted(() => {
 	if (props.column.roleId == null) {
@@ -35,6 +40,10 @@ onMounted(() => {
 	}
 });
 
+watch(soundSetting, v => {
+	updateColumn(props.column.id, { soundSetting: v });
+});
+
 async function setRole() {
 	const roles = (await misskeyApi('roles/list')).filter(x => x.isExplorable);
 	const { canceled, result: role } = await os.select({
@@ -50,10 +59,18 @@ async function setRole() {
 	});
 }
 
-const menu = [{
+function onNote() {
+	sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [{
 	icon: 'ti ti-pencil',
 	text: i18n.ts.role,
 	action: setRole,
+}, {
+	icon: 'ti ti-bell',
+	text: i18n.ts._deck.newNoteNotificationSettings,
+	action: () => soundSettingsButton(soundSetting),
 }];
 
 /*
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index f9066d9db760..a967335edff5 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		:withRenotes="withRenotes"
 		:withReplies="withReplies"
 		:onlyFiles="onlyFiles"
+		@note="onNote"
 	/>
 </XColumn>
 </template>
@@ -41,6 +42,10 @@ import * as os from '@/os.js';
 import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
 
 const props = defineProps<{
 	column: Column;
@@ -52,6 +57,7 @@ const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
 
 const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
 const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
 const withRenotes = ref(props.column.withRenotes ?? true);
 const withReplies = ref(props.column.withReplies ?? false);
 const onlyFiles = ref(props.column.onlyFiles ?? false);
@@ -74,6 +80,10 @@ watch(onlyFiles, v => {
 	});
 });
 
+watch(soundSetting, v => {
+	updateColumn(props.column.id, { soundSetting: v });
+});
+
 onMounted(() => {
 	if (props.column.tl == null) {
 		setType();
@@ -108,10 +118,18 @@ async function setType() {
 	});
 }
 
-const menu = [{
+function onNote() {
+	sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [{
 	icon: 'ti ti-pencil',
 	text: i18n.ts.timeline,
 	action: setType,
+}, {
+	icon: 'ti ti-bell',
+	text: i18n.ts._deck.newNoteNotificationSettings,
+	action: () => soundSettingsButton(soundSetting),
 }, {
 	type: 'switch',
 	text: i18n.ts.showRenotes,
diff --git a/packages/frontend/src/ui/deck/tl-note-notification.ts b/packages/frontend/src/ui/deck/tl-note-notification.ts
new file mode 100644
index 000000000000..275ea56ba03f
--- /dev/null
+++ b/packages/frontend/src/ui/deck/tl-note-notification.ts
@@ -0,0 +1,107 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import { Ref } from 'vue';
+import { SoundStore } from '@/store.js';
+import { getSoundDuration, playMisskeySfxFile, soundsTypes, SoundType } from '@/scripts/sound.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+
+export async function soundSettingsButton(soundSetting: Ref<SoundStore>): Promise<void> {
+	function getSoundTypeName(f: SoundType): string {
+		switch (f) {
+			case null:
+				return i18n.ts.none;
+			case '_driveFile_':
+				return i18n.ts._soundSettings.driveFile;
+			default:
+				return f;
+		}
+	}
+
+	const { canceled, result } = await os.form(i18n.ts.sound, {
+		type: {
+			type: 'enum',
+			label: i18n.ts.sound,
+			default: soundSetting.value.type ?? 'none',
+			enum: soundsTypes.map(f => ({
+				value: f ?? 'none', label: getSoundTypeName(f),
+			})),
+		},
+		soundFile: {
+			type: 'drive-file',
+			label: i18n.ts.file,
+			defaultFileId: soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileId : null,
+			hidden: v => v.type !== '_driveFile_',
+			validate: async (file: Misskey.entities.DriveFile) => {
+				if (!file.type.startsWith('audio')) {
+					os.alert({
+						type: 'warning',
+						title: i18n.ts._soundSettings.driveFileTypeWarn,
+						text: i18n.ts._soundSettings.driveFileTypeWarnDescription,
+					});
+					return false;
+				}
+
+				const duration = await getSoundDuration(file.url);
+				if (duration >= 2000) {
+					const { canceled } = await os.confirm({
+						type: 'warning',
+						title: i18n.ts._soundSettings.driveFileDurationWarn,
+						text: i18n.ts._soundSettings.driveFileDurationWarnDescription,
+						okText: i18n.ts.continue,
+						cancelText: i18n.ts.cancel,
+					});
+					if (canceled) return false;
+				}
+
+				return true;
+			},
+		},
+		volume: {
+			type: 'range',
+			label: i18n.ts.volume,
+			default: soundSetting.value.volume ?? 1,
+			textConverter: (v) => `${Math.floor(v * 100)}%`,
+			min: 0,
+			max: 1,
+			step: 0.05,
+		},
+		listen: {
+			type: 'button',
+			content: i18n.ts.listen,
+			action: (_, v) => {
+				const sound = buildSoundStore(v);
+				if (!sound) return;
+				playMisskeySfxFile(sound);
+			},
+		},
+	});
+	if (canceled) return;
+
+	const res = buildSoundStore(result);
+	if (res) soundSetting.value = res;
+
+	function buildSoundStore(result: any): SoundStore | null {
+		const type = (result.type === 'none' ? null : result.type) as SoundType;
+		const volume = result.volume as number;
+		const fileId = result.soundFile?.id ?? (soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileId : undefined);
+		const fileUrl = result.soundFile?.url ?? (soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileUrl : undefined);
+
+		if (type === '_driveFile_') {
+			if (!fileUrl || !fileId) {
+				os.alert({
+					type: 'warning',
+					text: i18n.ts._soundSettings.driveFileWarn,
+				});
+				return null;
+			}
+			return { type, volume, fileId, fileUrl };
+		} else {
+			return { type, volume };
+		}
+	}
+}
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 208f03dc3efc..11567677c9e7 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4441,7 +4441,6 @@ export type components = {
       caseSensitive: boolean;
       /** @default false */
       localOnly: boolean;
-      notify: boolean;
       /** @default false */
       excludeBots: boolean;
       /** @default false */
@@ -9748,7 +9747,6 @@ export type operations = {
           excludeBots?: boolean;
           withReplies: boolean;
           withFile: boolean;
-          notify: boolean;
         };
       };
     };
@@ -10030,7 +10028,6 @@ export type operations = {
           excludeBots?: boolean;
           withReplies?: boolean;
           withFile?: boolean;
-          notify?: boolean;
         };
       };
     };

From 4704dfe0611767fda8917a1a544475ba7bdd7cb8 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Mon, 27 May 2024 12:00:25 +0000
Subject: [PATCH 234/266] Bump version to 2024.5.0-beta.5

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index ca3883b804b5..6caf4fc11476 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-beta.4",
+	"version": "2024.5.0-beta.5",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index bad0142899b5..f010699ec611 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-beta.4",
+	"version": "2024.5.0-beta.5",
 	"description": "Misskey SDK for JavaScript",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",

From 934f9f80bd23c09842862784294dcde41fda4738 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Mon, 27 May 2024 21:25:07 +0900
Subject: [PATCH 235/266] =?UTF-8?q?docs:=20=E3=80=8CFeat:=20=E5=80=8B?=
 =?UTF-8?q?=E5=88=A5=E3=81=AE=E3=81=8A=E7=9F=A5=E3=82=89=E3=81=9B=E3=81=AB?=
 =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=AF=E3=81=A7=E9=A3=9B=E3=81=B9=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=80=8D=E3=81=AEcherry-pick?=
 =?UTF-8?q?=E5=85=83=E3=82=92=E6=8C=87=E5=AE=9A=20(#13891)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* docs: 「Feat: 個別のお知らせにリンクで飛べるように」のcherry-pick元を指定
cc misskey-dev#13885

* Update CHANGELOG.md

Co-authored-by: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a70fc7a8a0f..3904ab105710 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,7 +26,7 @@
 ### Client
 - Feat: アップロードするファイルの名前をランダム文字列にできるように
 - Feat: 個別のお知らせにリンクで飛べるように  
-  (Cherry-picked from https://github.com/MisskeyIO/misskey)
+  (Based on https://github.com/MisskeyIO/misskey/pull/639)
 - Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
 - Enhance: 広告がMisskeyと同一ドメインの場合はRouterで遷移するように
 - Enhance: リアクション・いいねの総数を表示するように

From de9e391e3486d8cb1bcf33744076c9ee17fa2aba Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 28 May 2024 00:02:22 +0900
Subject: [PATCH 236/266] [skip ci] update release manager actions

---
 .github/workflows/release-with-ready.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/release-with-ready.yml b/.github/workflows/release-with-ready.yml
index 139503e563e8..a0fad0e336b8 100644
--- a/.github/workflows/release-with-ready.yml
+++ b/.github/workflows/release-with-ready.yml
@@ -22,9 +22,11 @@ jobs:
       # PR情報を取得
       - name: Get PR
         run: |
-          pr_json=$(gh pr view ${{ github.event.pull_request.number }} --json isDraft,headRefName)
+          pr_json=$(gh pr view "$PR_NUMBER" --json isDraft,headRefName)
           echo "ref=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
         id: get_pr
+        env:
+          PR_NUMBER: ${{ github.event.pull_request.number }}
   release:
     uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
     needs: check

From 1bb1a3298645c2d5a3f678cb6676e19519ec1e48 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 28 May 2024 00:03:12 +0900
Subject: [PATCH 237/266] [skip ci] update release manager actions

---
 .github/workflows/release-edit-with-push.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml
index 890cb047bd05..86ee0b3fb54b 100644
--- a/.github/workflows/release-edit-with-push.yml
+++ b/.github/workflows/release-edit-with-push.yml
@@ -23,7 +23,7 @@ jobs:
       # headがrelease/かつopenのPRを1つ取得
       - name: Get PR
         run: |
-          echo "pr_number=$(gh pr list --limit 1 --head "${{ github.ref_name }}" --json number  --jq '.[] | .number')" >> $GITHUB_OUTPUT
+          echo "pr_number=$(gh pr list --limit 1 --head "$GITHUB_REF_NAME" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
         id: get_pr
       - name: Get target version
         uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v1

From 89b27d8587221a321b6ff9cdae4b714bbedd151a Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 28 May 2024 14:36:06 +0900
Subject: [PATCH 238/266] =?UTF-8?q?fix(federation):=20Inbox=E3=81=AB?=
 =?UTF-8?q?=E3=81=8D=E3=81=9FCreate,=20Announce=E3=81=AEobject=E3=81=8CBea?=
 =?UTF-8?q?rcaps=20url=E3=81=A0=E3=81=A3=E3=81=9F=E9=9A=9B=E3=81=AF?=
 =?UTF-8?q?=E3=82=B9=E3=82=AD=E3=83=83=E3=83=97=E3=81=99=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#13610)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(federation): AnnounceのobjectがLike出なかったらキューにためない
Fix https://github.com/misskey-dev/misskey/issues/13552

* revert

* better reason handlings

* result

* improve announce handling

* skip bearcaps

* also announce
---
 .../src/core/activitypub/ApInboxService.ts    | 117 ++++++++++--------
 .../core/activitypub/models/ApNoteService.ts  |   8 +-
 packages/backend/src/core/activitypub/type.ts |   1 +
 .../queue/processors/InboxProcessorService.ts |  13 +-
 4 files changed, 84 insertions(+), 55 deletions(-)

diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 1621c41bcc54..d0d206760cb9 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -28,6 +28,7 @@ import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserR
 import { bindThis } from '@/decorators.js';
 import type { MiRemoteUser } from '@/models/User.js';
 import { isNotNull } from '@/misc/is-not-null.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
 import { ApNoteService } from './models/ApNoteService.js';
 import { ApLoggerService } from './ApLoggerService.js';
@@ -36,9 +37,8 @@ import { ApResolverService } from './ApResolverService.js';
 import { ApAudienceService } from './ApAudienceService.js';
 import { ApPersonService } from './models/ApPersonService.js';
 import { ApQuestionService } from './models/ApQuestionService.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
 import type { Resolver } from './ApResolverService.js';
-import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js';
+import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove, IPost } from './type.js';
 
 @Injectable()
 export class ApInboxService {
@@ -90,13 +90,15 @@ export class ApInboxService {
 	}
 
 	@bindThis
-	public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
+	public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<string | void> {
+		let result = undefined as string | void;
 		if (isCollectionOrOrderedCollection(activity)) {
+			const results = [] as [string, string | void][];
 			const resolver = this.apResolverService.createResolver();
 			for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
 				const act = await resolver.resolve(item);
 				try {
-					await this.performOneActivity(actor, act);
+					results.push([getApId(item), await this.performOneActivity(actor, act)]);
 				} catch (err) {
 					if (err instanceof Error || typeof err === 'string') {
 						this.logger.error(err);
@@ -105,8 +107,13 @@ export class ApInboxService {
 					}
 				}
 			}
+
+			const hasReason = results.some(([, reason]) => (reason != null && !reason.startsWith('ok')));
+			if (hasReason) {
+				result = results.map(([id, reason]) => `${id}: ${reason}`).join('\n');
+			}
 		} else {
-			await this.performOneActivity(actor, activity);
+			result = await this.performOneActivity(actor, activity);
 		}
 
 		// ついでにリモートユーザーの情報が古かったら更新しておく
@@ -117,42 +124,43 @@ export class ApInboxService {
 				});
 			}
 		}
+		return result;
 	}
 
 	@bindThis
-	public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
+	public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<string | void> {
 		if (actor.isSuspended) return;
 
 		if (isCreate(activity)) {
-			await this.create(actor, activity);
+			return await this.create(actor, activity);
 		} else if (isDelete(activity)) {
-			await this.delete(actor, activity);
+			return await this.delete(actor, activity);
 		} else if (isUpdate(activity)) {
-			await this.update(actor, activity);
+			return await this.update(actor, activity);
 		} else if (isFollow(activity)) {
-			await this.follow(actor, activity);
+			return await this.follow(actor, activity);
 		} else if (isAccept(activity)) {
-			await this.accept(actor, activity);
+			return await this.accept(actor, activity);
 		} else if (isReject(activity)) {
-			await this.reject(actor, activity);
+			return await this.reject(actor, activity);
 		} else if (isAdd(activity)) {
-			await this.add(actor, activity).catch(err => this.logger.error(err));
+			return await this.add(actor, activity);
 		} else if (isRemove(activity)) {
-			await this.remove(actor, activity).catch(err => this.logger.error(err));
+			return await this.remove(actor, activity);
 		} else if (isAnnounce(activity)) {
-			await this.announce(actor, activity);
+			return await this.announce(actor, activity);
 		} else if (isLike(activity)) {
-			await this.like(actor, activity);
+			return await this.like(actor, activity);
 		} else if (isUndo(activity)) {
-			await this.undo(actor, activity);
+			return await this.undo(actor, activity);
 		} else if (isBlock(activity)) {
-			await this.block(actor, activity);
+			return await this.block(actor, activity);
 		} else if (isFlag(activity)) {
-			await this.flag(actor, activity);
+			return await this.flag(actor, activity);
 		} else if (isMove(activity)) {
-			await this.move(actor, activity);
+			return await this.move(actor, activity);
 		} else {
-			this.logger.warn(`unrecognized activity type: ${activity.type}`);
+			return `unrecognized activity type: ${activity.type}`;
 		}
 	}
 
@@ -234,38 +242,49 @@ export class ApInboxService {
 	}
 
 	@bindThis
-	private async add(actor: MiRemoteUser, activity: IAdd): Promise<void> {
+	private async add(actor: MiRemoteUser, activity: IAdd): Promise<string | void> {
 		if (actor.uri !== activity.actor) {
-			throw new Error('invalid actor');
+			return 'invalid actor';
 		}
 
 		if (activity.target == null) {
-			throw new Error('target is null');
+			return 'target is null';
 		}
 
 		if (activity.target === actor.featured) {
 			const note = await this.apNoteService.resolveNote(activity.object);
-			if (note == null) throw new Error('note not found');
+			if (note == null) return 'note not found';
 			await this.notePiningService.addPinned(actor, note.id);
 			return;
 		}
 
-		throw new Error(`unknown target: ${activity.target}`);
+		return `unknown target: ${activity.target}`;
 	}
 
 	@bindThis
-	private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<void> {
+	private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<string | void> {
 		const uri = getApId(activity);
 
 		this.logger.info(`Announce: ${uri}`);
 
+		const resolver = this.apResolverService.createResolver();
+
+		if (!activity.object) return 'skip: activity has no object property';
 		const targetUri = getApId(activity.object);
+		if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.';
+
+		const target = await resolver.resolve(activity.object).catch(e => {
+			this.logger.error(`Resolution failed: ${e}`);
+			return e;
+		});
+
+		if (isPost(target)) return await this.announceNote(actor, activity, target);
 
-		await this.announceNote(actor, activity, targetUri);
+		return `skip: unknown object type ${getApType(target)}`;
 	}
 
 	@bindThis
-	private async announceNote(actor: MiRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
+	private async announceNote(actor: MiRemoteUser, activity: IAnnounce, target: IPost): Promise<string | void> {
 		const uri = getApId(activity);
 
 		if (actor.isSuspended) {
@@ -288,24 +307,21 @@ export class ApInboxService {
 			// Announce対象をresolve
 			let renote;
 			try {
-				renote = await this.apNoteService.resolveNote(targetUri);
-				if (renote == null) throw new Error('announce target is null');
+				renote = await this.apNoteService.resolveNote(target);
+				if (renote == null) return 'announce target is null';
 			} catch (err) {
 				// 対象が4xxならスキップ
 				if (err instanceof StatusError) {
 					if (!err.isRetryable) {
-						this.logger.warn(`Ignored announce target ${targetUri} - ${err.statusCode}`);
-						return;
+						return `Ignored announce target ${target.id} - ${err.statusCode}`;
 					}
-
-					this.logger.warn(`Error in announce target ${targetUri} - ${err.statusCode}`);
+					return `Error in announce target ${target.id} - ${err.statusCode}`;
 				}
 				throw err;
 			}
 
 			if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) {
-				this.logger.warn('skip: invalid actor for this activity');
-				return;
+				return 'skip: invalid actor for this activity';
 			}
 
 			this.logger.info(`Creating the (Re)Note: ${uri}`);
@@ -314,8 +330,7 @@ export class ApInboxService {
 			const createdAt = activity.published ? new Date(activity.published) : null;
 
 			if (createdAt && createdAt < this.idService.parse(renote.id).date) {
-				this.logger.warn('skip: malformed createdAt');
-				return;
+				return 'skip: malformed createdAt';
 			}
 
 			await this.noteCreateService.create(actor, {
@@ -349,11 +364,15 @@ export class ApInboxService {
 	}
 
 	@bindThis
-	private async create(actor: MiRemoteUser, activity: ICreate): Promise<void> {
+	private async create(actor: MiRemoteUser, activity: ICreate): Promise<string | void> {
 		const uri = getApId(activity);
 
 		this.logger.info(`Create: ${uri}`);
 
+		if (!activity.object) return 'skip: activity has no object property';
+		const targetUri = getApId(activity.object);
+		if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.';
+
 		// copy audiences between activity <=> object.
 		if (typeof activity.object === 'object') {
 			const to = unique(concat([toArray(activity.to), toArray(activity.object.to)]));
@@ -380,7 +399,7 @@ export class ApInboxService {
 		if (isPost(object)) {
 			await this.createNote(resolver, actor, object, false, activity);
 		} else {
-			this.logger.warn(`Unknown type: ${getApType(object)}`);
+			return `Unknown type: ${getApType(object)}`;
 		}
 	}
 
@@ -422,7 +441,7 @@ export class ApInboxService {
 	@bindThis
 	private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
 		if (actor.uri !== activity.actor) {
-			throw new Error('invalid actor');
+			return 'invalid actor';
 		}
 
 		// 削除対象objectのtype
@@ -581,29 +600,29 @@ export class ApInboxService {
 	}
 
 	@bindThis
-	private async remove(actor: MiRemoteUser, activity: IRemove): Promise<void> {
+	private async remove(actor: MiRemoteUser, activity: IRemove): Promise<string | void> {
 		if (actor.uri !== activity.actor) {
-			throw new Error('invalid actor');
+			return 'invalid actor';
 		}
 
 		if (activity.target == null) {
-			throw new Error('target is null');
+			return 'target is null';
 		}
 
 		if (activity.target === actor.featured) {
 			const note = await this.apNoteService.resolveNote(activity.object);
-			if (note == null) throw new Error('note not found');
+			if (note == null) return 'note not found';
 			await this.notePiningService.removePinned(actor, note.id);
 			return;
 		}
 
-		throw new Error(`unknown target: ${activity.target}`);
+		return `unknown target: ${activity.target}`;
 	}
 
 	@bindThis
 	private async undo(actor: MiRemoteUser, activity: IUndo): Promise<string> {
 		if (actor.uri !== activity.actor) {
-			throw new Error('invalid actor');
+			return 'invalid actor';
 		}
 
 		const uri = activity.id ?? activity;
@@ -614,7 +633,7 @@ export class ApInboxService {
 
 		const object = await resolver.resolve(activity.object).catch(e => {
 			this.logger.error(`Resolution failed: ${e}`);
-			throw e;
+			return e;
 		});
 
 		// don't queue because the sender may attempt again when timeout
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 4e361b57bcf5..e6dff067f31f 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -81,20 +81,20 @@ export class ApNoteService {
 		const expectHost = this.utilityService.extractDbHost(uri);
 
 		if (!validPost.includes(getApType(object))) {
-			return new Error(`invalid Note: invalid object type ${getApType(object)}`);
+			return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: invalid object type ${getApType(object)}`);
 		}
 
 		if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) {
-			return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`);
+			return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`);
 		}
 
 		const actualHost = object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo));
 		if (object.attributedTo && actualHost !== expectHost) {
-			return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`);
+			return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`);
 		}
 
 		if (object.published && !this.idService.isSafeT(new Date(object.published).valueOf())) {
-			return new Error('invalid Note: published timestamp is malformed');
+			return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', 'invalid Note: published timestamp is malformed');
 		}
 
 		return null;
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index 09322888d518..5b6c6c8ca6cb 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -328,3 +328,4 @@ export const isAnnounce = (object: IObject): object is IAnnounce => getApType(ob
 export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block';
 export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag';
 export const isMove = (object: IObject): object is IMove => getApType(object) === 'Move';
+export const isNote = (object: IObject): object is IPost => getApType(object) === 'Note';
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index f465339075b9..fa7009f8f5d9 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -204,13 +204,22 @@ export class InboxProcessorService {
 
 		// アクティビティを処理
 		try {
-			await this.apInboxService.performActivity(authUser.user, activity);
+			const result = await this.apInboxService.performActivity(authUser.user, activity);
+			if (result && !result.startsWith('ok')) {
+				this.logger.warn(`inbox activity ignored (maybe): id=${activity.id} reason=${result}`);
+				return result;
+			}
 		} catch (e) {
 			if (e instanceof IdentifiableError) {
 				if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
 					return 'blocked notes with prohibited words';
 				}
-				if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') return 'actor has been suspended';
+				if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') {
+					return 'actor has been suspended';
+				}
+				if (e.id === 'd450b8a9-48e4-4dab-ae36-f4db763fda7c') { // invalid Note
+					return e.message;
+				}
 			}
 			throw e;
 		}

From 80f3cb96b02eaaeb513670224d33b8842414963e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 28 May 2024 17:06:33 +0900
Subject: [PATCH 239/266] feat: sentry integration (#13897)

* wip

* wip

* wip

* wip

* Update CHANGELOG.md

* Update ApiCallService.ts

* Update config.ts
---
 .config/docker_example.yml                    |  19 +-
 .config/example.yml                           |  15 +
 .devcontainer/devcontainer.yml                |  15 +
 CHANGELOG.md                                  |   1 +
 chart/files/default.yml                       |  16 +
 packages/backend/package.json                 |   4 +-
 packages/backend/src/boot/master.ts           |  20 +
 packages/backend/src/config.ts                |   7 +
 .../backend/src/server/api/ApiCallService.ts  |  77 ++-
 pnpm-lock.yaml                                | 630 ++++++++++++++++++
 10 files changed, 776 insertions(+), 28 deletions(-)

diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index acd169bf436e..42ac18de1b50 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -106,7 +106,7 @@ redis:
 #   ┌───────────────────────────┐
 #───┘ MeiliSearch configuration └─────────────────────────────
 
-# You can set scope to local (default value) or global 
+# You can set scope to local (default value) or global
 # (include notes from remote).
 
 #meilisearch:
@@ -136,6 +136,21 @@ redis:
 
 id: 'aidx'
 
+#   ┌────────────────┐
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+#  enableNodeProfiling: true
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
 #   ┌─────────────────────┐
 #───┘ Other configuration └─────────────────────────────────────
 
@@ -185,7 +200,7 @@ proxyRemoteFiles: true
 signToActivityPubGet: true
 
 # For security reasons, uploading attachments from the intranet is prohibited,
-# but exceptions can be made from the following settings. Default value is "undefined". 
+# but exceptions can be made from the following settings. Default value is "undefined".
 # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
 #allowedPrivateNetworks: [
 #  '127.0.0.1/32'
diff --git a/.config/example.yml b/.config/example.yml
index b0b7f140593d..b11cbd137328 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -205,6 +205,21 @@ redis:
 
 id: 'aidx'
 
+#   ┌────────────────┐
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+#  enableNodeProfiling: true
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
 #   ┌─────────────────────┐
 #───┘ Other configuration └─────────────────────────────────────
 
diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml
index 7ea09294692a..beefcfd0a2d5 100644
--- a/.devcontainer/devcontainer.yml
+++ b/.devcontainer/devcontainer.yml
@@ -132,6 +132,21 @@ redis:
 
 id: 'aidx'
 
+#   ┌────────────────┐
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+#  enableNodeProfiling: true
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
 #   ┌─────────────────────┐
 #───┘ Other configuration └─────────────────────────────────────
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3904ab105710..4091668b5400 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 - 管理者向け権限 `read:admin:show-users` は `read:admin:show-user` に統合されました。必要に応じてAPIトークンを再発行してください。
 
 ### General
+- Feat: エラートラッキングにSentryを使用できるようになりました
 - Enhance: URLプレビューの有効化・無効化を設定できるように #13569
 - Enhance: アンテナでBotによるノートを除外できるように  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
diff --git a/chart/files/default.yml b/chart/files/default.yml
index 4cc291e80a77..f98b8ebfee04 100644
--- a/chart/files/default.yml
+++ b/chart/files/default.yml
@@ -152,6 +152,22 @@ redis:
 # ID SETTINGS AFTER THAT!
 
 id: "aidx"
+
+#   ┌────────────────┐
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+#  enableNodeProfiling: true
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+#  options:
+#    dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
 #   ┌─────────────────────┐
 #───┘ Other configuration └─────────────────────────────────────
 
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 8e29252d759c..e034f75dc55e 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -4,7 +4,7 @@
 	"private": true,
 	"type": "module",
 	"engines": {
-		"node": ">=20.10.0"
+		"node": "^20.10.0"
 	},
 	"scripts": {
 		"start": "node ./built/boot/entry.js",
@@ -86,6 +86,8 @@
 		"@nestjs/core": "10.3.8",
 		"@nestjs/testing": "10.3.8",
 		"@peertube/http-signature": "1.7.0",
+		"@sentry/node": "^8.5.0",
+		"@sentry/profiling-node": "^8.5.0",
 		"@simplewebauthn/server": "10.0.0",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.5.0",
diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts
index 30f9477ccf81..75e1a80cd1df 100644
--- a/packages/backend/src/boot/master.ts
+++ b/packages/backend/src/boot/master.ts
@@ -10,6 +10,8 @@ import * as os from 'node:os';
 import cluster from 'node:cluster';
 import chalk from 'chalk';
 import chalkTemplate from 'chalk-template';
+import * as Sentry from '@sentry/node';
+import { nodeProfilingIntegration } from '@sentry/profiling-node';
 import Logger from '@/logger.js';
 import { loadConfig } from '@/config.js';
 import type { Config } from '@/config.js';
@@ -71,6 +73,24 @@ export async function masterMain() {
 
 	bootLogger.succ('Misskey initialized');
 
+	if (config.sentryForBackend) {
+		Sentry.init({
+			integrations: [
+				...(config.sentryForBackend.enableNodeProfiling ? [nodeProfilingIntegration()] : []),
+			],
+
+			// Performance Monitoring
+			tracesSampleRate: 1.0, //  Capture 100% of the transactions
+
+			// Set sampling rate for profiling - this is relative to tracesSampleRate
+			profilesSampleRate: 1.0,
+
+			maxBreadcrumbs: 0,
+
+			...config.sentryForBackend.options,
+		});
+	}
+
 	if (envOption.disableClustering) {
 		if (envOption.onlyServer) {
 			await server();
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 0ca1fa55c1f8..0ac521d4094e 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -7,6 +7,7 @@ import * as fs from 'node:fs';
 import { fileURLToPath } from 'node:url';
 import { dirname, resolve } from 'node:path';
 import * as yaml from 'js-yaml';
+import * as Sentry from '@sentry/node';
 import type { RedisOptions } from 'ioredis';
 
 type RedisOptionsSource = Partial<RedisOptions> & {
@@ -56,6 +57,8 @@ type Source = {
 		index: string;
 		scope?: 'local' | 'global' | string[];
 	};
+	sentryForBackend?: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; };
+	sentryForFrontend?: { options: Partial<Sentry.NodeOptions> };
 
 	publishTarballInsteadOfProvideRepositoryUrl?: boolean;
 
@@ -166,6 +169,8 @@ export type Config = {
 	redisForPubsub: RedisOptions & RedisOptionsSource;
 	redisForJobQueue: RedisOptions & RedisOptionsSource;
 	redisForTimelines: RedisOptions & RedisOptionsSource;
+	sentryForBackend: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; } | undefined;
+	sentryForFrontend: { options: Partial<Sentry.NodeOptions> } | undefined;
 	perChannelMaxNoteCacheCount: number;
 	perUserNotificationsMaxCount: number;
 	deactivateAntennaThreshold: number;
@@ -234,6 +239,8 @@ export function loadConfig(): Config {
 		redisForPubsub: config.redisForPubsub ? convertRedisOptions(config.redisForPubsub, host) : redis,
 		redisForJobQueue: config.redisForJobQueue ? convertRedisOptions(config.redisForJobQueue, host) : redis,
 		redisForTimelines: config.redisForTimelines ? convertRedisOptions(config.redisForTimelines, host) : redis,
+		sentryForBackend: config.sentryForBackend,
+		sentryForFrontend: config.sentryForFrontend,
 		id: config.id,
 		proxy: config.proxy,
 		proxySmtp: config.proxySmtp,
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index 9836689872ae..271ef80554a8 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -7,6 +7,7 @@ import { randomUUID } from 'node:crypto';
 import * as fs from 'node:fs';
 import * as stream from 'node:stream/promises';
 import { Inject, Injectable } from '@nestjs/common';
+import * as Sentry from '@sentry/node';
 import { DI } from '@/di-symbols.js';
 import { getIpHash } from '@/misc/get-ip-hash.js';
 import type { MiLocalUser, MiUser } from '@/models/User.js';
@@ -17,6 +18,7 @@ import { MetaService } from '@/core/MetaService.js';
 import { createTemp } from '@/misc/create-temp.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
+import type { Config } from '@/config.js';
 import { ApiError } from './error.js';
 import { RateLimiterService } from './RateLimiterService.js';
 import { ApiLoggerService } from './ApiLoggerService.js';
@@ -38,6 +40,9 @@ export class ApiCallService implements OnApplicationShutdown {
 	private userIpHistoriesClearIntervalId: NodeJS.Timeout;
 
 	constructor(
+		@Inject(DI.config)
+		private config: Config,
+
 		@Inject(DI.userIpsRepository)
 		private userIpsRepository: UserIpsRepository,
 
@@ -88,6 +93,48 @@ export class ApiCallService implements OnApplicationShutdown {
 		}
 	}
 
+	#onExecError(ep: IEndpoint, data: any, err: Error): void {
+		if (err instanceof ApiError || err instanceof AuthenticationError) {
+			throw err;
+		} else {
+			const errId = randomUUID();
+			this.logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, {
+				ep: ep.name,
+				ps: data,
+				e: {
+					message: err.message,
+					code: err.name,
+					stack: err.stack,
+					id: errId,
+				},
+			});
+			console.error(err, errId);
+
+			if (this.config.sentryForBackend) {
+				Sentry.captureMessage(`Internal error occurred in ${ep.name}: ${err.message}`, {
+					extra: {
+						ep: ep.name,
+						ps: data,
+						e: {
+							message: err.message,
+							code: err.name,
+							stack: err.stack,
+							id: errId,
+						},
+					},
+				});
+			}
+
+			throw new ApiError(null, {
+				e: {
+					message: err.message,
+					code: err.name,
+					id: errId,
+				},
+			});
+		}
+	}
+
 	@bindThis
 	public handleRequest(
 		endpoint: IEndpoint & { exec: any },
@@ -362,31 +409,11 @@ export class ApiCallService implements OnApplicationShutdown {
 		}
 
 		// API invoking
-		return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => {
-			if (err instanceof ApiError || err instanceof AuthenticationError) {
-				throw err;
-			} else {
-				const errId = randomUUID();
-				this.logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, {
-					ep: ep.name,
-					ps: data,
-					e: {
-						message: err.message,
-						code: err.name,
-						stack: err.stack,
-						id: errId,
-					},
-				});
-				console.error(err, errId);
-				throw new ApiError(null, {
-					e: {
-						message: err.message,
-						code: err.name,
-						id: errId,
-					},
-				});
-			}
-		});
+		if (this.config.sentryForBackend) {
+			return await Sentry.startSpan({ name: 'API: ' + ep.name }, () => ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err)));
+		} else {
+			return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err));
+		}
 	}
 
 	@bindThis
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 465539b83481..6bf1cf158c11 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -140,6 +140,12 @@ importers:
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
+      '@sentry/node':
+        specifier: ^8.5.0
+        version: 8.5.0
+      '@sentry/profiling-node':
+        specifier: ^8.5.0
+        version: 8.5.0
       '@simplewebauthn/server':
         specifier: 10.0.0
         version: 10.0.0(encoding@0.1.13)
@@ -3264,6 +3270,154 @@ packages:
   '@open-draft/until@2.1.0':
     resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
 
+  '@opentelemetry/api-logs@0.51.1':
+    resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==}
+    engines: {node: '>=14'}
+
+  '@opentelemetry/api@1.8.0':
+    resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==}
+    engines: {node: '>=8.0.0'}
+
+  '@opentelemetry/context-async-hooks@1.24.1':
+    resolution: {integrity: sha512-R5r6DO4kgEOVBxFXhXjwospLQkv+sYxwCfjvoZBe7Zm6KKXAV9kDSJhi/D1BweowdZmO+sdbENLs374gER8hpQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+  '@opentelemetry/core@1.24.1':
+    resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+  '@opentelemetry/instrumentation-connect@0.36.0':
+    resolution: {integrity: sha512-k9++bmJZ9zDEs3u3DnKTn2l7QTiNFg3gPx7G9rW0TPnP+xZoBSBTrEcGYBaqflQlrFG23Q58+X1sM2ayWPv5Fg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-express@0.39.0':
+    resolution: {integrity: sha512-AG8U7z7D0JcBu/7dDcwb47UMEzj9/FMiJV2iQZqrsZnxR3FjB9J9oIH2iszJYci2eUdp2WbdvtpD9RV/zmME5A==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-fastify@0.36.1':
+    resolution: {integrity: sha512-3Nfm43PI0I+3EX+1YbSy6xbDu276R1Dh1tqAk68yd4yirnIh52Kd5B+nJ8CgHA7o3UKakpBjj6vSzi5vNCzJIA==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-graphql@0.40.0':
+    resolution: {integrity: sha512-LVRdEHWACWOczv2imD+mhUrLMxsEjPPi32vIZJT57zygR5aUiA4em8X3aiGOCycgbMWkIu8xOSGSxdx3JmzN+w==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-hapi@0.38.0':
+    resolution: {integrity: sha512-ZcOqEuwuutTDYIjhDIStix22ECblG/i9pHje23QGs4Q4YS4RMaZ5hKCoQJxW88Z4K7T53rQkdISmoXFKDV8xMg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-http@0.51.1':
+    resolution: {integrity: sha512-6b3nZnFFEz/3xZ6w8bVxctPUWIPWiXuPQ725530JgxnN1cvYFd8CJ75PrHZNjynmzSSnqBkN3ef4R9N+RpMh8Q==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-ioredis@0.40.0':
+    resolution: {integrity: sha512-Jv/fH7KhpWe4KBirsiqeUJIYrsdR2iu2l4nWhfOlRvaZ+zYIiLEzTQR6QhBbyRoAbU4OuYJzjWusOmmpGBnwng==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-koa@0.40.0':
+    resolution: {integrity: sha512-dJc3H/bKMcgUYcQpLF+1IbmUKus0e5Fnn/+ru/3voIRHwMADT3rFSUcGLWSczkg68BCgz0vFWGDTvPtcWIFr7A==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-mongodb@0.43.0':
+    resolution: {integrity: sha512-bMKej7Y76QVUD3l55Q9YqizXybHUzF3pujsBFjqbZrRn2WYqtsDtTUlbCK7fvXNPwFInqZ2KhnTqd0gwo8MzaQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-mongoose@0.38.1':
+    resolution: {integrity: sha512-zaeiasdnRjXe6VhYCBMdkmAVh1S5MmXC/0spet+yqoaViGnYst/DOxPvhwg3yT4Yag5crZNWsVXnA538UjP6Ow==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-mysql2@0.38.1':
+    resolution: {integrity: sha512-qkpHMgWSDTYVB1vlZ9sspf7l2wdS5DDq/rbIepDwX5BA0N0068JTQqh0CgAh34tdFqSCnWXIhcyOXC2TtRb0sg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-mysql@0.38.1':
+    resolution: {integrity: sha512-+iBAawUaTfX/HAlvySwozx0C2B6LBfNPXX1W8Z2On1Uva33AGkw2UjL9XgIg1Pj4eLZ9R4EoJ/aFz+Xj4E/7Fw==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-nestjs-core@0.37.1':
+    resolution: {integrity: sha512-ebYQjHZEmGHWEALwwDGhSQVLBaurFnuLIkZD5igPXrt7ohfF4lc5/4al1LO+vKc0NHk8SJWStuRueT86ISA8Vg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-pg@0.41.0':
+    resolution: {integrity: sha512-BSlhpivzBD77meQNZY9fS4aKgydA8AJBzv2dqvxXFy/Hq64b7HURgw/ztbmwFeYwdF5raZZUifiiNSMLpOJoSA==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation@0.43.0':
+    resolution: {integrity: sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation@0.51.1':
+    resolution: {integrity: sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/redis-common@0.36.2':
+    resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==}
+    engines: {node: '>=14'}
+
+  '@opentelemetry/resources@1.24.1':
+    resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+  '@opentelemetry/sdk-metrics@1.24.1':
+    resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': '>=1.3.0 <1.9.0'
+
+  '@opentelemetry/sdk-trace-base@1.24.1':
+    resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+  '@opentelemetry/semantic-conventions@1.24.1':
+    resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==}
+    engines: {node: '>=14'}
+
+  '@opentelemetry/sql-common@0.40.1':
+    resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.1.0
+
   '@peculiar/asn1-android@2.3.10':
     resolution: {integrity: sha512-z9Rx9cFJv7UUablZISe7uksNbFJCq13hO0yEAOoIpAymALTLlvUOSLnGiQS7okPaM5dP42oTLhezH6XDXRXjGw==}
 
@@ -3287,6 +3441,9 @@ packages:
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
+  '@prisma/instrumentation@5.14.0':
+    resolution: {integrity: sha512-DeybWvIZzu/mUsOYP9MVd6AyBj+MP7xIMrcuIn25MX8FiQX39QBnET5KhszTAip/ToctUuDwSJ46QkIoyo3RFA==}
+
   '@radix-ui/react-compose-refs@1.0.1':
     resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
     peerDependencies:
@@ -3449,6 +3606,37 @@ packages:
   '@rushstack/ts-command-line@4.19.2':
     resolution: {integrity: sha512-cqmXXmBEBlzo9WtyUrHtF9e6kl0LvBY7aTSVX4jfnBfXWZQWnPq9JTFPlQZ+L/ZwjZ4HrNwQsOVvhe9oOucZkw==}
 
+  '@sentry/core@8.5.0':
+    resolution: {integrity: sha512-SO3ddBzGdha+Oflp+IKwBxj+7ds1q69OAT3VsypTd+WUFQdI9DIhR92Bjf+QQZCIzUNOi79VWOh3aOi3f6hMnw==}
+    engines: {node: '>=14.18'}
+
+  '@sentry/node@8.5.0':
+    resolution: {integrity: sha512-t9cHAx/wLJYtdVf2XlzKlRJGvwdAp1wjzG0tC4E1Znx74OuUS1cFNo5WrGuOi0/YcWSxiJaxBvtUcsWK86fIgw==}
+    engines: {node: '>=14.18'}
+
+  '@sentry/opentelemetry@8.5.0':
+    resolution: {integrity: sha512-AbxFUNjuTKQ9ugZrssmGtPxWkBr4USNoP7GjaaGCNwNzvIVYCa+i8dv7BROJiW2lsxNAremULEbh+nbVmhGxDA==}
+    engines: {node: '>=14.18'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.8.0
+      '@opentelemetry/core': ^1.24.1
+      '@opentelemetry/instrumentation': ^0.51.1
+      '@opentelemetry/sdk-trace-base': ^1.23.0
+      '@opentelemetry/semantic-conventions': ^1.23.0
+
+  '@sentry/profiling-node@8.5.0':
+    resolution: {integrity: sha512-nEXJqVNfZWYi4PakQXBZCJeH59UlnBv+zaYftDNUUXttCmzRXpL1ujNm5mJrJHlWjV7tgIFw02HW3nh2yyKOkw==}
+    engines: {node: '>=14.18'}
+    hasBin: true
+
+  '@sentry/types@8.5.0':
+    resolution: {integrity: sha512-eDgkSmKI4+XL0QZm4H3j/n1RgnrbnjXZmjj+LsfccRZQwbPu9bWlc8q7Y7Ty1gOsoUpX+TecNLp2a8CRID4KHA==}
+    engines: {node: '>=14.18'}
+
+  '@sentry/utils@8.5.0':
+    resolution: {integrity: sha512-fdrCzo8SAYiw9JBhkJPqYqJkDXZ/wICzN7+zcXIuzKNhE1hdoFjeKcPnpUI3bKZCG6e3hT1PTYQXhVw7GIZV9w==}
+    engines: {node: '>=14.18'}
+
   '@shikijs/core@1.4.0':
     resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==}
 
@@ -4254,12 +4442,18 @@ packages:
   '@types/connect@3.4.35':
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
 
+  '@types/connect@3.4.36':
+    resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==}
+
   '@types/content-disposition@0.5.8':
     resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==}
 
   '@types/cookie@0.6.0':
     resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
 
+  '@types/cookies@0.9.0':
+    resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==}
+
   '@types/cross-spawn@6.0.2':
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
 
@@ -4323,9 +4517,15 @@ packages:
   '@types/htmlescape@1.1.3':
     resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==}
 
+  '@types/http-assert@1.5.5':
+    resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==}
+
   '@types/http-cache-semantics@4.0.4':
     resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
 
+  '@types/http-errors@2.0.4':
+    resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
+
   '@types/http-link-header@1.0.5':
     resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
 
@@ -4362,9 +4562,21 @@ packages:
   '@types/jsrsasign@10.5.14':
     resolution: {integrity: sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==}
 
+  '@types/keygrip@1.0.6':
+    resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==}
+
   '@types/keyv@3.1.4':
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
 
+  '@types/koa-compose@3.2.8':
+    resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==}
+
+  '@types/koa@2.14.0':
+    resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==}
+
+  '@types/koa__router@12.0.3':
+    resolution: {integrity: sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==}
+
   '@types/lodash@4.14.191':
     resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
 
@@ -4401,6 +4613,9 @@ packages:
   '@types/mute-stream@0.0.4':
     resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==}
 
+  '@types/mysql@2.15.22':
+    resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==}
+
   '@types/node-fetch@2.6.4':
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
 
@@ -4441,9 +4656,15 @@ packages:
   '@types/offscreencanvas@2019.7.0':
     resolution: {integrity: sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==}
 
+  '@types/pg-pool@2.0.4':
+    resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==}
+
   '@types/pg@8.11.5':
     resolution: {integrity: sha512-2xMjVviMxneZHDHX5p5S6tsRRs7TpDHeeK7kTTMe/kAC/mRRNjWHjZg0rkiY+e17jXSZV3zJYDxXV8Cy72/Vuw==}
 
+  '@types/pg@8.6.1':
+    resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
+
   '@types/pretty-hrtime@1.0.1':
     resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==}
 
@@ -4507,6 +4728,9 @@ packages:
   '@types/serviceworker@0.0.67':
     resolution: {integrity: sha512-7TCH7iNsCSNb+aUD9M/36TekrWFSLCjNK8zw/3n5kOtRjbLtDfGYMXTrDnGhSfqXNwpqmt9Vd90w5C/ad1tX6Q==}
 
+  '@types/shimmer@1.0.5':
+    resolution: {integrity: sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==}
+
   '@types/simple-oauth2@5.0.7':
     resolution: {integrity: sha512-8JbWVJbiTSBQP/7eiyGKyXWAqp3dKQZpaA+pdW16FCi32ujkzRMG8JfjoAzdWt6W8U591ZNdHcPtP2D7ILTKuA==}
 
@@ -4900,6 +5124,16 @@ packages:
     resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
     engines: {node: '>= 0.6'}
 
+  acorn-import-assertions@1.9.0:
+    resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
+    peerDependencies:
+      acorn: ^8
+
+  acorn-import-attributes@1.9.5:
+    resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
+    peerDependencies:
+      acorn: ^8
+
   acorn-jsx@5.3.2:
     resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
     peerDependencies:
@@ -7111,6 +7345,12 @@ packages:
     resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
     engines: {node: '>=6'}
 
+  import-in-the-middle@1.4.2:
+    resolution: {integrity: sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==}
+
+  import-in-the-middle@1.7.4:
+    resolution: {integrity: sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==}
+
   import-lazy@4.0.0:
     resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
     engines: {node: '>=8'}
@@ -8275,6 +8515,9 @@ packages:
     resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==}
     engines: {node: '>= 8'}
 
+  module-details-from-path@1.0.3:
+    resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
+
   mri@1.2.0:
     resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
     engines: {node: '>=4'}
@@ -8393,6 +8636,10 @@ packages:
   nise@5.1.4:
     resolution: {integrity: sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==}
 
+  node-abi@3.62.0:
+    resolution: {integrity: sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==}
+    engines: {node: '>=10'}
+
   node-abort-controller@3.1.1:
     resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
 
@@ -8626,6 +8873,10 @@ packages:
     resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==}
     hasBin: true
 
+  opentelemetry-instrumentation-fetch-node@1.2.0:
+    resolution: {integrity: sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA==}
+    engines: {node: '>18.0.0'}
+
   optionator@0.9.3:
     resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
     engines: {node: '>= 0.8.0'}
@@ -9570,6 +9821,10 @@ packages:
     resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
     engines: {node: '>=0.10.0'}
 
+  require-in-the-middle@7.3.0:
+    resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==}
+    engines: {node: '>=8.6.0'}
+
   require-main-filename@2.0.0:
     resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
 
@@ -9784,6 +10039,9 @@ packages:
   shiki@1.4.0:
     resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==}
 
+  shimmer@1.2.1:
+    resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
+
   side-channel@1.0.4:
     resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
 
@@ -13733,6 +13991,203 @@ snapshots:
 
   '@open-draft/until@2.1.0': {}
 
+  '@opentelemetry/api-logs@0.51.1':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+
+  '@opentelemetry/api@1.8.0': {}
+
+  '@opentelemetry/context-async-hooks@1.24.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+
+  '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/semantic-conventions': 1.24.1
+
+  '@opentelemetry/instrumentation-connect@0.36.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      '@types/connect': 3.4.36
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-express@0.39.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-fastify@0.36.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-graphql@0.40.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-hapi@0.38.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-http@0.51.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      semver: 7.6.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-ioredis@0.40.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/redis-common': 0.36.2
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-koa@0.40.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      '@types/koa': 2.14.0
+      '@types/koa__router': 12.0.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-mongodb@0.43.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-mongoose@0.38.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-mysql2@0.38.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.8.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-mysql@0.38.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      '@types/mysql': 2.15.22
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-nestjs-core@0.37.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation-pg@0.41.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.8.0)
+      '@types/pg': 8.6.1
+      '@types/pg-pool': 2.0.4
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@types/shimmer': 1.0.5
+      import-in-the-middle: 1.4.2
+      require-in-the-middle: 7.3.0
+      semver: 7.6.0
+      shimmer: 1.2.1
+    transitivePeerDependencies:
+      - supports-color
+    optional: true
+
+  '@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/api-logs': 0.51.1
+      '@types/shimmer': 1.0.5
+      import-in-the-middle: 1.7.4
+      require-in-the-middle: 7.3.0
+      semver: 7.6.0
+      shimmer: 1.2.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/redis-common@0.36.2': {}
+
+  '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+
+  '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
+      lodash.merge: 4.6.2
+
+  '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+
+  '@opentelemetry/semantic-conventions@1.24.1': {}
+
+  '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.8.0)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+
   '@peculiar/asn1-android@2.3.10':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
@@ -13776,6 +14231,14 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
+  '@prisma/instrumentation@5.14.0':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
+    transitivePeerDependencies:
+      - supports-color
+
   '@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.28)(react@18.3.1)':
     dependencies:
       '@babel/runtime': 7.23.4
@@ -13922,6 +14385,72 @@ snapshots:
     transitivePeerDependencies:
       - '@types/node'
 
+  '@sentry/core@8.5.0':
+    dependencies:
+      '@sentry/types': 8.5.0
+      '@sentry/utils': 8.5.0
+
+  '@sentry/node@8.5.0':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/context-async-hooks': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-connect': 0.36.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-express': 0.39.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-fastify': 0.36.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-graphql': 0.40.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-hapi': 0.38.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-http': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-ioredis': 0.40.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-koa': 0.40.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-mongodb': 0.43.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-mongoose': 0.38.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-mysql': 0.38.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-mysql2': 0.38.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-nestjs-core': 0.37.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation-pg': 0.41.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      '@prisma/instrumentation': 5.14.0
+      '@sentry/core': 8.5.0
+      '@sentry/opentelemetry': 8.5.0(@opentelemetry/api@1.8.0)(@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.24.1)
+      '@sentry/types': 8.5.0
+      '@sentry/utils': 8.5.0
+    optionalDependencies:
+      opentelemetry-instrumentation-fetch-node: 1.2.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@sentry/opentelemetry@8.5.0(@opentelemetry/api@1.8.0)(@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.24.1)':
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+      '@sentry/core': 8.5.0
+      '@sentry/types': 8.5.0
+      '@sentry/utils': 8.5.0
+
+  '@sentry/profiling-node@8.5.0':
+    dependencies:
+      '@sentry/core': 8.5.0
+      '@sentry/node': 8.5.0
+      '@sentry/types': 8.5.0
+      '@sentry/utils': 8.5.0
+      detect-libc: 2.0.3
+      node-abi: 3.62.0
+    transitivePeerDependencies:
+      - supports-color
+
+  '@sentry/types@8.5.0': {}
+
+  '@sentry/utils@8.5.0':
+    dependencies:
+      '@sentry/types': 8.5.0
+
   '@shikijs/core@1.4.0': {}
 
   '@sideway/address@4.1.4':
@@ -15301,10 +15830,21 @@ snapshots:
     dependencies:
       '@types/node': 20.12.7
 
+  '@types/connect@3.4.36':
+    dependencies:
+      '@types/node': 20.12.7
+
   '@types/content-disposition@0.5.8': {}
 
   '@types/cookie@0.6.0': {}
 
+  '@types/cookies@0.9.0':
+    dependencies:
+      '@types/connect': 3.4.35
+      '@types/express': 4.17.17
+      '@types/keygrip': 1.0.6
+      '@types/node': 20.12.7
+
   '@types/cross-spawn@6.0.2':
     dependencies:
       '@types/node': 20.12.7
@@ -15372,8 +15912,12 @@ snapshots:
 
   '@types/htmlescape@1.1.3': {}
 
+  '@types/http-assert@1.5.5': {}
+
   '@types/http-cache-semantics@4.0.4': {}
 
+  '@types/http-errors@2.0.4': {}
+
   '@types/http-link-header@1.0.5':
     dependencies:
       '@types/node': 20.12.7
@@ -15411,10 +15955,31 @@ snapshots:
 
   '@types/jsrsasign@10.5.14': {}
 
+  '@types/keygrip@1.0.6': {}
+
   '@types/keyv@3.1.4':
     dependencies:
       '@types/node': 20.12.7
 
+  '@types/koa-compose@3.2.8':
+    dependencies:
+      '@types/koa': 2.14.0
+
+  '@types/koa@2.14.0':
+    dependencies:
+      '@types/accepts': 1.3.7
+      '@types/content-disposition': 0.5.8
+      '@types/cookies': 0.9.0
+      '@types/http-assert': 1.5.5
+      '@types/http-errors': 2.0.4
+      '@types/keygrip': 1.0.6
+      '@types/koa-compose': 3.2.8
+      '@types/node': 20.12.7
+
+  '@types/koa__router@12.0.3':
+    dependencies:
+      '@types/koa': 2.14.0
+
   '@types/lodash@4.14.191': {}
 
   '@types/long@4.0.2': {}
@@ -15445,6 +16010,10 @@ snapshots:
     dependencies:
       '@types/node': 20.12.7
 
+  '@types/mysql@2.15.22':
+    dependencies:
+      '@types/node': 20.12.7
+
   '@types/node-fetch@2.6.4':
     dependencies:
       '@types/node': 20.12.7
@@ -15491,12 +16060,22 @@ snapshots:
 
   '@types/offscreencanvas@2019.7.0': {}
 
+  '@types/pg-pool@2.0.4':
+    dependencies:
+      '@types/pg': 8.11.5
+
   '@types/pg@8.11.5':
     dependencies:
       '@types/node': 20.12.7
       pg-protocol: 1.6.0
       pg-types: 4.0.1
 
+  '@types/pg@8.6.1':
+    dependencies:
+      '@types/node': 20.12.7
+      pg-protocol: 1.6.1
+      pg-types: 2.2.0
+
   '@types/pretty-hrtime@1.0.1': {}
 
   '@types/prop-types@15.7.5': {}
@@ -15554,6 +16133,8 @@ snapshots:
 
   '@types/serviceworker@0.0.67': {}
 
+  '@types/shimmer@1.0.5': {}
+
   '@types/simple-oauth2@5.0.7': {}
 
   '@types/sinon@10.0.13':
@@ -16096,6 +16677,15 @@ snapshots:
       mime-types: 2.1.35
       negotiator: 0.6.3
 
+  acorn-import-assertions@1.9.0(acorn@8.11.3):
+    dependencies:
+      acorn: 8.11.3
+    optional: true
+
+  acorn-import-attributes@1.9.5(acorn@8.11.3):
+    dependencies:
+      acorn: 8.11.3
+
   acorn-jsx@5.3.2(acorn@7.4.1):
     dependencies:
       acorn: 7.4.1
@@ -19001,6 +19591,21 @@ snapshots:
       parent-module: 1.0.1
       resolve-from: 4.0.0
 
+  import-in-the-middle@1.4.2:
+    dependencies:
+      acorn: 8.11.3
+      acorn-import-assertions: 1.9.0(acorn@8.11.3)
+      cjs-module-lexer: 1.2.2
+      module-details-from-path: 1.0.3
+    optional: true
+
+  import-in-the-middle@1.7.4:
+    dependencies:
+      acorn: 8.11.3
+      acorn-import-attributes: 1.9.5(acorn@8.11.3)
+      cjs-module-lexer: 1.2.2
+      module-details-from-path: 1.0.3
+
   import-lazy@4.0.0: {}
 
   import-local@3.1.0:
@@ -20509,6 +21114,8 @@ snapshots:
 
   mock-socket@9.3.1: {}
 
+  module-details-from-path@1.0.3: {}
+
   mri@1.2.0: {}
 
   ms@2.0.0: {}
@@ -20645,6 +21252,10 @@ snapshots:
       just-extend: 4.2.1
       path-to-regexp: 1.8.0
 
+  node-abi@3.62.0:
+    dependencies:
+      semver: 7.6.0
+
   node-abort-controller@3.1.1: {}
 
   node-addon-api@3.2.1:
@@ -20897,6 +21508,15 @@ snapshots:
       undici: 5.28.2
       yargs-parser: 21.1.1
 
+  opentelemetry-instrumentation-fetch-node@1.2.0:
+    dependencies:
+      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.8.0)
+      '@opentelemetry/semantic-conventions': 1.24.1
+    transitivePeerDependencies:
+      - supports-color
+    optional: true
+
   optionator@0.9.3:
     dependencies:
       '@aashutoshrathi/word-wrap': 1.2.6
@@ -21910,6 +22530,14 @@ snapshots:
 
   require-from-string@2.0.2: {}
 
+  require-in-the-middle@7.3.0:
+    dependencies:
+      debug: 4.3.4(supports-color@8.1.1)
+      module-details-from-path: 1.0.3
+      resolve: 1.22.8
+    transitivePeerDependencies:
+      - supports-color
+
   require-main-filename@2.0.0: {}
 
   requires-port@1.0.0: {}
@@ -22168,6 +22796,8 @@ snapshots:
     dependencies:
       '@shikijs/core': 1.4.0
 
+  shimmer@1.2.1: {}
+
   side-channel@1.0.4:
     dependencies:
       call-bind: 1.0.2

From 244adef70e29a77cc65864ca4a8b46babd4bc38d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 28 May 2024 09:18:05 +0000
Subject: [PATCH 240/266] Bump version to 2024.5.0-rc.6

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 6caf4fc11476..a7d5f7eb5120 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-beta.5",
+	"version": "2024.5.0-rc.6",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index f010699ec611..f3bbb48999c9 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-beta.5",
+	"version": "2024.5.0-rc.6",
 	"description": "Misskey SDK for JavaScript",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",

From f75e46752e2911f48d856657cb458de2b1967730 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 28 May 2024 09:18:21 +0000
Subject: [PATCH 241/266] Bump version to 2024.5.0-rc.7

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index a7d5f7eb5120..ccaa59732234 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.6",
+	"version": "2024.5.0-rc.7",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index f3bbb48999c9..e72f929d6b9d 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.6",
+	"version": "2024.5.0-rc.7",
 	"description": "Misskey SDK for JavaScript",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",

From 44cafbb9f238e13e079135b96c4a791fb3b7faf0 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Wed, 29 May 2024 07:11:29 +0900
Subject: [PATCH 242/266] refactor: avoid `as any[]` on
 FetchInstanceMetadataService.ts (#13905)

* refactor: avoid `as any[]` on FetchInstanceMetadataService.ts

* apply suggestion

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 packages/backend/src/core/FetchInstanceMetadataService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts
index 8d173855f33d..aa16468ecb65 100644
--- a/packages/backend/src/core/FetchInstanceMetadataService.ts
+++ b/packages/backend/src/core/FetchInstanceMetadataService.ts
@@ -154,7 +154,7 @@ export class FetchInstanceMetadataService {
 				throw new Error('No wellknown links');
 			}
 
-			const links = wellknown.links as any[];
+			const links = wellknown.links as ({ rel: string, href: string; })[];
 
 			const link1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0');
 			const link2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0');

From e57ce4fa0f663210514ecda728562a73c0fe9c5e Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Wed, 29 May 2024 07:12:20 +0900
Subject: [PATCH 243/266] chore(backend): rename local variable (#13904)

much -> matched
---
 packages/backend/src/core/DriveService.ts | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 63fa26f69d5c..26cf532c7016 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -497,20 +497,20 @@ export class DriveService {
 
 		if (user && !force) {
 		// Check if there is a file with the same hash
-			const much = await this.driveFilesRepository.findOneBy({
+			const matched = await this.driveFilesRepository.findOneBy({
 				md5: info.md5,
 				userId: user.id,
 			});
 
-			if (much) {
-				this.registerLogger.info(`file with same hash is found: ${much.id}`);
-				if (sensitive && !much.isSensitive) {
+			if (matched) {
+				this.registerLogger.info(`file with same hash is found: ${matched.id}`);
+				if (sensitive && !matched.isSensitive) {
 					// The file is federated as sensitive for this time, but was federated as non-sensitive before.
 					// Therefore, update the file to sensitive.
-					await this.driveFilesRepository.update({ id: much.id }, { isSensitive: true });
-					much.isSensitive = true;
+					await this.driveFilesRepository.update({ id: matched.id }, { isSensitive: true });
+					matched.isSensitive = true;
 				}
-				return much;
+				return matched;
 			}
 		}
 

From cf670e8a3dc9830110312b54eceaea29cf20495c Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Wed, 29 May 2024 07:12:50 +0900
Subject: [PATCH 244/266] refactor(backend): avoid `as any` on
 CustomEmojiService.ts (#13903)

---
 packages/backend/src/core/CustomEmojiService.ts | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index 1c75566755ad..b1feca7fb4d3 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -346,10 +346,11 @@ export class CustomEmojiService implements OnApplicationShutdown {
 	@bindThis
 	public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<Record<string, string>> {
 		const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost)));
-		const res = {} as any;
+		const res = {} as Record<string, string>;
 		for (let i = 0; i < emojiNames.length; i++) {
-			if (emojis[i] != null) {
-				res[emojiNames[i]] = emojis[i];
+			const resolvedEmoji = emojis[i];
+			if (resolvedEmoji != null) {
+				res[emojiNames[i]] = resolvedEmoji;
 			}
 		}
 		return res;

From eaadd643ebdfb7b9cc5bd04eb68af740ced52c87 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Wed, 29 May 2024 20:57:48 +0900
Subject: [PATCH 245/266] chore(misskey-js): fix `repository` and add `license`
 in `package.json` (#13902)

---
 packages/misskey-js/package.json | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 6badc7f3eea8..f496d8bb1d51 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -3,6 +3,7 @@
 	"name": "misskey-js",
 	"version": "2024.3.1",
 	"description": "Misskey SDK for JavaScript",
+	"license": "MIT",
 	"main": "./built/index.js",
 	"types": "./built/index.d.ts",
 	"exports": {
@@ -30,7 +31,8 @@
 	},
 	"repository": {
 		"type": "git",
-		"url": "git+https://github.com/misskey-dev/misskey.js.git"
+		"url": "https://github.com/misskey-dev/misskey.git",
+		"directory": "packages/misskey-js"
 	},
 	"devDependencies": {
 		"@microsoft/api-extractor": "7.43.1",

From 24d4124ffcd3b26d2f9fbec87f917b584f494ece Mon Sep 17 00:00:00 2001
From: KanariKanaru <93921745+kanarikanaru@users.noreply.github.com>
Date: Thu, 30 May 2024 17:36:58 +0900
Subject: [PATCH 246/266] =?UTF-8?q?fix(frontend):=20=E3=83=8E=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=81=AB=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=81=8C?=
 =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=81=A6=E3=82=82=E3=83=95=E3=82=A1=E3=82=A4?=
 =?UTF-8?q?=E3=83=AB=E3=81=8C5=E3=81=A4=E4=BB=A5=E4=B8=8A=E3=81=82?=
 =?UTF-8?q?=E3=82=8B=E3=81=A8=E3=81=8D=E3=81=AF=E6=8A=98=E3=82=8A=E3=81=9F?=
 =?UTF-8?q?=E3=81=9F=E3=82=80=E3=82=88=E3=81=86=E3=81=AB=20(#13907)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: ノートにテキストがなくてもファイルが5つ以上あるときは折りたたむように

* 冗長な記述を修正

* Update CHANGELOG.md
---
 CHANGELOG.md                               |  1 +
 packages/frontend/src/scripts/collapsed.ts | 19 ++++++++++---------
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4091668b5400..865684aa2092 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -73,6 +73,7 @@
 - Fix: 通知をグループ化している際に、人数が正常に表示されないことがある問題を修正
 - Fix: 連合なしの状態の読み書きができない問題を修正
 - Fix: `/share` で日本語等を含むurlがurlエンコードされない問題を修正
+- Fix: ファイルを5つ以上添付してもテキストがないとノートが折りたたまれない問題を修正
 
 ### Server
 - Enhance: エンドポイント`antennas/update`の必須項目を`antennaId`のみに
diff --git a/packages/frontend/src/scripts/collapsed.ts b/packages/frontend/src/scripts/collapsed.ts
index 237bd37c7a02..4ec88a3c6573 100644
--- a/packages/frontend/src/scripts/collapsed.ts
+++ b/packages/frontend/src/scripts/collapsed.ts
@@ -6,15 +6,16 @@
 import * as Misskey from 'misskey-js';
 
 export function shouldCollapsed(note: Misskey.entities.Note, urls: string[]): boolean {
-	const collapsed = note.cw == null && note.text != null && (
-		(note.text.includes('$[x2')) ||
-		(note.text.includes('$[x3')) ||
-		(note.text.includes('$[x4')) ||
-		(note.text.includes('$[scale')) ||
-		(note.text.split('\n').length > 9) ||
-		(note.text.length > 500) ||
-		(note.files.length >= 5) ||
-		(urls.length >= 4)
+	const collapsed = note.cw == null && (
+		note.text != null && (
+			(note.text.includes('$[x2')) ||
+			(note.text.includes('$[x3')) ||
+			(note.text.includes('$[x4')) ||
+			(note.text.includes('$[scale')) ||
+			(note.text.split('\n').length > 9) ||
+			(note.text.length > 500) ||
+			(urls.length >= 4)
+		) || note.files.length >= 5
 	);
 
 	return collapsed;

From ac4a001e9f193e4727a8e65e59978a9464a56d75 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 10:11:11 +0900
Subject: [PATCH 247/266] fix code style

---
 .../MkCustomEmojiDetailedDialog.vue           | 108 +++++++++---------
 1 file changed, 56 insertions(+), 52 deletions(-)

diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
index 84b5375a41b7..c7f128872929 100644
--- a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
+++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
@@ -4,77 +4,81 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-  <MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
-    <template #header>:{{ emoji.name }}:</template>
-    <template #default>
-      <MkSpacer>
-        <div style="display: flex; flex-direction: column; gap: 1em;">
-          <div :class="$style.emojiImgWrapper">
-            <MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
-          </div>
-          <MkKeyValue :copy="`:${emoji.name}:`">
-            <template #key>{{ i18n.ts.name }}</template>
-            <template #value>{{ emoji.name }}</template>
-          </MkKeyValue>
-          <MkKeyValue>
-            <template #key>{{ i18n.ts.tags }}</template>
-            <template #value>
-              <div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
-              <div v-else :class="$style.aliases">
-                <span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
-                  {{ alias }}
-                </span>
-              </div>
-            </template>
-          </MkKeyValue>
-          <MkKeyValue>
-            <template #key>{{ i18n.ts.category }}</template>
-            <template #value>{{ emoji.category ?? i18n.ts.none }}</template>
-          </MkKeyValue>
-          <MkKeyValue>
-            <template #key>{{ i18n.ts.sensitive }}</template>
-            <template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
-          </MkKeyValue>
-          <MkKeyValue>
-            <template #key>{{ i18n.ts.localOnly }}</template>
-            <template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
-          </MkKeyValue>
-          <MkKeyValue>
-            <template #key>{{ i18n.ts.license }}</template>
-            <template #value><Mfm :text="emoji.license ?? i18n.ts.none" /></template>
-          </MkKeyValue>
-          <MkKeyValue :copy="emoji.url">
-            <template #key>{{ i18n.ts.emojiUrl }}</template>
-            <template #value>
-              <MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
-            </template>
-          </MkKeyValue>
-        </div>
-      </MkSpacer>
-    </template>
-  </MkModalWindow>
+<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
+	<template #header>:{{ emoji.name }}:</template>
+	<template #default>
+		<MkSpacer>
+			<div style="display: flex; flex-direction: column; gap: 1em;">
+				<div :class="$style.emojiImgWrapper">
+					<MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
+				</div>
+				<MkKeyValue :copy="`:${emoji.name}:`">
+					<template #key>{{ i18n.ts.name }}</template>
+					<template #value>{{ emoji.name }}</template>
+				</MkKeyValue>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.tags }}</template>
+					<template #value>
+						<div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
+						<div v-else :class="$style.aliases">
+							<span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
+								{{ alias }}
+							</span>
+						</div>
+					</template>
+				</MkKeyValue>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.category }}</template>
+					<template #value>{{ emoji.category ?? i18n.ts.none }}</template>
+				</MkKeyValue>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.sensitive }}</template>
+					<template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
+				</MkKeyValue>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.localOnly }}</template>
+					<template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
+				</MkKeyValue>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.license }}</template>
+					<template #value><Mfm :text="emoji.license ?? i18n.ts.none"/></template>
+				</MkKeyValue>
+				<MkKeyValue :copy="emoji.url">
+					<template #key>{{ i18n.ts.emojiUrl }}</template>
+					<template #value>
+						<MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
+					</template>
+				</MkKeyValue>
+			</div>
+		</MkSpacer>
+	</template>
+</MkModalWindow>
 </template>
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
 import { defineProps, shallowRef } from 'vue';
+import MkLink from '@/components/MkLink.vue';
 import { i18n } from '@/i18n.js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
-import MkLink from './MkLink.vue';
+
 const props = defineProps<{
   emoji: Misskey.entities.EmojiDetailed,
 }>();
+
 const emit = defineEmits<{
 	(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
 	(ev: 'cancel'): void;
 	(ev: 'closed'): void;
 }>();
+
 const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
-const cancel = () => {
+
+function cancel() {
 	emit('cancel');
 	dialogEl.value!.close();
-};
+}
 </script>
 
 <style lang="scss" module>

From be11fd75085e8f8c000a42b40bd583894121a708 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 10:12:23 +0900
Subject: [PATCH 248/266] =?UTF-8?q?enhance:=20=E3=82=B5=E3=83=BC=E3=83=90?=
 =?UTF-8?q?=E3=83=BC=E3=81=AE=E3=81=8A=E5=95=8F=E3=81=84=E5=90=88=E3=82=8F?=
 =?UTF-8?q?=E3=81=9B=E5=85=88URL=E3=82=92=E8=A8=AD=E5=AE=9A=E3=81=A7?=
 =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  8 ++++++++
 locales/ja-JP.yml                             |  2 ++
 .../migration/1717117195275-inquiryUrl.js     | 20 +++++++++++++++++++
 .../src/core/entities/MetaEntityService.ts    |  1 +
 packages/backend/src/models/Meta.ts           |  6 ++++++
 .../backend/src/models/json-schema/meta.ts    |  4 ++++
 .../src/server/NodeinfoServerService.ts       | 13 ++++++------
 .../src/server/api/endpoints/admin/meta.ts    |  5 +++++
 .../server/api/endpoints/admin/update-meta.ts |  5 +++++
 .../frontend/src/pages/admin/moderation.vue   |  9 +++++++++
 packages/frontend/src/pages/contact.vue       | 18 ++++++++++++++++-
 packages/misskey-js/src/autogen/types.ts      |  3 +++
 13 files changed, 88 insertions(+), 7 deletions(-)
 create mode 100644 packages/backend/migration/1717117195275-inquiryUrl.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 865684aa2092..efd07da891d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
 - Enhance: Goneを出さずに終了したサーバーへの配信停止を自動的に行うように
   - もしそのようなサーバーからから配信が届いた場合には自動的に配信を再開します
 - Enhance: 配信停止の理由を表示するように
+- Enhance: サーバーのお問い合わせ先URLを設定できるようになりました
 - Fix: Play作成時に設定した公開範囲が機能していない問題を修正
 - Fix: 正規化されていない状態のhashtagが連合されてきたhtmlに含まれているとhashtagが正しくhashtagに復元されない問題を修正
 - Fix: みつけるのアンケート欄にてチャンネルのアンケートが含まれてしまう問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index d4ded0bb5b8e..91bbe4ccb672 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -5471,6 +5471,14 @@ export interface Locale extends ILocale {
          * 有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。
          */
         "fanoutTimelineDbFallbackDescription": string;
+        /**
+         * 問い合わせ先URL
+         */
+        "inquiryUrl": string;
+        /**
+         * サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。
+         */
+        "inquiryUrlDescription": string;
     };
     "_accountMigration": {
         /**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index d7ceb971afed..b7083b494281 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1383,6 +1383,8 @@ _serverSettings:
   fanoutTimelineDescription: "有効にすると、各種タイムラインを取得する際のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。サーバーのメモリ容量が少ない場合、または動作が不安定な場合は無効にすることができます。"
   fanoutTimelineDbFallback: "データベースへのフォールバック"
   fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。"
+  inquiryUrl: "問い合わせ先URL"
+  inquiryUrlDescription: "サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。"
 
 _accountMigration:
   moveFrom: "別のアカウントからこのアカウントに移行"
diff --git a/packages/backend/migration/1717117195275-inquiryUrl.js b/packages/backend/migration/1717117195275-inquiryUrl.js
new file mode 100644
index 000000000000..3834df06d440
--- /dev/null
+++ b/packages/backend/migration/1717117195275-inquiryUrl.js
@@ -0,0 +1,20 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class InquiryUrl1717117195275 {
+    name = 'InquiryUrl1717117195275'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "trustedLinkUrlPatterns" TO "inquiryUrl"`);
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "inquiryUrl"`);
+        await queryRunner.query(`ALTER TABLE "meta" ADD "inquiryUrl" character varying(1024)`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "inquiryUrl"`);
+        await queryRunner.query(`ALTER TABLE "meta" ADD "inquiryUrl" character varying(3072) array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "inquiryUrl" TO "trustedLinkUrlPatterns"`);
+    }
+}
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index 9d054ab6a156..5dfec589e12a 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -67,6 +67,7 @@ export class MetaEntityService {
 			feedbackUrl: instance.feedbackUrl,
 			impressumUrl: instance.impressumUrl,
 			privacyPolicyUrl: instance.privacyPolicyUrl,
+			inquiryUrl: instance.inquiryUrl,
 			disableRegistration: instance.disableRegistration,
 			emailRequiredForSignup: instance.emailRequiredForSignup,
 			enableHcaptcha: instance.enableHcaptcha,
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 04a34bbbb42b..ad306fcad613 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -376,6 +376,12 @@ export class MiMeta {
 	})
 	public privacyPolicyUrl: string | null;
 
+	@Column('varchar', {
+		length: 1024,
+		nullable: true,
+	})
+	public inquiryUrl: string | null;
+
 	@Column('varchar', {
 		length: 8192,
 		nullable: true,
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
index 473339a1add5..e7bc6356e5e3 100644
--- a/packages/backend/src/models/json-schema/meta.ts
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -227,6 +227,10 @@ export const packedMetaLiteSchema = {
 			type: 'string',
 			optional: false, nullable: true,
 		},
+		inquiryUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
 		serverRules: {
 			type: 'array',
 			optional: false, nullable: false,
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index c1e5af08c903..cc18997fdc1c 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -37,12 +37,12 @@ export class NodeinfoServerService {
 	@bindThis
 	public getLinks() {
 		return [{
-				rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
-				href: this.config.url + nodeinfo2_1path
-			}, {
-				rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
-				href: this.config.url + nodeinfo2_0path,
-			}];
+			rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
+			href: this.config.url + nodeinfo2_1path,
+		}, {
+			rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
+			href: this.config.url + nodeinfo2_0path,
+		}];
 	}
 
 	@bindThis
@@ -108,6 +108,7 @@ export class NodeinfoServerService {
 					langs: meta.langs,
 					tosUrl: meta.termsOfServiceUrl,
 					privacyPolicyUrl: meta.privacyPolicyUrl,
+					inquiryUrl: meta.inquiryUrl,
 					impressumUrl: meta.impressumUrl,
 					repositoryUrl: meta.repositoryUrl,
 					feedbackUrl: meta.feedbackUrl,
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index f4ff5732715d..eee02a7123c7 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -427,6 +427,10 @@ export const meta = {
 				type: 'string',
 				optional: false, nullable: true,
 			},
+			inquiryUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
 			repositoryUrl: {
 				type: 'string',
 				optional: false, nullable: true,
@@ -513,6 +517,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				feedbackUrl: instance.feedbackUrl,
 				impressumUrl: instance.impressumUrl,
 				privacyPolicyUrl: instance.privacyPolicyUrl,
+				inquiryUrl: instance.inquiryUrl,
 				disableRegistration: instance.disableRegistration,
 				emailRequiredForSignup: instance.emailRequiredForSignup,
 				enableHcaptcha: instance.enableHcaptcha,
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index 2f62d30ada77..4e28ee687795 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -107,6 +107,7 @@ export const paramDef = {
 		feedbackUrl: { type: 'string', nullable: true },
 		impressumUrl: { type: 'string', nullable: true },
 		privacyPolicyUrl: { type: 'string', nullable: true },
+		inquiryUrl: { type: 'string', nullable: true },
 		useObjectStorage: { type: 'boolean' },
 		objectStorageBaseUrl: { type: 'string', nullable: true },
 		objectStorageBucket: { type: 'string', nullable: true },
@@ -422,6 +423,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.privacyPolicyUrl = ps.privacyPolicyUrl;
 			}
 
+			if (ps.inquiryUrl !== undefined) {
+				set.inquiryUrl = ps.inquiryUrl;
+			}
+
 			if (ps.useObjectStorage !== undefined) {
 				set.useObjectStorage = ps.useObjectStorage;
 			}
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index 9efb34ac9a01..a75799696d8f 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -30,6 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<template #label>{{ i18n.ts.privacyPolicyUrl }}</template>
 					</MkInput>
 
+					<MkInput v-model="inquiryUrl" type="url">
+						<template #prefix><i class="ti ti-link"></i></template>
+						<template #label>{{ i18n.ts._serverSettings.inquiryUrl }}</template>
+						<template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template>
+					</MkInput>
+
 					<MkTextarea v-model="preservedUsernames">
 						<template #label>{{ i18n.ts.preservedUsernames }}</template>
 						<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
@@ -86,6 +92,7 @@ const hiddenTags = ref<string>('');
 const preservedUsernames = ref<string>('');
 const tosUrl = ref<string | null>(null);
 const privacyPolicyUrl = ref<string | null>(null);
+const inquiryUrl = ref<string | null>(null);
 
 async function init() {
 	const meta = await misskeyApi('admin/meta');
@@ -97,6 +104,7 @@ async function init() {
 	preservedUsernames.value = meta.preservedUsernames.join('\n');
 	tosUrl.value = meta.tosUrl;
 	privacyPolicyUrl.value = meta.privacyPolicyUrl;
+	inquiryUrl.value = meta.inquiryUrl;
 }
 
 function save() {
@@ -105,6 +113,7 @@ function save() {
 		emailRequiredForSignup: emailRequiredForSignup.value,
 		tosUrl: tosUrl.value,
 		privacyPolicyUrl: privacyPolicyUrl.value,
+		inquiryUrl: inquiryUrl.value,
 		sensitiveWords: sensitiveWords.value.split('\n'),
 		prohibitedWords: prohibitedWords.value.split('\n'),
 		hiddenTags: hiddenTags.value.split('\n'),
diff --git a/packages/frontend/src/pages/contact.vue b/packages/frontend/src/pages/contact.vue
index 3a694a713277..bcdcf4327552 100644
--- a/packages/frontend/src/pages/contact.vue
+++ b/packages/frontend/src/pages/contact.vue
@@ -7,7 +7,21 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkStickyContainer>
 	<template #header><MkPageHeader/></template>
 	<MkSpacer :contentMax="600" :marginMin="20">
-		<div>{{ instance.maintainerEmail }}</div>
+		<div class="_gaps">
+			<MkKeyValue>
+				<template #key>{{ i18n.ts.inquiry }}</template>
+				<template #value>
+					<MkLink :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink>
+				</template>
+			</MkKeyValue>
+
+			<MkKeyValue>
+				<template #key>{{ i18n.ts.email }}</template>
+				<template #value>
+					<div>{{ instance.maintainerEmail }}</div>
+				</template>
+			</MkKeyValue>
+		</div>
 	</MkSpacer>
 </MkStickyContainer>
 </template>
@@ -16,6 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { instance } from '@/instance.js';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import MkLink from '@/components/MkLink.vue';
 
 definePageMetadata(() => ({
 	title: i18n.ts.inquiry,
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 11567677c9e7..9beb49fb6478 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4831,6 +4831,7 @@ export type components = {
       impressumUrl: string | null;
       logoImageUrl: string | null;
       privacyPolicyUrl: string | null;
+      inquiryUrl: string | null;
       serverRules: string[];
       themeColor: string | null;
       policies: components['schemas']['RolePolicies'];
@@ -4978,6 +4979,7 @@ export type operations = {
             shortName: string | null;
             objectStorageS3ForcePathStyle: boolean;
             privacyPolicyUrl: string | null;
+            inquiryUrl: string | null;
             repositoryUrl: string | null;
             /**
              * @deprecated
@@ -8906,6 +8908,7 @@ export type operations = {
           feedbackUrl?: string | null;
           impressumUrl?: string | null;
           privacyPolicyUrl?: string | null;
+          inquiryUrl?: string | null;
           useObjectStorage?: boolean;
           objectStorageBaseUrl?: string | null;
           objectStorageBucket?: string | null;

From 5b8f8e7087cb447e43724bd28b4bdfdf03d328c2 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 11:24:17 +0900
Subject: [PATCH 249/266] fix(backend): fix backward compatibility of antenna

---
 packages/backend/src/core/entities/AntennaEntityService.ts | 1 +
 packages/backend/src/models/json-schema/antenna.ts         | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts
index 4a17a3d80fba..e770028af3e9 100644
--- a/packages/backend/src/core/entities/AntennaEntityService.ts
+++ b/packages/backend/src/core/entities/AntennaEntityService.ts
@@ -43,6 +43,7 @@ export class AntennaEntityService {
 			withFile: antenna.withFile,
 			isActive: antenna.isActive,
 			hasUnreadNote: false, // TODO
+			notify: false, // 後方互換性のため
 		};
 	}
 }
diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts
index c4ac358fa60a..b5b9a5b42cdf 100644
--- a/packages/backend/src/models/json-schema/antenna.ts
+++ b/packages/backend/src/models/json-schema/antenna.ts
@@ -95,5 +95,10 @@ export const packedAntennaSchema = {
 			optional: false, nullable: false,
 			default: false,
 		},
+		notify: {
+			type: 'boolean',
+			optional: false, nullable: false,
+			default: false,
+		},
 	},
 } as const;

From 00827472374554c9795fe372d1a605ea733441fc Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 13:19:37 +0900
Subject: [PATCH 250/266] New Crowdin updates (#13892)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)
---
 locales/zh-CN.yml |  2 ++
 locales/zh-TW.yml | 11 ++++++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 3e500f8642b8..f92d997b5a3a 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -316,6 +316,7 @@ selectFile: "选择文件"
 selectFiles: "选择文件"
 selectFolder: "选择文件夹"
 selectFolders: "选择多个文件夹"
+fileNotSelected: "未选择文件"
 renameFile: "重命名文件"
 folderName: "文件夹名称"
 createFolder: "创建文件夹"
@@ -2358,6 +2359,7 @@ _deck:
   alwaysShowMainColumn: "总是显示主列"
   columnAlign: "列对齐"
   addColumn: "添加列"
+  newNoteNotificationSettings: "新帖子通知设定"
   configureColumn: "列设置"
   swapLeft: "向左移动"
   swapRight: "向右移动"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index fed7b642dcee..aac3f7662c7e 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -316,6 +316,7 @@ selectFile: "選擇檔案"
 selectFiles: "選擇檔案"
 selectFolder: "選擇資料夾"
 selectFolders: "選擇資料夾"
+fileNotSelected: "尚未選擇檔案"
 renameFile: "重新命名檔案"
 folderName: "資料夾名稱"
 createFolder: "新增資料夾"
@@ -471,7 +472,7 @@ retype: "重新輸入"
 noteOf: "{user}的貼文"
 quoteAttached: "引用"
 quoteQuestion: "是否要引用?"
-attachAsFileQuestion: "剪貼簿的文字較長。請問是否要改成附加檔案呢?"
+attachAsFileQuestion: "剪貼簿的文字較長。請問是否要將其以文字檔的方式附加呢?"
 noMessagesYet: "沒有訊息"
 newMessageExists: "有新的訊息"
 onlyOneFileCanBeAttached: "只能加入一個附件"
@@ -1025,6 +1026,7 @@ thisPostMayBeAnnoyingHome: "發佈到首頁"
 thisPostMayBeAnnoyingCancel: "退出"
 thisPostMayBeAnnoyingIgnore: "直接發佈貼文"
 collapseRenotes: "省略顯示已看過的轉發貼文"
+collapseRenotesDescription: "將已做過反應和轉發的貼文折疊顯示。"
 internalServerError: "內部伺服器錯誤"
 internalServerErrorDescription: "內部伺服器出現意外錯誤。"
 copyErrorInfo: "複製錯誤資訊"
@@ -1241,8 +1243,8 @@ alwaysConfirmFollow: "點擊追隨時總是顯示確認訊息"
 inquiry: "聯絡我們"
 _delivery:
   status: "傳送狀態"
-  stop: "已凍結"
-  resume: "繼續傳送"
+  stop: "停止傳送"
+  resume: "恢復傳送"
   _type:
     none: "直播中"
     manuallySuspended: "手動暫停中"
@@ -1373,6 +1375,8 @@ _serverSettings:
   fanoutTimelineDescription: "如果啟用的話,檢索各個時間軸的性能會顯著提昇,資料庫的負荷也會減少。不過,Redis 的記憶體使用量會增加。如果伺服器的記憶體容量比較少或者運行不穩定,可以停用。"
   fanoutTimelineDbFallback: "資料庫的回退"
   fanoutTimelineDbFallbackDescription: "若啟用,在時間軸沒有快取的情況下將執行回退處理以額外查詢資料庫。若停用,可以透過不執行回退處理來進一步減少伺服器的負荷,但會限制可取得的時間軸範圍。"
+  inquiryUrl: "聯絡表單網址"
+  inquiryUrlDescription: "指定伺服器運營者的聯絡表單網址或包含運營者聯絡資訊網頁的網址。"
 _accountMigration:
   moveFrom: "從其他帳戶遷移到這個帳戶"
   moveFromSub: "為另一個帳戶建立別名"
@@ -2358,6 +2362,7 @@ _deck:
   alwaysShowMainColumn: "總是顯示主欄"
   columnAlign: "對齊欄位"
   addColumn: "新增欄位"
+  newNoteNotificationSettings: "新貼文通知的設定"
   configureColumn: "欄位的設定"
   swapLeft: "向左移動"
   swapRight: "向右移動"

From eaa85f5aa3fb936725fc9326e3cdae62f696c2e7 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 13:28:11 +0900
Subject: [PATCH 251/266] fix test

---
 packages/backend/test/e2e/antennas.ts    | 1 +
 packages/misskey-js/src/autogen/types.ts | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index 4f78cc999d43..101238b60150 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -157,6 +157,7 @@ describe('アンテナ', () => {
 			withReplies: false,
 			excludeBots: false,
 			localOnly: false,
+			notify: false,
 		};
 		assert.deepStrictEqual(response, expected);
 	});
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 9beb49fb6478..2c80676f3ed3 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4449,6 +4449,8 @@ export type components = {
       isActive: boolean;
       /** @default false */
       hasUnreadNote: boolean;
+      /** @default false */
+      notify: boolean;
     };
     Clip: {
       /**

From 1e007b63aadc6bba6858539bef3b559b2811d232 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 04:38:45 +0000
Subject: [PATCH 252/266] Bump version to 2024.5.0-rc.8

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index ccaa59732234..04942d16bfee 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.7",
+	"version": "2024.5.0-rc.8",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index e3c9c0ff0628..1a611d8f1022 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.7",
+	"version": "2024.5.0-rc.8",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 97be1a53adec9edeeb83a8095b24efd4e8675787 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 14:59:02 +0900
Subject: [PATCH 253/266] Update 1717117195275-inquiryUrl.js

---
 packages/backend/migration/1717117195275-inquiryUrl.js | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/packages/backend/migration/1717117195275-inquiryUrl.js b/packages/backend/migration/1717117195275-inquiryUrl.js
index 3834df06d440..29ca31af1403 100644
--- a/packages/backend/migration/1717117195275-inquiryUrl.js
+++ b/packages/backend/migration/1717117195275-inquiryUrl.js
@@ -7,14 +7,10 @@ export class InquiryUrl1717117195275 {
     name = 'InquiryUrl1717117195275'
 
     async up(queryRunner) {
-        await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "trustedLinkUrlPatterns" TO "inquiryUrl"`);
-        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "inquiryUrl"`);
         await queryRunner.query(`ALTER TABLE "meta" ADD "inquiryUrl" character varying(1024)`);
     }
 
     async down(queryRunner) {
         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "inquiryUrl"`);
-        await queryRunner.query(`ALTER TABLE "meta" ADD "inquiryUrl" character varying(3072) array NOT NULL DEFAULT '{}'`);
-        await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "inquiryUrl" TO "trustedLinkUrlPatterns"`);
     }
 }

From 514a65e45330f09ad58cac3cab16bd888be80866 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Fri, 31 May 2024 15:32:42 +0900
Subject: [PATCH 254/266] perf(backend): avoid N+1 selects from `user` table
 when packing many entities (#13911)

* perf(backend): avoid N+1 selects from `user` table when packing many entities

* perf(backend): use `packMany` instead of mapping to `pack`
---
 .../entities/AbuseUserReportEntityService.ts  | 34 ++++++++--
 .../core/entities/BlockingEntityService.ts    | 14 ++--
 .../src/core/entities/ClipEntityService.ts    | 12 +++-
 .../core/entities/DriveFileEntityService.ts   | 10 ++-
 .../src/core/entities/FlashEntityService.ts   | 14 ++--
 .../entities/FollowRequestEntityService.ts    | 27 +++++++-
 .../core/entities/FollowingEntityService.ts   | 24 +++++--
 .../core/entities/GalleryPostEntityService.ts | 12 +++-
 .../core/entities/InviteCodeEntityService.ts  | 25 +++++--
 .../entities/ModerationLogEntityService.ts    | 17 +++--
 .../src/core/entities/MutingEntityService.ts  | 14 ++--
 .../src/core/entities/NoteEntityService.ts    | 12 +++-
 .../entities/NoteReactionEntityService.ts     | 11 +++-
 .../src/core/entities/PageEntityService.ts    | 12 +++-
 .../entities/RenoteMutingEntityService.ts     | 14 ++--
 .../core/entities/ReversiGameEntityService.ts | 66 +++++++++++++------
 .../core/entities/UserListEntityService.ts    |  5 +-
 .../server/api/endpoints/admin/roles/users.ts |  5 +-
 .../server/api/endpoints/drive/files/find.ts  |  2 +-
 .../api/endpoints/following/requests/list.ts  |  2 +-
 .../server/api/endpoints/notes/reactions.ts   |  2 +-
 .../src/server/api/endpoints/roles/users.ts   |  5 +-
 .../users/get-frequently-replied-users.ts     | 10 +--
 .../src/server/api/endpoints/users/show.ts    |  6 +-
 24 files changed, 268 insertions(+), 87 deletions(-)

diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
index 49f256d870df..b0e1d1ab3659 100644
--- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
+++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
@@ -10,6 +10,8 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
 import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
+import type { Packed } from '@/misc/json-schema.js';
 import { UserEntityService } from './UserEntityService.js';
 
 @Injectable()
@@ -26,6 +28,11 @@ export class AbuseUserReportEntityService {
 	@bindThis
 	public async pack(
 		src: MiAbuseUserReport['id'] | MiAbuseUserReport,
+		hint?: {
+			packedReporter?: Packed<'UserDetailedNotMe'>,
+			packedTargetUser?: Packed<'UserDetailedNotMe'>,
+			packedAssignee?: Packed<'UserDetailedNotMe'>,
+		},
 	) {
 		const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src });
 
@@ -37,13 +44,13 @@ export class AbuseUserReportEntityService {
 			reporterId: report.reporterId,
 			targetUserId: report.targetUserId,
 			assigneeId: report.assigneeId,
-			reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
+			reporter: hint?.packedReporter ?? this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
 				schema: 'UserDetailedNotMe',
 			}),
-			targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
+			targetUser: hint?.packedTargetUser ?? this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
 				schema: 'UserDetailedNotMe',
 			}),
-			assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
+			assignee: report.assigneeId ? hint?.packedAssignee ?? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
 				schema: 'UserDetailedNotMe',
 			}) : null,
 			forwarded: report.forwarded,
@@ -51,9 +58,24 @@ export class AbuseUserReportEntityService {
 	}
 
 	@bindThis
-	public packMany(
-		reports: any[],
+	public async packMany(
+		reports: MiAbuseUserReport[],
 	) {
-		return Promise.all(reports.map(x => this.pack(x)));
+		const _reporters = reports.map(({ reporter, reporterId }) => reporter ?? reporterId);
+		const _targetUsers = reports.map(({ targetUser, targetUserId }) => targetUser ?? targetUserId);
+		const _assignees = reports.map(({ assignee, assigneeId }) => assignee ?? assigneeId).filter(isNotNull);
+		const _userMap = await this.userEntityService.packMany(
+			[..._reporters, ..._targetUsers, ..._assignees],
+			null,
+			{ schema: 'UserDetailedNotMe' },
+		).then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(
+			reports.map(report => {
+				const packedReporter = _userMap.get(report.reporterId);
+				const packedTargetUser = _userMap.get(report.targetUserId);
+				const packedAssignee = report.assigneeId != null ? _userMap.get(report.assigneeId) : undefined;
+				return this.pack(report, { packedReporter, packedTargetUser, packedAssignee });
+			}),
+		);
 	}
 }
diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts
index c8c1520ceb50..1e699032e234 100644
--- a/packages/backend/src/core/entities/BlockingEntityService.ts
+++ b/packages/backend/src/core/entities/BlockingEntityService.ts
@@ -29,6 +29,9 @@ export class BlockingEntityService {
 	public async pack(
 		src: MiBlocking['id'] | MiBlocking,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hint?: {
+			blockee?: Packed<'UserDetailedNotMe'>,
+		},
 	): Promise<Packed<'Blocking'>> {
 		const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src });
 
@@ -36,17 +39,20 @@ export class BlockingEntityService {
 			id: blocking.id,
 			createdAt: this.idService.parse(blocking.id).date.toISOString(),
 			blockeeId: blocking.blockeeId,
-			blockee: this.userEntityService.pack(blocking.blockeeId, me, {
+			blockee: hint?.blockee ?? this.userEntityService.pack(blocking.blockeeId, me, {
 				schema: 'UserDetailedNotMe',
 			}),
 		});
 	}
 
 	@bindThis
-	public packMany(
-		blockings: any[],
+	public async packMany(
+		blockings: MiBlocking[],
 		me: { id: MiUser['id'] },
 	) {
-		return Promise.all(blockings.map(x => this.pack(x, me)));
+		const _blockees = blockings.map(({ blockee, blockeeId }) => blockee ?? blockeeId);
+		const _userMap = await this.userEntityService.packMany(_blockees, me, { schema: 'UserDetailedNotMe' })
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(blockings.map(blocking => this.pack(blocking, me, { blockee: _userMap.get(blocking.blockeeId) })));
 	}
 }
diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts
index ce49c3458c54..3855a2843688 100644
--- a/packages/backend/src/core/entities/ClipEntityService.ts
+++ b/packages/backend/src/core/entities/ClipEntityService.ts
@@ -35,6 +35,9 @@ export class ClipEntityService {
 	public async pack(
 		src: MiClip['id'] | MiClip,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hint?: {
+			packedUser?: Packed<'UserLite'>
+		},
 	): Promise<Packed<'Clip'>> {
 		const meId = me ? me.id : null;
 		const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src });
@@ -44,7 +47,7 @@ export class ClipEntityService {
 			createdAt: this.idService.parse(clip.id).date.toISOString(),
 			lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null,
 			userId: clip.userId,
-			user: this.userEntityService.pack(clip.user ?? clip.userId),
+			user: hint?.packedUser ?? this.userEntityService.pack(clip.user ?? clip.userId),
 			name: clip.name,
 			description: clip.description,
 			isPublic: clip.isPublic,
@@ -55,11 +58,14 @@ export class ClipEntityService {
 	}
 
 	@bindThis
-	public packMany(
+	public async packMany(
 		clips: MiClip[],
 		me?: { id: MiUser['id'] } | null | undefined,
 	) {
-		return Promise.all(clips.map(x => this.pack(x, me)));
+		const _users = clips.map(({ user, userId }) => user ?? userId);
+		const _userMap = await this.userEntityService.packMany(_users, me)
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(clips.map(clip => this.pack(clip, me, { packedUser: _userMap.get(clip.userId) })));
 	}
 }
 
diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts
index 26bf386cbc4b..02ff2e775468 100644
--- a/packages/backend/src/core/entities/DriveFileEntityService.ts
+++ b/packages/backend/src/core/entities/DriveFileEntityService.ts
@@ -222,6 +222,9 @@ export class DriveFileEntityService {
 	public async packNullable(
 		src: MiDriveFile['id'] | MiDriveFile,
 		options?: PackOptions,
+		hint?: {
+			packedUser?: Packed<'UserLite'>
+		},
 	): Promise<Packed<'DriveFile'> | null> {
 		const opts = Object.assign({
 			detail: false,
@@ -249,7 +252,7 @@ export class DriveFileEntityService {
 				detail: true,
 			}) : null,
 			userId: file.userId,
-			user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null,
+			user: (opts.withUser && file.userId) ? hint?.packedUser ?? this.userEntityService.pack(file.userId) : null,
 		});
 	}
 
@@ -258,7 +261,10 @@ export class DriveFileEntityService {
 		files: MiDriveFile[],
 		options?: PackOptions,
 	): Promise<Packed<'DriveFile'>[]> {
-		const items = await Promise.all(files.map(f => this.packNullable(f, options)));
+		const _user = files.map(({ user, userId }) => user ?? userId).filter(isNotNull);
+		const _userMap = await this.userEntityService.packMany(_user)
+			.then(users => new Map(users.map(user => [user.id, user])));
+		const items = await Promise.all(files.map(f => this.packNullable(f, options, f.userId ? { packedUser: _userMap.get(f.userId) } : {})));
 		return items.filter(isNotNull);
 	}
 
diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts
index db4cf6d360d6..d110f7afc632 100644
--- a/packages/backend/src/core/entities/FlashEntityService.ts
+++ b/packages/backend/src/core/entities/FlashEntityService.ts
@@ -33,6 +33,9 @@ export class FlashEntityService {
 	public async pack(
 		src: MiFlash['id'] | MiFlash,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hint?: {
+			packedUser?: Packed<'UserLite'>
+		},
 	): Promise<Packed<'Flash'>> {
 		const meId = me ? me.id : null;
 		const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
@@ -42,7 +45,7 @@ export class FlashEntityService {
 			createdAt: this.idService.parse(flash.id).date.toISOString(),
 			updatedAt: flash.updatedAt.toISOString(),
 			userId: flash.userId,
-			user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
+			user: hint?.packedUser ?? this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
 			title: flash.title,
 			summary: flash.summary,
 			script: flash.script,
@@ -52,11 +55,14 @@ export class FlashEntityService {
 	}
 
 	@bindThis
-	public packMany(
-		flashs: MiFlash[],
+	public async packMany(
+		flashes: MiFlash[],
 		me?: { id: MiUser['id'] } | null | undefined,
 	) {
-		return Promise.all(flashs.map(x => this.pack(x, me)));
+		const _users = flashes.map(({ user, userId }) => user ?? userId);
+		const _userMap = await this.userEntityService.packMany(_users, me)
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(flashes.map(flash => this.pack(flash, me, { packedUser: _userMap.get(flash.userId) })));
 	}
 }
 
diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts
index 763b75101faa..0101ec8aa735 100644
--- a/packages/backend/src/core/entities/FollowRequestEntityService.ts
+++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts
@@ -10,6 +10,7 @@ import type { } from '@/models/Blocking.js';
 import type { MiUser } from '@/models/User.js';
 import type { MiFollowRequest } from '@/models/FollowRequest.js';
 import { bindThis } from '@/decorators.js';
+import type { Packed } from '@/misc/json-schema.js';
 import { UserEntityService } from './UserEntityService.js';
 
 @Injectable()
@@ -26,14 +27,36 @@ export class FollowRequestEntityService {
 	public async pack(
 		src: MiFollowRequest['id'] | MiFollowRequest,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hint?: {
+			packedFollower?: Packed<'UserLite'>,
+			packedFollowee?: Packed<'UserLite'>,
+		},
 	) {
 		const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src });
 
 		return {
 			id: request.id,
-			follower: await this.userEntityService.pack(request.followerId, me),
-			followee: await this.userEntityService.pack(request.followeeId, me),
+			follower: hint?.packedFollower ?? await this.userEntityService.pack(request.followerId, me),
+			followee: hint?.packedFollowee ?? await this.userEntityService.pack(request.followeeId, me),
 		};
 	}
+
+	@bindThis
+	public async packMany(
+		requests: MiFollowRequest[],
+		me?: { id: MiUser['id'] } | null | undefined,
+	) {
+		const _followers = requests.map(({ follower, followerId }) => follower ?? followerId);
+		const _followees = requests.map(({ followee, followeeId }) => followee ?? followeeId);
+		const _userMap = await this.userEntityService.packMany([..._followers, ..._followees], me)
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(
+			requests.map(req => {
+				const packedFollower = _userMap.get(req.followerId);
+				const packedFollowee = _userMap.get(req.followeeId);
+				return this.pack(req, me, { packedFollower, packedFollowee });
+			}),
+		);
+	}
 }
 
diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts
index 24cd33e3f7d4..d2dbaf227030 100644
--- a/packages/backend/src/core/entities/FollowingEntityService.ts
+++ b/packages/backend/src/core/entities/FollowingEntityService.ts
@@ -78,6 +78,10 @@ export class FollowingEntityService {
 			populateFollowee?: boolean;
 			populateFollower?: boolean;
 		},
+		hint?: {
+			packedFollowee?: Packed<'UserDetailedNotMe'>,
+			packedFollower?: Packed<'UserDetailedNotMe'>,
+		},
 	): Promise<Packed<'Following'>> {
 		const following = typeof src === 'object' ? src : await this.followingsRepository.findOneByOrFail({ id: src });
 
@@ -88,25 +92,35 @@ export class FollowingEntityService {
 			createdAt: this.idService.parse(following.id).date.toISOString(),
 			followeeId: following.followeeId,
 			followerId: following.followerId,
-			followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
+			followee: opts.populateFollowee ? hint?.packedFollowee ?? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
 				schema: 'UserDetailedNotMe',
 			}) : undefined,
-			follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, {
+			follower: opts.populateFollower ? hint?.packedFollower ?? this.userEntityService.pack(following.follower ?? following.followerId, me, {
 				schema: 'UserDetailedNotMe',
 			}) : undefined,
 		});
 	}
 
 	@bindThis
-	public packMany(
-		followings: any[],
+	public async packMany(
+		followings: MiFollowing[],
 		me?: { id: MiUser['id'] } | null | undefined,
 		opts?: {
 			populateFollowee?: boolean;
 			populateFollower?: boolean;
 		},
 	) {
-		return Promise.all(followings.map(x => this.pack(x, me, opts)));
+		const _followees = opts?.populateFollowee ? followings.map(({ followee, followeeId }) => followee ?? followeeId) : [];
+		const _followers = opts?.populateFollower ? followings.map(({ follower, followerId }) => follower ?? followerId) : [];
+		const _userMap = await this.userEntityService.packMany([..._followees, ..._followers], me, { schema: 'UserDetailedNotMe' })
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(
+			followings.map(following => {
+				const packedFollowee = opts?.populateFollowee ? _userMap.get(following.followeeId) : undefined;
+				const packedFollower = opts?.populateFollower ? _userMap.get(following.followerId) : undefined;
+				return this.pack(following, me, opts, { packedFollowee, packedFollower });
+			}),
+		);
 	}
 }
 
diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts
index 101182a9e5aa..9746a4c1af3a 100644
--- a/packages/backend/src/core/entities/GalleryPostEntityService.ts
+++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts
@@ -35,6 +35,9 @@ export class GalleryPostEntityService {
 	public async pack(
 		src: MiGalleryPost['id'] | MiGalleryPost,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hint?: {
+			packedUser?: Packed<'UserLite'>
+		},
 	): Promise<Packed<'GalleryPost'>> {
 		const meId = me ? me.id : null;
 		const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src });
@@ -44,7 +47,7 @@ export class GalleryPostEntityService {
 			createdAt: this.idService.parse(post.id).date.toISOString(),
 			updatedAt: post.updatedAt.toISOString(),
 			userId: post.userId,
-			user: this.userEntityService.pack(post.user ?? post.userId, me),
+			user: hint?.packedUser ?? this.userEntityService.pack(post.user ?? post.userId, me),
 			title: post.title,
 			description: post.description,
 			fileIds: post.fileIds,
@@ -58,11 +61,14 @@ export class GalleryPostEntityService {
 	}
 
 	@bindThis
-	public packMany(
+	public async packMany(
 		posts: MiGalleryPost[],
 		me?: { id: MiUser['id'] } | null | undefined,
 	) {
-		return Promise.all(posts.map(x => this.pack(x, me)));
+		const _users = posts.map(({ user, userId }) => user ?? userId);
+		const _userMap = await this.userEntityService.packMany(_users, me)
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(posts.map(post => this.pack(post, me, { packedUser: _userMap.get(post.userId) })));
 	}
 }
 
diff --git a/packages/backend/src/core/entities/InviteCodeEntityService.ts b/packages/backend/src/core/entities/InviteCodeEntityService.ts
index 891543bc0fec..26f57e129903 100644
--- a/packages/backend/src/core/entities/InviteCodeEntityService.ts
+++ b/packages/backend/src/core/entities/InviteCodeEntityService.ts
@@ -12,6 +12,7 @@ import type { MiUser } from '@/models/User.js';
 import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
 import { UserEntityService } from './UserEntityService.js';
 
 @Injectable()
@@ -29,6 +30,10 @@ export class InviteCodeEntityService {
 	public async pack(
 		src: MiRegistrationTicket['id'] | MiRegistrationTicket,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hints?: {
+			packedCreatedBy?: Packed<'UserLite'>,
+			packedUsedBy?: Packed<'UserLite'>,
+		},
 	): Promise<Packed<'InviteCode'>> {
 		const target = typeof src === 'object' ? src : await this.registrationTicketsRepository.findOneOrFail({
 			where: {
@@ -42,18 +47,28 @@ export class InviteCodeEntityService {
 			code: target.code,
 			expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null,
 			createdAt: this.idService.parse(target.id).date.toISOString(),
-			createdBy: target.createdBy ? await this.userEntityService.pack(target.createdBy, me) : null,
-			usedBy: target.usedBy ? await this.userEntityService.pack(target.usedBy, me) : null,
+			createdBy: target.createdBy ? hints?.packedCreatedBy ?? await this.userEntityService.pack(target.createdBy, me) : null,
+			usedBy: target.usedBy ? hints?.packedUsedBy ?? await this.userEntityService.pack(target.usedBy, me) : null,
 			usedAt: target.usedAt ? target.usedAt.toISOString() : null,
 			used: !!target.usedAt,
 		});
 	}
 
 	@bindThis
-	public packMany(
-		targets: any[],
+	public async packMany(
+		tickets: MiRegistrationTicket[],
 		me: { id: MiUser['id'] },
 	) {
-		return Promise.all(targets.map(x => this.pack(x, me)));
+		const _createdBys = tickets.map(({ createdBy, createdById }) => createdBy ?? createdById).filter(isNotNull);
+		const _usedBys = tickets.map(({ usedBy, usedById }) => usedBy ?? usedById).filter(isNotNull);
+		const _userMap = await this.userEntityService.packMany([..._createdBys, ..._usedBys], me)
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(
+			tickets.map(ticket => {
+				const packedCreatedBy = ticket.createdById != null ? _userMap.get(ticket.createdById) : undefined;
+				const packedUsedBy = ticket.usedById != null ? _userMap.get(ticket.usedById) : undefined;
+				return this.pack(ticket, me, { packedCreatedBy, packedUsedBy });
+			}),
+		);
 	}
 }
diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts
index 205e147bd106..bf1b2a002cee 100644
--- a/packages/backend/src/core/entities/ModerationLogEntityService.ts
+++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts
@@ -8,9 +8,10 @@ import { DI } from '@/di-symbols.js';
 import type { ModerationLogsRepository } from '@/models/_.js';
 import { awaitAll } from '@/misc/prelude/await-all.js';
 import type { } from '@/models/Blocking.js';
-import type { MiModerationLog } from '@/models/ModerationLog.js';
+import { MiModerationLog } from '@/models/ModerationLog.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
+import type { Packed } from '@/misc/json-schema.js';
 import { UserEntityService } from './UserEntityService.js';
 
 @Injectable()
@@ -27,6 +28,9 @@ export class ModerationLogEntityService {
 	@bindThis
 	public async pack(
 		src: MiModerationLog['id'] | MiModerationLog,
+		hint?: {
+			packedUser?: Packed<'UserDetailedNotMe'>,
+		},
 	) {
 		const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src });
 
@@ -36,17 +40,20 @@ export class ModerationLogEntityService {
 			type: log.type,
 			info: log.info,
 			userId: log.userId,
-			user: this.userEntityService.pack(log.user ?? log.userId, null, {
+			user: hint?.packedUser ?? this.userEntityService.pack(log.user ?? log.userId, null, {
 				schema: 'UserDetailedNotMe',
 			}),
 		});
 	}
 
 	@bindThis
-	public packMany(
-		reports: any[],
+	public async packMany(
+		reports: MiModerationLog[],
 	) {
-		return Promise.all(reports.map(x => this.pack(x)));
+		const _users = reports.map(({ user, userId }) => user ?? userId);
+		const _userMap = await this.userEntityService.packMany(_users, null, { schema: 'UserDetailedNotMe' })
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(reports.map(report => this.pack(report, { packedUser: _userMap.get(report.userId) })));
 	}
 }
 
diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts
index 0a52f429a2b2..d361a20271b6 100644
--- a/packages/backend/src/core/entities/MutingEntityService.ts
+++ b/packages/backend/src/core/entities/MutingEntityService.ts
@@ -30,6 +30,9 @@ export class MutingEntityService {
 	public async pack(
 		src: MiMuting['id'] | MiMuting,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hints?: {
+			packedMutee?: Packed<'UserDetailedNotMe'>,
+		},
 	): Promise<Packed<'Muting'>> {
 		const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src });
 
@@ -38,18 +41,21 @@ export class MutingEntityService {
 			createdAt: this.idService.parse(muting.id).date.toISOString(),
 			expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
 			muteeId: muting.muteeId,
-			mutee: this.userEntityService.pack(muting.muteeId, me, {
+			mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
 				schema: 'UserDetailedNotMe',
 			}),
 		});
 	}
 
 	@bindThis
-	public packMany(
-		mutings: any[],
+	public async packMany(
+		mutings: MiMuting[],
 		me: { id: MiUser['id'] },
 	) {
-		return Promise.all(mutings.map(x => this.pack(x, me)));
+		const _mutees = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
+		const _userMap = await this.userEntityService.packMany(_mutees, me, { schema: 'UserDetailedNotMe' })
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
 	}
 }
 
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 22d01462e773..2ce72c50b87f 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -290,6 +290,7 @@ export class NoteEntityService implements OnModuleInit {
 			_hint_?: {
 				myReactions: Map<MiNote['id'], string | null>;
 				packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
+				packedUsers: Map<MiUser['id'], Packed<'UserLite'>>
 			};
 		},
 	): Promise<Packed<'Note'>> {
@@ -319,12 +320,13 @@ export class NoteEntityService implements OnModuleInit {
 			.filter(x => x.startsWith(':') && x.includes('@') && !x.includes('@.')) // リモートカスタム絵文字のみ
 			.map(x => this.reactionService.decodeReaction(x).reaction.replaceAll(':', ''));
 		const packedFiles = options?._hint_?.packedFiles;
+		const packedUsers = options?._hint_?.packedUsers;
 
 		const packed: Packed<'Note'> = await awaitAll({
 			id: note.id,
 			createdAt: this.idService.parse(note.id).date.toISOString(),
 			userId: note.userId,
-			user: this.userEntityService.pack(note.user ?? note.userId, me),
+			user: packedUsers?.get(note.userId) ?? this.userEntityService.pack(note.user ?? note.userId, me),
 			text: text,
 			cw: note.cw,
 			visibility: note.visibility,
@@ -449,12 +451,20 @@ export class NoteEntityService implements OnModuleInit {
 		// TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく
 		const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
 		const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
+		const users = [
+			...notes.map(({ user, userId }) => user ?? userId),
+			...notes.map(({ replyUserId }) => replyUserId).filter(isNotNull),
+			...notes.map(({ renoteUserId }) => renoteUserId).filter(isNotNull),
+		];
+		const packedUsers = await this.userEntityService.packMany(users, me)
+			.then(users => new Map(users.map(u => [u.id, u])));
 
 		return await Promise.all(notes.map(n => this.pack(n, me, {
 			...options,
 			_hint_: {
 				myReactions: myReactionsMap,
 				packedFiles,
+				packedUsers,
 			},
 		})));
 	}
diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts
index 3f4fa3cf969d..46ec13704cfd 100644
--- a/packages/backend/src/core/entities/NoteReactionEntityService.ts
+++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts
@@ -52,6 +52,9 @@ export class NoteReactionEntityService implements OnModuleInit {
 		options?: {
 			withNote: boolean;
 		},
+		hints?: {
+			packedUser?: Packed<'UserLite'>
+		},
 	): Promise<Packed<'NoteReaction'>> {
 		const opts = Object.assign({
 			withNote: false,
@@ -62,7 +65,7 @@ export class NoteReactionEntityService implements OnModuleInit {
 		return {
 			id: reaction.id,
 			createdAt: this.idService.parse(reaction.id).date.toISOString(),
-			user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
+			user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
 			type: this.reactionService.convertLegacyReaction(reaction.reaction),
 			...(opts.withNote ? {
 				note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
@@ -81,7 +84,9 @@ export class NoteReactionEntityService implements OnModuleInit {
 		const opts = Object.assign({
 			withNote: false,
 		}, options);
-
-		return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts)));
+		const _users = reactions.map(({ user, userId }) => user ?? userId);
+		const _userMap = await this.userEntityService.packMany(_users, me)
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
 	}
 }
diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts
index 65c69a49a7ec..142d9e81dbc8 100644
--- a/packages/backend/src/core/entities/PageEntityService.ts
+++ b/packages/backend/src/core/entities/PageEntityService.ts
@@ -40,6 +40,9 @@ export class PageEntityService {
 	public async pack(
 		src: MiPage['id'] | MiPage,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hint?: {
+			packedUser?: Packed<'UserLite'>
+		},
 	): Promise<Packed<'Page'>> {
 		const meId = me ? me.id : null;
 		const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src });
@@ -91,7 +94,7 @@ export class PageEntityService {
 			createdAt: this.idService.parse(page.id).date.toISOString(),
 			updatedAt: page.updatedAt.toISOString(),
 			userId: page.userId,
-			user: this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
+			user: hint?.packedUser ?? this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
 			content: page.content,
 			variables: page.variables,
 			title: page.title,
@@ -110,11 +113,14 @@ export class PageEntityService {
 	}
 
 	@bindThis
-	public packMany(
+	public async packMany(
 		pages: MiPage[],
 		me?: { id: MiUser['id'] } | null | undefined,
 	) {
-		return Promise.all(pages.map(x => this.pack(x, me)));
+		const _users = pages.map(({ user, userId }) => user ?? userId);
+		const _userMap = await this.userEntityService.packMany(_users, me)
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(pages.map(page => this.pack(page, me, { packedUser: _userMap.get(page.userId) })));
 	}
 }
 
diff --git a/packages/backend/src/core/entities/RenoteMutingEntityService.ts b/packages/backend/src/core/entities/RenoteMutingEntityService.ts
index 0b05a5db8090..e4e154109a32 100644
--- a/packages/backend/src/core/entities/RenoteMutingEntityService.ts
+++ b/packages/backend/src/core/entities/RenoteMutingEntityService.ts
@@ -30,6 +30,9 @@ export class RenoteMutingEntityService {
 	public async pack(
 		src: MiRenoteMuting['id'] | MiRenoteMuting,
 		me?: { id: MiUser['id'] } | null | undefined,
+		hints?: {
+			packedMutee?: Packed<'UserDetailedNotMe'>
+		},
 	): Promise<Packed<'RenoteMuting'>> {
 		const muting = typeof src === 'object' ? src : await this.renoteMutingsRepository.findOneByOrFail({ id: src });
 
@@ -37,18 +40,21 @@ export class RenoteMutingEntityService {
 			id: muting.id,
 			createdAt: this.idService.parse(muting.id).date.toISOString(),
 			muteeId: muting.muteeId,
-			mutee: this.userEntityService.pack(muting.muteeId, me, {
+			mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
 				schema: 'UserDetailedNotMe',
 			}),
 		});
 	}
 
 	@bindThis
-	public packMany(
-		mutings: any[],
+	public async packMany(
+		mutings: MiRenoteMuting[],
 		me: { id: MiUser['id'] },
 	) {
-		return Promise.all(mutings.map(x => this.pack(x, me)));
+		const _users = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
+		const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailedNotMe' })
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
 	}
 }
 
diff --git a/packages/backend/src/core/entities/ReversiGameEntityService.ts b/packages/backend/src/core/entities/ReversiGameEntityService.ts
index 32cbe631e4e2..df042e75c177 100644
--- a/packages/backend/src/core/entities/ReversiGameEntityService.ts
+++ b/packages/backend/src/core/entities/ReversiGameEntityService.ts
@@ -28,13 +28,15 @@ export class ReversiGameEntityService {
 	@bindThis
 	public async packDetail(
 		src: MiReversiGame['id'] | MiReversiGame,
+		hint?: {
+			packedUser1?: Packed<'UserLite'>,
+			packedUser2?: Packed<'UserLite'>,
+		},
 	): Promise<Packed<'ReversiGameDetailed'>> {
 		const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
 
-		const users = await Promise.all([
-			this.userEntityService.pack(game.user1 ?? game.user1Id),
-			this.userEntityService.pack(game.user2 ?? game.user2Id),
-		]);
+		const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
+		const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
 
 		return await awaitAll({
 			id: game.id,
@@ -49,10 +51,10 @@ export class ReversiGameEntityService {
 			user2Ready: game.user2Ready,
 			user1Id: game.user1Id,
 			user2Id: game.user2Id,
-			user1: users[0],
-			user2: users[1],
+			user1,
+			user2,
 			winnerId: game.winnerId,
-			winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
+			winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
 			surrenderedUserId: game.surrenderedUserId,
 			timeoutUserId: game.timeoutUserId,
 			black: game.black,
@@ -68,22 +70,35 @@ export class ReversiGameEntityService {
 	}
 
 	@bindThis
-	public packDetailMany(
-		xs: MiReversiGame[],
+	public async packDetailMany(
+		games: MiReversiGame[],
 	) {
-		return Promise.all(xs.map(x => this.packDetail(x)));
+		const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
+		const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
+		const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(
+			games.map(game => {
+				return this.packDetail(game, {
+					packedUser1: _userMap.get(game.user1Id),
+					packedUser2: _userMap.get(game.user2Id),
+				});
+			}),
+		);
 	}
 
 	@bindThis
 	public async packLite(
 		src: MiReversiGame['id'] | MiReversiGame,
+		hint?: {
+			packedUser1?: Packed<'UserLite'>,
+			packedUser2?: Packed<'UserLite'>,
+		},
 	): Promise<Packed<'ReversiGameLite'>> {
 		const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
 
-		const users = await Promise.all([
-			this.userEntityService.pack(game.user1 ?? game.user1Id),
-			this.userEntityService.pack(game.user2 ?? game.user2Id),
-		]);
+		const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
+		const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
 
 		return await awaitAll({
 			id: game.id,
@@ -94,10 +109,10 @@ export class ReversiGameEntityService {
 			isEnded: game.isEnded,
 			user1Id: game.user1Id,
 			user2Id: game.user2Id,
-			user1: users[0],
-			user2: users[1],
+			user1,
+			user2,
 			winnerId: game.winnerId,
-			winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
+			winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
 			surrenderedUserId: game.surrenderedUserId,
 			timeoutUserId: game.timeoutUserId,
 			black: game.black,
@@ -111,10 +126,21 @@ export class ReversiGameEntityService {
 	}
 
 	@bindThis
-	public packLiteMany(
-		xs: MiReversiGame[],
+	public async packLiteMany(
+		games: MiReversiGame[],
 	) {
-		return Promise.all(xs.map(x => this.packLite(x)));
+		const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
+		const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
+		const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
+			.then(users => new Map(users.map(u => [u.id, u])));
+		return Promise.all(
+			games.map(game => {
+				return this.packLite(game, {
+					packedUser1: _userMap.get(game.user1Id),
+					packedUser2: _userMap.get(game.user2Id),
+				});
+			}),
+		);
 	}
 }
 
diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts
index 09cab245212e..b77249c5cb72 100644
--- a/packages/backend/src/core/entities/UserListEntityService.ts
+++ b/packages/backend/src/core/entities/UserListEntityService.ts
@@ -50,11 +50,14 @@ export class UserListEntityService {
 	public async packMembershipsMany(
 		memberships: MiUserListMembership[],
 	) {
+		const _users = memberships.map(({ user, userId }) => user ?? userId);
+		const _userMap = await this.userEntityService.packMany(_users)
+			.then(users => new Map(users.map(u => [u.id, u])));
 		return Promise.all(memberships.map(async x => ({
 			id: x.id,
 			createdAt: this.idService.parse(x.id).date.toISOString(),
 			userId: x.userId,
-			user: await this.userEntityService.pack(x.userId),
+			user: _userMap.get(x.userId) ?? await this.userEntityService.pack(x.userId),
 			withReplies: x.withReplies,
 		})));
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
index 45758d4f50f4..198166bec26f 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
@@ -89,10 +89,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				.limit(ps.limit)
 				.getMany();
 
+			const _users = assigns.map(({ user, userId }) => user ?? userId);
+			const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
+				.then(users => new Map(users.map(u => [u.id, u])));
 			return await Promise.all(assigns.map(async assign => ({
 				id: assign.id,
 				createdAt: this.idService.parse(assign.id).date.toISOString(),
-				user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
+				user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
 				expiresAt: assign.expiresAt?.toISOString() ?? null,
 			})));
 		});
diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts
index 595a6957b2f3..502d42f9e0b8 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/find.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				folderId: ps.folderId ?? IsNull(),
 			});
 
-			return await Promise.all(files.map(file => this.driveFileEntityService.pack(file, { self: true })));
+			return await this.driveFileEntityService.packMany(files, { self: true });
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts
index 88f559138b4f..fa59e38976d8 100644
--- a/packages/backend/src/server/api/endpoints/following/requests/list.ts
+++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts
@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				.limit(ps.limit)
 				.getMany();
 
-			return await Promise.all(requests.map(req => this.followRequestEntityService.pack(req)));
+			return await this.followRequestEntityService.packMany(requests, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts
index a0a1fd9728c9..97b12ab7f765 100644
--- a/packages/backend/src/server/api/endpoints/notes/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts
@@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const reactions = await query.limit(ps.limit).getMany();
 
-			return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me)));
+			return await this.noteReactionEntityService.packMany(reactions, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts
index 85d100ce1c5a..48d350af5935 100644
--- a/packages/backend/src/server/api/endpoints/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/roles/users.ts
@@ -92,9 +92,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				.limit(ps.limit)
 				.getMany();
 
+			const _users = assigns.map(({ user, userId }) => user ?? userId);
+			const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
+				.then(users => new Map(users.map(u => [u.id, u])));
 			return await Promise.all(assigns.map(async assign => ({
 				id: assign.id,
-				user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
+				user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
 			})));
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
index 02aa037466e5..9248a2fa6811 100644
--- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
+++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
@@ -118,12 +118,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]);
 
 			// Extract top replied users
-			const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit);
+			const topRepliedUserIds = repliedUsersSorted.slice(0, ps.limit);
 
 			// Make replies object (includes weights)
-			const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
-				user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }),
-				weight: repliedUsers[user] / peak,
+			const _userMap = await this.userEntityService.packMany(topRepliedUserIds, me, { schema: 'UserDetailed' })
+				.then(users => new Map(users.map(u => [u.id, u])));
+			const repliesObj = await Promise.all(topRepliedUserIds.map(async (userId) => ({
+				user: _userMap.get(userId) ?? await this.userEntityService.pack(userId, me, { schema: 'UserDetailed' }),
+				weight: repliedUsers[userId] / peak,
 			})));
 
 			return repliesObj;
diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts
index 26cfa921c56a..062326e28d1e 100644
--- a/packages/backend/src/server/api/endpoints/users/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/show.ts
@@ -117,9 +117,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					if (user != null) _users.push(user);
 				}
 
-				return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
-					schema: 'UserDetailed',
-				})));
+				const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
+					.then(users => new Map(users.map(u => [u.id, u])));
+				return _users.map(u => _userMap.get(u.id)!);
 			} else {
 				// Lookup user
 				if (typeof ps.host === 'string' && typeof ps.username === 'string') {

From dc55adbaf799de67e933e5207801502f49f9efa9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 07:06:41 +0000
Subject: [PATCH 255/266] Bump version to 2024.5.0-rc.9

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 04942d16bfee..26a15b694c51 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.8",
+	"version": "2024.5.0-rc.9",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1a611d8f1022..6ec4674c0855 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.8",
+	"version": "2024.5.0-rc.9",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 030082f7567e6896add3180fbc48f2d6ee4a151e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 19:35:27 +0900
Subject: [PATCH 256/266] :art:

---
 locales/index.d.ts                          |  4 ++++
 locales/ja-JP.yml                           |  1 +
 packages/frontend/src/pages/admin/index.vue | 16 ++++++++--------
 3 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 91bbe4ccb672..0b1b86d37378 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -3364,6 +3364,10 @@ export interface Locale extends ILocale {
      * 管理者情報が設定されていません。
      */
     "noMaintainerInformationWarning": string;
+    /**
+     * 問い合わせ先URLが設定されていません。
+     */
+    "noInquiryUrlWarning": string;
     /**
      * Botプロテクションが設定されていません。
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b7083b494281..a89cfbd843a3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -837,6 +837,7 @@ administration: "管理"
 accounts: "アカウント"
 switch: "切り替え"
 noMaintainerInformationWarning: "管理者情報が設定されていません。"
+noInquiryUrlWarning: "問い合わせ先URLが設定されていません。"
 noBotProtectionWarning: "Botプロテクションが設定されていません。"
 configure: "設定する"
 postToGallery: "ギャラリーへ投稿"
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index eef1c8afa944..794feae202ad 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -12,10 +12,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
 				</div>
 
-				<MkInfo v-if="thereIsUnresolvedAbuseReport" warn class="info">{{ i18n.ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>
-				<MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
-				<MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
-				<MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
+				<div class="_gaps_s">
+					<MkInfo v-if="thereIsUnresolvedAbuseReport" warn>{{ i18n.ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>
+					<MkInfo v-if="noMaintainerInformation" warn>{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
+					<MkInfo v-if="noInquiryUrl" warn>{{ i18n.ts.noInquiryUrlWarning }} <MkA to="/admin/moderation" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
+					<MkInfo v-if="noBotProtection" warn>{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
+					<MkInfo v-if="noEmailServer" warn>{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
+				</div>
 
 				<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
 			</div>
@@ -61,6 +64,7 @@ const pageProps = ref({});
 let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
 let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
 let noEmailServer = !instance.enableEmail;
+let noInquiryUrl = isEmpty(instance.inquiryUrl);
 const thereIsUnresolvedAbuseReport = ref(false);
 const currentPage = computed(() => router.currentRef.value.child);
 
@@ -348,10 +352,6 @@ defineExpose({
 
 	> .nav {
 		.lxpfedzu {
-			> .info {
-				margin: 16px 0;
-			}
-
 			> .banner {
 				margin: 16px;
 

From 374c8791d72ff55b79b2152726a174261cc997d3 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 11:13:42 +0000
Subject: [PATCH 257/266] Bump version to 2024.5.0-rc.10

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 26a15b694c51..bc56cc45fb82 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.9",
+	"version": "2024.5.0-rc.10",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 6ec4674c0855..ce47ee4093c4 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.9",
+	"version": "2024.5.0-rc.10",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 46164f879b9bd672cd7f5eea34180a196b0967b8 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 11:20:13 +0000
Subject: [PATCH 258/266] Bump version to 2024.5.0-rc.11

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index bc56cc45fb82..4f5e15014cf9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.10",
+	"version": "2024.5.0-rc.11",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index ce47ee4093c4..dfda3029602c 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.10",
+	"version": "2024.5.0-rc.11",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 2eaa3e256ff7310bd63197aafe7e72fa207e347b Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 20:42:02 +0900
Subject: [PATCH 259/266] Update README.md for Sentry

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index 24013a7bd817..92e8fef6396e 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,10 @@
 
 ## Thanks
 
+<a href="https://sentry.io/"><img src="https://github.com/misskey-dev/misskey/assets/4439005/98576556-222f-467a-94be-e98dbda1d852" height="30" alt="Sentry" /></a>
+
+Thanks to [Sentry](https://sentry.io/) for providing the error tracking platform that helps us catch unexpected errors.
+
 <a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" height="30" alt="Chromatic" /></a>
 
 Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.

From 316d192bc0fb6dde1fb18c0cfe0d76a247d62388 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 12:05:47 +0000
Subject: [PATCH 260/266] Bump version to 2024.5.0-rc.12

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 4f5e15014cf9..998d612b5b21 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.11",
+	"version": "2024.5.0-rc.12",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index dfda3029602c..1b361c429526 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.11",
+	"version": "2024.5.0-rc.12",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 27d1b7e6156699184bed278c9a43d5b94e159d4e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 21:09:19 +0900
Subject: [PATCH 261/266] 2024.5.0

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 998d612b5b21..b1786e16f196 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.12",
+	"version": "2024.5.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1b361c429526..4ff1a57309a7 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.12",
+	"version": "2024.5.0",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 61eec93f4e93686c362fd68c8ff1f1ca96e1d790 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 31 May 2024 21:16:35 +0900
Subject: [PATCH 262/266] Revert "2024.5.0"

This reverts commit 27d1b7e6156699184bed278c9a43d5b94e159d4e.
---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index b1786e16f196..998d612b5b21 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0",
+	"version": "2024.5.0-rc.12",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 4ff1a57309a7..1b361c429526 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0",
+	"version": "2024.5.0-rc.12",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From a59aa20be827f23f9d15f7a7368779bcc89ece25 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 12:18:52 +0000
Subject: [PATCH 263/266] Bump version to 2024.5.0-rc.13

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 998d612b5b21..175561e4677b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.12",
+	"version": "2024.5.0-rc.13",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1b361c429526..a49bfab94101 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.12",
+	"version": "2024.5.0-rc.13",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 6078081c336ab42535cbea173c728d2aef08d5d2 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 12:24:53 +0000
Subject: [PATCH 264/266] [skip ci] Release: 2024.5.0

---
 package.json                     | 2 +-
 packages/misskey-js/package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 175561e4677b..b1786e16f196 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0-rc.13",
+	"version": "2024.5.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index a49bfab94101..4ff1a57309a7 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0-rc.13",
+	"version": "2024.5.0",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From ecf7945fe8e4554c0db248512fbc51a41cfaf2c9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 31 May 2024 12:25:00 +0000
Subject: [PATCH 265/266] [skip ci] Update CHANGELOG.md (prepend template)

---
 CHANGELOG.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f78ba677d6b..9d4ef23d272d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+## Unreleased
+
+### General
+-
+
+### Client
+-
+
+### Server
+-
+
+
 ## 2024.5.0
 
 ### Note

From 2b8056a8525e1b38536aa0406ba9446a88b365b9 Mon Sep 17 00:00:00 2001
From: Acid Chicken <root@acid-chicken.com>
Date: Sat, 1 Jun 2024 11:16:44 +0900
Subject: [PATCH 266/266] fix(backend): use insertOne insteadof
 insert/findOneOrFail combination (#13908)

* fix(backend): use insertOne insteadof insert/findOneOrFail combination

* fix: typo

* fix(backend): inherit mainAlias?

* refactor(backend): use extend

* fix(backend): invalid entityTarget

* fix(backend): fake where

* chore: debug

* chore: debug

* test: log

* fix(backend): column names

* fix(backend): remove dummy from

* revert: log

* fix(backend): position

* fix(backend): automatic aliasing

* chore(backend): alias

* chore(backend): remove from

* fix(backend): type

* fix(backend): avoid pure name

* test(backend): fix type

* chore(backend): use cte

* fix(backend): avoid useless alias

* fix(backend): fix typo

* fix(backend): __disambiguation__

* fix(backend): quote

* chore(backend): t

* chore(backend): accessible

* chore(backend): concrete returning

* fix(backend): quote

* chore: log more

* chore: log metadata

* chore(backend): use raw

* fix(backend): returning column name

* fix(backend): transform

* build(backend): wanna logging

* build(backend): transform empty

* build(backend): build alias

* build(backend): restore name

* chore: return entity

* fix: test case

* test(backend): 204

* chore(backend): log sql

* chore(backend): assert user joined

* fix(backend): typo

* chore(backend): log long sql

* chore(backend): log join

* chore(backend): log join depth null

* chore(backend): joinAttributes

* chore(backend): override createJoinExpression

* chore: join log

* fix(backend): escape

* test(backend): log log

* chore(backend): join gonna success?

* chore(backend): relations

* chore(backend): undefined

* chore(backend): target

* chore(backend): remove log

* chore(backend): log chart update

* chore(backend): log columns

* chore(backend): check hasMetadata

* chore(backend): unshift id when not included

* chore(backend): missing select

* chore(backend): remove debug code
---
 .../backend/src/core/AnnouncementService.ts   |   4 +-
 .../src/core/AvatarDecorationService.ts       |   4 +-
 packages/backend/src/core/ClipService.ts      |   4 +-
 .../backend/src/core/CustomEmojiService.ts    |   4 +-
 packages/backend/src/core/DriveService.ts     |   6 +-
 .../src/core/FederatedInstanceService.ts      |   4 +-
 packages/backend/src/core/RelayService.ts     |   4 +-
 packages/backend/src/core/ReversiService.ts   |   7 +-
 packages/backend/src/core/RoleService.ts      |   8 +-
 .../backend/src/core/UserFollowingService.ts  |   4 +-
 .../core/activitypub/models/ApNoteService.ts  |   4 +-
 packages/backend/src/core/chart/core.ts       |  23 +-
 .../backend/src/models/RepositoryModule.ts    | 136 ++++++------
 packages/backend/src/models/_.ts              | 205 ++++++++++++------
 .../ImportAntennasProcessorService.ts         |   4 +-
 .../ImportUserListsProcessorService.ts        |   4 +-
 .../backend/src/server/api/SigninService.ts   |   4 +-
 .../src/server/api/SignupApiService.ts        |   4 +-
 .../server/api/endpoints/admin/ad/create.ts   |   4 +-
 .../api/endpoints/admin/invite/create.ts      |   4 +-
 .../server/api/endpoints/antennas/create.ts   |   4 +-
 .../src/server/api/endpoints/app/create.ts    |   4 +-
 .../api/endpoints/auth/session/generate.ts    |   4 +-
 .../server/api/endpoints/channels/create.ts   |   4 +-
 .../api/endpoints/drive/folders/create.ts     |   4 +-
 .../src/server/api/endpoints/flash/create.ts  |   4 +-
 .../api/endpoints/gallery/posts/create.ts     |   4 +-
 .../server/api/endpoints/i/webhooks/create.ts |   4 +-
 .../src/server/api/endpoints/invite/create.ts |   4 +-
 .../server/api/endpoints/notes/polls/vote.ts  |   4 +-
 .../src/server/api/endpoints/pages/create.ts  |   4 +-
 .../users/lists/create-from-public.ts         |   4 +-
 .../api/endpoints/users/lists/create.ts       |   4 +-
 .../api/endpoints/users/report-abuse.ts       |   4 +-
 packages/backend/test/e2e/move.ts             |   4 +-
 packages/backend/test/e2e/reversi-game.ts     |  33 +++
 36 files changed, 319 insertions(+), 215 deletions(-)
 create mode 100644 packages/backend/test/e2e/reversi-game.ts

diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts
index 9b60df2cae99..40a9db01c057 100644
--- a/packages/backend/src/core/AnnouncementService.ts
+++ b/packages/backend/src/core/AnnouncementService.ts
@@ -67,7 +67,7 @@ export class AnnouncementService {
 
 	@bindThis
 	public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
-		const announcement = await this.announcementsRepository.insert({
+		const announcement = await this.announcementsRepository.insertOne({
 			id: this.idService.gen(),
 			updatedAt: null,
 			title: values.title,
@@ -79,7 +79,7 @@ export class AnnouncementService {
 			silence: values.silence,
 			needConfirmationToRead: values.needConfirmationToRead,
 			userId: values.userId,
-		}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		const packed = await this.announcementEntityService.pack(announcement);
 
diff --git a/packages/backend/src/core/AvatarDecorationService.ts b/packages/backend/src/core/AvatarDecorationService.ts
index 21e31d79a429..8b54bbe01241 100644
--- a/packages/backend/src/core/AvatarDecorationService.ts
+++ b/packages/backend/src/core/AvatarDecorationService.ts
@@ -55,10 +55,10 @@ export class AvatarDecorationService implements OnApplicationShutdown {
 
 	@bindThis
 	public async create(options: Partial<MiAvatarDecoration>, moderator?: MiUser): Promise<MiAvatarDecoration> {
-		const created = await this.avatarDecorationsRepository.insert({
+		const created = await this.avatarDecorationsRepository.insertOne({
 			id: this.idService.gen(),
 			...options,
-		}).then(x => this.avatarDecorationsRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		this.globalEventService.publishInternalEvent('avatarDecorationCreated', created);
 
diff --git a/packages/backend/src/core/ClipService.ts b/packages/backend/src/core/ClipService.ts
index bb8be26ce6c5..9fd1ebad8724 100644
--- a/packages/backend/src/core/ClipService.ts
+++ b/packages/backend/src/core/ClipService.ts
@@ -45,13 +45,13 @@ export class ClipService {
 			throw new ClipService.TooManyClipsError();
 		}
 
-		const clip = await this.clipsRepository.insert({
+		const clip = await this.clipsRepository.insertOne({
 			id: this.idService.gen(),
 			userId: me.id,
 			name: name,
 			isPublic: isPublic,
 			description: description,
-		}).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		return clip;
 	}
diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index b1feca7fb4d3..7e11b9cdca15 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -68,7 +68,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
 		localOnly: boolean;
 		roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
 	}, moderator?: MiUser): Promise<MiEmoji> {
-		const emoji = await this.emojisRepository.insert({
+		const emoji = await this.emojisRepository.insertOne({
 			id: this.idService.gen(),
 			updatedAt: new Date(),
 			name: data.name,
@@ -82,7 +82,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
 			isSensitive: data.isSensitive,
 			localOnly: data.localOnly,
 			roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction,
-		}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		if (data.host == null) {
 			this.localEmojisCache.refresh();
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 26cf532c7016..37c5d1adf7b7 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -220,7 +220,7 @@ export class DriveService {
 			file.size = size;
 			file.storedInternal = false;
 
-			return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
+			return await this.driveFilesRepository.insertOne(file);
 		} else { // use internal storage
 			const accessKey = randomUUID();
 			const thumbnailAccessKey = 'thumbnail-' + randomUUID();
@@ -254,7 +254,7 @@ export class DriveService {
 			file.md5 = hash;
 			file.size = size;
 
-			return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
+			return await this.driveFilesRepository.insertOne(file);
 		}
 	}
 
@@ -615,7 +615,7 @@ export class DriveService {
 				file.type = info.type.mime;
 				file.storedInternal = false;
 
-				file = await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
+				file = await this.driveFilesRepository.insertOne(file);
 			} catch (err) {
 			// duplicate key error (when already registered)
 				if (isDuplicateKeyValueError(err)) {
diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts
index 66db2067d9f8..6799f2c5bbdc 100644
--- a/packages/backend/src/core/FederatedInstanceService.ts
+++ b/packages/backend/src/core/FederatedInstanceService.ts
@@ -55,11 +55,11 @@ export class FederatedInstanceService implements OnApplicationShutdown {
 		const index = await this.instancesRepository.findOneBy({ host });
 
 		if (index == null) {
-			const i = await this.instancesRepository.insert({
+			const i = await this.instancesRepository.insertOne({
 				id: this.idService.gen(),
 				host,
 				firstRetrievedAt: new Date(),
-			}).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			this.federatedInstanceCache.set(host, i);
 			return i;
diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts
index e9dc9b57afef..8dd3d64f5b29 100644
--- a/packages/backend/src/core/RelayService.ts
+++ b/packages/backend/src/core/RelayService.ts
@@ -53,11 +53,11 @@ export class RelayService {
 
 	@bindThis
 	public async addRelay(inbox: string): Promise<MiRelay> {
-		const relay = await this.relaysRepository.insert({
+		const relay = await this.relaysRepository.insertOne({
 			id: this.idService.gen(),
 			inbox,
 			status: 'requesting',
-		}).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		const relayActor = await this.getRelayActor();
 		const follow = await this.apRendererService.renderFollowRelay(relay, relayActor);
diff --git a/packages/backend/src/core/ReversiService.ts b/packages/backend/src/core/ReversiService.ts
index 439bc0884585..7f939b99c7a6 100644
--- a/packages/backend/src/core/ReversiService.ts
+++ b/packages/backend/src/core/ReversiService.ts
@@ -281,7 +281,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
 
 	@bindThis
 	private async matched(parentId: MiUser['id'], childId: MiUser['id'], options: { noIrregularRules: boolean; }): Promise<MiReversiGame> {
-		const game = await this.reversiGamesRepository.insert({
+		const game = await this.reversiGamesRepository.insertOne({
 			id: this.idService.gen(),
 			user1Id: parentId,
 			user2Id: childId,
@@ -294,10 +294,7 @@ export class ReversiService implements OnApplicationShutdown, OnModuleInit {
 			bw: 'random',
 			isLlotheo: false,
 			noIrregularRules: options.noIrregularRules,
-		}).then(x => this.reversiGamesRepository.findOneOrFail({
-			where: { id: x.identifiers[0].id },
-			relations: ['user1', 'user2'],
-		}));
+		}, { relations: ['user1', 'user2'] });
 		this.cacheGame(game);
 
 		const packed = await this.reversiGameEntityService.packDetail(game);
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 70c537f9abdc..d6eea702970b 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -471,12 +471,12 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 			}
 		}
 
-		const created = await this.roleAssignmentsRepository.insert({
+		const created = await this.roleAssignmentsRepository.insertOne({
 			id: this.idService.gen(now),
 			expiresAt: expiresAt,
 			roleId: roleId,
 			userId: userId,
-		}).then(x => this.roleAssignmentsRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		this.rolesRepository.update(roleId, {
 			lastUsedAt: new Date(),
@@ -558,7 +558,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 	@bindThis
 	public async create(values: Partial<MiRole>, moderator?: MiUser): Promise<MiRole> {
 		const date = new Date();
-		const created = await this.rolesRepository.insert({
+		const created = await this.rolesRepository.insertOne({
 			id: this.idService.gen(date.getTime()),
 			updatedAt: date,
 			lastUsedAt: date,
@@ -576,7 +576,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 			canEditMembersByModerator: values.canEditMembersByModerator,
 			displayOrder: values.displayOrder,
 			policies: values.policies,
-		}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		this.globalEventService.publishInternalEvent('roleCreated', created);
 
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index deeecdeb1f12..406ea040316a 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -517,7 +517,7 @@ export class UserFollowingService implements OnModuleInit {
 			followerId: follower.id,
 		});
 
-		const followRequest = await this.followRequestsRepository.insert({
+		const followRequest = await this.followRequestsRepository.insertOne({
 			id: this.idService.gen(),
 			followerId: follower.id,
 			followeeId: followee.id,
@@ -531,7 +531,7 @@ export class UserFollowingService implements OnModuleInit {
 			followeeHost: followee.host,
 			followeeInbox: this.userEntityService.isRemoteUser(followee) ? followee.inbox : undefined,
 			followeeSharedInbox: this.userEntityService.isRemoteUser(followee) ? followee.sharedInbox : undefined,
-		}).then(x => this.followRequestsRepository.findOneByOrFail(x.identifiers[0]));
+		});
 
 		// Publish receiveRequest event
 		if (this.userEntityService.isLocalUser(followee)) {
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index e6dff067f31f..c6e6b3a1e8db 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -407,7 +407,7 @@ export class ApNoteService {
 
 			this.logger.info(`register emoji host=${host}, name=${name}`);
 
-			return await this.emojisRepository.insert({
+			return await this.emojisRepository.insertOne({
 				id: this.idService.gen(),
 				host,
 				name,
@@ -416,7 +416,7 @@ export class ApNoteService {
 				publicUrl: tag.icon.url,
 				updatedAt: new Date(),
 				aliases: [],
-			}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
+			});
 		}));
 	}
 }
diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts
index f10e30ef1048..af5485a46ee9 100644
--- a/packages/backend/src/core/chart/core.ts
+++ b/packages/backend/src/core/chart/core.ts
@@ -14,7 +14,8 @@ import { EntitySchema, LessThan, Between } from 'typeorm';
 import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/misc/prelude/time.js';
 import type Logger from '@/logger.js';
 import { bindThis } from '@/decorators.js';
-import type { Repository, DataSource } from 'typeorm';
+import { MiRepository, miRepository } from '@/models/_.js';
+import type { DataSource, Repository } from 'typeorm';
 
 const COLUMN_PREFIX = '___' as const;
 const UNIQUE_TEMP_COLUMN_PREFIX = 'unique_temp___' as const;
@@ -145,10 +146,10 @@ export default abstract class Chart<T extends Schema> {
 		group: string | null;
 	}[] = [];
 	// ↓にしたいけどfindOneとかで型エラーになる
-	//private repositoryForHour: Repository<RawRecord<T>>;
-	//private repositoryForDay: Repository<RawRecord<T>>;
-	private repositoryForHour: Repository<{ id: number; group?: string | null; date: number; }>;
-	private repositoryForDay: Repository<{ id: number; group?: string | null; date: number; }>;
+	//private repositoryForHour: Repository<RawRecord<T>> & MiRepository<RawRecord<T>>;
+	//private repositoryForDay: Repository<RawRecord<T>> & MiRepository<RawRecord<T>>;
+	private repositoryForHour: Repository<{ id: number; group?: string | null; date: number; }> & MiRepository<{ id: number; group?: string | null; date: number; }>;
+	private repositoryForDay: Repository<{ id: number; group?: string | null; date: number; }> & MiRepository<{ id: number; group?: string | null; date: number; }>;
 
 	/**
 	 * 1日に一回程度実行されれば良いような計算処理を入れる(主にCASCADE削除などアプリケーション側で感知できない変動によるズレの修正用)
@@ -211,6 +212,10 @@ export default abstract class Chart<T extends Schema> {
 	} {
 		const createEntity = (span: 'hour' | 'day'): EntitySchema => new EntitySchema({
 			name:
+				span === 'hour' ? `ChartX${name}` :
+				span === 'day' ? `ChartDayX${name}` :
+				new Error('not happen') as never,
+			tableName:
 				span === 'hour' ? `__chart__${camelToSnake(name)}` :
 				span === 'day' ? `__chart_day__${camelToSnake(name)}` :
 				new Error('not happen') as never,
@@ -271,8 +276,8 @@ export default abstract class Chart<T extends Schema> {
 		this.logger = logger;
 
 		const { hour, day } = Chart.schemaToEntity(name, schema, grouped);
-		this.repositoryForHour = db.getRepository<{ id: number; group?: string | null; date: number; }>(hour);
-		this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day);
+		this.repositoryForHour = db.getRepository<{ id: number; group?: string | null; date: number; }>(hour).extend(miRepository as MiRepository<{ id: number; group?: string | null; date: number; }>);
+		this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day).extend(miRepository as MiRepository<{ id: number; group?: string | null; date: number; }>);
 	}
 
 	@bindThis
@@ -387,11 +392,11 @@ export default abstract class Chart<T extends Schema> {
 			}
 
 			// 新規ログ挿入
-			log = await repository.insert({
+			log = await repository.insertOne({
 				date: date,
 				...(group ? { group: group } : {}),
 				...columns,
-			}).then(x => repository.findOneByOrFail(x.identifiers[0])) as RawRecord<T>;
+			}) as RawRecord<T>;
 
 			this.logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): New commit created`);
 
diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts
index bd447570ddf0..d3062d6b36f8 100644
--- a/packages/backend/src/models/RepositoryModule.ts
+++ b/packages/backend/src/models/RepositoryModule.ts
@@ -5,409 +5,409 @@
 
 import { Module } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame } from './_.js';
+import { MiRepository, MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame, miRepository } from './_.js';
 import type { DataSource } from 'typeorm';
 import type { Provider } from '@nestjs/common';
 
 const $usersRepository: Provider = {
 	provide: DI.usersRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUser),
+	useFactory: (db: DataSource) => db.getRepository(MiUser).extend(miRepository as MiRepository<MiUser>),
 	inject: [DI.db],
 };
 
 const $notesRepository: Provider = {
 	provide: DI.notesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiNote),
+	useFactory: (db: DataSource) => db.getRepository(MiNote).extend(miRepository as MiRepository<MiNote>),
 	inject: [DI.db],
 };
 
 const $announcementsRepository: Provider = {
 	provide: DI.announcementsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAnnouncement),
+	useFactory: (db: DataSource) => db.getRepository(MiAnnouncement).extend(miRepository as MiRepository<MiAnnouncement>),
 	inject: [DI.db],
 };
 
 const $announcementReadsRepository: Provider = {
 	provide: DI.announcementReadsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAnnouncementRead),
+	useFactory: (db: DataSource) => db.getRepository(MiAnnouncementRead).extend(miRepository as MiRepository<MiAnnouncementRead>),
 	inject: [DI.db],
 };
 
 const $appsRepository: Provider = {
 	provide: DI.appsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiApp),
+	useFactory: (db: DataSource) => db.getRepository(MiApp).extend(miRepository as MiRepository<MiApp>),
 	inject: [DI.db],
 };
 
 const $avatarDecorationsRepository: Provider = {
 	provide: DI.avatarDecorationsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAvatarDecoration),
+	useFactory: (db: DataSource) => db.getRepository(MiAvatarDecoration).extend(miRepository as MiRepository<MiAvatarDecoration>),
 	inject: [DI.db],
 };
 
 const $noteFavoritesRepository: Provider = {
 	provide: DI.noteFavoritesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiNoteFavorite),
+	useFactory: (db: DataSource) => db.getRepository(MiNoteFavorite).extend(miRepository as MiRepository<MiNoteFavorite>),
 	inject: [DI.db],
 };
 
 const $noteThreadMutingsRepository: Provider = {
 	provide: DI.noteThreadMutingsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiNoteThreadMuting),
+	useFactory: (db: DataSource) => db.getRepository(MiNoteThreadMuting).extend(miRepository as MiRepository<MiNoteThreadMuting>),
 	inject: [DI.db],
 };
 
 const $noteReactionsRepository: Provider = {
 	provide: DI.noteReactionsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiNoteReaction),
+	useFactory: (db: DataSource) => db.getRepository(MiNoteReaction).extend(miRepository as MiRepository<MiNoteReaction>),
 	inject: [DI.db],
 };
 
 const $noteUnreadsRepository: Provider = {
 	provide: DI.noteUnreadsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiNoteUnread),
+	useFactory: (db: DataSource) => db.getRepository(MiNoteUnread).extend(miRepository as MiRepository<MiNoteUnread>),
 	inject: [DI.db],
 };
 
 const $pollsRepository: Provider = {
 	provide: DI.pollsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiPoll),
+	useFactory: (db: DataSource) => db.getRepository(MiPoll).extend(miRepository as MiRepository<MiPoll>),
 	inject: [DI.db],
 };
 
 const $pollVotesRepository: Provider = {
 	provide: DI.pollVotesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiPollVote),
+	useFactory: (db: DataSource) => db.getRepository(MiPollVote).extend(miRepository as MiRepository<MiPollVote>),
 	inject: [DI.db],
 };
 
 const $userProfilesRepository: Provider = {
 	provide: DI.userProfilesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserProfile),
+	useFactory: (db: DataSource) => db.getRepository(MiUserProfile).extend(miRepository as MiRepository<MiUserProfile>),
 	inject: [DI.db],
 };
 
 const $userKeypairsRepository: Provider = {
 	provide: DI.userKeypairsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserKeypair),
+	useFactory: (db: DataSource) => db.getRepository(MiUserKeypair).extend(miRepository as MiRepository<MiUserKeypair>),
 	inject: [DI.db],
 };
 
 const $userPendingsRepository: Provider = {
 	provide: DI.userPendingsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserPending),
+	useFactory: (db: DataSource) => db.getRepository(MiUserPending).extend(miRepository as MiRepository<MiUserPending>),
 	inject: [DI.db],
 };
 
 const $userSecurityKeysRepository: Provider = {
 	provide: DI.userSecurityKeysRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserSecurityKey),
+	useFactory: (db: DataSource) => db.getRepository(MiUserSecurityKey).extend(miRepository as MiRepository<MiUserSecurityKey>),
 	inject: [DI.db],
 };
 
 const $userPublickeysRepository: Provider = {
 	provide: DI.userPublickeysRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserPublickey),
+	useFactory: (db: DataSource) => db.getRepository(MiUserPublickey).extend(miRepository as MiRepository<MiUserPublickey>),
 	inject: [DI.db],
 };
 
 const $userListsRepository: Provider = {
 	provide: DI.userListsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserList),
+	useFactory: (db: DataSource) => db.getRepository(MiUserList).extend(miRepository as MiRepository<MiUserList>),
 	inject: [DI.db],
 };
 
 const $userListFavoritesRepository: Provider = {
 	provide: DI.userListFavoritesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserListFavorite),
+	useFactory: (db: DataSource) => db.getRepository(MiUserListFavorite).extend(miRepository as MiRepository<MiUserListFavorite>),
 	inject: [DI.db],
 };
 
 const $userListMembershipsRepository: Provider = {
 	provide: DI.userListMembershipsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserListMembership),
+	useFactory: (db: DataSource) => db.getRepository(MiUserListMembership).extend(miRepository as MiRepository<MiUserListMembership>),
 	inject: [DI.db],
 };
 
 const $userNotePiningsRepository: Provider = {
 	provide: DI.userNotePiningsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserNotePining),
+	useFactory: (db: DataSource) => db.getRepository(MiUserNotePining).extend(miRepository as MiRepository<MiUserNotePining>),
 	inject: [DI.db],
 };
 
 const $userIpsRepository: Provider = {
 	provide: DI.userIpsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserIp),
+	useFactory: (db: DataSource) => db.getRepository(MiUserIp).extend(miRepository as MiRepository<MiUserIp>),
 	inject: [DI.db],
 };
 
 const $usedUsernamesRepository: Provider = {
 	provide: DI.usedUsernamesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUsedUsername),
+	useFactory: (db: DataSource) => db.getRepository(MiUsedUsername).extend(miRepository as MiRepository<MiUsedUsername>),
 	inject: [DI.db],
 };
 
 const $followingsRepository: Provider = {
 	provide: DI.followingsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiFollowing),
+	useFactory: (db: DataSource) => db.getRepository(MiFollowing).extend(miRepository as MiRepository<MiFollowing>),
 	inject: [DI.db],
 };
 
 const $followRequestsRepository: Provider = {
 	provide: DI.followRequestsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiFollowRequest),
+	useFactory: (db: DataSource) => db.getRepository(MiFollowRequest).extend(miRepository as MiRepository<MiFollowRequest>),
 	inject: [DI.db],
 };
 
 const $instancesRepository: Provider = {
 	provide: DI.instancesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiInstance),
+	useFactory: (db: DataSource) => db.getRepository(MiInstance).extend(miRepository as MiRepository<MiInstance>),
 	inject: [DI.db],
 };
 
 const $emojisRepository: Provider = {
 	provide: DI.emojisRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiEmoji),
+	useFactory: (db: DataSource) => db.getRepository(MiEmoji).extend(miRepository as MiRepository<MiEmoji>),
 	inject: [DI.db],
 };
 
 const $driveFilesRepository: Provider = {
 	provide: DI.driveFilesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiDriveFile),
+	useFactory: (db: DataSource) => db.getRepository(MiDriveFile).extend(miRepository as MiRepository<MiDriveFile>),
 	inject: [DI.db],
 };
 
 const $driveFoldersRepository: Provider = {
 	provide: DI.driveFoldersRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiDriveFolder),
+	useFactory: (db: DataSource) => db.getRepository(MiDriveFolder).extend(miRepository as MiRepository<MiDriveFolder>),
 	inject: [DI.db],
 };
 
 const $metasRepository: Provider = {
 	provide: DI.metasRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiMeta),
+	useFactory: (db: DataSource) => db.getRepository(MiMeta).extend(miRepository as MiRepository<MiMeta>),
 	inject: [DI.db],
 };
 
 const $mutingsRepository: Provider = {
 	provide: DI.mutingsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiMuting),
+	useFactory: (db: DataSource) => db.getRepository(MiMuting).extend(miRepository as MiRepository<MiMuting>),
 	inject: [DI.db],
 };
 
 const $renoteMutingsRepository: Provider = {
 	provide: DI.renoteMutingsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiRenoteMuting),
+	useFactory: (db: DataSource) => db.getRepository(MiRenoteMuting).extend(miRepository as MiRepository<MiRenoteMuting>),
 	inject: [DI.db],
 };
 
 const $blockingsRepository: Provider = {
 	provide: DI.blockingsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiBlocking),
+	useFactory: (db: DataSource) => db.getRepository(MiBlocking).extend(miRepository as MiRepository<MiBlocking>),
 	inject: [DI.db],
 };
 
 const $swSubscriptionsRepository: Provider = {
 	provide: DI.swSubscriptionsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiSwSubscription),
+	useFactory: (db: DataSource) => db.getRepository(MiSwSubscription).extend(miRepository as MiRepository<MiSwSubscription>),
 	inject: [DI.db],
 };
 
 const $hashtagsRepository: Provider = {
 	provide: DI.hashtagsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiHashtag),
+	useFactory: (db: DataSource) => db.getRepository(MiHashtag).extend(miRepository as MiRepository<MiHashtag>),
 	inject: [DI.db],
 };
 
 const $abuseUserReportsRepository: Provider = {
 	provide: DI.abuseUserReportsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAbuseUserReport),
+	useFactory: (db: DataSource) => db.getRepository(MiAbuseUserReport).extend(miRepository as MiRepository<MiAbuseUserReport>),
 	inject: [DI.db],
 };
 
 const $registrationTicketsRepository: Provider = {
 	provide: DI.registrationTicketsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiRegistrationTicket),
+	useFactory: (db: DataSource) => db.getRepository(MiRegistrationTicket).extend(miRepository as MiRepository<MiRegistrationTicket>),
 	inject: [DI.db],
 };
 
 const $authSessionsRepository: Provider = {
 	provide: DI.authSessionsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAuthSession),
+	useFactory: (db: DataSource) => db.getRepository(MiAuthSession).extend(miRepository as MiRepository<MiAuthSession>),
 	inject: [DI.db],
 };
 
 const $accessTokensRepository: Provider = {
 	provide: DI.accessTokensRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAccessToken),
+	useFactory: (db: DataSource) => db.getRepository(MiAccessToken).extend(miRepository as MiRepository<MiAccessToken>),
 	inject: [DI.db],
 };
 
 const $signinsRepository: Provider = {
 	provide: DI.signinsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiSignin),
+	useFactory: (db: DataSource) => db.getRepository(MiSignin).extend(miRepository as MiRepository<MiSignin>),
 	inject: [DI.db],
 };
 
 const $pagesRepository: Provider = {
 	provide: DI.pagesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiPage),
+	useFactory: (db: DataSource) => db.getRepository(MiPage).extend(miRepository as MiRepository<MiPage>),
 	inject: [DI.db],
 };
 
 const $pageLikesRepository: Provider = {
 	provide: DI.pageLikesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiPageLike),
+	useFactory: (db: DataSource) => db.getRepository(MiPageLike).extend(miRepository as MiRepository<MiPageLike>),
 	inject: [DI.db],
 };
 
 const $galleryPostsRepository: Provider = {
 	provide: DI.galleryPostsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiGalleryPost),
+	useFactory: (db: DataSource) => db.getRepository(MiGalleryPost).extend(miRepository as MiRepository<MiGalleryPost>),
 	inject: [DI.db],
 };
 
 const $galleryLikesRepository: Provider = {
 	provide: DI.galleryLikesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiGalleryLike),
+	useFactory: (db: DataSource) => db.getRepository(MiGalleryLike).extend(miRepository as MiRepository<MiGalleryLike>),
 	inject: [DI.db],
 };
 
 const $moderationLogsRepository: Provider = {
 	provide: DI.moderationLogsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiModerationLog),
+	useFactory: (db: DataSource) => db.getRepository(MiModerationLog).extend(miRepository as MiRepository<MiModerationLog>),
 	inject: [DI.db],
 };
 
 const $clipsRepository: Provider = {
 	provide: DI.clipsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiClip),
+	useFactory: (db: DataSource) => db.getRepository(MiClip).extend(miRepository as MiRepository<MiClip>),
 	inject: [DI.db],
 };
 
 const $clipNotesRepository: Provider = {
 	provide: DI.clipNotesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiClipNote),
+	useFactory: (db: DataSource) => db.getRepository(MiClipNote).extend(miRepository as MiRepository<MiClipNote>),
 	inject: [DI.db],
 };
 
 const $clipFavoritesRepository: Provider = {
 	provide: DI.clipFavoritesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiClipFavorite),
+	useFactory: (db: DataSource) => db.getRepository(MiClipFavorite).extend(miRepository as MiRepository<MiClipFavorite>),
 	inject: [DI.db],
 };
 
 const $antennasRepository: Provider = {
 	provide: DI.antennasRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAntenna),
+	useFactory: (db: DataSource) => db.getRepository(MiAntenna).extend(miRepository as MiRepository<MiAntenna>),
 	inject: [DI.db],
 };
 
 const $promoNotesRepository: Provider = {
 	provide: DI.promoNotesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiPromoNote),
+	useFactory: (db: DataSource) => db.getRepository(MiPromoNote).extend(miRepository as MiRepository<MiPromoNote>),
 	inject: [DI.db],
 };
 
 const $promoReadsRepository: Provider = {
 	provide: DI.promoReadsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiPromoRead),
+	useFactory: (db: DataSource) => db.getRepository(MiPromoRead).extend(miRepository as MiRepository<MiPromoRead>),
 	inject: [DI.db],
 };
 
 const $relaysRepository: Provider = {
 	provide: DI.relaysRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiRelay),
+	useFactory: (db: DataSource) => db.getRepository(MiRelay).extend(miRepository as MiRepository<MiRelay>),
 	inject: [DI.db],
 };
 
 const $channelsRepository: Provider = {
 	provide: DI.channelsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiChannel),
+	useFactory: (db: DataSource) => db.getRepository(MiChannel).extend(miRepository as MiRepository<MiChannel>),
 	inject: [DI.db],
 };
 
 const $channelFollowingsRepository: Provider = {
 	provide: DI.channelFollowingsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiChannelFollowing),
+	useFactory: (db: DataSource) => db.getRepository(MiChannelFollowing).extend(miRepository as MiRepository<MiChannelFollowing>),
 	inject: [DI.db],
 };
 
 const $channelFavoritesRepository: Provider = {
 	provide: DI.channelFavoritesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiChannelFavorite),
+	useFactory: (db: DataSource) => db.getRepository(MiChannelFavorite).extend(miRepository as MiRepository<MiChannelFavorite>),
 	inject: [DI.db],
 };
 
 const $registryItemsRepository: Provider = {
 	provide: DI.registryItemsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiRegistryItem),
+	useFactory: (db: DataSource) => db.getRepository(MiRegistryItem).extend(miRepository as MiRepository<MiRegistryItem>),
 	inject: [DI.db],
 };
 
 const $webhooksRepository: Provider = {
 	provide: DI.webhooksRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiWebhook),
+	useFactory: (db: DataSource) => db.getRepository(MiWebhook).extend(miRepository as MiRepository<MiWebhook>),
 	inject: [DI.db],
 };
 
 const $adsRepository: Provider = {
 	provide: DI.adsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiAd),
+	useFactory: (db: DataSource) => db.getRepository(MiAd).extend(miRepository as MiRepository<MiAd>),
 	inject: [DI.db],
 };
 
 const $passwordResetRequestsRepository: Provider = {
 	provide: DI.passwordResetRequestsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiPasswordResetRequest),
+	useFactory: (db: DataSource) => db.getRepository(MiPasswordResetRequest).extend(miRepository as MiRepository<MiPasswordResetRequest>),
 	inject: [DI.db],
 };
 
 const $retentionAggregationsRepository: Provider = {
 	provide: DI.retentionAggregationsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiRetentionAggregation),
+	useFactory: (db: DataSource) => db.getRepository(MiRetentionAggregation).extend(miRepository as MiRepository<MiRetentionAggregation>),
 	inject: [DI.db],
 };
 
 const $flashsRepository: Provider = {
 	provide: DI.flashsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiFlash),
+	useFactory: (db: DataSource) => db.getRepository(MiFlash).extend(miRepository as MiRepository<MiFlash>),
 	inject: [DI.db],
 };
 
 const $flashLikesRepository: Provider = {
 	provide: DI.flashLikesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiFlashLike),
+	useFactory: (db: DataSource) => db.getRepository(MiFlashLike).extend(miRepository as MiRepository<MiFlashLike>),
 	inject: [DI.db],
 };
 
 const $rolesRepository: Provider = {
 	provide: DI.rolesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiRole),
+	useFactory: (db: DataSource) => db.getRepository(MiRole).extend(miRepository as MiRepository<MiRole>),
 	inject: [DI.db],
 };
 
 const $roleAssignmentsRepository: Provider = {
 	provide: DI.roleAssignmentsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiRoleAssignment),
+	useFactory: (db: DataSource) => db.getRepository(MiRoleAssignment).extend(miRepository as MiRepository<MiRoleAssignment>),
 	inject: [DI.db],
 };
 
 const $userMemosRepository: Provider = {
 	provide: DI.userMemosRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiUserMemo),
+	useFactory: (db: DataSource) => db.getRepository(MiUserMemo).extend(miRepository as MiRepository<MiUserMemo>),
 	inject: [DI.db],
 };
 
 const $bubbleGameRecordsRepository: Provider = {
 	provide: DI.bubbleGameRecordsRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiBubbleGameRecord),
+	useFactory: (db: DataSource) => db.getRepository(MiBubbleGameRecord).extend(miRepository as MiRepository<MiBubbleGameRecord>),
 	inject: [DI.db],
 };
 
 const $reversiGamesRepository: Provider = {
 	provide: DI.reversiGamesRepository,
-	useFactory: (db: DataSource) => db.getRepository(MiReversiGame),
+	useFactory: (db: DataSource) => db.getRepository(MiReversiGame).extend(miRepository as MiRepository<MiReversiGame>),
 	inject: [DI.db],
 };
 
diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts
index 43d42d80dd32..2e6a41586e45 100644
--- a/packages/backend/src/models/_.ts
+++ b/packages/backend/src/models/_.ts
@@ -3,6 +3,13 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { FindOneOptions, InsertQueryBuilder, ObjectLiteral, Repository, SelectQueryBuilder, TypeORMError } from 'typeorm';
+import { DriverUtils } from 'typeorm/driver/DriverUtils.js';
+import { RelationCountLoader } from 'typeorm/query-builder/relation-count/RelationCountLoader.js';
+import { RelationIdLoader } from 'typeorm/query-builder/relation-id/RelationIdLoader.js';
+import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js';
+import { ObjectUtils } from 'typeorm/util/ObjectUtils.js';
+import { OrmUtils } from 'typeorm/util/OrmUtils.js';
 import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
 import { MiAccessToken } from '@/models/AccessToken.js';
 import { MiAd } from '@/models/Ad.js';
@@ -70,8 +77,70 @@ import { MiFlashLike } from '@/models/FlashLike.js';
 import { MiUserListFavorite } from '@/models/UserListFavorite.js';
 import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
 import { MiReversiGame } from '@/models/ReversiGame.js';
+import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
 
-import type { Repository } from 'typeorm';
+export interface MiRepository<T extends ObjectLiteral> {
+	createTableColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
+	createTableColumnNamesWithPrimaryKey(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
+	insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
+	selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
+}
+
+export const miRepository = {
+	createTableColumnNames(queryBuilder) {
+		// @ts-expect-error -- protected
+		const insertedColumns = queryBuilder.getInsertedColumns();
+		if (insertedColumns.length) {
+			return insertedColumns.map(column => column.databaseName);
+		}
+		if (!queryBuilder.expressionMap.mainAlias?.hasMetadata && !queryBuilder.expressionMap.insertColumns.length) {
+			// @ts-expect-error -- protected
+			const valueSets = queryBuilder.getValueSets();
+			if (valueSets.length === 1) {
+				return Object.keys(valueSets[0]);
+			}
+		}
+		return queryBuilder.expressionMap.insertColumns;
+	},
+	createTableColumnNamesWithPrimaryKey(queryBuilder) {
+		const columnNames = this.createTableColumnNames(queryBuilder);
+		if (!columnNames.includes('id')) {
+			columnNames.unshift('id');
+		}
+		return columnNames;
+	},
+	async insertOne(entity, findOptions?) {
+		const queryBuilder = this.createQueryBuilder().insert().values(entity);
+		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+		const mainAlias = queryBuilder.expressionMap.mainAlias!;
+		const name = mainAlias.name;
+		mainAlias.name = 't';
+		const columnNames = this.createTableColumnNamesWithPrimaryKey(queryBuilder);
+		queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
+		const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames });
+		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+		builder.expressionMap.mainAlias!.tablePath = 'cte';
+		this.selectAliasColumnNames(queryBuilder, builder);
+		if (findOptions) {
+			builder.setFindOptions(findOptions);
+		}
+		const raw = await builder.execute();
+		mainAlias.name = name;
+		const relationId = await new RelationIdLoader(builder.connection, this.queryRunner, builder.expressionMap.relationIdAttributes).load(raw);
+		const relationCount = await new RelationCountLoader(builder.connection, this.queryRunner, builder.expressionMap.relationCountAttributes).load(raw);
+		const result = new RawSqlResultsToEntityTransformer(builder.expressionMap, builder.connection.driver, relationId, relationCount, this.queryRunner).transform(raw, mainAlias);
+		return result[0];
+	},
+	selectAliasColumnNames(queryBuilder, builder) {
+		let selectOrAddSelect = (selection: string, selectionAliasName?: string) => {
+			selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName);
+			return builder.select(selection, selectionAliasName);
+		};
+		for (const columnName of this.createTableColumnNamesWithPrimaryKey(queryBuilder)) {
+			selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`);
+		}
+	},
+} satisfies MiRepository<ObjectLiteral>;
 
 export {
 	MiAbuseUserReport,
@@ -143,70 +212,70 @@ export {
 	MiReversiGame,
 };
 
-export type AbuseUserReportsRepository = Repository<MiAbuseUserReport>;
-export type AccessTokensRepository = Repository<MiAccessToken>;
-export type AdsRepository = Repository<MiAd>;
-export type AnnouncementsRepository = Repository<MiAnnouncement>;
-export type AnnouncementReadsRepository = Repository<MiAnnouncementRead>;
-export type AntennasRepository = Repository<MiAntenna>;
-export type AppsRepository = Repository<MiApp>;
-export type AvatarDecorationsRepository = Repository<MiAvatarDecoration>;
-export type AuthSessionsRepository = Repository<MiAuthSession>;
-export type BlockingsRepository = Repository<MiBlocking>;
-export type ChannelFollowingsRepository = Repository<MiChannelFollowing>;
-export type ChannelFavoritesRepository = Repository<MiChannelFavorite>;
-export type ClipsRepository = Repository<MiClip>;
-export type ClipNotesRepository = Repository<MiClipNote>;
-export type ClipFavoritesRepository = Repository<MiClipFavorite>;
-export type DriveFilesRepository = Repository<MiDriveFile>;
-export type DriveFoldersRepository = Repository<MiDriveFolder>;
-export type EmojisRepository = Repository<MiEmoji>;
-export type FollowingsRepository = Repository<MiFollowing>;
-export type FollowRequestsRepository = Repository<MiFollowRequest>;
-export type GalleryLikesRepository = Repository<MiGalleryLike>;
-export type GalleryPostsRepository = Repository<MiGalleryPost>;
-export type HashtagsRepository = Repository<MiHashtag>;
-export type InstancesRepository = Repository<MiInstance>;
-export type MetasRepository = Repository<MiMeta>;
-export type ModerationLogsRepository = Repository<MiModerationLog>;
-export type MutingsRepository = Repository<MiMuting>;
-export type RenoteMutingsRepository = Repository<MiRenoteMuting>;
-export type NotesRepository = Repository<MiNote>;
-export type NoteFavoritesRepository = Repository<MiNoteFavorite>;
-export type NoteReactionsRepository = Repository<MiNoteReaction>;
-export type NoteThreadMutingsRepository = Repository<MiNoteThreadMuting>;
-export type NoteUnreadsRepository = Repository<MiNoteUnread>;
-export type PagesRepository = Repository<MiPage>;
-export type PageLikesRepository = Repository<MiPageLike>;
-export type PasswordResetRequestsRepository = Repository<MiPasswordResetRequest>;
-export type PollsRepository = Repository<MiPoll>;
-export type PollVotesRepository = Repository<MiPollVote>;
-export type PromoNotesRepository = Repository<MiPromoNote>;
-export type PromoReadsRepository = Repository<MiPromoRead>;
-export type RegistrationTicketsRepository = Repository<MiRegistrationTicket>;
-export type RegistryItemsRepository = Repository<MiRegistryItem>;
-export type RelaysRepository = Repository<MiRelay>;
-export type SigninsRepository = Repository<MiSignin>;
-export type SwSubscriptionsRepository = Repository<MiSwSubscription>;
-export type UsedUsernamesRepository = Repository<MiUsedUsername>;
-export type UsersRepository = Repository<MiUser>;
-export type UserIpsRepository = Repository<MiUserIp>;
-export type UserKeypairsRepository = Repository<MiUserKeypair>;
-export type UserListsRepository = Repository<MiUserList>;
-export type UserListFavoritesRepository = Repository<MiUserListFavorite>;
-export type UserListMembershipsRepository = Repository<MiUserListMembership>;
-export type UserNotePiningsRepository = Repository<MiUserNotePining>;
-export type UserPendingsRepository = Repository<MiUserPending>;
-export type UserProfilesRepository = Repository<MiUserProfile>;
-export type UserPublickeysRepository = Repository<MiUserPublickey>;
-export type UserSecurityKeysRepository = Repository<MiUserSecurityKey>;
-export type WebhooksRepository = Repository<MiWebhook>;
-export type ChannelsRepository = Repository<MiChannel>;
-export type RetentionAggregationsRepository = Repository<MiRetentionAggregation>;
-export type RolesRepository = Repository<MiRole>;
-export type RoleAssignmentsRepository = Repository<MiRoleAssignment>;
-export type FlashsRepository = Repository<MiFlash>;
-export type FlashLikesRepository = Repository<MiFlashLike>;
-export type UserMemoRepository = Repository<MiUserMemo>;
-export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord>;
-export type ReversiGamesRepository = Repository<MiReversiGame>;
+export type AbuseUserReportsRepository = Repository<MiAbuseUserReport> & MiRepository<MiAbuseUserReport>;
+export type AccessTokensRepository = Repository<MiAccessToken> & MiRepository<MiAccessToken>;
+export type AdsRepository = Repository<MiAd> & MiRepository<MiAd>;
+export type AnnouncementsRepository = Repository<MiAnnouncement> & MiRepository<MiAnnouncement>;
+export type AnnouncementReadsRepository = Repository<MiAnnouncementRead> & MiRepository<MiAnnouncementRead>;
+export type AntennasRepository = Repository<MiAntenna> & MiRepository<MiAntenna>;
+export type AppsRepository = Repository<MiApp> & MiRepository<MiApp>;
+export type AvatarDecorationsRepository = Repository<MiAvatarDecoration> & MiRepository<MiAvatarDecoration>;
+export type AuthSessionsRepository = Repository<MiAuthSession> & MiRepository<MiAuthSession>;
+export type BlockingsRepository = Repository<MiBlocking> & MiRepository<MiBlocking>;
+export type ChannelFollowingsRepository = Repository<MiChannelFollowing> & MiRepository<MiChannelFollowing>;
+export type ChannelFavoritesRepository = Repository<MiChannelFavorite> & MiRepository<MiChannelFavorite>;
+export type ClipsRepository = Repository<MiClip> & MiRepository<MiClip>;
+export type ClipNotesRepository = Repository<MiClipNote> & MiRepository<MiClipNote>;
+export type ClipFavoritesRepository = Repository<MiClipFavorite> & MiRepository<MiClipFavorite>;
+export type DriveFilesRepository = Repository<MiDriveFile> & MiRepository<MiDriveFile>;
+export type DriveFoldersRepository = Repository<MiDriveFolder> & MiRepository<MiDriveFolder>;
+export type EmojisRepository = Repository<MiEmoji> & MiRepository<MiEmoji>;
+export type FollowingsRepository = Repository<MiFollowing> & MiRepository<MiFollowing>;
+export type FollowRequestsRepository = Repository<MiFollowRequest> & MiRepository<MiFollowRequest>;
+export type GalleryLikesRepository = Repository<MiGalleryLike> & MiRepository<MiGalleryLike>;
+export type GalleryPostsRepository = Repository<MiGalleryPost> & MiRepository<MiGalleryPost>;
+export type HashtagsRepository = Repository<MiHashtag> & MiRepository<MiHashtag>;
+export type InstancesRepository = Repository<MiInstance> & MiRepository<MiInstance>;
+export type MetasRepository = Repository<MiMeta> & MiRepository<MiMeta>;
+export type ModerationLogsRepository = Repository<MiModerationLog> & MiRepository<MiModerationLog>;
+export type MutingsRepository = Repository<MiMuting> & MiRepository<MiMuting>;
+export type RenoteMutingsRepository = Repository<MiRenoteMuting> & MiRepository<MiRenoteMuting>;
+export type NotesRepository = Repository<MiNote> & MiRepository<MiNote>;
+export type NoteFavoritesRepository = Repository<MiNoteFavorite> & MiRepository<MiNoteFavorite>;
+export type NoteReactionsRepository = Repository<MiNoteReaction> & MiRepository<MiNoteReaction>;
+export type NoteThreadMutingsRepository = Repository<MiNoteThreadMuting> & MiRepository<MiNoteThreadMuting>;
+export type NoteUnreadsRepository = Repository<MiNoteUnread> & MiRepository<MiNoteUnread>;
+export type PagesRepository = Repository<MiPage> & MiRepository<MiPage>;
+export type PageLikesRepository = Repository<MiPageLike> & MiRepository<MiPageLike>;
+export type PasswordResetRequestsRepository = Repository<MiPasswordResetRequest> & MiRepository<MiPasswordResetRequest>;
+export type PollsRepository = Repository<MiPoll> & MiRepository<MiPoll>;
+export type PollVotesRepository = Repository<MiPollVote> & MiRepository<MiPollVote>;
+export type PromoNotesRepository = Repository<MiPromoNote> & MiRepository<MiPromoNote>;
+export type PromoReadsRepository = Repository<MiPromoRead> & MiRepository<MiPromoRead>;
+export type RegistrationTicketsRepository = Repository<MiRegistrationTicket> & MiRepository<MiRegistrationTicket>;
+export type RegistryItemsRepository = Repository<MiRegistryItem> & MiRepository<MiRegistryItem>;
+export type RelaysRepository = Repository<MiRelay> & MiRepository<MiRelay>;
+export type SigninsRepository = Repository<MiSignin> & MiRepository<MiSignin>;
+export type SwSubscriptionsRepository = Repository<MiSwSubscription> & MiRepository<MiSwSubscription>;
+export type UsedUsernamesRepository = Repository<MiUsedUsername> & MiRepository<MiUsedUsername>;
+export type UsersRepository = Repository<MiUser> & MiRepository<MiUser>;
+export type UserIpsRepository = Repository<MiUserIp> & MiRepository<MiUserIp>;
+export type UserKeypairsRepository = Repository<MiUserKeypair> & MiRepository<MiUserKeypair>;
+export type UserListsRepository = Repository<MiUserList> & MiRepository<MiUserList>;
+export type UserListFavoritesRepository = Repository<MiUserListFavorite> & MiRepository<MiUserListFavorite>;
+export type UserListMembershipsRepository = Repository<MiUserListMembership> & MiRepository<MiUserListMembership>;
+export type UserNotePiningsRepository = Repository<MiUserNotePining> & MiRepository<MiUserNotePining>;
+export type UserPendingsRepository = Repository<MiUserPending> & MiRepository<MiUserPending>;
+export type UserProfilesRepository = Repository<MiUserProfile> & MiRepository<MiUserProfile>;
+export type UserPublickeysRepository = Repository<MiUserPublickey> & MiRepository<MiUserPublickey>;
+export type UserSecurityKeysRepository = Repository<MiUserSecurityKey> & MiRepository<MiUserSecurityKey>;
+export type WebhooksRepository = Repository<MiWebhook> & MiRepository<MiWebhook>;
+export type ChannelsRepository = Repository<MiChannel> & MiRepository<MiChannel>;
+export type RetentionAggregationsRepository = Repository<MiRetentionAggregation> & MiRepository<MiRetentionAggregation>;
+export type RolesRepository = Repository<MiRole> & MiRepository<MiRole>;
+export type RoleAssignmentsRepository = Repository<MiRoleAssignment> & MiRepository<MiRoleAssignment>;
+export type FlashsRepository = Repository<MiFlash> & MiRepository<MiFlash>;
+export type FlashLikesRepository = Repository<MiFlashLike> & MiRepository<MiFlashLike>;
+export type UserMemoRepository = Repository<MiUserMemo> & MiRepository<MiUserMemo>;
+export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord> & MiRepository<MiBubbleGameRecord>;
+export type ReversiGamesRepository = Repository<MiReversiGame> & MiRepository<MiReversiGame>;
diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
index e5b7c5ac5213..9c033b73e2b7 100644
--- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
@@ -76,7 +76,7 @@ export class ImportAntennasProcessorService {
 					this.logger.warn('Validation Failed');
 					continue;
 				}
-				const result = await this.antennasRepository.insert({
+				const result = await this.antennasRepository.insertOne({
 					id: this.idService.gen(now.getTime()),
 					lastUsedAt: now,
 					userId: job.data.user.id,
@@ -91,7 +91,7 @@ export class ImportAntennasProcessorService {
 					excludeBots: antenna.excludeBots,
 					withReplies: antenna.withReplies,
 					withFile: antenna.withFile,
-				}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
+				});
 				this.logger.succ('Antenna created: ' + result.id);
 				this.globalEventService.publishInternalEvent('antennaCreated', result);
 			}
diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts
index a5992c28c846..db9255b35d5c 100644
--- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts
@@ -79,11 +79,11 @@ export class ImportUserListsProcessorService {
 				});
 
 				if (list == null) {
-					list = await this.userListsRepository.insert({
+					list = await this.userListsRepository.insertOne({
 						id: this.idService.gen(),
 						userId: user.id,
 						name: listName,
-					}).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0]));
+					});
 				}
 
 				let target = this.utilityService.isSelfHost(host!) ? await this.usersRepository.findOneBy({
diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts
index 714e56e8c3fa..70306c31135b 100644
--- a/packages/backend/src/server/api/SigninService.ts
+++ b/packages/backend/src/server/api/SigninService.ts
@@ -29,13 +29,13 @@ export class SigninService {
 	public signin(request: FastifyRequest, reply: FastifyReply, user: MiLocalUser) {
 		setImmediate(async () => {
 			// Append signin history
-			const record = await this.signinsRepository.insert({
+			const record = await this.signinsRepository.insertOne({
 				id: this.idService.gen(),
 				userId: user.id,
 				ip: request.ip,
 				headers: request.headers as any,
 				success: true,
-			}).then(x => this.signinsRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			// Publish signin event
 			this.globalEventService.publishMainStream(user.id, 'signin', await this.signinEntityService.pack(record));
diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts
index 546de48e6bdb..632b0c62bc5d 100644
--- a/packages/backend/src/server/api/SignupApiService.ts
+++ b/packages/backend/src/server/api/SignupApiService.ts
@@ -183,13 +183,13 @@ export class SignupApiService {
 			const salt = await bcrypt.genSalt(8);
 			const hash = await bcrypt.hash(password, salt);
 
-			const pendingUser = await this.userPendingsRepository.insert({
+			const pendingUser = await this.userPendingsRepository.insertOne({
 				id: this.idService.gen(),
 				code,
 				email: emailAddress!,
 				username: username,
 				password: hash,
-			}).then(x => this.userPendingsRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			const link = `${this.config.url}/signup-complete/${code}`;
 
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
index 1e7a9fb3ecf3..955154f4fb08 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
@@ -50,7 +50,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private moderationLogService: ModerationLogService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const ad = await this.adsRepository.insert({
+			const ad = await this.adsRepository.insertOne({
 				id: this.idService.gen(),
 				expiresAt: new Date(ps.expiresAt),
 				startsAt: new Date(ps.startsAt),
@@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				ratio: ps.ratio,
 				place: ps.place,
 				memo: ps.memo,
-			}).then(r => this.adsRepository.findOneByOrFail({ id: r.identifiers[0].id }));
+			});
 
 			this.moderationLogService.log(me, 'createAd', {
 				adId: ad.id,
diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
index 0f551e1ba2c4..5ecae3161a39 100644
--- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
@@ -66,11 +66,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const ticketsPromises = [];
 
 			for (let i = 0; i < ps.count; i++) {
-				ticketsPromises.push(this.registrationTicketsRepository.insert({
+				ticketsPromises.push(this.registrationTicketsRepository.insertOne({
 					id: this.idService.gen(),
 					expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null,
 					code: generateInviteCode(),
-				}).then(x => this.registrationTicketsRepository.findOneByOrFail(x.identifiers[0])));
+				}));
 			}
 
 			const tickets = await Promise.all(ticketsPromises);
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index 6b7bacb05499..ec081985142c 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -112,7 +112,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const now = new Date();
 
-			const antenna = await this.antennasRepository.insert({
+			const antenna = await this.antennasRepository.insertOne({
 				id: this.idService.gen(now.getTime()),
 				lastUsedAt: now,
 				userId: me.id,
@@ -127,7 +127,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				excludeBots: ps.excludeBots,
 				withReplies: ps.withReplies,
 				withFile: ps.withFile,
-			}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			this.globalEventService.publishInternalEvent('antennaCreated', antenna);
 
diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts
index 492705d6f921..ba847fc4f0ee 100644
--- a/packages/backend/src/server/api/endpoints/app/create.ts
+++ b/packages/backend/src/server/api/endpoints/app/create.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1')));
 
 			// Create account
-			const app = await this.appsRepository.insert({
+			const app = await this.appsRepository.insertOne({
 				id: this.idService.gen(),
 				userId: me ? me.id : null,
 				name: ps.name,
@@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				permission,
 				callbackUrl: ps.callbackUrl,
 				secret: secret,
-			}).then(x => this.appsRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			return await this.appEntityService.pack(app, null, {
 				detail: true,
diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts
index 26dd8931385e..f8ddfdb75cbd 100644
--- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts
+++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts
@@ -78,11 +78,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const token = randomUUID();
 
 			// Create session token document
-			const doc = await this.authSessionsRepository.insert({
+			const doc = await this.authSessionsRepository.insertOne({
 				id: this.idService.gen(),
 				appId: app.id,
 				token: token,
-			}).then(x => this.authSessionsRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			return {
 				token: doc.token,
diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts
index 2866db54240c..e3a6d2d670e6 100644
--- a/packages/backend/src/server/api/endpoints/channels/create.ts
+++ b/packages/backend/src/server/api/endpoints/channels/create.ts
@@ -80,7 +80,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				}
 			}
 
-			const channel = await this.channelsRepository.insert({
+			const channel = await this.channelsRepository.insertOne({
 				id: this.idService.gen(),
 				userId: me.id,
 				name: ps.name,
@@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				isSensitive: ps.isSensitive ?? false,
 				...(ps.color !== undefined ? { color: ps.color } : {}),
 				allowRenoteToExternal: ps.allowRenoteToExternal ?? true,
-			} as MiChannel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0]));
+			} as MiChannel);
 
 			return await this.channelEntityService.pack(channel, me);
 		});
diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts
index c94070d9ff09..08d9d9cdc340 100644
--- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts
+++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts
@@ -75,12 +75,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			// Create folder
-			const folder = await this.driveFoldersRepository.insert({
+			const folder = await this.driveFoldersRepository.insertOne({
 				id: this.idService.gen(),
 				name: ps.name,
 				parentId: parent !== null ? parent.id : null,
 				userId: me.id,
-			}).then(x => this.driveFoldersRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			const folderObj = await this.driveFolderEntityService.pack(folder);
 
diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts
index 361496e17e12..64f13a577e35 100644
--- a/packages/backend/src/server/api/endpoints/flash/create.ts
+++ b/packages/backend/src/server/api/endpoints/flash/create.ts
@@ -59,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private idService: IdService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const flash = await this.flashsRepository.insert({
+			const flash = await this.flashsRepository.insertOne({
 				id: this.idService.gen(),
 				userId: me.id,
 				updatedAt: new Date(),
@@ -68,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				script: ps.script,
 				permissions: ps.permissions,
 				visibility: ps.visibility,
-			}).then(x => this.flashsRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			return await this.flashEntityService.pack(flash);
 		});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
index b07cdf1ed979..46f8998810d4 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
@@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new Error();
 			}
 
-			const post = await this.galleryPostsRepository.insert(new MiGalleryPost({
+			const post = await this.galleryPostsRepository.insertOne(new MiGalleryPost({
 				id: this.idService.gen(),
 				updatedAt: new Date(),
 				title: ps.title,
@@ -84,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				userId: me.id,
 				isSensitive: ps.isSensitive,
 				fileIds: files.map(file => file.id),
-			})).then(x => this.galleryPostsRepository.findOneByOrFail(x.identifiers[0]));
+			}));
 
 			return await this.galleryPostEntityService.pack(post, me);
 		});
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
index 535a3ea30850..c69238028804 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
@@ -89,14 +89,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.tooManyWebhooks);
 			}
 
-			const webhook = await this.webhooksRepository.insert({
+			const webhook = await this.webhooksRepository.insertOne({
 				id: this.idService.gen(),
 				userId: me.id,
 				name: ps.name,
 				url: ps.url,
 				secret: ps.secret,
 				on: ps.on,
-			}).then(x => this.webhooksRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			this.globalEventService.publishInternalEvent('webhookCreated', webhook);
 
diff --git a/packages/backend/src/server/api/endpoints/invite/create.ts b/packages/backend/src/server/api/endpoints/invite/create.ts
index 0ff125ad9cbe..a70b587da7e5 100644
--- a/packages/backend/src/server/api/endpoints/invite/create.ts
+++ b/packages/backend/src/server/api/endpoints/invite/create.ts
@@ -66,13 +66,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				}
 			}
 
-			const ticket = await this.registrationTicketsRepository.insert({
+			const ticket = await this.registrationTicketsRepository.insertOne({
 				id: this.idService.gen(),
 				createdBy: me,
 				createdById: me.id,
 				expiresAt: policies.inviteExpirationTime ? new Date(Date.now() + (policies.inviteExpirationTime * 1000 * 60)) : null,
 				code: generateInviteCode(),
-			}).then(x => this.registrationTicketsRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			return await this.inviteCodeEntityService.pack(ticket, me);
 		});
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
index a91c506afd27..f33f49075bc0 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
@@ -144,12 +144,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			// Create vote
-			const vote = await this.pollVotesRepository.insert({
+			const vote = await this.pollVotesRepository.insertOne({
 				id: this.idService.gen(createdAt.getTime()),
 				noteId: note.id,
 				userId: me.id,
 				choice: ps.choice,
-			}).then(x => this.pollVotesRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			// Increment votes count
 			const index = ps.choice + 1; // In SQL, array index is 1 based
diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts
index 3a02d359f80b..fa03b0b4575f 100644
--- a/packages/backend/src/server/api/endpoints/pages/create.ts
+++ b/packages/backend/src/server/api/endpoints/pages/create.ts
@@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				}
 			});
 
-			const page = await this.pagesRepository.insert(new MiPage({
+			const page = await this.pagesRepository.insertOne(new MiPage({
 				id: this.idService.gen(),
 				updatedAt: new Date(),
 				title: ps.title,
@@ -117,7 +117,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				alignCenter: ps.alignCenter,
 				hideTitleWhenPinned: ps.hideTitleWhenPinned,
 				font: ps.font,
-			})).then(x => this.pagesRepository.findOneByOrFail(x.identifiers[0]));
+			}));
 
 			return await this.pageEntityService.pack(page);
 		});
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
index e2db71c5c7f4..8504da0209d2 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
@@ -104,11 +104,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.tooManyUserLists);
 			}
 
-			const userList = await this.userListsRepository.insert({
+			const userList = await this.userListsRepository.insertOne({
 				id: this.idService.gen(),
 				userId: me.id,
 				name: ps.name,
-			} as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0]));
+			} as MiUserList);
 
 			const users = (await this.userListMembershipsRepository.findBy({
 				userListId: ps.listId,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts
index 952580e639fd..9378bde5cb55 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts
@@ -65,11 +65,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.tooManyUserLists);
 			}
 
-			const userList = await this.userListsRepository.insert({
+			const userList = await this.userListsRepository.insertOne({
 				id: this.idService.gen(),
 				userId: me.id,
 				name: ps.name,
-			} as MiUserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0]));
+			} as MiUserList);
 
 			return await this.userListEntityService.pack(userList);
 		});
diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
index 1750dd6206f1..48e14b68cca5 100644
--- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts
+++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
@@ -82,14 +82,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.cannotReportAdmin);
 			}
 
-			const report = await this.abuseUserReportsRepository.insert({
+			const report = await this.abuseUserReportsRepository.insertOne({
 				id: this.idService.gen(),
 				targetUserId: user.id,
 				targetUserHost: user.host,
 				reporterId: me.id,
 				reporterHost: null,
 				comment: ps.comment,
-			}).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0]));
+			});
 
 			// Publish event to moderators
 			setImmediate(async () => {
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index 35050130dc1b..74cf61a7857a 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -9,7 +9,7 @@ process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import { loadConfig } from '@/config.js';
-import { MiUser, UsersRepository } from '@/models/_.js';
+import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
 import { jobQueue } from '@/boot/common.js';
 import { api, initTestDb, signup, sleep, successfulApiCall, uploadFile } from '../utils.js';
@@ -42,7 +42,7 @@ describe('Account Move', () => {
 		dave = await signup({ username: 'dave' });
 		eve = await signup({ username: 'eve' });
 		frank = await signup({ username: 'frank' });
-		Users = connection.getRepository(MiUser);
+		Users = connection.getRepository(MiUser).extend(miRepository as MiRepository<MiUser>);
 	}, 1000 * 60 * 2);
 
 	afterAll(async () => {
diff --git a/packages/backend/test/e2e/reversi-game.ts b/packages/backend/test/e2e/reversi-game.ts
new file mode 100644
index 000000000000..788255beac12
--- /dev/null
+++ b/packages/backend/test/e2e/reversi-game.ts
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+process.env.NODE_ENV = 'test';
+
+import * as assert from 'assert';
+import { ReversiMatchResponse } from 'misskey-js/entities.js';
+import { api, signup } from '../utils.js';
+import type * as misskey from 'misskey-js';
+
+describe('ReversiGame', () => {
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+
+	beforeAll(async () => {
+		alice = await signup({ username: 'alice' });
+		bob = await signup({ username: 'bob' });
+	}, 1000 * 60 * 2);
+
+	test('matches when alice invites bob and bob accepts', async () => {
+		const response1 = await api('reversi/match', { userId: bob.id }, alice);
+		assert.strictEqual(response1.status, 204);
+		assert.strictEqual(response1.body, null);
+		const response2 = await api('reversi/match', { userId: alice.id }, bob);
+		assert.strictEqual(response2.status, 200);
+		assert.notStrictEqual(response2.body, null);
+		const body = response2.body as ReversiMatchResponse;
+		assert.strictEqual(body.user1.id, alice.id);
+		assert.strictEqual(body.user2.id, bob.id);
+	});
+});