From c80d91dc3887c54e2c58448adf56edfa62ed9242 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:04:17 -0700
Subject: [PATCH 01/56] chore: formatting

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

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 4022a6bfb0..d3b7c3b823 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -97,7 +97,8 @@
 
 	let fontSize = localStorage.getItem("fontSize");
 	if (fontSize) {
-		if (fontSize < 10) { // need to do this for now, as values before were 1, 2, 3 depending on the option
+		if (fontSize < 10) {
+			// need to do this for now, as values before were 1, 2, 3 depending on the option
 			localStorage.setItem("fontSize", null);
 			fontSize = localStorage.getItem("fontSize");
 		}

From 5a7aca3b91ef687c0472ba91d4530771b3fabbe8 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:06:32 -0700
Subject: [PATCH 02/56] fix: "24"th hour doesn't exist, it's 0

---
 packages/client/src/pages/user/home.vue | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue
index 9c4e97a4cc..83dba3628c 100644
--- a/packages/client/src/pages/user/home.vue
+++ b/packages/client/src/pages/user/home.vue
@@ -400,9 +400,11 @@ const timeForThem = $computed(() => {
 			timeZone: tz,
 			hour12: false,
 		});
-		return ` (${theirTime.split(",")[1].trim().split(":")[0]}:${theirTime
-			.split(" ")[1]
-			.slice(-5, -3)})`;
+		return ` (${theirTime
+			.split(",")[1]
+			.trim()
+			.split(":")[0]
+			.replace("24", "0")}:${theirTime.split(" ")[1].slice(-5, -3)})`;
 	}
 
 	return "";

From beb96f7cff9a147ec7b8e084974c6ec9e2201961 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:15:00 -0700
Subject: [PATCH 03/56] feat: :monocle_face: bring back misskey's moderation
 displays on profile

---
 packages/client/src/components/MkSignin.vue   |  9 ++++++-
 packages/client/src/pages/user/home.vue       | 24 +++++++++++++++----
 .../src/scripts/show-suspended-dialog.ts      | 10 --------
 3 files changed, 28 insertions(+), 15 deletions(-)
 delete mode 100644 packages/client/src/scripts/show-suspended-dialog.ts

diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 59f066f6e6..c95d89bd1d 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -160,7 +160,6 @@
 <script lang="ts" setup>
 import { defineAsyncComponent } from "vue";
 import { toUnicode } from "punycode/";
-import { showSuspendedDialog } from "../scripts/show-suspended-dialog";
 import MkButton from "@/components/MkButton.vue";
 import MkInput from "@/components/form/input.vue";
 import MkInfo from "@/components/MkInfo.vue";
@@ -367,6 +366,14 @@ function resetPassword() {
 		"closed"
 	);
 }
+
+function showSuspendedDialog() {
+	os.alert({
+		type: "error",
+		title: i18n.ts.yourAccountSuspendedTitle,
+		text: i18n.ts.yourAccountSuspendedDescription,
+	});
+}
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue
index 83dba3628c..f2a014e94b 100644
--- a/packages/client/src/pages/user/home.vue
+++ b/packages/client/src/pages/user/home.vue
@@ -7,9 +7,22 @@
 			:class="{ wide: !narrow }"
 		>
 			<div class="main">
-				<!-- TODO -->
-				<!-- <div class="punished" v-if="user.isSuspended"><i class="ph-warning ph-bold ph-lg" style="margin-right: 8px;"></i> {{ i18n.ts.userSuspended }}</div> -->
-				<!-- <div class="punished" v-if="user.isSilenced"><i class="ph-warning ph-bold ph-lg" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> -->
+				<div v-if="$i?.isModerator || $i?.isAdmin">
+					<div class="punished" v-if="user.isSilenced">
+						<i
+							class="ph-warning ph-bold ph-lg"
+							style="margin-right: 8px; color: var(--warn)"
+						></i>
+						{{ i18n.ts.silenced }}
+					</div>
+					<div class="punished" v-if="user.isSuspended">
+						<i
+							class="ph-warning ph-bold ph-lg"
+							style="margin-right: 8px; color: var(--warn)"
+						></i>
+						{{ i18n.ts.suspended }}
+					</div>
+				</div>
 
 				<div class="profile">
 					<MkMoved
@@ -447,7 +460,10 @@ onUnmounted(() => {
 	> .main {
 		> .punished {
 			font-size: 0.8em;
-			padding: 16px;
+			padding: 10px;
+			color: var(--infoWarnBg);
+			background-color: var(--infoWarnFg);
+			border-radius: 10px;
 		}
 
 		> .profile {
diff --git a/packages/client/src/scripts/show-suspended-dialog.ts b/packages/client/src/scripts/show-suspended-dialog.ts
deleted file mode 100644
index 45246021d2..0000000000
--- a/packages/client/src/scripts/show-suspended-dialog.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import * as os from "@/os";
-import { i18n } from "@/i18n";
-
-export function showSuspendedDialog() {
-	return os.alert({
-		type: "error",
-		title: i18n.ts.yourAccountSuspendedTitle,
-		text: i18n.ts.yourAccountSuspendedDescription,
-	});
-}

From 8151da6b6ae6dc74db50aa1778e5ff0423f3cffa Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:16:10 -0700
Subject: [PATCH 04/56] fix: :adhesive_bandage: YYYYMMDD with dashes

---
 packages/client/src/components/MkDialog.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue
index 31c98d8520..d0d42ac037 100644
--- a/packages/client/src/components/MkDialog.vue
+++ b/packages/client/src/components/MkDialog.vue
@@ -290,7 +290,7 @@ function formatDateToYYYYMMDD(date) {
 	const year = date.getFullYear();
 	const month = ("0" + (date.getMonth() + 1)).slice(-2);
 	const day = ("0" + date.getDate()).slice(-2);
-	return `${year}${month}${day}`;
+	return `${year}-${month}-${day}`;
 }
 
 async function openSearchFilters(ev) {

From 40841227b15baa797e92377e52fcbded02c26977 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:20:57 -0700
Subject: [PATCH 05/56] chore: :passport_control: improve gitea templates

---
 issue_template/feature.yaml | 20 --------------------
 pull_request_template.yml   |  2 ++
 2 files changed, 2 insertions(+), 20 deletions(-)

diff --git a/issue_template/feature.yaml b/issue_template/feature.yaml
index 7ebed1b06f..455125ea7c 100644
--- a/issue_template/feature.yaml
+++ b/issue_template/feature.yaml
@@ -40,26 +40,6 @@ body:
       placeholder: stop.voring.me
     validations:
       required: false
-  - type: dropdown
-    id: browsers
-    attributes:
-      label: What browser are you using?
-      multiple: false
-      options:
-        - Firefox
-        - Chrome
-        - Brave
-        - Librewolf
-        - Chromium
-        - Safari
-        - Microsoft Edge
-        - Other (Please Specify)
-  - type: textarea
-    id: logs
-    attributes:
-      label: Relevant log output
-      description: Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. This will be automatically formatted into code, so no need for backticks.
-      render: shell
   - type: checkboxes
     id: terms
     attributes:
diff --git a/pull_request_template.yml b/pull_request_template.yml
index 41d04f59fe..85160164e0 100644
--- a/pull_request_template.yml
+++ b/pull_request_template.yml
@@ -26,3 +26,5 @@ body:
           required: true
         - label: I have made sure to run `pnpm run format` before submitting this pull request
           required: true
+        - label: I used [conventional commits](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits) when making commits
+          required: false

From c0397474c8b0a5de3acd02e413f298662f76cd09 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:23:18 -0700
Subject: [PATCH 06/56] fix: :rotating_light: fix unused import

---
 packages/client/src/account.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/client/src/account.ts b/packages/client/src/account.ts
index fd9bf48e68..6d858292a5 100644
--- a/packages/client/src/account.ts
+++ b/packages/client/src/account.ts
@@ -1,6 +1,5 @@
 import { defineAsyncComponent, reactive } from "vue";
 import * as misskey from "calckey-js";
-import { showSuspendedDialog } from "./scripts/show-suspended-dialog";
 import { i18n } from "./i18n";
 import { del, get, set } from "@/scripts/idb-proxy";
 import { apiUrl } from "@/config";

From 6a6f5e57da537e8fb823d4072368c4c71ec474cc Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:25:12 -0700
Subject: [PATCH 07/56] chore: :passport_control: conventional commits in body,
 not checkbox

---
 pull_request_template.yml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/pull_request_template.yml b/pull_request_template.yml
index 85160164e0..d3009cfc84 100644
--- a/pull_request_template.yml
+++ b/pull_request_template.yml
@@ -5,7 +5,7 @@ body:
   - type: markdown
     attributes:
       value: |
-                Thanks for taking the time to make Calckey better!
+                Thanks for taking the time to make Calckey better! It's not required, but please consider using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) when making your commits. If you use VSCode, please use the [Conventional Commits extension](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits).
   - type: textarea
     id: about
     attributes:
@@ -26,5 +26,3 @@ body:
           required: true
         - label: I have made sure to run `pnpm run format` before submitting this pull request
           required: true
-        - label: I used [conventional commits](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits) when making commits
-          required: false

From de5d210b04d463791aa5c1d46bca48f68ccbb9cd Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:32:01 -0700
Subject: [PATCH 08/56] fix: :adhesive_bandage: day isn't decreased by 1

---
 packages/client/src/components/MkDialog.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue
index d0d42ac037..20f0b9a74d 100644
--- a/packages/client/src/components/MkDialog.vue
+++ b/packages/client/src/components/MkDialog.vue
@@ -289,7 +289,7 @@ function onInputKeydown(evt: KeyboardEvent) {
 function formatDateToYYYYMMDD(date) {
 	const year = date.getFullYear();
 	const month = ("0" + (date.getMonth() + 1)).slice(-2);
-	const day = ("0" + date.getDate()).slice(-2);
+	const day = ("0" + (date.getDate() + 1)).slice(-2);
 	return `${year}-${month}-${day}`;
 }
 

From cd929a01c1b8b074bc399bf8c9f1796c48b991ef Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:42:00 -0700
Subject: [PATCH 09/56] refactor: :lipstick: style punishments

---
 packages/client/src/pages/user/home.vue | 61 +++++++++++++++----------
 1 file changed, 36 insertions(+), 25 deletions(-)

diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue
index f2a014e94b..d02b6f665e 100644
--- a/packages/client/src/pages/user/home.vue
+++ b/packages/client/src/pages/user/home.vue
@@ -7,23 +7,6 @@
 			:class="{ wide: !narrow }"
 		>
 			<div class="main">
-				<div v-if="$i?.isModerator || $i?.isAdmin">
-					<div class="punished" v-if="user.isSilenced">
-						<i
-							class="ph-warning ph-bold ph-lg"
-							style="margin-right: 8px; color: var(--warn)"
-						></i>
-						{{ i18n.ts.silenced }}
-					</div>
-					<div class="punished" v-if="user.isSuspended">
-						<i
-							class="ph-warning ph-bold ph-lg"
-							style="margin-right: 8px; color: var(--warn)"
-						></i>
-						{{ i18n.ts.suspended }}
-					</div>
-				</div>
-
 				<div class="profile">
 					<MkMoved
 						v-if="user.movedToUri"
@@ -130,6 +113,25 @@
 									class="followed"
 									>{{ i18n.ts.followsYou }}</span
 								>
+								<div
+									v-if="$i?.isModerator || $i?.isAdmin"
+									class="punishments"
+								>
+									<span
+										class="punished"
+										v-if="user.isSilenced"
+									>
+										<i class="ph-warning ph-bold ph-lg"></i>
+										{{ i18n.ts.silenced }}
+									</span>
+									<span
+										class="punished"
+										v-if="user.isSuspended"
+									>
+										<i class="ph-warning ph-bold ph-lg"></i>
+										{{ i18n.ts.suspended }}
+									</span>
+								</div>
 							</div>
 							<div class="bottom">
 								<span class="username"
@@ -458,14 +460,6 @@ onUnmounted(() => {
 <style lang="scss" scoped>
 .ftskorzw {
 	> .main {
-		> .punished {
-			font-size: 0.8em;
-			padding: 10px;
-			color: var(--infoWarnBg);
-			background-color: var(--infoWarnFg);
-			border-radius: 10px;
-		}
-
 		> .profile {
 			> .main {
 				position: relative;
@@ -522,6 +516,23 @@ onUnmounted(() => {
 						border-radius: 6px;
 					}
 
+					> .punishments {
+						display: flex;
+						gap: 1rem;
+						margin-top: 0.5rem;
+
+						> .punished {
+							padding: 10px;
+							color: var(--infoWarnBg);
+							background-color: var(--infoWarnFg);
+							border-radius: 10px;
+
+							> i {
+								margin-right: 4px;
+							}
+						}
+					}
+
 					> .title {
 						position: absolute;
 						bottom: 0;

From f3aad508d6d20a5734719855e3a11a173c8402bd Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 17:48:29 -0700
Subject: [PATCH 10/56] chore: :arrow_up: up pnpm

---
 package.json   |    2 +-
 pnpm-lock.yaml | 1700 ++++++++++++++++++++----------------------------
 2 files changed, 691 insertions(+), 1011 deletions(-)

diff --git a/package.json b/package.json
index d577699108..b2c90463b8 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
 		"type": "git",
 		"url": "https://codeberg.org/calckey/calckey.git"
 	},
-	"packageManager": "pnpm@8.6.1",
+	"packageManager": "pnpm@8.6.2",
 	"private": true,
 	"scripts": {
 		"rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 463f4247a6..e98ca7feeb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.1'
+lockfileVersion: '6.0'
 
 settings:
   autoInstallPeers: true
@@ -173,7 +173,7 @@ importers:
         version: 0.4.0
       chokidar:
         specifier: ^3.3.1
-        version: 3.5.3
+        version: 3.3.1
       cli-highlight:
         specifier: 2.1.11
         version: 2.1.11
@@ -260,7 +260,7 @@ importers:
         version: 2.1.0
       koa-views:
         specifier: 7.0.2
-        version: 7.0.2(@types/koa@2.13.5)(ejs@3.1.8)(pug@3.0.2)
+        version: 7.0.2(@types/koa@2.13.5)(ejs@3.1.9)(pug@3.0.2)
       meilisearch:
         specifier: 0.33.0
         version: 0.33.0
@@ -415,7 +415,7 @@ importers:
     devDependencies:
       '@swc/cli':
         specifier: ^0.1.62
-        version: 0.1.62(@swc/core@1.3.62)(chokidar@3.5.3)
+        version: 0.1.62(@swc/core@1.3.62)(chokidar@3.3.1)
       '@swc/core':
         specifier: ^1.3.62
         version: 1.3.62
@@ -625,7 +625,7 @@ importers:
         version: 4.4.0
       semver:
         specifier: ^7.3.8
-        version: 7.3.8
+        version: 7.5.1
     devDependencies:
       '@microsoft/api-extractor':
         specifier: ^7.19.3
@@ -659,7 +659,7 @@ importers:
         version: 9.0.8
       ts-jest:
         specifier: ^27.1.2
-        version: 27.1.2(@babel/core@7.21.4)(@types/jest@27.4.0)(jest@27.4.5)(typescript@4.5.4)
+        version: 27.1.2(@babel/core@7.22.5)(@types/jest@27.4.0)(jest@27.4.5)(typescript@4.5.4)
       ts-node:
         specifier: 10.4.0
         version: 10.4.0(@types/node@17.0.5)(typescript@4.5.4)
@@ -917,7 +917,7 @@ importers:
     devDependencies:
       '@swc/cli':
         specifier: ^0.1.62
-        version: 0.1.62(@swc/core@1.3.62)(chokidar@3.5.3)
+        version: 0.1.62(@swc/core@1.3.62)(chokidar@3.3.1)
       '@swc/core':
         specifier: ^1.3.62
         version: 1.3.62
@@ -946,67 +946,33 @@ packages:
     resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
     engines: {node: '>=6.0.0'}
     dependencies:
-      '@jridgewell/gen-mapping': 0.3.2
+      '@jridgewell/gen-mapping': 0.3.3
       '@jridgewell/trace-mapping': 0.3.18
 
-  /@babel/code-frame@7.18.6:
-    resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
+  /@babel/code-frame@7.22.5:
+    resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/highlight': 7.18.6
+      '@babel/highlight': 7.22.5
 
-  /@babel/code-frame@7.21.4:
-    resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/highlight': 7.18.6
-
-  /@babel/compat-data@7.21.4:
-    resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/compat-data@7.22.3:
-    resolution: {integrity: sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==}
+  /@babel/compat-data@7.22.5:
+    resolution: {integrity: sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==}
     engines: {node: '>=6.9.0'}
 
-  /@babel/core@7.21.4:
-    resolution: {integrity: sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==}
+  /@babel/core@7.22.5:
+    resolution: {integrity: sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==}
     engines: {node: '>=6.9.0'}
     dependencies:
       '@ampproject/remapping': 2.2.1
-      '@babel/code-frame': 7.21.4
-      '@babel/generator': 7.21.4
-      '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4)
-      '@babel/helper-module-transforms': 7.21.2
-      '@babel/helpers': 7.21.0
-      '@babel/parser': 7.22.4
-      '@babel/template': 7.20.7
-      '@babel/traverse': 7.21.4
-      '@babel/types': 7.22.4
-      convert-source-map: 1.9.0
-      debug: 4.3.4(supports-color@8.1.1)
-      gensync: 1.0.0-beta.2
-      json5: 2.2.3
-      semver: 6.3.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/core@7.22.1:
-    resolution: {integrity: sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@ampproject/remapping': 2.2.1
-      '@babel/code-frame': 7.21.4
-      '@babel/generator': 7.22.3
-      '@babel/helper-compilation-targets': 7.22.1(@babel/core@7.22.1)
-      '@babel/helper-module-transforms': 7.22.1
-      '@babel/helpers': 7.22.3
-      '@babel/parser': 7.22.4
-      '@babel/template': 7.21.9
-      '@babel/traverse': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/code-frame': 7.22.5
+      '@babel/generator': 7.22.5
+      '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.22.5)
+      '@babel/helper-module-transforms': 7.22.5
+      '@babel/helpers': 7.22.5
+      '@babel/parser': 7.22.5
+      '@babel/template': 7.22.5
+      '@babel/traverse': 7.22.5
+      '@babel/types': 7.22.5
       convert-source-map: 1.9.0
       debug: 4.3.4(supports-color@8.1.1)
       gensync: 1.0.0-beta.2
@@ -1015,344 +981,268 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
-  /@babel/generator@7.20.7:
-    resolution: {integrity: sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==}
+  /@babel/generator@7.22.5:
+    resolution: {integrity: sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.4
-      '@jridgewell/gen-mapping': 0.3.2
-      jsesc: 2.5.2
-
-  /@babel/generator@7.21.4:
-    resolution: {integrity: sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.22.4
-      '@jridgewell/gen-mapping': 0.3.2
-      '@jridgewell/trace-mapping': 0.3.18
-      jsesc: 2.5.2
-    dev: true
-
-  /@babel/generator@7.22.3:
-    resolution: {integrity: sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
       '@jridgewell/gen-mapping': 0.3.3
       '@jridgewell/trace-mapping': 0.3.18
       jsesc: 2.5.2
 
-  /@babel/helper-compilation-targets@7.21.4(@babel/core@7.21.4):
-    resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==}
+  /@babel/helper-compilation-targets@7.22.5(@babel/core@7.22.5):
+    resolution: {integrity: sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/compat-data': 7.21.4
-      '@babel/core': 7.21.4
-      '@babel/helper-validator-option': 7.21.0
-      browserslist: 4.21.7
-      lru-cache: 5.1.1
-      semver: 6.3.0
-    dev: true
-
-  /@babel/helper-compilation-targets@7.22.1(@babel/core@7.22.1):
-    resolution: {integrity: sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/compat-data': 7.22.3
-      '@babel/core': 7.22.1
-      '@babel/helper-validator-option': 7.21.0
-      browserslist: 4.21.7
+      '@babel/compat-data': 7.22.5
+      '@babel/core': 7.22.5
+      '@babel/helper-validator-option': 7.22.5
+      browserslist: 4.21.8
       lru-cache: 5.1.1
       semver: 6.3.0
 
-  /@babel/helper-environment-visitor@7.18.9:
-    resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
+  /@babel/helper-environment-visitor@7.22.5:
+    resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==}
     engines: {node: '>=6.9.0'}
 
-  /@babel/helper-environment-visitor@7.22.1:
-    resolution: {integrity: sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==}
-    engines: {node: '>=6.9.0'}
-
-  /@babel/helper-function-name@7.19.0:
-    resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==}
+  /@babel/helper-function-name@7.22.5:
+    resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/template': 7.20.7
-      '@babel/types': 7.22.4
+      '@babel/template': 7.22.5
+      '@babel/types': 7.22.5
 
-  /@babel/helper-function-name@7.21.0:
-    resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
+  /@babel/helper-hoist-variables@7.22.5:
+    resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/template': 7.20.7
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
 
-  /@babel/helper-hoist-variables@7.18.6:
-    resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
+  /@babel/helper-module-imports@7.22.5:
+    resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
 
-  /@babel/helper-module-imports@7.21.4:
-    resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==}
+  /@babel/helper-module-transforms@7.22.5:
+    resolution: {integrity: sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.4
-
-  /@babel/helper-module-transforms@7.21.2:
-    resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-environment-visitor': 7.18.9
-      '@babel/helper-module-imports': 7.21.4
-      '@babel/helper-simple-access': 7.21.5
-      '@babel/helper-split-export-declaration': 7.18.6
-      '@babel/helper-validator-identifier': 7.19.1
-      '@babel/template': 7.20.7
-      '@babel/traverse': 7.21.4
-      '@babel/types': 7.22.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/helper-module-transforms@7.22.1:
-    resolution: {integrity: sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-environment-visitor': 7.22.1
-      '@babel/helper-module-imports': 7.21.4
-      '@babel/helper-simple-access': 7.21.5
-      '@babel/helper-split-export-declaration': 7.18.6
-      '@babel/helper-validator-identifier': 7.19.1
-      '@babel/template': 7.21.9
-      '@babel/traverse': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/helper-environment-visitor': 7.22.5
+      '@babel/helper-module-imports': 7.22.5
+      '@babel/helper-simple-access': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.5
+      '@babel/helper-validator-identifier': 7.22.5
+      '@babel/template': 7.22.5
+      '@babel/traverse': 7.22.5
+      '@babel/types': 7.22.5
     transitivePeerDependencies:
       - supports-color
 
-  /@babel/helper-plugin-utils@7.20.2:
-    resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==}
+  /@babel/helper-plugin-utils@7.22.5:
+    resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
     engines: {node: '>=6.9.0'}
-    dev: true
 
-  /@babel/helper-plugin-utils@7.21.5:
-    resolution: {integrity: sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==}
-    engines: {node: '>=6.9.0'}
-    dev: false
-
-  /@babel/helper-simple-access@7.21.5:
-    resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==}
+  /@babel/helper-simple-access@7.22.5:
+    resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
 
-  /@babel/helper-split-export-declaration@7.18.6:
-    resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
+  /@babel/helper-split-export-declaration@7.22.5:
+    resolution: {integrity: sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
 
-  /@babel/helper-string-parser@7.21.5:
-    resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==}
+  /@babel/helper-string-parser@7.22.5:
+    resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
     engines: {node: '>=6.9.0'}
 
-  /@babel/helper-validator-identifier@7.19.1:
-    resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
+  /@babel/helper-validator-identifier@7.22.5:
+    resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
     engines: {node: '>=6.9.0'}
 
-  /@babel/helper-validator-option@7.21.0:
-    resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==}
+  /@babel/helper-validator-option@7.22.5:
+    resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==}
     engines: {node: '>=6.9.0'}
 
-  /@babel/helpers@7.21.0:
-    resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==}
+  /@babel/helpers@7.22.5:
+    resolution: {integrity: sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/template': 7.20.7
-      '@babel/traverse': 7.21.4
-      '@babel/types': 7.22.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/helpers@7.22.3:
-    resolution: {integrity: sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/template': 7.21.9
-      '@babel/traverse': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/template': 7.22.5
+      '@babel/traverse': 7.22.5
+      '@babel/types': 7.22.5
     transitivePeerDependencies:
       - supports-color
 
-  /@babel/highlight@7.18.6:
-    resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
+  /@babel/highlight@7.22.5:
+    resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/helper-validator-identifier': 7.19.1
+      '@babel/helper-validator-identifier': 7.22.5
       chalk: 2.4.2
       js-tokens: 4.0.0
 
-  /@babel/parser@7.22.4:
-    resolution: {integrity: sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==}
+  /@babel/parser@7.22.5:
+    resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==}
     engines: {node: '>=6.0.0'}
     hasBin: true
     dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
 
-  /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.22.1):
+  /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.22.5):
     resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.1
-      '@babel/helper-plugin-utils': 7.21.5
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.1)
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.5)
     dev: false
 
-  /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.4):
+  /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.5):
     resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.4):
+  /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.22.5):
     resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.4):
+  /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.5):
     resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.22.1):
+  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.22.5):
     resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.1
-      '@babel/helper-plugin-utils': 7.21.5
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: false
 
-  /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.4):
+  /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.22.5):
     resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.4):
+  /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.5):
     resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.4):
+  /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.5):
     resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.4):
+  /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.5):
     resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.4):
+  /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.5):
     resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.4):
+  /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.5):
     resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.4):
+  /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.5):
     resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.4):
+  /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.5):
     resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.4):
+  /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.22.5):
     resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.21.4):
-    resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==}
+  /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.22.5):
+    resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/core': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-modules-commonjs@7.21.5(@babel/core@7.22.1):
-    resolution: {integrity: sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==}
+  /@babel/plugin-transform-modules-commonjs@7.22.5(@babel/core@7.22.5):
+    resolution: {integrity: sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.1
-      '@babel/helper-module-transforms': 7.22.1
-      '@babel/helper-plugin-utils': 7.21.5
-      '@babel/helper-simple-access': 7.21.5
+      '@babel/core': 7.22.5
+      '@babel/helper-module-transforms': 7.22.5
+      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/helper-simple-access': 7.22.5
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -1364,86 +1254,43 @@ packages:
       regenerator-runtime: 0.13.11
     dev: true
 
-  /@babel/runtime@7.22.3:
-    resolution: {integrity: sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==}
+  /@babel/runtime@7.22.5:
+    resolution: {integrity: sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==}
     engines: {node: '>=6.9.0'}
     dependencies:
       regenerator-runtime: 0.13.11
 
-  /@babel/template@7.20.7:
-    resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
+  /@babel/template@7.22.5:
+    resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/code-frame': 7.18.6
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/code-frame': 7.22.5
+      '@babel/parser': 7.22.5
+      '@babel/types': 7.22.5
 
-  /@babel/template@7.21.9:
-    resolution: {integrity: sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==}
+  /@babel/traverse@7.22.5:
+    resolution: {integrity: sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/code-frame': 7.21.4
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
-
-  /@babel/traverse@7.20.12:
-    resolution: {integrity: sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/code-frame': 7.18.6
-      '@babel/generator': 7.20.7
-      '@babel/helper-environment-visitor': 7.18.9
-      '@babel/helper-function-name': 7.19.0
-      '@babel/helper-hoist-variables': 7.18.6
-      '@babel/helper-split-export-declaration': 7.18.6
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/code-frame': 7.22.5
+      '@babel/generator': 7.22.5
+      '@babel/helper-environment-visitor': 7.22.5
+      '@babel/helper-function-name': 7.22.5
+      '@babel/helper-hoist-variables': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.5
+      '@babel/parser': 7.22.5
+      '@babel/types': 7.22.5
       debug: 4.3.4(supports-color@8.1.1)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
 
-  /@babel/traverse@7.21.4:
-    resolution: {integrity: sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==}
+  /@babel/types@7.22.5:
+    resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/code-frame': 7.21.4
-      '@babel/generator': 7.21.4
-      '@babel/helper-environment-visitor': 7.18.9
-      '@babel/helper-function-name': 7.21.0
-      '@babel/helper-hoist-variables': 7.18.6
-      '@babel/helper-split-export-declaration': 7.18.6
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
-      debug: 4.3.4(supports-color@8.1.1)
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/traverse@7.22.4:
-    resolution: {integrity: sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/code-frame': 7.21.4
-      '@babel/generator': 7.22.3
-      '@babel/helper-environment-visitor': 7.22.1
-      '@babel/helper-function-name': 7.21.0
-      '@babel/helper-hoist-variables': 7.18.6
-      '@babel/helper-split-export-declaration': 7.18.6
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
-      debug: 4.3.4(supports-color@8.1.1)
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-
-  /@babel/types@7.22.4:
-    resolution: {integrity: sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-string-parser': 7.21.5
-      '@babel/helper-validator-identifier': 7.19.1
+      '@babel/helper-string-parser': 7.22.5
+      '@babel/helper-validator-identifier': 7.22.5
       to-fast-properties: 2.0.0
 
   /@bcoe/v8-coverage@0.2.3:
@@ -1464,12 +1311,12 @@ packages:
     dependencies:
       '@bull-board/api': 5.2.0(@bull-board/ui@5.2.0)
       '@bull-board/ui': 5.2.0
-      ejs: 3.1.8
+      ejs: 3.1.9
       koa: 2.13.4
       koa-mount: 4.0.0
       koa-router: 10.1.1
       koa-static: 5.0.0
-      koa-views: 7.0.2(@types/koa@2.13.5)(ejs@3.1.8)(pug@3.0.2)
+      koa-views: 7.0.2(@types/koa@2.13.5)(ejs@3.1.9)(pug@3.0.2)
     transitivePeerDependencies:
       - '@types/koa'
       - arc-templates
@@ -1540,7 +1387,7 @@ packages:
       '@types/oauth': 0.9.1
       '@types/ws': 8.5.4
       axios: 1.2.2
-      dayjs: 1.11.7
+      dayjs: 1.11.8
       form-data: 4.0.0
       https-proxy-agent: 5.0.1
       oauth: 0.10.0
@@ -1956,8 +1803,8 @@ packages:
     dependencies:
       ajv: 6.12.6
       debug: 4.3.4(supports-color@8.1.1)
-      espree: 9.4.1
-      globals: 13.19.0
+      espree: 9.5.2
+      globals: 13.20.0
       ignore: 5.2.4
       import-fresh: 3.3.0
       js-yaml: 4.1.0
@@ -1987,10 +1834,6 @@ packages:
     resolution: {integrity: sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
-  /@gar/promisify@1.1.3:
-    resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
-    dev: false
-
   /@hapi/hoek@9.3.0:
     resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
     dev: true
@@ -2032,6 +1875,18 @@ packages:
   /@ioredis/commands@1.2.0:
     resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
 
+  /@isaacs/cliui@8.0.2:
+    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+    engines: {node: '>=12'}
+    dependencies:
+      string-width: 5.1.2
+      string-width-cjs: /string-width@4.2.3
+      strip-ansi: 7.1.0
+      strip-ansi-cjs: /strip-ansi@6.0.1
+      wrap-ansi: 8.1.0
+      wrap-ansi-cjs: /wrap-ansi@7.0.0
+    dev: false
+
   /@istanbuljs/load-nyc-config@1.1.0:
     resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
     engines: {node: '>=8'}
@@ -2053,7 +1908,7 @@ packages:
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       chalk: 4.1.2
       jest-message-util: 27.5.1
       jest-util: 27.5.1
@@ -2074,7 +1929,7 @@ packages:
       '@jest/test-result': 27.5.1
       '@jest/transform': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.8.1
@@ -2111,7 +1966,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       jest-mock: 27.5.1
     dev: true
 
@@ -2121,7 +1976,7 @@ packages:
     dependencies:
       '@jest/types': 27.5.1
       '@sinonjs/fake-timers': 8.1.0
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       jest-message-util: 27.5.1
       jest-mock: 27.5.1
       jest-util: 27.5.1
@@ -2150,7 +2005,7 @@ packages:
       '@jest/test-result': 27.5.1
       '@jest/transform': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -2209,7 +2064,7 @@ packages:
     resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
-      '@babel/core': 7.21.4
+      '@babel/core': 7.22.5
       '@jest/types': 27.5.1
       babel-plugin-istanbul: 6.1.1
       chalk: 4.1.2
@@ -2234,19 +2089,11 @@ packages:
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       '@types/yargs': 16.0.5
       chalk: 4.1.2
     dev: true
 
-  /@jridgewell/gen-mapping@0.3.2:
-    resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/set-array': 1.1.2
-      '@jridgewell/sourcemap-codec': 1.4.15
-      '@jridgewell/trace-mapping': 0.3.17
-
   /@jridgewell/gen-mapping@0.3.3:
     resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
     engines: {node: '>=6.0.0'}
@@ -2259,17 +2106,14 @@ packages:
     resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
     engines: {node: '>=6.0.0'}
 
+  /@jridgewell/resolve-uri@3.1.1:
+    resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
+    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.2:
-    resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
-    dependencies:
-      '@jridgewell/gen-mapping': 0.3.2
-      '@jridgewell/trace-mapping': 0.3.17
-    dev: true
-
   /@jridgewell/source-map@0.3.3:
     resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==}
     dependencies:
@@ -2283,12 +2127,6 @@ packages:
   /@jridgewell/sourcemap-codec@1.4.15:
     resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
 
-  /@jridgewell/trace-mapping@0.3.17:
-    resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
-    dependencies:
-      '@jridgewell/resolve-uri': 3.1.0
-      '@jridgewell/sourcemap-codec': 1.4.14
-
   /@jridgewell/trace-mapping@0.3.18:
     resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
     dependencies:
@@ -2298,7 +2136,7 @@ packages:
   /@jridgewell/trace-mapping@0.3.9:
     resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
     dependencies:
-      '@jridgewell/resolve-uri': 3.1.0
+      '@jridgewell/resolve-uri': 3.1.1
       '@jridgewell/sourcemap-codec': 1.4.15
 
   /@koa/cors@3.4.3:
@@ -2349,7 +2187,7 @@ packages:
       npmlog: 5.0.1
       rimraf: 3.0.2
       semver: 7.5.1
-      tar: 6.1.13
+      tar: 6.1.15
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -2367,7 +2205,7 @@ packages:
       npmlog: 5.0.1
       rimraf: 3.0.2
       semver: 7.5.1
-      tar: 6.1.13
+      tar: 6.1.15
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -2427,48 +2265,48 @@ packages:
       os-filter-obj: 2.0.0
     dev: true
 
-  /@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0:
-    resolution: {integrity: sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==}
+  /@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2:
+    resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     dev: false
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0:
-    resolution: {integrity: sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==}
+  /@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2:
+    resolution: {integrity: sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     dev: false
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0:
-    resolution: {integrity: sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==}
+  /@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
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0:
-    resolution: {integrity: sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==}
+  /@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2:
+    resolution: {integrity: sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     dev: false
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0:
-    resolution: {integrity: sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==}
+  /@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2:
+    resolution: {integrity: sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     dev: false
     optional: true
 
-  /@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0:
-    resolution: {integrity: sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==}
+  /@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2:
+    resolution: {integrity: sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
@@ -2498,23 +2336,13 @@ packages:
       '@nodelib/fs.scandir': 2.1.5
       fastq: 1.15.0
 
-  /@npmcli/fs@2.1.2:
-    resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.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}
     dependencies:
-      '@gar/promisify': 1.1.3
       semver: 7.5.1
     dev: false
 
-  /@npmcli/move-file@2.0.1:
-    resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
-    deprecated: This functionality has been moved to @npmcli/fs
-    dependencies:
-      mkdirp: 1.0.4
-      rimraf: 3.0.2
-    dev: false
-
   /@nsfw-filter/gif-frames@1.0.2:
     resolution: {integrity: sha512-XZrbJWEN8YfVla5i+PD4Wj51rRlJ8OgnXiPjjOt/OsrbsCR9GZRD4jr953oNWcwiRaoIcOCFWQNMQukO7Yb1dA==}
     dependencies:
@@ -2551,6 +2379,13 @@ packages:
     resolution: {integrity: sha512-9oYmohi2fo87w3DbtfjBoJxrklVojgXq2wuGcqxKccj2TxMq7UIusKDrGUZYOQHM9pUKkh+kvfmutETMGDR6gg==}
     dev: true
 
+  /@pkgjs/parseargs@0.11.0:
+    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+    engines: {node: '>=14'}
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@redis/bloom@1.2.0(@redis/client@1.5.8):
     resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
     peerDependencies:
@@ -2614,12 +2449,12 @@ packages:
     engines: {node: '>=12.0.0'}
     dependencies:
       '@redocly/ajv': 8.11.0
-      '@types/node': 14.18.36
+      '@types/node': 14.18.51
       colorette: 1.4.0
       js-levenshtein: 1.1.6
       js-yaml: 4.1.0
       lodash.isequal: 4.5.0
-      minimatch: 5.1.2
+      minimatch: 5.1.6
       node-fetch: 2.6.11
       pluralize: 8.0.0
       yaml-ast-parser: 0.0.43
@@ -2741,7 +2576,7 @@ packages:
       '@types/argparse': 1.0.38
       argparse: 1.0.10
       colors: 1.2.5
-      string-argv: 0.3.1
+      string-argv: 0.3.2
     dev: true
 
   /@sideway/address@4.1.4:
@@ -2762,8 +2597,8 @@ packages:
     resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
     engines: {node: '>=10'}
 
-  /@sindresorhus/is@5.3.0:
-    resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
+  /@sindresorhus/is@5.4.1:
+    resolution: {integrity: sha512-axlrvsHlHlFmKKMEg4VyvMzFr93JWJj4eIfXY1STVuO2fsImCa7ncaiG5gC8HKOX590AW5RtRsC41/B+OfrSqw==}
     engines: {node: '>=14.16'}
     dev: false
 
@@ -2788,7 +2623,7 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@swc/cli@0.1.62(@swc/core@1.3.62)(chokidar@3.5.3):
+  /@swc/cli@0.1.62(@swc/core@1.3.62)(chokidar@3.3.1):
     resolution: {integrity: sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==}
     engines: {node: '>= 12.13'}
     hasBin: true
@@ -2801,7 +2636,7 @@ packages:
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
       '@swc/core': 1.3.62
-      chokidar: 3.5.3
+      chokidar: 3.3.1
       commander: 7.2.0
       fast-glob: 3.2.12
       semver: 7.5.1
@@ -2814,7 +2649,6 @@ packages:
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [android]
-    requiresBuild: true
     dependencies:
       '@swc/wasm': 1.2.130
 
@@ -2921,7 +2755,6 @@ packages:
 
   /@swc/wasm@1.2.130:
     resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
-    requiresBuild: true
 
   /@syuilo/aiscript@0.11.1:
     resolution: {integrity: sha512-chwOIA3yLUKvOB0G611hjLArKTeOWNmTm3lHERSaDW1d+dS6do56naX6Lkwy2UpnwWC0qzeNSgg35elk6t2gZg==}
@@ -3051,7 +2884,7 @@ packages:
       seedrandom: ^3.0.5
     dependencies:
       '@tensorflow/tfjs-core': 3.21.0
-      '@types/node-fetch': 2.6.2
+      '@types/node-fetch': 2.6.4
       node-fetch: 2.6.11
       seedrandom: 3.0.5
       string_decoder: 1.3.0
@@ -3066,7 +2899,7 @@ packages:
       seedrandom: ^3.0.5
     dependencies:
       '@tensorflow/tfjs-core': 4.2.0
-      '@types/node-fetch': 2.6.2
+      '@types/node-fetch': 2.6.4
       node-fetch: 2.6.11
       seedrandom: 3.0.5
       string_decoder: 1.3.0
@@ -3122,7 +2955,7 @@ packages:
       '@tensorflow/tfjs-layers': 3.21.0(@tensorflow/tfjs-core@3.21.0)
       argparse: 1.0.10
       chalk: 4.1.2
-      core-js: 3.27.1
+      core-js: 3.31.0
       regenerator-runtime: 0.13.11
       yargs: 16.2.0
     transitivePeerDependencies:
@@ -3142,7 +2975,7 @@ packages:
       '@tensorflow/tfjs-layers': 4.2.0(@tensorflow/tfjs-core@4.2.0)
       argparse: 1.0.10
       chalk: 4.1.2
-      core-js: 3.27.1
+      core-js: 3.31.0
       regenerator-runtime: 0.13.11
       yargs: 16.2.0
     transitivePeerDependencies:
@@ -3172,8 +3005,8 @@ packages:
   /@tsconfig/node14@1.0.3:
     resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
 
-  /@tsconfig/node16@1.0.3:
-    resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==}
+  /@tsconfig/node16@1.0.4:
+    resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
 
   /@tsd/typescript@4.5.5:
     resolution: {integrity: sha512-TxQ9QiUT94ZjKu++ta/iwTMVHsix4ApohnaHPTSd58WQuTjPIELP0tUYcW7lT6psz7yZiU4eRw+X4v/XV830Sw==}
@@ -3195,33 +3028,33 @@ packages:
     resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
     dev: true
 
-  /@types/babel__core@7.20.0:
-    resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
+  /@types/babel__core@7.20.1:
+    resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==}
     dependencies:
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/parser': 7.22.5
+      '@babel/types': 7.22.5
       '@types/babel__generator': 7.6.4
       '@types/babel__template': 7.4.1
-      '@types/babel__traverse': 7.18.3
+      '@types/babel__traverse': 7.20.1
     dev: true
 
   /@types/babel__generator@7.6.4:
     resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
     dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
     dev: true
 
   /@types/babel__template@7.4.1:
     resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
     dependencies:
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/parser': 7.22.5
+      '@babel/types': 7.22.5
     dev: true
 
-  /@types/babel__traverse@7.18.3:
-    resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==}
+  /@types/babel__traverse@7.20.1:
+    resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==}
     dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
     dev: true
 
   /@types/bcryptjs@2.4.2:
@@ -3277,12 +3110,12 @@ packages:
     resolution: {integrity: sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/express': 4.17.15
+      '@types/express': 4.17.17
       '@types/keygrip': 1.0.2
       '@types/node': 18.11.18
 
-  /@types/disposable-email-domains@1.0.2:
-    resolution: {integrity: sha512-SDKwyYTjk3y5aZBxxc38yRecpJPjsqn57STz1bNxYYlv4k11bBe7QB8w4llXDTmQXKT1mFvgGmJv+8Zdu3YmJw==}
+  /@types/disposable-email-domains@1.0.4:
+    resolution: {integrity: sha512-AmKPD8vBZzvey/jeg+YAIH/xJE3D6edOXz+YUooSCcHesGzFyzke83kj1j4d0LUR9nkSHIRklUVdcAMleuWLpg==}
     dev: false
 
   /@types/escape-regexp@0.0.1:
@@ -3292,7 +3125,7 @@ packages:
   /@types/eslint-scope@3.7.4:
     resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==}
     dependencies:
-      '@types/eslint': 8.40.0
+      '@types/eslint': 8.40.2
       '@types/estree': 1.0.1
     dev: true
 
@@ -3300,11 +3133,11 @@ packages:
     resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==}
     dependencies:
       '@types/estree': 1.0.1
-      '@types/json-schema': 7.0.11
+      '@types/json-schema': 7.0.12
     dev: true
 
-  /@types/eslint@8.40.0:
-    resolution: {integrity: sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g==}
+  /@types/eslint@8.40.2:
+    resolution: {integrity: sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==}
     dependencies:
       '@types/estree': 1.0.1
       '@types/json-schema': 7.0.12
@@ -3322,20 +3155,21 @@ packages:
     resolution: {integrity: sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==}
     dev: true
 
-  /@types/express-serve-static-core@4.17.32:
-    resolution: {integrity: sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==}
+  /@types/express-serve-static-core@4.17.35:
+    resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==}
     dependencies:
       '@types/node': 18.11.18
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
+      '@types/send': 0.17.1
 
-  /@types/express@4.17.15:
-    resolution: {integrity: sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==}
+  /@types/express@4.17.17:
+    resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==}
     dependencies:
       '@types/body-parser': 1.19.2
-      '@types/express-serve-static-core': 4.17.32
+      '@types/express-serve-static-core': 4.17.35
       '@types/qs': 6.9.7
-      '@types/serve-static': 1.15.0
+      '@types/serve-static': 1.15.1
 
   /@types/fluent-ffmpeg@2.1.20:
     resolution: {integrity: sha512-B+OvhCdJ3LgEq2PhvWNOiB/EfwnXLElfMCgc4Z1K5zXgSfo9I6uGKwR/lqmNPFQuebNnes7re3gqkV77SyypLg==}
@@ -3343,23 +3177,16 @@ packages:
       '@types/node': 18.11.18
     dev: true
 
-  /@types/formidable@2.0.5:
-    resolution: {integrity: sha512-uvMcdn/KK3maPOaVUAc3HEYbCEhjaGFwww4EsX6IJfWIJ1tzHtDHczuImH3GKdusPnAAmzB07St90uabZeCKPA==}
+  /@types/formidable@2.0.6:
+    resolution: {integrity: sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==}
     dependencies:
       '@types/node': 18.11.18
     dev: false
 
-  /@types/glob-stream@6.1.1:
-    resolution: {integrity: sha512-AGOUTsTdbPkRS0qDeyeS+6KypmfVpbT5j23SN8UPG63qjKXNKjXn6V9wZUr8Fin0m9l8oGYaPK8b2WUMF8xI1A==}
-    dependencies:
-      '@types/glob': 8.1.0
-      '@types/node': 18.11.18
-    dev: true
-
   /@types/glob-stream@8.0.0:
     resolution: {integrity: sha512-fxTWwdQmX9LWSHD7ZLlv3BHR992mKcVcDnT/2v+l/QZZo7TfDdyasqlSYVzOnMGWhRbrWeWkbj/mgezFjKynhw==}
     dependencies:
-      '@types/node': 20.2.5
+      '@types/node': 18.11.18
       '@types/picomatch': 2.3.0
       '@types/streamx': 2.9.1
     dev: true
@@ -3368,13 +3195,13 @@ packages:
     resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.2.5
+      '@types/node': 18.11.18
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
     dev: true
 
   /@types/gulp-rename@2.0.1:
@@ -3387,7 +3214,7 @@ packages:
   /@types/gulp-rename@2.0.2:
     resolution: {integrity: sha512-CQsXqTVtAXqrPd4IbrrlJEEzRkUR3RXsyZbrVoOVqjlchDDmnyRDatAUisjpQjjCg/wjJrSiNg8T1uAbJ/7Qqg==}
     dependencies:
-      '@types/node': 20.2.5
+      '@types/node': 18.11.18
       '@types/vinyl': 2.0.7
     dev: true
 
@@ -3395,8 +3222,8 @@ packages:
     resolution: {integrity: sha512-spgZHJFqiEJGwqGlf7T/k4nkBpBcLgP7T0EfN6G2vvnhUfvd4uO1h8RwpXOE8x/54DVYUs1XCAtBHkX/R3axAQ==}
     dependencies:
       '@types/undertaker': 1.2.8
-      '@types/vinyl-fs': 2.4.12
-      chokidar: 3.5.3
+      '@types/vinyl-fs': 3.0.2
+      chokidar: 3.3.1
     dev: true
 
   /@types/gulp@4.0.11:
@@ -3404,7 +3231,7 @@ packages:
     dependencies:
       '@types/undertaker': 1.2.8
       '@types/vinyl-fs': 3.0.2
-      chokidar: 3.5.3
+      chokidar: 3.3.1
     dev: true
 
   /@types/http-assert@1.5.3:
@@ -3460,10 +3287,6 @@ packages:
       parse5: 7.1.2
     dev: true
 
-  /@types/json-schema@7.0.11:
-    resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
-    dev: true
-
   /@types/json-schema@7.0.12:
     resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
     dev: true
@@ -3533,7 +3356,7 @@ packages:
     resolution: {integrity: sha512-AB/NB+oFHcLOZJYFv3bG5Af8YbwYCD9/zK0WcKALsbjI/FRKrcXTUTC64RebDrkyOkBm3bpCgpGndhAH/3YQ2Q==}
     deprecated: This is a stub types definition. koa-views provides its own type definitions, so you do not need this installed.
     dependencies:
-      koa-views: 7.0.2(@types/koa@2.13.5)(ejs@3.1.8)(pug@3.0.2)
+      koa-views: 7.0.2(@types/koa@2.13.5)(ejs@3.1.9)(pug@3.0.2)
     transitivePeerDependencies:
       - '@types/koa'
       - arc-templates
@@ -3630,6 +3453,9 @@ packages:
     resolution: {integrity: sha512-W9UC9DOPNBRTUocqqPZmzX3cbHmlZBI9jLn6SuxZz0n5QrPk382Ig3hbBWHqYU8TRUmTCJJhuanXeyMTavF7Mg==}
     dev: true
 
+  /@types/mime@1.3.2:
+    resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
+
   /@types/mime@3.0.1:
     resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
 
@@ -3651,8 +3477,8 @@ packages:
       '@types/node': 18.11.18
     dev: true
 
-  /@types/node-fetch@2.6.2:
-    resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
+  /@types/node-fetch@2.6.4:
+    resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     dependencies:
       '@types/node': 18.11.18
       form-data: 3.0.1
@@ -3669,8 +3495,8 @@ packages:
     resolution: {integrity: sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==}
     dev: true
 
-  /@types/node@14.18.36:
-    resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==}
+  /@types/node@14.18.51:
+    resolution: {integrity: sha512-P9bsdGFPpVtofEKlhWMVS2qqx1A/rt9QBfihWlklfHHpUpjtYse5AzFz6j4DWrARLYh6gRnw9+5+DJcrq3KvBA==}
 
   /@types/node@17.0.5:
     resolution: {integrity: sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==}
@@ -3679,10 +3505,6 @@ packages:
   /@types/node@18.11.18:
     resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
 
-  /@types/node@20.2.5:
-    resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==}
-    dev: true
-
   /@types/nodemailer@6.4.8:
     resolution: {integrity: sha512-oVsJSCkqViCn8/pEu2hfjwVO+Gb3e+eTWjg3PcjeFKRItfKpKwHphQqbYmPQrlMk+op7pNNWPbsJIEthpFN/OQ==}
     dependencies:
@@ -3710,8 +3532,8 @@ packages:
     resolution: {integrity: sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==}
     dev: true
 
-  /@types/prettier@2.7.2:
-    resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==}
+  /@types/prettier@2.7.3:
+    resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==}
     dev: true
 
   /@types/probe-image-size@7.2.0:
@@ -3789,8 +3611,14 @@ packages:
     resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==}
     dev: true
 
-  /@types/serve-static@1.15.0:
-    resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==}
+  /@types/send@0.17.1:
+    resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==}
+    dependencies:
+      '@types/mime': 1.3.2
+      '@types/node': 18.11.18
+
+  /@types/serve-static@1.15.1:
+    resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
     dependencies:
       '@types/mime': 3.0.1
       '@types/node': 18.11.18
@@ -3826,7 +3654,7 @@ packages:
   /@types/streamx@2.9.1:
     resolution: {integrity: sha512-9bywzhouyedmci7WCIPFwJ8zASDnxt2gaVUy52X0p0Tt085IJSAEP0L6j4SSNeDMSLzpYu6cPz0GrJZ7kPJ6Bg==}
     dependencies:
-      '@types/node': 20.2.5
+      '@types/node': 18.11.18
     dev: true
 
   /@types/throttle-debounce@5.0.0:
@@ -3861,19 +3689,11 @@ packages:
     resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==}
     dev: true
 
-  /@types/vinyl-fs@2.4.12:
-    resolution: {integrity: sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==}
-    dependencies:
-      '@types/glob-stream': 6.1.1
-      '@types/node': 18.11.18
-      '@types/vinyl': 2.0.7
-    dev: true
-
   /@types/vinyl-fs@3.0.2:
     resolution: {integrity: sha512-ctNcmmzbMIKooXjRkyyUCOu2Z4AyqibL+RhXoF3pb7K7j+ezItnakmpm31LymkYHSIM5ey0tjIFzTvFOTSBCGw==}
     dependencies:
       '@types/glob-stream': 8.0.0
-      '@types/node': 20.2.5
+      '@types/node': 18.11.18
       '@types/vinyl': 2.0.7
     dev: true
 
@@ -3923,7 +3743,7 @@ packages:
     resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 14.18.36
+      '@types/node': 18.11.18
     dev: true
     optional: true
 
@@ -3946,7 +3766,7 @@ packages:
       functional-red-black-tree: 1.0.1
       ignore: 5.2.4
       regexpp: 3.2.0
-      semver: 7.3.8
+      semver: 7.5.1
       tsutils: 3.21.0(typescript@4.5.4)
       typescript: 4.5.4
     transitivePeerDependencies:
@@ -3959,7 +3779,7 @@ packages:
     peerDependencies:
       eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
     dependencies:
-      '@types/json-schema': 7.0.11
+      '@types/json-schema': 7.0.12
       '@typescript-eslint/scope-manager': 5.8.1
       '@typescript-eslint/types': 5.8.1
       '@typescript-eslint/typescript-estree': 5.8.1(typescript@4.5.4)
@@ -4018,7 +3838,7 @@ packages:
       debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
-      semver: 7.3.8
+      semver: 7.5.1
       tsutils: 3.21.0(typescript@4.5.4)
       typescript: 4.5.4
     transitivePeerDependencies:
@@ -4047,7 +3867,7 @@ packages:
   /@vue/compiler-core@3.3.4:
     resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
     dependencies:
-      '@babel/parser': 7.22.4
+      '@babel/parser': 7.22.5
       '@vue/shared': 3.3.4
       estree-walker: 2.0.2
       source-map-js: 1.0.2
@@ -4063,7 +3883,7 @@ packages:
   /@vue/compiler-sfc@2.7.14:
     resolution: {integrity: sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==}
     dependencies:
-      '@babel/parser': 7.22.4
+      '@babel/parser': 7.22.5
       postcss: 8.4.24
       source-map: 0.6.1
     dev: true
@@ -4071,7 +3891,7 @@ packages:
   /@vue/compiler-sfc@3.3.4:
     resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
     dependencies:
-      '@babel/parser': 7.22.4
+      '@babel/parser': 7.22.5
       '@vue/compiler-core': 3.3.4
       '@vue/compiler-dom': 3.3.4
       '@vue/compiler-ssr': 3.3.4
@@ -4093,7 +3913,7 @@ packages:
   /@vue/reactivity-transform@3.3.4:
     resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
     dependencies:
-      '@babel/parser': 7.22.4
+      '@babel/parser': 7.22.5
       '@vue/compiler-core': 3.3.4
       '@vue/shared': 3.3.4
       estree-walker: 2.0.2
@@ -4325,7 +4145,7 @@ packages:
   /acorn-globals@7.0.1:
     resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==}
     dependencies:
-      acorn: 8.8.1
+      acorn: 8.8.2
       acorn-walk: 8.2.0
     dev: false
 
@@ -4358,11 +4178,6 @@ packages:
     engines: {node: '>=0.4.0'}
     hasBin: true
 
-  /acorn@8.8.1:
-    resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-
   /acorn@8.8.2:
     resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
     engines: {node: '>=0.4.0'}
@@ -4487,7 +4302,6 @@ packages:
   /ansi-regex@6.0.1:
     resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
     engines: {node: '>=12'}
-    dev: true
 
   /ansi-styles@2.2.1:
     resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==}
@@ -4514,7 +4328,6 @@ packages:
   /ansi-styles@6.2.1:
     resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
     engines: {node: '>=12'}
-    dev: true
 
   /ansi-wrap@0.1.0:
     resolution: {integrity: sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==}
@@ -4577,7 +4390,7 @@ packages:
       lodash.isplainobject: 4.0.6
       lodash.union: 4.6.0
       normalize-path: 3.0.0
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
     dev: false
 
   /archiver@5.3.1:
@@ -4587,8 +4400,8 @@ packages:
       archiver-utils: 2.1.0
       async: 3.2.4
       buffer-crc32: 0.2.13
-      readable-stream: 3.6.0
-      readdir-glob: 1.1.2
+      readable-stream: 3.6.2
+      readdir-glob: 1.1.3
       tar-stream: 2.2.0
       zip-stream: 4.1.0
     dev: false
@@ -4602,7 +4415,7 @@ packages:
     engines: {node: '>=10'}
     dependencies:
       delegates: 1.0.0
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: false
 
   /are-we-there-yet@3.0.1:
@@ -4627,7 +4440,7 @@ packages:
     dependencies:
       '@mapbox/node-pre-gyp': 1.0.10
       '@phc/format': 1.0.0
-      node-addon-api: 5.0.0
+      node-addon-api: 5.1.0
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -4781,7 +4594,7 @@ packages:
   /async-mutex@0.4.0:
     resolution: {integrity: sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==}
     dependencies:
-      tslib: 2.4.1
+      tslib: 2.5.3
     dev: false
 
   /async-settle@1.0.0:
@@ -4815,14 +4628,14 @@ packages:
   /autolinker@4.0.0:
     resolution: {integrity: sha512-fl5Kh6BmEEZx+IWBfEirnRUU5+cOiV0OK7PEt0RBKvJMJ8GaRseIOeDU3FKf4j3CE5HVefcjHmhYPOcaVt0bZw==}
     dependencies:
-      tslib: 2.4.1
+      tslib: 2.5.3
     dev: false
 
   /autoprefixer@6.7.7:
     resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==}
     dependencies:
       browserslist: 1.7.7
-      caniuse-db: 1.0.30001443
+      caniuse-db: 1.0.30001502
       normalize-range: 0.1.2
       num2fraction: 1.2.2
       postcss: 5.2.18
@@ -4849,7 +4662,7 @@ packages:
       '@ava/typescript':
         optional: true
     dependencies:
-      acorn: 8.8.1
+      acorn: 8.8.2
       acorn-walk: 8.2.0
       ansi-styles: 6.2.1
       arrgv: 1.0.2
@@ -4857,9 +4670,9 @@ packages:
       callsites: 4.0.0
       cbor: 8.1.0
       chalk: 5.2.0
-      chokidar: 3.5.3
+      chokidar: 3.3.1
       chunkd: 2.0.1
-      ci-info: 3.7.1
+      ci-info: 3.8.0
       ci-parallel-vars: 1.0.1
       clean-yaml-object: 0.1.0
       cli-truncate: 3.1.0
@@ -4893,7 +4706,7 @@ packages:
       supertap: 3.0.1
       temp-dir: 3.0.0
       write-file-atomic: 5.0.1
-      yargs: 17.6.2
+      yargs: 17.7.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -4968,29 +4781,29 @@ packages:
     peerDependencies:
       eslint: '>= 4.12.1'
     dependencies:
-      '@babel/code-frame': 7.18.6
-      '@babel/parser': 7.22.4
-      '@babel/traverse': 7.20.12
-      '@babel/types': 7.22.4
+      '@babel/code-frame': 7.22.5
+      '@babel/parser': 7.22.5
+      '@babel/traverse': 7.22.5
+      '@babel/types': 7.22.5
       eslint: 8.42.0
       eslint-visitor-keys: 1.3.0
-      resolve: 1.22.1
+      resolve: 1.22.2
     transitivePeerDependencies:
       - supports-color
     dev: false
 
-  /babel-jest@27.5.1(@babel/core@7.21.4):
+  /babel-jest@27.5.1(@babel/core@7.22.5):
     resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     peerDependencies:
       '@babel/core': ^7.8.0
     dependencies:
-      '@babel/core': 7.21.4
+      '@babel/core': 7.22.5
       '@jest/transform': 27.5.1
       '@jest/types': 27.5.1
-      '@types/babel__core': 7.20.0
+      '@types/babel__core': 7.20.1
       babel-plugin-istanbul: 6.1.1
-      babel-preset-jest: 27.5.1(@babel/core@7.21.4)
+      babel-preset-jest: 27.5.1(@babel/core@7.22.5)
       chalk: 4.1.2
       graceful-fs: 4.2.11
       slash: 3.0.0
@@ -5002,7 +4815,7 @@ packages:
     resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
     engines: {node: '>=8'}
     dependencies:
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-plugin-utils': 7.22.5
       '@istanbuljs/load-nyc-config': 1.1.0
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-instrument: 5.2.1
@@ -5015,48 +4828,48 @@ packages:
     resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
-      '@babel/template': 7.21.9
-      '@babel/types': 7.22.4
-      '@types/babel__core': 7.20.0
-      '@types/babel__traverse': 7.18.3
+      '@babel/template': 7.22.5
+      '@babel/types': 7.22.5
+      '@types/babel__core': 7.20.1
+      '@types/babel__traverse': 7.20.1
     dev: true
 
-  /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.4):
+  /babel-preset-current-node-syntax@1.0.1(@babel/core@7.22.5):
     resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4)
-      '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.4)
-      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4)
-      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.4)
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4)
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4)
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4)
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4)
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4)
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4)
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4)
-      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4)
+      '@babel/core': 7.22.5
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.5)
+      '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.22.5)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.5)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.5)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.5)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.5)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.5)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.5)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.5)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.5)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.5)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.5)
     dev: true
 
-  /babel-preset-jest@27.5.1(@babel/core@7.21.4):
+  /babel-preset-jest@27.5.1(@babel/core@7.22.5):
     resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.21.4
+      '@babel/core': 7.22.5
       babel-plugin-jest-hoist: 27.5.1
-      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4)
+      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5)
     dev: true
 
   /babel-walk@3.0.0-canary-5:
     resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==}
     engines: {node: '>= 10.0.0'}
     dependencies:
-      '@babel/types': 7.22.4
+      '@babel/types': 7.22.5
 
   /bach@1.2.0:
     resolution: {integrity: sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==}
@@ -5152,7 +4965,7 @@ packages:
     dependencies:
       buffer: 5.7.1
       inherits: 2.0.4
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: false
 
   /blob-util@2.0.2:
@@ -5239,19 +5052,19 @@ packages:
     deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.
     hasBin: true
     dependencies:
-      caniuse-db: 1.0.30001443
-      electron-to-chromium: 1.4.284
+      caniuse-db: 1.0.30001502
+      electron-to-chromium: 1.4.430
     dev: true
 
-  /browserslist@4.21.7:
-    resolution: {integrity: sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==}
+  /browserslist@4.21.8:
+    resolution: {integrity: sha512-j+7xYe+v+q2Id9qbBeCI8WX5NmZSRe8es1+0xntD/+gaWXznP8tFEkv5IgSaHf5dS1YwVMbX/4W6m937mj+wQw==}
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
     dependencies:
-      caniuse-lite: 1.0.30001495
-      electron-to-chromium: 1.4.420
+      caniuse-lite: 1.0.30001502
+      electron-to-chromium: 1.4.430
       node-releases: 2.0.12
-      update-browserslist-db: 1.0.11(browserslist@4.21.7)
+      update-browserslist-db: 1.0.11(browserslist@4.21.8)
 
   /bs-logger@0.2.6:
     resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
@@ -5290,7 +5103,7 @@ packages:
     resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==}
     dependencies:
       base64-js: 1.5.1
-      ieee754: 1.2.1
+      ieee754: 1.1.13
       isarray: 1.0.0
     dev: false
 
@@ -5319,12 +5132,12 @@ packages:
     resolution: {integrity: sha512-o9m/7HjS/Or3vqRd59evBlWCXd9Lp+ALppKseoSKHaykK46SmRjAilX98PgmOz1yeVaurt8D5UtvEt4bUjM3eA==}
     engines: {node: '>=12'}
     dependencies:
-      cron-parser: 4.7.1
+      cron-parser: 4.8.1
       debuglog: 1.0.1
       get-port: 5.1.1
       ioredis: 5.3.2
       lodash: 4.17.21
-      msgpackr: 1.8.1
+      msgpackr: 1.9.5
       semver: 7.5.1
       uuid: 8.3.2
     transitivePeerDependencies:
@@ -5343,30 +5156,22 @@ packages:
     engines: {node: '>= 0.8'}
     dev: false
 
-  /cacache@16.1.3:
-    resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+  /cacache@17.1.3:
+    resolution: {integrity: sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dependencies:
-      '@npmcli/fs': 2.1.2
-      '@npmcli/move-file': 2.0.1
-      chownr: 2.0.0
-      fs-minipass: 2.1.0
-      glob: 8.1.0
-      infer-owner: 1.0.4
+      '@npmcli/fs': 3.1.0
+      fs-minipass: 3.0.2
+      glob: 10.2.7
       lru-cache: 7.18.3
-      minipass: 3.3.6
+      minipass: 5.0.0
       minipass-collect: 1.0.2
       minipass-flush: 1.0.5
       minipass-pipeline: 1.2.4
-      mkdirp: 1.0.4
       p-map: 4.0.0
-      promise-inflight: 1.0.1
-      rimraf: 3.0.2
-      ssri: 9.0.1
+      ssri: 10.0.4
       tar: 6.1.15
-      unique-filename: 2.0.1
-    transitivePeerDependencies:
-      - bluebird
+      unique-filename: 3.0.0
     dev: false
 
   /cache-base@1.0.1:
@@ -5401,21 +5206,21 @@ packages:
     engines: {node: '>=14.16'}
     dev: false
 
-  /cacheable-request@10.2.5:
-    resolution: {integrity: sha512-5RwYYCfzjNPsyJxb/QpaM0bfzx+kw5/YpDhZPm9oMIDntHFQ9YXeyV47ZvzlTE0XrrrbyO2UITJH4GF9eRLdXQ==}
+  /cacheable-request@10.2.10:
+    resolution: {integrity: sha512-v6WB+Epm/qO4Hdlio/sfUn69r5Shgh39SsE9DSd4bIezP0mblOlObI+I0kUEM7J0JFc+I7pSeMeYaOYtX1N/VQ==}
     engines: {node: '>=14.16'}
     dependencies:
       '@types/http-cache-semantics': 4.0.1
       get-stream: 6.0.1
-      http-cache-semantics: 4.1.0
+      http-cache-semantics: 4.1.1
       keyv: 4.5.2
       mimic-response: 4.0.0
       normalize-url: 8.0.0
       responselike: 3.0.0
     dev: false
 
-  /cacheable-request@7.0.2:
-    resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==}
+  /cacheable-request@7.0.4:
+    resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
     engines: {node: '>=8'}
     dependencies:
       clone-response: 1.0.3
@@ -5435,7 +5240,7 @@ packages:
     resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
     dependencies:
       function-bind: 1.1.1
-      get-intrinsic: 1.1.3
+      get-intrinsic: 1.2.1
 
   /callsites@3.1.0:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
@@ -5473,17 +5278,17 @@ packages:
     resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==}
     dependencies:
       browserslist: 1.7.7
-      caniuse-db: 1.0.30001443
+      caniuse-db: 1.0.30001502
       lodash.memoize: 4.1.2
       lodash.uniq: 4.5.0
     dev: true
 
-  /caniuse-db@1.0.30001443:
-    resolution: {integrity: sha512-4KKthVYyooNIOhO1w0OJ13EhEwOGECMrZdkeyDydhvYXaTDA3WdhR8amoJnAgpSgcCR26aOAWk6N9ANVYlv2oQ==}
+  /caniuse-db@1.0.30001502:
+    resolution: {integrity: sha512-nNj61ClInBeMsDqHo20wgMJ40Jhyg9l6W0Z9tipmsc99g43TGvlJky4LP3TlrXbLrVCzRWVS4Gox606M6Hw9Ow==}
     dev: true
 
-  /caniuse-lite@1.0.30001495:
-    resolution: {integrity: sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==}
+  /caniuse-lite@1.0.30001502:
+    resolution: {integrity: sha512-AZ+9tFXw1sS0o0jcpJQIXvFTOB/xGiQ4OQ2t98QX3NDn2EZTSRBC801gxrsGgViuq2ak/NLkNgSNEPtCr5lfKg==}
 
   /canonicalize@1.0.8:
     resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
@@ -5624,8 +5429,8 @@ packages:
       lodash.some: 4.6.0
     dev: false
 
-  /chokidar@3.5.3:
-    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+  /chokidar@3.3.1:
+    resolution: {integrity: sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==}
     engines: {node: '>= 8.10.0'}
     dependencies:
       anymatch: 3.1.3
@@ -5634,9 +5439,9 @@ packages:
       is-binary-path: 2.1.0
       is-glob: 4.0.3
       normalize-path: 3.0.0
-      readdirp: 3.6.0
+      readdirp: 3.3.0
     optionalDependencies:
-      fsevents: 2.3.2
+      fsevents: 2.1.3
 
   /chownr@1.1.4:
     resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
@@ -5656,8 +5461,8 @@ packages:
     resolution: {integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==}
     dev: true
 
-  /ci-info@3.7.1:
-    resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
+  /ci-info@3.8.0:
+    resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
     engines: {node: '>=8'}
     dev: true
 
@@ -5671,8 +5476,8 @@ packages:
       lodash: 4.17.21
     dev: true
 
-  /cjs-module-lexer@1.2.2:
-    resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
+  /cjs-module-lexer@1.2.3:
+    resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==}
     dev: true
 
   /clap@1.2.3:
@@ -5822,7 +5627,7 @@ packages:
     dependencies:
       inherits: 2.0.4
       process-nextick-args: 2.0.1
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
     dev: true
 
   /cluster-key-slot@1.1.2:
@@ -5834,7 +5639,7 @@ packages:
     dependencies:
       inflation: 2.0.0
       qs: 6.11.2
-      raw-body: 2.5.1
+      raw-body: 2.5.2
       type-is: 1.6.18
     dev: false
 
@@ -5843,7 +5648,7 @@ packages:
     dependencies:
       inflation: 2.0.0
       qs: 6.11.2
-      raw-body: 2.5.1
+      raw-body: 2.5.2
       type-is: 1.6.18
     dev: false
 
@@ -5945,10 +5750,6 @@ packages:
     resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
     dev: false
 
-  /colorette@2.0.19:
-    resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
-    dev: true
-
   /colorette@2.0.20:
     resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
     dev: true
@@ -6029,7 +5830,7 @@ packages:
       buffer-crc32: 0.2.13
       crc32-stream: 4.0.2
       normalize-path: 3.0.0
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: false
 
   /concat-map@0.0.1:
@@ -6041,7 +5842,7 @@ packages:
     dependencies:
       buffer-from: 1.1.2
       inherits: 2.0.4
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
       typedarray: 0.0.6
 
   /concordance@5.0.4:
@@ -6076,9 +5877,10 @@ packages:
     resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
     dev: false
 
-  /consolidate@0.16.0(ejs@3.1.8)(pug@3.0.2):
+  /consolidate@0.16.0(ejs@3.1.9)(pug@3.0.2):
     resolution: {integrity: sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==}
     engines: {node: '>= 0.10.0'}
+    deprecated: Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog
     peerDependencies:
       arc-templates: ^0.5.3
       atpl: '>=0.7.6'
@@ -6242,14 +6044,14 @@ packages:
         optional: true
     dependencies:
       bluebird: 3.7.2
-      ejs: 3.1.8
+      ejs: 3.1.9
       pug: 3.0.2
 
   /constantinople@4.0.1:
     resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
     dependencies:
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/parser': 7.22.5
+      '@babel/types': 7.22.5
 
   /content-disposition@0.5.4:
     resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
@@ -6257,8 +6059,8 @@ packages:
     dependencies:
       safe-buffer: 5.2.1
 
-  /content-type@1.0.4:
-    resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==}
+  /content-type@1.0.5:
+    resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
     engines: {node: '>= 0.6'}
     dev: false
 
@@ -6294,15 +6096,9 @@ packages:
     resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
     dev: false
 
-  /core-js@3.27.1:
-    resolution: {integrity: sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==}
+  /core-js@3.31.0:
+    resolution: {integrity: sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ==}
     requiresBuild: true
-    dev: false
-
-  /core-js@3.30.2:
-    resolution: {integrity: sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg==}
-    requiresBuild: true
-    dev: true
 
   /core-util-is@1.0.2:
     resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@@ -6321,17 +6117,17 @@ packages:
     engines: {node: '>= 10'}
     dependencies:
       crc-32: 1.2.2
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: false
 
   /create-require@1.1.1:
     resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
 
-  /cron-parser@4.7.1:
-    resolution: {integrity: sha512-WguFaoQ0hQ61SgsCZLHUcNbAvlK0lypKXu62ARguefYmjzaOXIVRNrAmyXzabTwUn4sQvQLkk6bjH+ipGfw8bA==}
+  /cron-parser@4.8.1:
+    resolution: {integrity: sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==}
     engines: {node: '>=12.0.0'}
     dependencies:
-      luxon: 3.2.1
+      luxon: 3.3.0
     dev: false
 
   /cropperjs@2.0.0-beta.2:
@@ -6349,21 +6145,12 @@ packages:
       cross-spawn: 7.0.3
     dev: true
 
-  /cross-fetch@3.1.5:
-    resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
-    dependencies:
-      node-fetch: 2.6.7
-    transitivePeerDependencies:
-      - encoding
-    dev: true
-
   /cross-fetch@3.1.6:
     resolution: {integrity: sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==}
     dependencies:
       node-fetch: 2.6.11
     transitivePeerDependencies:
       - encoding
-    dev: false
 
   /cross-spawn@5.1.0:
     resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
@@ -6494,7 +6281,7 @@ packages:
     dependencies:
       '@cypress/request': 2.88.11
       '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
-      '@types/node': 14.18.36
+      '@types/node': 14.18.51
       '@types/sinonjs__fake-timers': 8.1.1
       '@types/sizzle': 2.3.3
       arch: 2.2.0
@@ -6508,7 +6295,7 @@ packages:
       cli-table3: 0.6.3
       commander: 5.1.0
       common-tags: 1.8.2
-      dayjs: 1.11.7
+      dayjs: 1.11.8
       debug: 4.3.4(supports-color@8.1.1)
       enquirer: 2.3.6
       eventemitter2: 6.4.7
@@ -6524,7 +6311,7 @@ packages:
       listr2: 3.14.0(enquirer@2.3.6)
       lodash: 4.17.21
       log-symbols: 4.1.0
-      minimist: 1.2.7
+      minimist: 1.2.8
       ospath: 1.2.2
       pretty-bytes: 5.6.0
       proxy-from-env: 1.0.0
@@ -6578,7 +6365,7 @@ packages:
     resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
     engines: {node: '>=0.11'}
     dependencies:
-      '@babel/runtime': 7.22.3
+      '@babel/runtime': 7.22.5
 
   /date-time@3.1.0:
     resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
@@ -6587,8 +6374,8 @@ packages:
       time-zone: 1.0.0
     dev: true
 
-  /dayjs@1.11.7:
-    resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==}
+  /dayjs@1.11.8:
+    resolution: {integrity: sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==}
 
   /debug@2.6.9:
     resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
@@ -6677,7 +6464,7 @@ packages:
   /deep-email-validator@0.1.21:
     resolution: {integrity: sha512-DBAmMzbr+MAubXQ+TS9tZuPwLcdKscb8YzKZiwoLqF3NmaeEgXvSSHhZ0EXOFeKFE2FNWC4mNXCyiQ/JdFXUwg==}
     dependencies:
-      '@types/disposable-email-domains': 1.0.2
+      '@types/disposable-email-domains': 1.0.4
       axios: 0.24.0
       disposable-email-domains: 1.0.62
       mailcheck: 1.1.1
@@ -6697,15 +6484,9 @@ packages:
   /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'}
-    dev: true
-
   /deepmerge@4.3.1:
     resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
     engines: {node: '>=0.10.0'}
-    dev: false
 
   /default-compare@1.0.0:
     resolution: {integrity: sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==}
@@ -6723,8 +6504,8 @@ packages:
     resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
     engines: {node: '>=10'}
 
-  /define-properties@1.1.4:
-    resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
+  /define-properties@1.2.0:
+    resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
     engines: {node: '>= 0.4'}
     dependencies:
       has-property-descriptors: 1.0.0
@@ -6939,7 +6720,7 @@ packages:
     dependencies:
       end-of-stream: 1.4.4
       inherits: 2.0.4
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
       stream-shift: 1.0.1
     dev: true
 
@@ -6952,7 +6733,6 @@ packages:
 
   /eastasianwidth@0.2.0:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
-    dev: true
 
   /ecc-jsbn@0.1.2:
     resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
@@ -6979,19 +6759,15 @@ packages:
     resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
     dev: false
 
-  /ejs@3.1.8:
-    resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==}
+  /ejs@3.1.9:
+    resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
     engines: {node: '>=0.10.0'}
     hasBin: true
     dependencies:
-      jake: 10.8.5
+      jake: 10.8.7
 
-  /electron-to-chromium@1.4.284:
-    resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==}
-    dev: true
-
-  /electron-to-chromium@1.4.420:
-    resolution: {integrity: sha512-BpPy2KXZc+UPbI8NGo2QdHU1Mkq11pO/zaNDHY57L09K/0ytrPw+IiLOUvZ1NjI5BlAVF5DkNr1UBUS76Tc4ow==}
+  /electron-to-chromium@1.4.430:
+    resolution: {integrity: sha512-FytjTbGwz///F+ToZ5XSeXbbSaXalsVRXsz2mHityI5gfxft7ieW3HqFLkU5V1aIrY42aflICqbmFoDxW10etg==}
 
   /emittery@0.8.1:
     resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==}
@@ -7008,7 +6784,6 @@ packages:
 
   /emoji-regex@9.2.2:
     resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
-    dev: true
 
   /emojis-list@3.0.0:
     resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
@@ -7037,8 +6812,8 @@ packages:
     dependencies:
       once: 1.4.0
 
-  /enhanced-resolve@5.14.1:
-    resolution: {integrity: sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==}
+  /enhanced-resolve@5.15.0:
+    resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==}
     engines: {node: '>=10.13.0'}
     dependencies:
       graceful-fs: 4.2.11
@@ -7060,10 +6835,6 @@ packages:
     resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
     dev: false
 
-  /entities@4.4.0:
-    resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==}
-    engines: {node: '>=0.12'}
-
   /entities@4.5.0:
     resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
     engines: {node: '>=0.12'}
@@ -7089,8 +6860,8 @@ packages:
       is-arrayish: 0.2.1
     dev: true
 
-  /es-module-lexer@1.2.1:
-    resolution: {integrity: sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==}
+  /es-module-lexer@1.3.0:
+    resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==}
     dev: true
 
   /es5-ext@0.10.62:
@@ -7233,14 +7004,6 @@ packages:
       estraverse: 4.3.0
     dev: true
 
-  /eslint-scope@7.1.1:
-    resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dependencies:
-      esrecurse: 4.3.0
-      estraverse: 5.3.0
-    dev: true
-
   /eslint-scope@7.2.0:
     resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -7268,11 +7031,6 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
-  /eslint-visitor-keys@3.3.0:
-    resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dev: true
-
   /eslint-visitor-keys@3.4.1:
     resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -7338,17 +7096,17 @@ packages:
       doctrine: 3.0.0
       enquirer: 2.3.6
       escape-string-regexp: 4.0.0
-      eslint-scope: 7.1.1
+      eslint-scope: 7.2.0
       eslint-utils: 3.0.0(eslint@8.6.0)
-      eslint-visitor-keys: 3.3.0
-      espree: 9.4.1
-      esquery: 1.4.0
+      eslint-visitor-keys: 3.4.1
+      espree: 9.5.2
+      esquery: 1.5.0
       esutils: 2.0.3
       fast-deep-equal: 3.1.3
       file-entry-cache: 6.0.1
       functional-red-black-tree: 1.0.1
       glob-parent: 6.0.2
-      globals: 13.19.0
+      globals: 13.20.0
       ignore: 4.0.6
       import-fresh: 3.3.0
       imurmurhash: 0.1.4
@@ -7362,7 +7120,7 @@ packages:
       optionator: 0.9.1
       progress: 2.0.3
       regexpp: 3.2.0
-      semver: 7.3.8
+      semver: 7.5.1
       strip-ansi: 6.0.1
       strip-json-comments: 3.1.1
       text-table: 0.2.0
@@ -7371,15 +7129,6 @@ packages:
       - supports-color
     dev: true
 
-  /espree@9.4.1:
-    resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    dependencies:
-      acorn: 8.8.2
-      acorn-jsx: 5.3.2(acorn@8.8.2)
-      eslint-visitor-keys: 3.3.0
-    dev: true
-
   /espree@9.5.2:
     resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -7399,13 +7148,6 @@ packages:
     engines: {node: '>=4'}
     hasBin: true
 
-  /esquery@1.4.0:
-    resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
-    engines: {node: '>=0.10'}
-    dependencies:
-      estraverse: 5.3.0
-    dev: true
-
   /esquery@1.5.0:
     resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
     engines: {node: '>=0.10'}
@@ -7586,6 +7328,10 @@ packages:
       jest-message-util: 27.5.1
     dev: true
 
+  /exponential-backoff@3.1.1:
+    resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
+    dev: false
+
   /ext-list@2.2.2:
     resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==}
     engines: {node: '>=0.10.0'}
@@ -7882,9 +7628,9 @@ packages:
   /fix-esm@1.0.1:
     resolution: {integrity: sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw==}
     dependencies:
-      '@babel/core': 7.22.1
-      '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.22.1)
-      '@babel/plugin-transform-modules-commonjs': 7.21.5(@babel/core@7.22.1)
+      '@babel/core': 7.22.5
+      '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.22.5)
+      '@babel/plugin-transform-modules-commonjs': 7.22.5(@babel/core@7.22.5)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -7926,7 +7672,7 @@ packages:
     resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
     dependencies:
       inherits: 2.0.4
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
     dev: true
 
   /focus-trap-vue@4.0.2(focus-trap@7.4.3)(vue@3.3.4):
@@ -7974,6 +7720,14 @@ packages:
       for-in: 1.0.2
     dev: true
 
+  /foreground-child@3.1.1:
+    resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
+    engines: {node: '>=14'}
+    dependencies:
+      cross-spawn: 7.0.3
+      signal-exit: 4.0.2
+    dev: false
+
   /forever-agent@0.6.1:
     resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
 
@@ -8092,6 +7846,13 @@ packages:
       minipass: 3.3.6
     dev: false
 
+  /fs-minipass@3.0.2:
+    resolution: {integrity: sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+    dependencies:
+      minipass: 5.0.0
+    dev: false
+
   /fs-mkdirp-stream@1.0.0:
     resolution: {integrity: sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==}
     engines: {node: '>= 0.10'}
@@ -8103,11 +7864,20 @@ packages:
   /fs.realpath@1.0.0:
     resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
 
+  /fsevents@2.1.3:
+    resolution: {integrity: sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    deprecated: '"Please update to latest v2.3 or v2.2"'
+    requiresBuild: true
+    optional: true
+
   /fsevents@2.3.2:
     resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
     os: [darwin]
     requiresBuild: true
+    dev: true
     optional: true
 
   /fulcon@1.0.2:
@@ -8168,13 +7938,6 @@ packages:
     resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
     engines: {node: 6.* || 8.* || >= 10.*}
 
-  /get-intrinsic@1.1.3:
-    resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==}
-    dependencies:
-      function-bind: 1.1.1
-      has: 1.0.3
-      has-symbols: 1.0.3
-
   /get-intrinsic@1.2.1:
     resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
     dependencies:
@@ -8286,7 +8049,7 @@ packages:
       is-negated-glob: 1.0.0
       ordered-read-streams: 1.0.1
       pumpify: 1.5.1
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
       remove-trailing-separator: 1.1.0
       to-absolute-glob: 2.0.2
       unique-stream: 2.3.1
@@ -8302,7 +8065,7 @@ packages:
     dependencies:
       anymatch: 2.0.0
       async-done: 1.3.2
-      chokidar: 3.5.3
+      chokidar: 3.3.1
       is-negated-glob: 1.0.0
       just-debounce: 1.1.0
       normalize-path: 3.0.0
@@ -8311,6 +8074,18 @@ packages:
       - supports-color
     dev: true
 
+  /glob@10.2.7:
+    resolution: {integrity: sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+    dependencies:
+      foreground-child: 3.1.1
+      jackspeak: 2.2.1
+      minimatch: 9.0.1
+      minipass: 5.0.0
+      path-scurry: 1.9.2
+    dev: false
+
   /glob@7.2.0:
     resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==}
     dependencies:
@@ -8373,13 +8148,6 @@ packages:
     resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
     engines: {node: '>=4'}
 
-  /globals@13.19.0:
-    resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      type-fest: 0.20.2
-    dev: true
-
   /globals@13.20.0:
     resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
     engines: {node: '>=8'}
@@ -8424,7 +8192,7 @@ packages:
   /gopd@1.0.1:
     resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
     dependencies:
-      get-intrinsic: 1.1.3
+      get-intrinsic: 1.2.1
     dev: false
 
   /got@11.8.5:
@@ -8436,7 +8204,7 @@ packages:
       '@types/cacheable-request': 6.0.3
       '@types/responselike': 1.0.0
       cacheable-lookup: 5.0.4
-      cacheable-request: 7.0.2
+      cacheable-request: 7.0.4
       decompress-response: 6.0.0
       http2-wrapper: 1.0.3
       lowercase-keys: 2.0.0
@@ -8447,10 +8215,10 @@ packages:
     resolution: {integrity: sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==}
     engines: {node: '>=14.16'}
     dependencies:
-      '@sindresorhus/is': 5.3.0
+      '@sindresorhus/is': 5.4.1
       '@szmarczak/http-timer': 5.0.1
       cacheable-lookup: 7.0.0
-      cacheable-request: 10.2.5
+      cacheable-request: 10.2.10
       decompress-response: 6.0.0
       form-data-encoder: 2.1.4
       get-stream: 6.0.1
@@ -8528,7 +8296,7 @@ packages:
     engines: {node: '>=10'}
     dependencies:
       plugin-error: 1.0.1
-      terser: 5.16.1
+      terser: 5.18.0
       through2: 4.0.2
       vinyl-sourcemaps-apply: 0.2.1
     dev: true
@@ -8600,7 +8368,7 @@ packages:
   /has-property-descriptors@1.0.0:
     resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
     dependencies:
-      get-intrinsic: 1.1.3
+      get-intrinsic: 1.2.1
     dev: true
 
   /has-proto@1.0.1:
@@ -8728,7 +8496,7 @@ packages:
       domutils: 1.7.0
       entities: 1.1.2
       inherits: 2.0.4
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: false
 
   /htmlparser2@8.0.2:
@@ -8747,10 +8515,6 @@ packages:
       http-errors: 1.8.1
     dev: false
 
-  /http-cache-semantics@4.1.0:
-    resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
-    dev: false
-
   /http-cache-semantics@4.1.1:
     resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
 
@@ -8975,10 +8739,6 @@ packages:
     resolution: {integrity: sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==}
     dev: true
 
-  /infer-owner@1.0.4:
-    resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
-    dev: false
-
   /inflation@2.0.0:
     resolution: {integrity: sha512-m3xv4hJYR2oXw4o4Y5l6P5P16WYmazYof+el6Al3f+YlggGj6qT9kImBAnzDelRALnP5d3h4jGBPKzYCizjZZw==}
     engines: {node: '>= 0.8.0'}
@@ -9080,8 +8840,8 @@ packages:
     resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==}
     dev: false
 
-  /ipaddr.js@2.0.1:
-    resolution: {integrity: sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==}
+  /ipaddr.js@2.1.0:
+    resolution: {integrity: sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==}
     engines: {node: '>= 10'}
     dev: false
 
@@ -9151,19 +8911,13 @@ packages:
     resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
     hasBin: true
     dependencies:
-      ci-info: 3.7.1
+      ci-info: 3.8.0
     dev: true
 
-  /is-core-module@2.11.0:
-    resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
-    dependencies:
-      has: 1.0.3
-
   /is-core-module@2.12.1:
     resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==}
     dependencies:
       has: 1.0.3
-    dev: true
 
   /is-data-descriptor@0.1.4:
     resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==}
@@ -9197,8 +8951,8 @@ packages:
       kind-of: 6.0.3
     dev: true
 
-  /is-electron@2.2.1:
-    resolution: {integrity: sha512-r8EEQQsqT+Gn0aXFx7lTFygYQhILLCB+wn0WCDL5LZRINeLH/Rvw1j2oKodELLXYNImQ3CRlVsY8wW4cGOsyuw==}
+  /is-electron@2.2.2:
+    resolution: {integrity: sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==}
     dev: false
 
   /is-error@2.2.2:
@@ -9482,8 +9236,8 @@ packages:
     resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
     engines: {node: '>=8'}
     dependencies:
-      '@babel/core': 7.22.1
-      '@babel/parser': 7.22.4
+      '@babel/core': 7.22.5
+      '@babel/parser': 7.22.5
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.0
       semver: 6.3.0
@@ -9527,8 +9281,17 @@ packages:
       textextensions: 3.3.0
     dev: true
 
-  /jake@10.8.5:
-    resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
+  /jackspeak@2.2.1:
+    resolution: {integrity: sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==}
+    engines: {node: '>=14'}
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+    dev: false
+
+  /jake@10.8.7:
+    resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
@@ -9553,7 +9316,7 @@ packages:
       '@jest/environment': 27.5.1
       '@jest/test-result': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       chalk: 4.1.2
       co: 4.6.0
       dedent: 0.7.0
@@ -9612,13 +9375,13 @@ packages:
       ts-node:
         optional: true
     dependencies:
-      '@babel/core': 7.21.4
+      '@babel/core': 7.22.5
       '@jest/test-sequencer': 27.5.1
       '@jest/types': 27.5.1
-      babel-jest: 27.5.1(@babel/core@7.21.4)
+      babel-jest: 27.5.1(@babel/core@7.22.5)
       chalk: 4.1.2
-      ci-info: 3.7.1
-      deepmerge: 4.2.2
+      ci-info: 3.8.0
+      deepmerge: 4.3.1
       glob: 7.2.3
       graceful-fs: 4.2.11
       jest-circus: 27.5.1
@@ -9679,7 +9442,7 @@ packages:
       '@jest/environment': 27.5.1
       '@jest/fake-timers': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       jest-mock: 27.5.1
       jest-util: 27.5.1
       jsdom: 16.7.0
@@ -9697,7 +9460,7 @@ packages:
       '@jest/environment': 27.5.1
       '@jest/fake-timers': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       jest-mock: 27.5.1
       jest-util: 27.5.1
     dev: true
@@ -9705,7 +9468,7 @@ packages:
   /jest-fetch-mock@3.0.3:
     resolution: {integrity: sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==}
     dependencies:
-      cross-fetch: 3.1.5
+      cross-fetch: 3.1.6
       promise-polyfill: 8.3.0
     transitivePeerDependencies:
       - encoding
@@ -9722,7 +9485,7 @@ packages:
     dependencies:
       '@jest/types': 27.5.1
       '@types/graceful-fs': 4.1.6
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -9744,7 +9507,7 @@ packages:
       '@jest/source-map': 27.5.1
       '@jest/test-result': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       chalk: 4.1.2
       co: 4.6.0
       expect: 27.5.1
@@ -9783,7 +9546,7 @@ packages:
     resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
-      '@babel/code-frame': 7.18.6
+      '@babel/code-frame': 7.22.5
       '@jest/types': 27.5.1
       '@types/stack-utils': 2.0.1
       chalk: 4.1.2
@@ -9799,7 +9562,7 @@ packages:
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
     dev: true
 
   /jest-pnp-resolver@1.2.3(jest-resolve@27.5.1):
@@ -9841,7 +9604,7 @@ packages:
       jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1)
       jest-util: 27.5.1
       jest-validate: 27.5.1
-      resolve: 1.22.1
+      resolve: 1.22.2
       resolve.exports: 1.1.1
       slash: 3.0.0
     dev: true
@@ -9855,7 +9618,7 @@ packages:
       '@jest/test-result': 27.5.1
       '@jest/transform': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       chalk: 4.1.2
       emittery: 0.8.1
       graceful-fs: 4.2.11
@@ -9890,7 +9653,7 @@ packages:
       '@jest/transform': 27.5.1
       '@jest/types': 27.5.1
       chalk: 4.1.2
-      cjs-module-lexer: 1.2.2
+      cjs-module-lexer: 1.2.3
       collect-v8-coverage: 1.0.1
       execa: 5.1.1
       glob: 7.2.3
@@ -9912,7 +9675,7 @@ packages:
     resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       graceful-fs: 4.2.11
     dev: true
 
@@ -9920,16 +9683,16 @@ packages:
     resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
-      '@babel/core': 7.21.4
-      '@babel/generator': 7.20.7
-      '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4)
-      '@babel/traverse': 7.20.12
-      '@babel/types': 7.22.4
+      '@babel/core': 7.22.5
+      '@babel/generator': 7.22.5
+      '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.5)
+      '@babel/traverse': 7.22.5
+      '@babel/types': 7.22.5
       '@jest/transform': 27.5.1
       '@jest/types': 27.5.1
-      '@types/babel__traverse': 7.18.3
-      '@types/prettier': 2.7.2
-      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4)
+      '@types/babel__traverse': 7.20.1
+      '@types/prettier': 2.7.3
+      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5)
       chalk: 4.1.2
       expect: 27.5.1
       graceful-fs: 4.2.11
@@ -9941,7 +9704,7 @@ packages:
       jest-util: 27.5.1
       natural-compare: 1.4.0
       pretty-format: 27.5.1
-      semver: 7.3.8
+      semver: 7.5.1
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -9951,9 +9714,9 @@ packages:
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       chalk: 4.1.2
-      ci-info: 3.7.1
+      ci-info: 3.8.0
       graceful-fs: 4.2.11
       picomatch: 2.3.1
     dev: true
@@ -9976,7 +9739,7 @@ packages:
     dependencies:
       '@jest/test-result': 27.5.1
       '@jest/types': 27.5.1
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       jest-util: 27.5.1
@@ -9996,7 +9759,7 @@ packages:
     resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
     engines: {node: '>= 10.13.0'}
     dependencies:
-      '@types/node': 17.0.5
+      '@types/node': 18.11.18
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
@@ -10031,8 +9794,8 @@ packages:
     engines: {node: '>= 0.6.0'}
     dev: false
 
-  /joi@17.7.0:
-    resolution: {integrity: sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==}
+  /joi@17.9.2:
+    resolution: {integrity: sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==}
     dependencies:
       '@hapi/hoek': 9.3.0
       '@hapi/topo': 5.1.0
@@ -10053,9 +9816,9 @@ packages:
     resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
     dev: true
 
-  /js-beautify@1.14.7:
-    resolution: {integrity: sha512-5SOX1KXPFKx+5f6ZrPsIPEY7NwKeQz47n3jm2i+XeHx9MoRsfQenlOP13FQhWvg8JRS0+XLO6XYUQ2GX+q+T9A==}
-    engines: {node: '>=10'}
+  /js-beautify@1.14.8:
+    resolution: {integrity: sha512-4S7HFeI9YfRvRgKnEweohs0tgJj28InHVIj4Nl8Htf96Y6pHg3+tJrmo4ucAM9f7l4SHbFI3IvFAZ2a1eQPbyg==}
+    engines: {node: '>=12'}
     hasBin: true
     dependencies:
       config-chain: 1.1.13
@@ -10136,11 +9899,11 @@ packages:
       http-proxy-agent: 4.0.1
       https-proxy-agent: 5.0.1
       is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.2
+      nwsapi: 2.2.5
       parse5: 6.0.1
       saxes: 5.0.1
       symbol-tree: 3.2.4
-      tough-cookie: 4.1.2
+      tough-cookie: 4.1.3
       w3c-hr-time: 1.0.2
       w3c-xmlserializer: 2.0.0
       webidl-conversions: 6.1.0
@@ -10165,7 +9928,7 @@ packages:
         optional: true
     dependencies:
       abab: 2.0.6
-      acorn: 8.8.1
+      acorn: 8.8.2
       acorn-globals: 7.0.1
       cssom: 0.5.0
       cssstyle: 2.3.0
@@ -10178,11 +9941,11 @@ packages:
       http-proxy-agent: 5.0.0
       https-proxy-agent: 5.0.1
       is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.2
+      nwsapi: 2.2.5
       parse5: 7.1.2
       saxes: 6.0.0
       symbol-tree: 3.2.4
-      tough-cookie: 4.1.2
+      tough-cookie: 4.1.3
       w3c-xmlserializer: 4.0.0
       webidl-conversions: 7.0.0
       whatwg-encoding: 2.0.0
@@ -10232,7 +9995,7 @@ packages:
     dependencies:
       json5: 2.2.3
       loader-utils: 2.0.4
-      schema-utils: 3.1.1
+      schema-utils: 3.2.0
       webpack: 5.85.1(@swc/core@1.3.62)(webpack-cli@5.1.3)
     dev: true
 
@@ -10373,11 +10136,11 @@ packages:
     resolution: {integrity: sha512-M8ZvMD8r+kPHy28aWP9VxL7kY8oPWA+C7ZgCljrCMeaU7uX6wsIQgDHskyrAr9sw+jqnIXyv4Mlxri5R4InIJg==}
     dependencies:
       '@types/co-body': 6.1.0
-      '@types/formidable': 2.0.5
+      '@types/formidable': 2.0.6
       '@types/koa': 2.13.5
       co-body: 6.1.0
       formidable: 2.1.1
-      zod: 3.20.3
+      zod: 3.21.4
     dev: false
 
   /koa-bodyparser@4.3.0:
@@ -10478,8 +10241,9 @@ packages:
       - supports-color
     dev: false
 
-  /koa-views@7.0.2(@types/koa@2.13.5)(ejs@3.1.8)(pug@3.0.2):
+  /koa-views@7.0.2(@types/koa@2.13.5)(ejs@3.1.9)(pug@3.0.2):
     resolution: {integrity: sha512-dvx3mdVeSVuIPEaKAoGbxLcenudvhl821xxyuRbcoA+bOJ2dvN8wlGjkLu0ZFMlkCscXZV6lzxy28rafeazI/w==}
+    deprecated: This package is deprecated, please use the new fork @ladjs/koa-views. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/koa-views for updates and release changelog
     peerDependencies:
       '@types/koa': ^2.13.1
     peerDependenciesMeta:
@@ -10487,7 +10251,7 @@ packages:
         optional: true
     dependencies:
       '@types/koa': 2.13.5
-      consolidate: 0.16.0(ejs@3.1.8)(pug@3.0.2)
+      consolidate: 0.16.0(ejs@3.1.9)(pug@3.0.2)
       debug: 4.3.4(supports-color@8.1.1)
       get-paths: 0.0.7
       koa-send: 5.0.1
@@ -10557,7 +10321,7 @@ packages:
       accepts: 1.3.8
       cache-content-type: 1.0.1
       content-disposition: 0.5.4
-      content-type: 1.0.4
+      content-type: 1.0.5
       cookies: 0.8.0
       debug: 4.3.4(supports-color@8.1.1)
       delegates: 1.0.0
@@ -10618,7 +10382,7 @@ packages:
     resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
     engines: {node: '>= 0.6.3'}
     dependencies:
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
 
   /lcid@1.0.0:
     resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==}
@@ -10664,7 +10428,7 @@ packages:
       is-plain-object: 2.0.4
       object.map: 1.0.1
       rechoir: 0.6.2
-      resolve: 1.22.1
+      resolve: 1.22.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -10683,12 +10447,12 @@ packages:
         optional: true
     dependencies:
       cli-truncate: 2.1.0
-      colorette: 2.0.19
+      colorette: 2.0.20
       enquirer: 2.3.6
       log-update: 4.0.0
       p-map: 4.0.0
       rfdc: 1.3.0
-      rxjs: 7.8.0
+      rxjs: 7.8.1
       through: 2.3.8
       wrap-ansi: 7.0.0
     dev: true
@@ -10886,8 +10650,13 @@ packages:
     engines: {node: '>=12'}
     dev: false
 
-  /luxon@3.2.1:
-    resolution: {integrity: sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==}
+  /lru-cache@9.1.2:
+    resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==}
+    engines: {node: 14 || >=16.14}
+    dev: false
+
+  /luxon@3.3.0:
+    resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
     engines: {node: '>=12'}
     dev: false
 
@@ -10911,28 +10680,26 @@ packages:
   /make-error@1.3.6:
     resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
 
-  /make-fetch-happen@10.2.1:
-    resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+  /make-fetch-happen@11.1.1:
+    resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dependencies:
       agentkeepalive: 4.3.0
-      cacache: 16.1.3
+      cacache: 17.1.3
       http-cache-semantics: 4.1.1
       http-proxy-agent: 5.0.0
       https-proxy-agent: 5.0.1
       is-lambda: 1.0.1
       lru-cache: 7.18.3
-      minipass: 3.3.6
-      minipass-collect: 1.0.2
-      minipass-fetch: 2.1.2
+      minipass: 5.0.0
+      minipass-fetch: 3.0.3
       minipass-flush: 1.0.5
       minipass-pipeline: 1.2.4
       negotiator: 0.6.3
       promise-retry: 2.0.1
       socks-proxy-agent: 7.0.0
-      ssri: 9.0.1
+      ssri: 10.0.4
     transitivePeerDependencies:
-      - bluebird
       - supports-color
     dev: false
 
@@ -10988,7 +10755,7 @@ packages:
     dependencies:
       findup-sync: 2.0.0
       micromatch: 3.1.10
-      resolve: 1.22.1
+      resolve: 1.22.2
       stack-trace: 0.0.10
     transitivePeerDependencies:
       - supports-color
@@ -11157,19 +10924,19 @@ packages:
       brace-expansion: 2.0.1
     dev: true
 
-  /minimatch@5.1.2:
-    resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==}
-    engines: {node: '>=10'}
-    dependencies:
-      brace-expansion: 2.0.1
-    dev: false
-
   /minimatch@5.1.6:
     resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
     engines: {node: '>=10'}
     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'}
+    dependencies:
+      brace-expansion: 2.0.1
+    dev: false
+
   /minimist-options@4.1.0:
     resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
     engines: {node: '>= 6'}
@@ -11179,10 +10946,6 @@ packages:
       kind-of: 6.0.3
     dev: true
 
-  /minimist@1.2.7:
-    resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
-    dev: true
-
   /minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 
@@ -11193,11 +10956,11 @@ packages:
       minipass: 3.3.6
     dev: false
 
-  /minipass-fetch@2.1.2:
-    resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+  /minipass-fetch@3.0.3:
+    resolution: {integrity: sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dependencies:
-      minipass: 3.3.6
+      minipass: 5.0.0
       minipass-sized: 1.0.3
       minizlib: 2.1.2
     optionalDependencies:
@@ -11240,13 +11003,6 @@ packages:
       yallist: 4.0.0
     dev: false
 
-  /minipass@4.0.0:
-    resolution: {integrity: sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==}
-    engines: {node: '>=8'}
-    dependencies:
-      yallist: 4.0.0
-    dev: false
-
   /minipass@5.0.0:
     resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
     engines: {node: '>=8'}
@@ -11305,7 +11061,7 @@ packages:
     dependencies:
       ansi-colors: 4.1.1
       browser-stdout: 1.3.1
-      chokidar: 3.5.3
+      chokidar: 3.3.1
       debug: 4.3.4(supports-color@8.1.1)
       diff: 5.0.0
       escape-string-regexp: 4.0.0
@@ -11344,26 +11100,26 @@ packages:
   /ms@2.1.3:
     resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
 
-  /msgpackr-extract@2.2.0:
-    resolution: {integrity: sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==}
+  /msgpackr-extract@3.0.2:
+    resolution: {integrity: sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==}
     hasBin: true
     requiresBuild: true
     dependencies:
-      node-gyp-build-optional-packages: 5.0.3
+      node-gyp-build-optional-packages: 5.0.7
     optionalDependencies:
-      '@msgpackr-extract/msgpackr-extract-darwin-arm64': 2.2.0
-      '@msgpackr-extract/msgpackr-extract-darwin-x64': 2.2.0
-      '@msgpackr-extract/msgpackr-extract-linux-arm': 2.2.0
-      '@msgpackr-extract/msgpackr-extract-linux-arm64': 2.2.0
-      '@msgpackr-extract/msgpackr-extract-linux-x64': 2.2.0
-      '@msgpackr-extract/msgpackr-extract-win32-x64': 2.2.0
+      '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.2
+      '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.2
+      '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.2
+      '@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.8.1:
-    resolution: {integrity: sha512-05fT4J8ZqjYlR4QcRDIhLCYKUOHXk7C/xa62GzMKj74l3up9k2QZ3LgFc6qWdsPHl91QA2WLWqWc8b8t7GLNNw==}
+  /msgpackr@1.9.5:
+    resolution: {integrity: sha512-/IJ3cFSN6Ci3eG2wLhbFEL6GT63yEaoN/R5My2QkV6zro+OJaVRLPlwvxY7EtHYSmDlQpk8stvOQTL2qJFkDRg==}
     optionalDependencies:
-      msgpackr-extract: 2.2.0
+      msgpackr-extract: 3.0.2
     dev: false
 
   /multer@1.4.4-lts.1:
@@ -11501,15 +11257,15 @@ packages:
   /next-tick@1.1.0:
     resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
 
-  /node-abi@3.43.0:
-    resolution: {integrity: sha512-QB0MMv+tn9Ur2DtJrc8y09n0n6sw88CyDniWSX2cHW10goQXYPK9ZpFJOktDS4ron501edPX6h9i7Pg+RnH5nQ==}
+  /node-abi@3.45.0:
+    resolution: {integrity: sha512-iwXuFrMAcFVi/ZoZiqq8BzAdsLw9kxDfTC0HMyjXfSL/6CSDAGD5UmR7azrAgWV1zKYq7dUUMj4owusBWKLsiQ==}
     engines: {node: '>=10'}
     dependencies:
       semver: 7.5.1
     dev: false
 
-  /node-addon-api@5.0.0:
-    resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==}
+  /node-addon-api@5.1.0:
+    resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
     dev: false
 
   /node-addon-api@6.1.0:
@@ -11535,19 +11291,6 @@ packages:
         optional: true
     dependencies:
       whatwg-url: 5.0.0
-    dev: false
-
-  /node-fetch@2.6.7:
-    resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
-    engines: {node: 4.x || >=6.0.0}
-    peerDependencies:
-      encoding: ^0.1.0
-    peerDependenciesMeta:
-      encoding:
-        optional: true
-    dependencies:
-      whatwg-url: 5.0.0
-    dev: true
 
   /node-fetch@3.3.1:
     resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==}
@@ -11557,8 +11300,8 @@ 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==}
+  /node-gyp-build-optional-packages@5.0.7:
+    resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==}
     hasBin: true
     dev: false
     optional: true
@@ -11568,15 +11311,16 @@ packages:
     hasBin: true
     dev: false
 
-  /node-gyp@9.3.1:
-    resolution: {integrity: sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg==}
+  /node-gyp@9.4.0:
+    resolution: {integrity: sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==}
     engines: {node: ^12.13 || ^14.13 || >=16}
     hasBin: true
     dependencies:
       env-paths: 2.2.1
+      exponential-backoff: 3.1.1
       glob: 7.2.3
       graceful-fs: 4.2.11
-      make-fetch-happen: 10.2.1
+      make-fetch-happen: 11.1.1
       nopt: 6.0.0
       npmlog: 6.0.2
       rimraf: 3.0.2
@@ -11584,7 +11328,6 @@ packages:
       tar: 6.1.15
       which: 2.0.2
     transitivePeerDependencies:
-      - bluebird
       - supports-color
     dev: false
 
@@ -11623,7 +11366,7 @@ packages:
     resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
     dependencies:
       hosted-git-info: 2.8.9
-      resolve: 1.22.1
+      resolve: 1.22.2
       semver: 5.7.1
       validate-npm-package-license: 3.0.4
     dev: true
@@ -11633,8 +11376,8 @@ packages:
     engines: {node: '>=10'}
     dependencies:
       hosted-git-info: 4.1.0
-      is-core-module: 2.11.0
-      semver: 7.3.8
+      is-core-module: 2.12.1
+      semver: 7.5.1
       validate-npm-package-license: 3.0.4
     dev: true
 
@@ -11744,8 +11487,8 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /nwsapi@2.2.2:
-    resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==}
+  /nwsapi@2.2.5:
+    resolution: {integrity: sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==}
 
   /oauth-sign@0.9.0:
     resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
@@ -11797,7 +11540,7 @@ packages:
     engines: {node: '>= 0.4'}
     dependencies:
       call-bind: 1.0.2
-      define-properties: 1.1.4
+      define-properties: 1.2.0
       has-symbols: 1.0.3
       object-keys: 1.1.1
     dev: true
@@ -11908,7 +11651,7 @@ packages:
   /ordered-read-streams@1.0.1:
     resolution: {integrity: sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==}
     dependencies:
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
     dev: true
 
   /os-filter-obj@2.0.0:
@@ -12070,7 +11813,7 @@ packages:
     resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
     engines: {node: '>=8'}
     dependencies:
-      '@babel/code-frame': 7.18.6
+      '@babel/code-frame': 7.22.5
       error-ex: 1.3.2
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
@@ -12117,7 +11860,7 @@ packages:
   /parse5@7.1.2:
     resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
     dependencies:
-      entities: 4.4.0
+      entities: 4.5.0
 
   /parseurl@1.3.3:
     resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
@@ -12186,6 +11929,14 @@ packages:
       path-root-regex: 0.1.2
     dev: true
 
+  /path-scurry@1.9.2:
+    resolution: {integrity: sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dependencies:
+      lru-cache: 9.1.2
+      minipass: 5.0.0
+    dev: false
+
   /path-to-regexp@6.2.1:
     resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
     dev: false
@@ -12645,7 +12396,7 @@ packages:
       minimist: 1.2.8
       mkdirp-classic: 0.5.3
       napi-build-utils: 1.0.2
-      node-abi: 3.43.0
+      node-abi: 3.45.0
       pump: 3.0.0
       rc: 1.2.8
       simple-get: 4.0.1
@@ -12710,7 +12461,7 @@ packages:
     dependencies:
       condense-newlines: 0.2.1
       extend-shallow: 2.0.1
-      js-beautify: 1.14.7
+      js-beautify: 1.14.8
 
   /prismjs@1.29.0:
     resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
@@ -12721,7 +12472,7 @@ packages:
     resolution: {integrity: sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw==}
     dependencies:
       ip-regex: 4.3.0
-      ipaddr.js: 2.0.1
+      ipaddr.js: 2.1.0
       is-ip: 3.1.0
       netmask: 2.0.2
     dev: false
@@ -12730,7 +12481,7 @@ packages:
     resolution: {integrity: sha512-ts/YFVwfBeLq61f9+KsOhXW6RH0wvY0gU50R6QZYzgFhggyyLK6WDFeYdjfi/HMnBm2hecLvsR3PB3JcRxDk+A==}
     dependencies:
       ip-regex: 4.3.0
-      ipaddr.js: 2.0.1
+      ipaddr.js: 2.1.0
       is-ip: 3.1.0
       netmask: 2.0.2
     dev: false
@@ -12752,15 +12503,6 @@ packages:
     resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
     engines: {node: '>=0.4.0'}
 
-  /promise-inflight@1.0.1:
-    resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
-    peerDependencies:
-      bluebird: '*'
-    peerDependenciesMeta:
-      bluebird:
-        optional: true
-    dev: false
-
   /promise-limit@2.7.0:
     resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==}
     dev: false
@@ -12844,7 +12586,7 @@ packages:
       jstransformer: 1.0.0
       pug-error: 2.0.0
       pug-walk: 2.0.0
-      resolve: 1.22.1
+      resolve: 1.22.2
 
   /pug-lexer@5.0.1:
     resolution: {integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==}
@@ -13040,8 +12782,8 @@ packages:
     resolution: {integrity: sha512-5FJbRW/Jkkdk29ksedAfWFkQkhbUrMx3QJGwMKAypeIiQf4yrLW+gtPKZiaWt4zPrtw1uGufOjGO7UGM6VllsQ==}
     dev: false
 
-  /raw-body@2.5.1:
-    resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
+  /raw-body@2.5.2:
+    resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
     engines: {node: '>= 0.8'}
     dependencies:
       bytes: 3.1.2
@@ -13073,9 +12815,8 @@ packages:
     dependencies:
       install-artifact-from-github: 1.3.3
       nan: 2.17.0
-      node-gyp: 9.3.1
+      node-gyp: 9.4.0
     transitivePeerDependencies:
-      - bluebird
       - supports-color
     dev: false
 
@@ -13128,8 +12869,8 @@ packages:
       string_decoder: 0.10.31
     dev: false
 
-  /readable-stream@2.3.7:
-    resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
+  /readable-stream@2.3.8:
+    resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
     dependencies:
       core-util-is: 1.0.3
       inherits: 2.0.4
@@ -13139,14 +12880,6 @@ 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'}
-    dependencies:
-      inherits: 2.0.4
-      string_decoder: 1.3.0
-      util-deprecate: 1.0.2
-
   /readable-stream@3.6.2:
     resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
     engines: {node: '>= 6'}
@@ -13154,22 +12887,21 @@ packages:
       inherits: 2.0.4
       string_decoder: 1.3.0
       util-deprecate: 1.0.2
-    dev: false
 
   /readable-web-to-node-stream@3.0.2:
     resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==}
     engines: {node: '>=8'}
     dependencies:
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
 
-  /readdir-glob@1.1.2:
-    resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==}
+  /readdir-glob@1.1.3:
+    resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
     dependencies:
-      minimatch: 5.1.2
+      minimatch: 5.1.6
     dev: false
 
-  /readdirp@3.6.0:
-    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+  /readdirp@3.3.0:
+    resolution: {integrity: sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==}
     engines: {node: '>=8.10.0'}
     dependencies:
       picomatch: 2.3.1
@@ -13178,7 +12910,7 @@ packages:
     resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==}
     engines: {node: '>= 0.10'}
     dependencies:
-      resolve: 1.22.1
+      resolve: 1.22.2
     dev: true
 
   /rechoir@0.8.0:
@@ -13324,7 +13056,7 @@ packages:
     dependencies:
       escape-string-regexp: 1.0.5
       object-assign: 4.1.1
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
     dev: true
 
   /request-progress@3.0.0:
@@ -13444,18 +13176,10 @@ packages:
   /resolve@1.19.0:
     resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==}
     dependencies:
-      is-core-module: 2.11.0
+      is-core-module: 2.12.1
       path-parse: 1.0.7
     dev: true
 
-  /resolve@1.22.1:
-    resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
-    hasBin: true
-    dependencies:
-      is-core-module: 2.11.0
-      path-parse: 1.0.7
-      supports-preserve-symlinks-flag: 1.0.0
-
   /resolve@1.22.2:
     resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==}
     hasBin: true
@@ -13463,7 +13187,6 @@ packages:
       is-core-module: 2.12.1
       path-parse: 1.0.7
       supports-preserve-symlinks-flag: 1.0.0
-    dev: true
 
   /responselike@2.0.1:
     resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
@@ -13557,8 +13280,8 @@ packages:
     dependencies:
       queue-microtask: 1.2.3
 
-  /rxjs@7.8.0:
-    resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==}
+  /rxjs@7.8.1:
+    resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
     dependencies:
       tslib: 2.5.3
     dev: true
@@ -13598,7 +13321,7 @@ packages:
     engines: {node: '>=14.0.0'}
     hasBin: true
     dependencies:
-      chokidar: 3.5.3
+      chokidar: 3.3.1
       immutable: 4.3.0
       source-map-js: 1.0.2
     dev: true
@@ -13624,17 +13347,8 @@ packages:
       xmlchars: 2.2.0
     dev: false
 
-  /schema-utils@3.1.1:
-    resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==}
-    engines: {node: '>= 10.13.0'}
-    dependencies:
-      '@types/json-schema': 7.0.11
-      ajv: 6.12.6
-      ajv-keywords: 3.5.2(ajv@6.12.6)
-    dev: true
-
-  /schema-utils@3.1.2:
-    resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==}
+  /schema-utils@3.2.0:
+    resolution: {integrity: sha512-0zTyLGyDJYd/MBxG1AhJkKa6fpEBds4OQO2ut0w7OYG+ZGhGea09lijvzsqegYSik88zc7cUtIlnnO+/BvD6gQ==}
     engines: {node: '>= 10.13.0'}
     dependencies:
       '@types/json-schema': 7.0.12
@@ -13685,6 +13399,7 @@ packages:
     hasBin: true
     dependencies:
       lru-cache: 6.0.0
+    dev: true
 
   /semver@7.5.1:
     resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==}
@@ -13810,7 +13525,6 @@ packages:
   /signal-exit@4.0.2:
     resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==}
     engines: {node: '>=14'}
-    dev: true
 
   /simple-concat@1.0.1:
     resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
@@ -13995,11 +13709,11 @@ packages:
     engines: {node: '>= 0.10'}
     dev: true
 
-  /spdx-correct@3.1.1:
-    resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
+  /spdx-correct@3.2.0:
+    resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
     dependencies:
       spdx-expression-parse: 3.0.1
-      spdx-license-ids: 3.0.12
+      spdx-license-ids: 3.0.13
     dev: true
 
   /spdx-exceptions@2.3.0:
@@ -14010,11 +13724,11 @@ packages:
     resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
     dependencies:
       spdx-exceptions: 2.3.0
-      spdx-license-ids: 3.0.12
+      spdx-license-ids: 3.0.13
     dev: true
 
-  /spdx-license-ids@3.0.12:
-    resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==}
+  /spdx-license-ids@3.0.13:
+    resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==}
     dev: true
 
   /speakeasy@2.0.0:
@@ -14068,11 +13782,11 @@ packages:
     resolution: {integrity: sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==}
     dev: true
 
-  /ssri@9.0.1:
-    resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+  /ssri@10.0.4:
+    resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dependencies:
-      minipass: 3.3.6
+      minipass: 5.0.0
     dev: false
 
   /stack-trace@0.0.10:
@@ -14159,8 +13873,8 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /string-argv@0.3.1:
-    resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
+  /string-argv@0.3.2:
+    resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
     engines: {node: '>=0.6.19'}
     dev: true
 
@@ -14196,7 +13910,6 @@ packages:
       eastasianwidth: 0.2.0
       emoji-regex: 9.2.2
       strip-ansi: 7.1.0
-    dev: true
 
   /string_decoder@0.10.31:
     resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==}
@@ -14235,7 +13948,6 @@ packages:
     engines: {node: '>=12'}
     dependencies:
       ansi-regex: 6.0.1
-    dev: true
 
   /strip-bom@2.0.0:
     resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==}
@@ -14457,7 +14169,7 @@ packages:
       end-of-stream: 1.4.4
       fs-constants: 1.0.0
       inherits: 2.0.4
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: false
 
   /tar@4.4.19:
@@ -14474,18 +14186,6 @@ packages:
     dev: false
     optional: true
 
-  /tar@6.1.13:
-    resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==}
-    engines: {node: '>=10'}
-    dependencies:
-      chownr: 2.0.0
-      fs-minipass: 2.1.0
-      minipass: 4.0.0
-      minizlib: 2.1.2
-      mkdirp: 1.0.4
-      yallist: 4.0.0
-    dev: false
-
   /tar@6.1.15:
     resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==}
     engines: {node: '>=10'}
@@ -14530,25 +14230,14 @@ packages:
       '@jridgewell/trace-mapping': 0.3.18
       '@swc/core': 1.3.62
       jest-worker: 27.5.1
-      schema-utils: 3.1.2
+      schema-utils: 3.2.0
       serialize-javascript: 6.0.1
-      terser: 5.17.7
+      terser: 5.18.0
       webpack: 5.85.1(@swc/core@1.3.62)(webpack-cli@5.1.3)
     dev: true
 
-  /terser@5.16.1:
-    resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      '@jridgewell/source-map': 0.3.2
-      acorn: 8.8.1
-      commander: 2.20.3
-      source-map-support: 0.5.21
-    dev: true
-
-  /terser@5.17.7:
-    resolution: {integrity: sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==}
+  /terser@5.18.0:
+    resolution: {integrity: sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
@@ -14570,14 +14259,14 @@ packages:
       bmp-js: 0.1.0
       file-type: 12.4.2
       idb-keyval: 3.2.0
-      is-electron: 2.2.1
+      is-electron: 2.2.2
       is-url: 1.2.4
       node-fetch: 2.6.11
       opencollective-postinstall: 2.0.3
       regenerator-runtime: 0.13.11
       resolve-url: 0.2.1
       tesseract.js-core: 3.0.2
-      wasm-feature-detect: 1.4.0
+      wasm-feature-detect: 1.5.1
       zlibjs: 0.3.1
     transitivePeerDependencies:
       - encoding
@@ -14644,14 +14333,14 @@ packages:
   /through2@2.0.5:
     resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
     dependencies:
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
       xtend: 4.0.2
     dev: true
 
   /through2@4.0.2:
     resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
     dependencies:
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: true
 
   /through@2.3.4:
@@ -14759,8 +14448,8 @@ packages:
       psl: 1.9.0
       punycode: 2.3.0
 
-  /tough-cookie@4.1.2:
-    resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
+  /tough-cookie@4.1.3:
+    resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
     engines: {node: '>=6'}
     dependencies:
       psl: 1.9.0
@@ -14801,7 +14490,7 @@ packages:
       escape-string-regexp: 5.0.0
     dev: true
 
-  /ts-jest@27.1.2(@babel/core@7.21.4)(@types/jest@27.4.0)(jest@27.4.5)(typescript@4.5.4):
+  /ts-jest@27.1.2(@babel/core@7.22.5)(@types/jest@27.4.0)(jest@27.4.5)(typescript@4.5.4):
     resolution: {integrity: sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA==}
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     hasBin: true
@@ -14822,7 +14511,7 @@ packages:
       esbuild:
         optional: true
     dependencies:
-      '@babel/core': 7.21.4
+      '@babel/core': 7.22.5
       '@types/jest': 27.4.0
       bs-logger: 0.2.6
       fast-json-stable-stringify: 2.1.0
@@ -14831,7 +14520,7 @@ packages:
       json5: 2.2.3
       lodash.memoize: 4.1.2
       make-error: 1.3.6
-      semver: 7.3.8
+      semver: 7.5.1
       typescript: 4.5.4
       yargs-parser: 20.2.9
     dev: true
@@ -14844,7 +14533,7 @@ packages:
       webpack: ^5.0.0
     dependencies:
       chalk: 4.1.2
-      enhanced-resolve: 5.14.1
+      enhanced-resolve: 5.15.0
       micromatch: 4.0.5
       semver: 7.5.1
       typescript: 5.1.3
@@ -14869,9 +14558,9 @@ packages:
       '@tsconfig/node10': 1.0.9
       '@tsconfig/node12': 1.0.11
       '@tsconfig/node14': 1.0.3
-      '@tsconfig/node16': 1.0.3
+      '@tsconfig/node16': 1.0.4
       '@types/node': 17.0.5
-      acorn: 8.8.1
+      acorn: 8.8.2
       acorn-walk: 8.2.0
       arg: 4.1.3
       create-require: 1.1.1
@@ -14900,9 +14589,9 @@ packages:
       '@tsconfig/node10': 1.0.9
       '@tsconfig/node12': 1.0.11
       '@tsconfig/node14': 1.0.3
-      '@tsconfig/node16': 1.0.3
+      '@tsconfig/node16': 1.0.4
       '@types/node': 18.11.18
-      acorn: 8.8.1
+      acorn: 8.8.2
       acorn-walk: 8.2.0
       arg: 4.1.3
       create-require: 1.1.1
@@ -14916,7 +14605,7 @@ packages:
     resolution: {integrity: sha512-vq+i6VpE83IeMsSJVcFN03ZBofADhr8/gIJXjxpbnTRfN/MFXy0+SBaKG2o7p95QqXBGkeG98HYz3IkOOveFbg==}
     hasBin: true
     dependencies:
-      chokidar: 3.5.3
+      chokidar: 3.3.1
       commander: 9.5.0
       globby: 11.1.0
       mylas: 2.1.13
@@ -14950,10 +14639,6 @@ packages:
     resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
     dev: true
 
-  /tslib@2.4.1:
-    resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
-    dev: false
-
   /tslib@2.5.3:
     resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==}
 
@@ -15203,16 +14888,16 @@ packages:
     resolution: {integrity: sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==}
     dev: true
 
-  /unique-filename@2.0.1:
-    resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+  /unique-filename@3.0.0:
+    resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dependencies:
-      unique-slug: 3.0.0
+      unique-slug: 4.0.0
     dev: false
 
-  /unique-slug@3.0.0:
-    resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==}
-    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+  /unique-slug@4.0.0:
+    resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
+    engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dependencies:
       imurmurhash: 0.1.4
     dev: false
@@ -15259,13 +14944,13 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /update-browserslist-db@1.0.11(browserslist@4.21.7):
+  /update-browserslist-db@1.0.11(browserslist@4.21.8):
     resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
     hasBin: true
     peerDependencies:
       browserslist: '>= 4.21.0'
     dependencies:
-      browserslist: 4.21.7
+      browserslist: 4.21.8
       escalade: 3.1.1
       picocolors: 1.0.0
 
@@ -15375,7 +15060,7 @@ packages:
   /validate-npm-package-license@3.0.4:
     resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
     dependencies:
-      spdx-correct: 3.1.1
+      spdx-correct: 3.2.0
       spdx-expression-parse: 3.0.1
     dev: true
 
@@ -15422,7 +15107,7 @@ packages:
       lead: 1.0.0
       object.assign: 4.1.4
       pumpify: 1.5.1
-      readable-stream: 2.3.7
+      readable-stream: 2.3.8
       remove-bom-buffer: 3.0.0
       remove-bom-stream: 1.2.0
       resolve-options: 1.1.0
@@ -15589,10 +15274,10 @@ packages:
     hasBin: true
     dependencies:
       axios: 0.25.0(debug@4.3.4)
-      joi: 17.7.0
+      joi: 17.9.2
       lodash: 4.17.21
       minimist: 1.2.8
-      rxjs: 7.8.0
+      rxjs: 7.8.1
     transitivePeerDependencies:
       - debug
     dev: true
@@ -15603,8 +15288,8 @@ packages:
       makeerror: 1.0.12
     dev: true
 
-  /wasm-feature-detect@1.4.0:
-    resolution: {integrity: sha512-9z4Yk3SeHQdb7E4g6ywQYcUt1CHr9HFiCIA5h29rv0gmW4lCE5dnDhheiJI5qzVPSDnXHyXQPLGPyucsuIzacw==}
+  /wasm-feature-detect@1.5.1:
+    resolution: {integrity: sha512-GHr23qmuehNXHY4902/hJ6EV5sUANIJC3R/yMfQ7hWDg3nfhlcJfnIL96R2ohpIwa62araN6aN4bLzzzq5GXkg==}
     dev: false
 
   /watchpack@2.4.0:
@@ -15715,10 +15400,10 @@ packages:
       '@webassemblyjs/wasm-parser': 1.11.6
       acorn: 8.8.2
       acorn-import-assertions: 1.9.0(acorn@8.8.2)
-      browserslist: 4.21.7
+      browserslist: 4.21.8
       chrome-trace-event: 1.0.3
-      enhanced-resolve: 5.14.1
-      es-module-lexer: 1.2.1
+      enhanced-resolve: 5.15.0
+      es-module-lexer: 1.3.0
       eslint-scope: 5.1.1
       events: 3.3.0
       glob-to-regexp: 0.4.1
@@ -15727,7 +15412,7 @@ packages:
       loader-runner: 4.3.0
       mime-types: 2.1.35
       neo-async: 2.6.2
-      schema-utils: 3.1.2
+      schema-utils: 3.2.0
       tapable: 2.2.1
       terser-webpack-plugin: 5.3.9(@swc/core@1.3.62)(webpack@5.85.1)
       watchpack: 2.4.0
@@ -15855,8 +15540,8 @@ packages:
     resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
     engines: {node: '>= 10.0.0'}
     dependencies:
-      '@babel/parser': 7.22.4
-      '@babel/types': 7.22.4
+      '@babel/parser': 7.22.5
+      '@babel/types': 7.22.5
       assert-never: 1.2.1
       babel-walk: 3.0.0-canary-5
 
@@ -15892,6 +15577,15 @@ packages:
       string-width: 4.2.3
       strip-ansi: 6.0.1
 
+  /wrap-ansi@8.1.0:
+    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-styles: 6.2.1
+      string-width: 5.1.2
+      strip-ansi: 7.1.0
+    dev: false
+
   /wrappy@1.0.2:
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
 
@@ -15973,7 +15667,7 @@ packages:
   /xml2js@0.4.19:
     resolution: {integrity: sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==}
     dependencies:
-      sax: 1.2.4
+      sax: 1.2.1
       xmlbuilder: 9.0.7
     dev: false
 
@@ -16051,11 +15745,11 @@ packages:
   /yargs-parser@20.2.4:
     resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==}
     engines: {node: '>=10'}
-    dev: true
 
   /yargs-parser@20.2.9:
     resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
     engines: {node: '>=10'}
+    dev: true
 
   /yargs-parser@21.1.1:
     resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
@@ -16105,20 +15799,7 @@ packages:
       require-directory: 2.1.1
       string-width: 4.2.3
       y18n: 5.0.8
-      yargs-parser: 20.2.9
-
-  /yargs@17.6.2:
-    resolution: {integrity: sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==}
-    engines: {node: '>=12'}
-    dependencies:
-      cliui: 8.0.1
-      escalade: 3.1.1
-      get-caller-file: 2.0.5
-      require-directory: 2.1.1
-      string-width: 4.2.3
-      y18n: 5.0.8
-      yargs-parser: 21.1.1
-    dev: true
+      yargs-parser: 20.2.4
 
   /yargs@17.7.2:
     resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
@@ -16131,7 +15812,6 @@ packages:
       string-width: 4.2.3
       y18n: 5.0.8
       yargs-parser: 21.1.1
-    dev: false
 
   /yargs@7.1.2:
     resolution: {integrity: sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==}
@@ -16194,15 +15874,15 @@ packages:
     dependencies:
       archiver-utils: 2.1.0
       compress-commons: 4.1.1
-      readable-stream: 3.6.0
+      readable-stream: 3.6.2
     dev: false
 
   /zlibjs@0.3.1:
     resolution: {integrity: sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==}
     dev: false
 
-  /zod@3.20.3:
-    resolution: {integrity: sha512-+MLeeUcLTlnzVo5xDn9+LVN9oX4esvgZ7qfZczBN+YVUvZBafIrPPVyG2WdjMWU2Qkb2ZAh2M8lpqf1wIoGqJQ==}
+  /zod@3.21.4:
+    resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
     dev: false
 
   github.com/misskey-dev/browser-image-resizer/56f504427ad7f6500e141a6d9f3aee42023d7f3e:
@@ -16216,7 +15896,7 @@ packages:
     name: plyr
     version: 3.7.0
     dependencies:
-      core-js: 3.30.2
+      core-js: 3.31.0
       custom-event-polyfill: 1.0.7
       loadjs: 4.2.0
       rangetouch: 2.0.1

From 7517c77ca9e6cfec33e779c4fa18895247a2c0be Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Tue, 13 Jun 2023 19:13:54 -0700
Subject: [PATCH 11/56] build: :hammer: add cargo clean to clean-all

---
 scripts/clean-all.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/scripts/clean-all.js b/scripts/clean-all.js
index e347ee6a9f..c3d85bf3a5 100644
--- a/scripts/clean-all.js
+++ b/scripts/clean-all.js
@@ -18,4 +18,9 @@ const execa = require('execa');
 		cwd: __dirname + '/../',
 		stdio: 'inherit'
 	});
+
+	execa('cargo', ['clean'], {
+		cwd: __dirname + '/../packages/backend/native-utils',
+		stdio: 'inherit'
+	});
 })();

From d4bf86d0f80f7c621056a68896a081683135a1c8 Mon Sep 17 00:00:00 2001
From: Kainoa Kanter <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 04:59:43 +0200
Subject: [PATCH 12/56] chore: Added translation using Weblate (Portuguese
 (Brazil))

---
 locales/pt_BR.yml | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 locales/pt_BR.yml

diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/locales/pt_BR.yml
@@ -0,0 +1 @@
+{}

From 76dc36fd53e5f61333fd6d7c0dc47705001facbb Mon Sep 17 00:00:00 2001
From: Ettore Atalan <atalanttore@googlemail.com>
Date: Tue, 13 Jun 2023 17:16:06 +0000
Subject: [PATCH 13/56] chore: Translated using Weblate (German)

Currently translated at 98.6% (1762 of 1787 strings)

Translation: Calckey/locales
Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/
---
 locales/de-DE.yml | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 796787cb4b..5d0459395c 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1468,9 +1468,13 @@ _widgets:
   aichan: "Ai"
   _userList:
     chooseList: Wählen Sie eine Liste aus
-  userList: Nutzerliste
+  userList: Benutzerliste
+  serverInfo: Server-Infos
+  meiliStatus: Server-Status
+  meiliSize: Indexgröße
+  meiliIndexCount: Indexierte Beiträge
 _cw:
-  hide: "Inhalt verbergen"
+  hide: "Verbergen"
   show: "Inhalt anzeigen"
   chars: "{count} Zeichen"
   files: "{count} Datei(en)"
@@ -1929,10 +1933,11 @@ _deck:
     widgets: "Widgets"
     notifications: "Benachrichtigungen"
     tl: "Timeline"
-    antenna: "News-Picker"
+    antenna: "Antenne"
     list: "Listen"
     mentions: "Erwähnungen"
     direct: "Direktnachrichten"
+    channel: Kanal
   renameProfile: Arbeitsbereich umbenennen
   nameAlreadyExists: Der Name für den Arbeitsbereich ist bereits vorhanden.
 enableRecommendedTimeline: '"Favoriten"-Timeline einschalten'
@@ -2073,11 +2078,12 @@ _experiments:
   title: Funktionstests
   postEditingCaption: Zeigt die Option für Nutzer an, ihre bestehenden Beiträge über
     das Menü "Beitragsoptionen" zu bearbeiten
+  enablePostImports: Beitragsimporte aktivieren
 noGraze: Bitte deaktivieren Sie die Browsererweiterung "Graze for Mastodon", da sie
   die Funktion von Calckey stört.
 indexFrom: Indexieren ab Beitragskennung aufwärts
-indexNotice: Wird indexiert. Dies kann einige Zeit dauern. Bitte den Server für mindestens
-  eine Stunde nicht neu starten.
+indexNotice: Wird jetzt indexiert. Dies wird wahrscheinlich eine Weile dauern, bitte
+  starten Sie Ihren Server für mindestens eine Stunde nicht neu.
 customKaTeXMacroDescription: "Richten Sie Makros ein, um mathematische Ausdrücke einfach
   zu schreiben! Die Notation entspricht den LaTeX-Befehlsdefinitionen und wird als\n
   \\newcommand{\\name}{content} or \\newcommand{\\name}[number of arguments]{content}\n
@@ -2097,3 +2103,12 @@ image: Bild
 video: Video
 audio: Audio
 indexFromDescription: Leer lassen, um jeden Beitrag zu indexieren
+_filters:
+  fromUser: Von Benutzer
+  notesAfter: Beiträge nach
+  withFile: Mit Datei
+  fromDomain: Von Domain
+  notesBefore: Beiträge vor
+isBot: Dieses Konto ist ein Bot
+isModerator: Moderator
+isAdmin: Administrator

From 3075c535a220a655c1149f502166c82199c462c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Al=C3=A9xia?= <alexiacordeiro@tutanota.com>
Date: Wed, 14 Jun 2023 03:08:26 +0000
Subject: [PATCH 14/56] chore: Translated using Weblate (Portuguese (Brazil))

Currently translated at 0.6% (12 of 1787 strings)

Translation: Calckey/locales
Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pt_BR/
---
 locales/pt_BR.yml | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml
index 0967ef424b..d56a9fcae4 100644
--- a/locales/pt_BR.yml
+++ b/locales/pt_BR.yml
@@ -1 +1,14 @@
-{}
+username: Nome de usuário
+ok: OK
+_lang_: Inglês
+headlineMisskey: Uma plataforma de mídia social descentralizada e de código aberto
+  que é gratuita para sempre! 🚀
+search: Pesquisar
+gotIt: Entendi!
+introMisskey: Bem vinde! Calckey é uma plataforma de mídia social descentralizada
+  e de código aberto que é gratuita para sempre! 🚀
+searchPlaceholder: Pesquise no Calckey
+notifications: Notificações
+password: Senha
+forgotPassword: Esqueci a senha
+cancel: Cancelar

From 9f7151e3aa592e42d912462ded52835db4f92924 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 11:52:35 -0700
Subject: [PATCH 15/56] =?UTF-8?q?style:=20=F0=9F=92=84=20full=20follow=20b?=
 =?UTF-8?q?utton=20for=20userinfo=20in=20userlist?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/client/src/components/MkUserInfo.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/client/src/components/MkUserInfo.vue b/packages/client/src/components/MkUserInfo.vue
index e2b579eaf5..f53aa7b8ef 100644
--- a/packages/client/src/components/MkUserInfo.vue
+++ b/packages/client/src/components/MkUserInfo.vue
@@ -49,7 +49,7 @@
 			v-if="$i && user.id != $i.id"
 			class="koudoku-button"
 			:user="user"
-			mini
+			full
 		/>
 	</div>
 </template>

From 8b201fddbd008679e37e1c6e75b7e69f12255712 Mon Sep 17 00:00:00 2001
From: Freeplay <freeplay@duck.com>
Date: Wed, 14 Jun 2023 14:57:16 -0400
Subject: [PATCH 16/56] hide acc menu button in notifications

---
 packages/client/src/components/MkFollowButton.vue | 3 ++-
 packages/client/src/components/MkNotification.vue | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/client/src/components/MkFollowButton.vue b/packages/client/src/components/MkFollowButton.vue
index 8d00235a03..04f5e33115 100644
--- a/packages/client/src/components/MkFollowButton.vue
+++ b/packages/client/src/components/MkFollowButton.vue
@@ -1,5 +1,5 @@
 <template>
-	<button class="menu _button" @click.stop="menu" v-tooltip="i18n.ts.menu">
+	<button v-if="!hideMenu" class="menu _button" @click.stop="menu" v-tooltip="i18n.ts.menu">
 		<i class="ph-dots-three-outline ph-bold ph-lg"></i>
 	</button>
 	<button
@@ -74,6 +74,7 @@ const props = withDefaults(
 		user: Misskey.entities.UserDetailed;
 		full?: boolean;
 		large?: boolean;
+		hideMenu?: boolean;
 	}>(),
 	{
 		full: false,
diff --git a/packages/client/src/components/MkNotification.vue b/packages/client/src/components/MkNotification.vue
index 268774c66d..ac51008676 100644
--- a/packages/client/src/components/MkNotification.vue
+++ b/packages/client/src/components/MkNotification.vue
@@ -219,6 +219,7 @@
 					<MkFollowButton
 						:user="notification.user"
 						:full="true"
+						:hideMenu="true"
 					/></div
 			></span>
 			<span

From 8f61ff7f337ec4b35c79337ff0771b90c27504b9 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 20:17:56 -0700
Subject: [PATCH 17/56] =?UTF-8?q?fix:=20=F0=9F=9A=B8=20make=20"show=20repl?=
 =?UTF-8?q?ies=20in=20timeline"=20work=20as=20expected?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Syuilo <syuilotan@yahoo.co.jp>
---
 ...684206886988-remove-showTimelineReplies.js | 15 ++++++++
 .../native-utils/src/model/entity/user.rs     |  2 -
 packages/backend/src/models/entities/user.ts  |  6 ---
 .../backend/src/models/repositories/user.ts   |  1 -
 .../src/remote/activitypub/models/person.ts   |  1 -
 .../api/common/generate-replies-query.ts      | 17 ++++-----
 .../src/server/api/endpoints/i/update.ts      |  3 --
 .../api/endpoints/notes/global-timeline.ts    |  7 +++-
 .../api/endpoints/notes/hybrid-timeline.ts    |  7 +++-
 .../api/endpoints/notes/local-timeline.ts     |  7 +++-
 .../endpoints/notes/recommended-timeline.ts   |  7 +++-
 .../server/api/endpoints/notes/timeline.ts    |  7 +++-
 .../api/stream/channels/global-timeline.ts    |  5 ++-
 .../api/stream/channels/home-timeline.ts      |  5 ++-
 .../api/stream/channels/hybrid-timeline.ts    |  5 ++-
 .../api/stream/channels/local-timeline.ts     |  5 ++-
 .../stream/channels/recommended-timeline.ts   |  5 ++-
 packages/backend/test/e2e/users.ts            | 19 +---------
 .../client/src/components/MkFollowButton.vue  |  7 +++-
 packages/client/src/components/MkTimeline.vue | 35 +++++++++++++++---
 .../client/src/pages/settings/general.vue     | 23 ++----------
 .../pages/settings/preferences-backups.vue    |  1 +
 packages/client/src/store.ts                  |  4 ++
 packages/client/src/stream.ts                 | 37 ++++++++++++++-----
 24 files changed, 145 insertions(+), 86 deletions(-)
 create mode 100644 packages/backend/migration/1684206886988-remove-showTimelineReplies.js

diff --git a/packages/backend/migration/1684206886988-remove-showTimelineReplies.js b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js
new file mode 100644
index 0000000000..e5f8483c7f
--- /dev/null
+++ b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js
@@ -0,0 +1,15 @@
+export class RemoveShowTimelineReplies1684206886988 {
+	name = "RemoveShowTimelineReplies1684206886988";
+
+	async up(queryRunner) {
+		await queryRunner.query(
+			`ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`,
+		);
+	}
+
+	async down(queryRunner) {
+		await queryRunner.query(
+			`ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`,
+		);
+	}
+}
diff --git a/packages/backend/native-utils/src/model/entity/user.rs b/packages/backend/native-utils/src/model/entity/user.rs
index f30fd8ace8..e76ae08c7f 100644
--- a/packages/backend/native-utils/src/model/entity/user.rs
+++ b/packages/backend/native-utils/src/model/entity/user.rs
@@ -63,8 +63,6 @@ pub struct Model {
     pub hide_online_status: bool,
     #[sea_orm(column_name = "isDeleted")]
     pub is_deleted: bool,
-    #[sea_orm(column_name = "showTimelineReplies")]
-    pub show_timeline_replies: bool,
     #[sea_orm(column_name = "driveCapacityOverrideMb")]
     pub drive_capacity_override_mb: Option<i32>,
     #[sea_orm(column_name = "movedToUri")]
diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts
index 53dc7e60b0..ddad9f3b23 100644
--- a/packages/backend/src/models/entities/user.ts
+++ b/packages/backend/src/models/entities/user.ts
@@ -249,12 +249,6 @@ export class User {
 	})
 	public followersUri: string | null;
 
-	@Column("boolean", {
-		default: false,
-		comment: "Whether to show users replying to other users in the timeline.",
-	})
-	public showTimelineReplies: boolean;
-
 	@Index({ unique: true })
 	@Column("char", {
 		length: 16,
diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts
index 1ca9b32893..48c8d75b3b 100644
--- a/packages/backend/src/models/repositories/user.ts
+++ b/packages/backend/src/models/repositories/user.ts
@@ -567,7 +567,6 @@ export const UserRepository = db.getRepository(User).extend({
 						mutedInstances: profile!.mutedInstances,
 						mutingNotificationTypes: profile!.mutingNotificationTypes,
 						emailNotificationTypes: profile!.emailNotificationTypes,
-						showTimelineReplies: user.showTimelineReplies || falsy,
 				  }
 				: {}),
 
diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts
index 9e21fa9ff5..f8208e6d7b 100644
--- a/packages/backend/src/remote/activitypub/models/person.ts
+++ b/packages/backend/src/remote/activitypub/models/person.ts
@@ -279,7 +279,6 @@ export async function createPerson(
 					tags,
 					isBot,
 					isCat: (person as any).isCat === true,
-					showTimelineReplies: false,
 				}),
 			)) as IRemoteUser;
 
diff --git a/packages/backend/src/server/api/common/generate-replies-query.ts b/packages/backend/src/server/api/common/generate-replies-query.ts
index 140c1d74a0..845fef130f 100644
--- a/packages/backend/src/server/api/common/generate-replies-query.ts
+++ b/packages/backend/src/server/api/common/generate-replies-query.ts
@@ -4,7 +4,8 @@ import { Brackets } from "typeorm";
 
 export function generateRepliesQuery(
 	q: SelectQueryBuilder<any>,
-	me?: Pick<User, "id" | "showTimelineReplies"> | null,
+	withReplies: boolean,
+	me?: Pick<User, "id"> | null,
 ) {
 	if (me == null) {
 		q.andWhere(
@@ -20,25 +21,21 @@ export function generateRepliesQuery(
 					);
 			}),
 		);
-	} else if (!me.showTimelineReplies) {
+	} else if (!withReplies) {
 		q.andWhere(
 			new Brackets((qb) => {
 				qb.where("note.replyId IS NULL") // 返信ではない
 					.orWhere("note.replyUserId = :meId", { meId: me.id }) // 返信だけど自分のノートへの返信
 					.orWhere(
 						new Brackets((qb) => {
-							qb.where(
-								// 返信だけど自分の行った返信
-								"note.replyId IS NOT NULL",
-							).andWhere("note.userId = :meId", { meId: me.id });
+							qb.where("note.replyId IS NOT NULL") // 返信だけど自分の行った返信
+								.andWhere("note.userId = :meId", { meId: me.id });
 						}),
 					)
 					.orWhere(
 						new Brackets((qb) => {
-							qb.where(
-								// 返信だけど投稿者自身への返信
-								"note.replyId IS NOT NULL",
-							).andWhere("note.replyUserId = note.userId");
+							qb.where("note.replyId IS NOT NULL") // 返信だけど投稿者自身への返信
+								.andWhere("note.replyUserId = note.userId");
 						}),
 					);
 			}),
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index f3ff704ce7..0637251a6b 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -106,7 +106,6 @@ export const paramDef = {
 		isBot: { type: "boolean" },
 		isCat: { type: "boolean" },
 		speakAsCat: { type: "boolean" },
-		showTimelineReplies: { type: "boolean" },
 		injectFeaturedNote: { type: "boolean" },
 		receiveAnnouncementEmail: { type: "boolean" },
 		alwaysMarkNsfw: { type: "boolean" },
@@ -185,8 +184,6 @@ export default define(meta, paramDef, async (ps, _user, token) => {
 	if (typeof ps.publicReactions === "boolean")
 		profileUpdates.publicReactions = ps.publicReactions;
 	if (typeof ps.isBot === "boolean") updates.isBot = ps.isBot;
-	if (typeof ps.showTimelineReplies === "boolean")
-		updates.showTimelineReplies = ps.showTimelineReplies;
 	if (typeof ps.carefulBot === "boolean")
 		profileUpdates.carefulBot = ps.carefulBot;
 	if (typeof ps.autoAcceptFollowed === "boolean")
diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
index 78a1932830..0a365a6dfd 100644
--- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
@@ -53,6 +53,11 @@ export const paramDef = {
 		untilId: { type: "string", format: "misskey:id" },
 		sinceDate: { type: "integer" },
 		untilDate: { type: "integer" },
+		withReplies: {
+			type: "boolean",
+			default: false,
+			description: "Show replies in the timeline",
+		},
 	},
 	required: [],
 } as const;
@@ -87,7 +92,7 @@ export default define(meta, paramDef, async (ps, user) => {
 		.leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar")
 		.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
 
-	generateRepliesQuery(query, user);
+	generateRepliesQuery(query, ps.withReplies, user);
 	if (user) {
 		generateMutedUserQuery(query, user);
 		generateMutedNoteQuery(query, user);
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 508b268cc0..4e32b0ab29 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -60,6 +60,11 @@ export const paramDef = {
 			default: false,
 			description: "Only show notes that have attached files.",
 		},
+		withReplies: {
+			type: "boolean",
+			default: false,
+			description: "Show replies in the timeline",
+		},
 	},
 	required: [],
 } as const;
@@ -104,7 +109,7 @@ export default define(meta, paramDef, async (ps, user) => {
 		.setParameters(followingQuery.getParameters());
 
 	generateChannelQuery(query, user);
-	generateRepliesQuery(query, user);
+	generateRepliesQuery(query, ps.withReplies, user);
 	generateVisibilityQuery(query, user);
 	generateMutedUserQuery(query, user);
 	generateMutedNoteQuery(query, user);
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 797c6d77cb..82e93e371f 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -63,6 +63,11 @@ export const paramDef = {
 		untilId: { type: "string", format: "misskey:id" },
 		sinceDate: { type: "integer" },
 		untilDate: { type: "integer" },
+		withReplies: {
+			type: "boolean",
+			default: false,
+			description: "Show replies in the timeline",
+		},
 	},
 	required: [],
 } as const;
@@ -97,7 +102,7 @@ export default define(meta, paramDef, async (ps, user) => {
 		.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
 
 	generateChannelQuery(query, user);
-	generateRepliesQuery(query, user);
+	generateRepliesQuery(query, ps.withReplies, user);
 	generateVisibilityQuery(query, user);
 	if (user) generateMutedUserQuery(query, user);
 	if (user) generateMutedNoteQuery(query, user);
diff --git a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts
index 321ab4ad7d..d3b5cbff50 100644
--- a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts
@@ -63,6 +63,11 @@ export const paramDef = {
 		untilId: { type: "string", format: "misskey:id" },
 		sinceDate: { type: "integer" },
 		untilDate: { type: "integer" },
+		withReplies: {
+			type: "boolean",
+			default: false,
+			description: "Show replies in the timeline",
+		},
 	},
 	required: [],
 } as const;
@@ -100,7 +105,7 @@ export default define(meta, paramDef, async (ps, user) => {
 		.leftJoinAndSelect("renoteUser.banner", "renoteUserBanner");
 
 	generateChannelQuery(query, user);
-	generateRepliesQuery(query, user);
+	generateRepliesQuery(query, ps.withReplies, user);
 	generateVisibilityQuery(query, user);
 	if (user) generateMutedUserQuery(query, user);
 	if (user) generateMutedNoteQuery(query, user);
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 62996efdde..d629deebb6 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -54,6 +54,11 @@ export const paramDef = {
 			default: false,
 			description: "Only show notes that have attached files.",
 		},
+		withReplies: {
+			type: "boolean",
+			default: false,
+			description: "Show replies in the timeline",
+		},
 	},
 	required: [],
 } as const;
@@ -100,7 +105,7 @@ export default define(meta, paramDef, async (ps, user) => {
 		.setParameters(followingQuery.getParameters());
 
 	generateChannelQuery(query, user);
-	generateRepliesQuery(query, user);
+	generateRepliesQuery(query, ps.withReplies, user);
 	generateVisibilityQuery(query, user);
 	generateMutedUserQuery(query, user);
 	generateMutedNoteQuery(query, user);
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 4e8263bbed..2257be2b8c 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -9,6 +9,7 @@ export default class extends Channel {
 	public readonly chName = "globalTimeline";
 	public static shouldShare = true;
 	public static requireCredential = false;
+	private withReplies: boolean;
 
 	constructor(id: string, connection: Channel["connection"]) {
 		super(id, connection);
@@ -22,6 +23,8 @@ export default class extends Channel {
 				return;
 		}
 
+		this.withReplies = params.withReplies as boolean;
+
 		// Subscribe events
 		this.subscriber.on("notesStream", this.onNote);
 	}
@@ -31,7 +34,7 @@ export default class extends Channel {
 		if (note.channelId != null) return;
 
 		// 関係ない返信は除外
-		if (note.reply && !this.user!.showTimelineReplies) {
+		if (note.reply && !this.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (
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 bdcd8a2839..47875aeda7 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -8,6 +8,7 @@ export default class extends Channel {
 	public readonly chName = "homeTimeline";
 	public static shouldShare = true;
 	public static requireCredential = true;
+	private withReplies: boolean;
 
 	constructor(id: string, connection: Channel["connection"]) {
 		super(id, connection);
@@ -15,6 +16,8 @@ export default class extends Channel {
 	}
 
 	public async init(params: any) {
+		this.withReplies = params.withReplies as boolean;
+
 		// Subscribe events
 		this.subscriber.on("notesStream", this.onNote);
 	}
@@ -39,7 +42,7 @@ export default class extends Channel {
 			return;
 
 		// 関係ない返信は除外
-		if (note.reply && !this.user!.showTimelineReplies) {
+		if (note.reply && !this.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (
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 9205d609d1..1f1a9b831a 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -9,6 +9,7 @@ export default class extends Channel {
 	public readonly chName = "hybridTimeline";
 	public static shouldShare = true;
 	public static requireCredential = true;
+	private withReplies: boolean;
 
 	constructor(id: string, connection: Channel["connection"]) {
 		super(id, connection);
@@ -24,6 +25,8 @@ export default class extends Channel {
 		)
 			return;
 
+		this.withReplies = params.withReplies as boolean;
+
 		// Subscribe events
 		this.subscriber.on("notesStream", this.onNote);
 	}
@@ -56,7 +59,7 @@ export default class extends Channel {
 			return;
 
 		// 関係ない返信は除外
-		if (note.reply && !this.user!.showTimelineReplies) {
+		if (note.reply && !this.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (
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 df0845b5b3..bd488bdd7d 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -8,6 +8,7 @@ export default class extends Channel {
 	public readonly chName = "localTimeline";
 	public static shouldShare = true;
 	public static requireCredential = false;
+	private withReplies: boolean;
 
 	constructor(id: string, connection: Channel["connection"]) {
 		super(id, connection);
@@ -21,6 +22,8 @@ export default class extends Channel {
 				return;
 		}
 
+		this.withReplies = params.withReplies as boolean;
+
 		// Subscribe events
 		this.subscriber.on("notesStream", this.onNote);
 	}
@@ -32,7 +35,7 @@ export default class extends Channel {
 			return;
 
 		// 関係ない返信は除外
-		if (note.reply && !this.user!.showTimelineReplies) {
+		if (note.reply && !this.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (
diff --git a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts
index d030c1e7e1..0b78d8b66c 100644
--- a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts
@@ -9,6 +9,7 @@ export default class extends Channel {
 	public readonly chName = "recommendedTimeline";
 	public static shouldShare = true;
 	public static requireCredential = true;
+	private withReplies: boolean;
 
 	constructor(id: string, connection: Channel["connection"]) {
 		super(id, connection);
@@ -24,6 +25,8 @@ export default class extends Channel {
 		)
 			return;
 
+		this.withReplies = params.withReplies as boolean;
+
 		// Subscribe events
 		this.subscriber.on("notesStream", this.onNote);
 	}
@@ -54,7 +57,7 @@ export default class extends Channel {
 			return;
 
 		// 関係ない返信は除外
-		if (note.reply && !this.user!.showTimelineReplies) {
+		if (note.reply && !this.withReplies) {
 			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 1eb304df65..672080d9af 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -44,12 +44,7 @@ describe("ユーザー", () => {
 		};
 
 	type MeDetailed = UserDetailedNotMe &
-		misskey.entities.MeDetailed & {
-			showTimelineReplies: boolean;
-			achievements: object[];
-			loggedInDays: number;
-			policies: object;
-		};
+		misskey.entities.MeDetailed
 
 	type User = MeDetailed & { token: string };
 
@@ -172,9 +167,6 @@ describe("ユーザー", () => {
 			mutedInstances: user.mutedInstances,
 			mutingNotificationTypes: user.mutingNotificationTypes,
 			emailNotificationTypes: user.emailNotificationTypes,
-			showTimelineReplies: user.showTimelineReplies,
-			achievements: user.achievements,
-			loggedInDays: user.loggedInDays,
 			policies: user.policies,
 			...(security
 				? {
@@ -479,13 +471,6 @@ describe("ユーザー", () => {
 			"follow",
 			"receiveFollowRequest",
 		]);
-		assert.strictEqual(response.showTimelineReplies, false);
-		assert.deepStrictEqual(response.achievements, []);
-		assert.deepStrictEqual(response.loggedInDays, 0);
-		assert.deepStrictEqual(response.policies, DEFAULT_POLICIES);
-		assert.notStrictEqual(response.email, undefined);
-		assert.strictEqual(response.emailVerified, false);
-		assert.deepStrictEqual(response.securityKeysList, []);
 	});
 
 	//#endregion
@@ -551,8 +536,6 @@ describe("ユーザー", () => {
 		{ parameters: (): object => ({ isBot: false }) },
 		{ parameters: (): object => ({ isCat: true }) },
 		{ parameters: (): object => ({ isCat: false }) },
-		{ parameters: (): object => ({ showTimelineReplies: true }) },
-		{ parameters: (): object => ({ showTimelineReplies: false }) },
 		{ parameters: (): object => ({ injectFeaturedNote: true }) },
 		{ parameters: (): object => ({ injectFeaturedNote: false }) },
 		{ parameters: (): object => ({ receiveAnnouncementEmail: true }) },
diff --git a/packages/client/src/components/MkFollowButton.vue b/packages/client/src/components/MkFollowButton.vue
index 04f5e33115..bff393fddf 100644
--- a/packages/client/src/components/MkFollowButton.vue
+++ b/packages/client/src/components/MkFollowButton.vue
@@ -1,5 +1,10 @@
 <template>
-	<button v-if="!hideMenu" class="menu _button" @click.stop="menu" v-tooltip="i18n.ts.menu">
+	<button
+		v-if="!hideMenu"
+		class="menu _button"
+		@click.stop="menu"
+		v-tooltip="i18n.ts.menu"
+	>
 		<i class="ph-dots-three-outline ph-bold ph-lg"></i>
 	</button>
 	<button
diff --git a/packages/client/src/components/MkTimeline.vue b/packages/client/src/components/MkTimeline.vue
index 11fe175d9c..c3366c17ac 100644
--- a/packages/client/src/components/MkTimeline.vue
+++ b/packages/client/src/components/MkTimeline.vue
@@ -91,7 +91,12 @@ if (props.src === "antenna") {
 	connection.on("note", prepend);
 } else if (props.src === "home") {
 	endpoint = "notes/timeline";
-	connection = stream.useChannel("homeTimeline");
+	query = {
+		withReplies: defaultStore.state.showTimelineReplies,
+	};
+	connection = stream.useChannel("homeTimeline", {
+		withReplies: defaultStore.state.showTimelineReplies,
+	});
 	connection.on("note", prepend);
 
 	connection2 = stream.useChannel("main");
@@ -102,28 +107,48 @@ if (props.src === "antenna") {
 	tlHintClosed = defaultStore.state.tlHomeHintClosed;
 } else if (props.src === "local") {
 	endpoint = "notes/local-timeline";
-	connection = stream.useChannel("localTimeline");
+	query = {
+		withReplies: defaultStore.state.showTimelineReplies,
+	};
+	connection = stream.useChannel("localTimeline", {
+		withReplies: defaultStore.state.showTimelineReplies,
+	});
 	connection.on("note", prepend);
 
 	tlHint = i18n.ts._tutorial.step5_4;
 	tlHintClosed = defaultStore.state.tlLocalHintClosed;
 } else if (props.src === "recommended") {
 	endpoint = "notes/recommended-timeline";
-	connection = stream.useChannel("recommendedTimeline");
+	query = {
+		withReplies: defaultStore.state.showTimelineReplies,
+	};
+	connection = stream.useChannel("recommendedTimeline", {
+		withReplies: defaultStore.state.showTimelineReplies,
+	});
 	connection.on("note", prepend);
 
 	tlHint = i18n.ts._tutorial.step5_6;
 	tlHintClosed = defaultStore.state.tlRecommendedHintClosed;
 } else if (props.src === "social") {
 	endpoint = "notes/hybrid-timeline";
-	connection = stream.useChannel("hybridTimeline");
+	query = {
+		withReplies: defaultStore.state.showTimelineReplies,
+	};
+	connection = stream.useChannel("hybridTimeline", {
+		withReplies: defaultStore.state.showTimelineReplies,
+	});
 	connection.on("note", prepend);
 
 	tlHint = i18n.ts._tutorial.step5_5;
 	tlHintClosed = defaultStore.state.tlSocialHintClosed;
 } else if (props.src === "global") {
 	endpoint = "notes/global-timeline";
-	connection = stream.useChannel("globalTimeline");
+	query = {
+		withReplies: defaultStore.state.showTimelineReplies,
+	};
+	connection = stream.useChannel("globalTimeline", {
+		withReplies: defaultStore.state.showTimelineReplies,
+	});
 	connection.on("note", prepend);
 
 	tlHint = i18n.ts._tutorial.step5_7;
diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue
index cf75d4fe6e..36a4e2a13c 100644
--- a/packages/client/src/pages/settings/general.vue
+++ b/packages/client/src/pages/settings/general.vue
@@ -54,7 +54,7 @@
 			<FormSwitch v-model="disablePagesScript" class="_formBlock">{{
 				i18n.ts.disablePagesScript
 			}}</FormSwitch>
-			<FormSwitch v-model="profile.showTimelineReplies" class="_formBlock"
+			<FormSwitch v-model="showTimelineReplies" class="_formBlock"
 				>{{ i18n.ts.flagShowTimelineReplies
 				}}<template #caption
 					>{{ i18n.ts.flagShowTimelineRepliesDescription }}
@@ -258,24 +258,6 @@ const lang = ref(localStorage.getItem("lang"));
 const fontSize = ref(localStorage.getItem("fontSize"));
 const useSystemFont = ref(localStorage.getItem("useSystemFont") != null);
 
-const profile = reactive({
-	showTimelineReplies: $i?.showTimelineReplies,
-});
-watch(
-	() => profile,
-	() => {
-		save();
-	},
-	{
-		deep: true,
-	}
-);
-function save() {
-	os.apiWithDialog("i/update", {
-		showTimelineReplies: !!profile.showTimelineReplies,
-	});
-}
-
 async function reloadAsk() {
 	const { canceled } = await os.confirm({
 		type: "info",
@@ -360,6 +342,9 @@ const swipeOnDesktop = computed(
 const showAdminUpdates = computed(
 	defaultStore.makeGetterSetter("showAdminUpdates")
 );
+const showTimelineReplies = computed(
+	defaultStore.makeGetterSetter("showTimelineReplies")
+);
 
 watch(lang, () => {
 	localStorage.setItem("lang", lang.value as string);
diff --git a/packages/client/src/pages/settings/preferences-backups.vue b/packages/client/src/pages/settings/preferences-backups.vue
index 14bb27f91e..313024d869 100644
--- a/packages/client/src/pages/settings/preferences-backups.vue
+++ b/packages/client/src/pages/settings/preferences-backups.vue
@@ -115,6 +115,7 @@ const defaultStoreSaveKeys: (keyof (typeof defaultStore)["state"])[] = [
 	"enableCustomKaTeXMacro",
 	"enableEmojiReactions",
 	"showEmojisInReactionNotifications",
+	"showTimelineReplies",
 ];
 const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
 	"lightTheme",
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
index 5199c336a4..c8ce96b0ee 100644
--- a/packages/client/src/store.ts
+++ b/packages/client/src/store.ts
@@ -330,6 +330,10 @@ export const defaultStore = markRaw(
 			where: "account",
 			default: true,
 		},
+		showTimelineReplies: {
+			where: "device",
+			default: true,
+		}
 	}),
 );
 
diff --git a/packages/client/src/stream.ts b/packages/client/src/stream.ts
index e96b045659..0bc6198da1 100644
--- a/packages/client/src/stream.ts
+++ b/packages/client/src/stream.ts
@@ -3,13 +3,30 @@ import { markRaw } from "vue";
 import { $i } from "@/account";
 import { url } from "@/config";
 
-export const stream = markRaw(
-	new Misskey.Stream(
-		url,
-		$i
-			? {
-					token: $i.token,
-			  }
-			: null,
-	),
-);
+let stream: Misskey.Stream | null = null;
+
+export function useStream(): Misskey.Stream {
+	if (stream) return stream;
+
+	stream = markRaw(
+		new Misskey.Stream(
+			url,
+			$i
+				? {
+						token: $i.token,
+				  }
+				: null
+		)
+	);
+
+	window.setTimeout(heartbeat, 1000 * 60);
+
+	return stream;
+}
+
+function heartbeat(): void {
+	if (stream != null && document.visibilityState === "visible") {
+		stream.send("ping");
+	}
+	window.setTimeout(heartbeat, 1000 * 60);
+}

From e728a40a9a990a9de8caeeef60f9adf61e78c39a Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 20:25:22 -0700
Subject: [PATCH 18/56] =?UTF-8?q?docs:=20=F0=9F=93=9D=20changelog?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CALCKEY.md | 3 ++-
 README.md  | 4 +++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/CALCKEY.md b/CALCKEY.md
index d1585adc34..b44b8bbf74 100644
--- a/CALCKEY.md
+++ b/CALCKEY.md
@@ -8,7 +8,6 @@
   - Rewrite backend in Rust and [Rocket](https://rocket.rs/)
   - Use [Magic RegExP](https://regexp.dev/) for RegEx 🦄
 - Function
-  - Federate with note edits
   - User "choices" (recommended users) like Mastodon and Soapbox
   - Join Reason system like Mastodon/Pleroma
   - Option to publicize server blocks
@@ -124,6 +123,8 @@
 - Improve system emails
 - Mod mail
 - Focus trapping and button labels
+- Meilisearch with filters
+- Post editing
 
 ## Implemented (remote)
 
diff --git a/README.md b/README.md
index 346ad74219..796632be2d 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,8 @@
 - Read **[this document](./CALCKEY.md)** all for current and future differences.
 - Notable differences:
   - Improved UI/UX (especially on mobile)
+  - Post editing
+  - Content importing
   - Improved notifications
   - Improved server security
   - Improved accessibility
@@ -37,7 +39,7 @@
   - Better intro tutorial
   - Compatibility with Mastodon clients/apps
   - Backfill user information
-  - Sonic search
+  - Advanced search
   - Many more user and admin settings
   - [So much more!](./CALCKEY.md)
 

From 084e9072d18d7231dd50cfaa4b4d1c35326f35f5 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 20:32:59 -0700
Subject: [PATCH 19/56] fix: :ambulance: fix stream.ts

---
 packages/client/src/stream.ts | 30 +++++++++++-------------------
 1 file changed, 11 insertions(+), 19 deletions(-)

diff --git a/packages/client/src/stream.ts b/packages/client/src/stream.ts
index 0bc6198da1..70e860250b 100644
--- a/packages/client/src/stream.ts
+++ b/packages/client/src/stream.ts
@@ -3,26 +3,18 @@ import { markRaw } from "vue";
 import { $i } from "@/account";
 import { url } from "@/config";
 
-let stream: Misskey.Stream | null = null;
+export const stream = markRaw(
+	new Misskey.Stream(
+		url,
+		$i
+			? {
+					token: $i.token,
+			  }
+			: null,
+	),
+);
 
-export function useStream(): Misskey.Stream {
-	if (stream) return stream;
-
-	stream = markRaw(
-		new Misskey.Stream(
-			url,
-			$i
-				? {
-						token: $i.token,
-				  }
-				: null
-		)
-	);
-
-	window.setTimeout(heartbeat, 1000 * 60);
-
-	return stream;
-}
+window.setTimeout(heartbeat, 1000 * 60);
 
 function heartbeat(): void {
 	if (stream != null && document.visibilityState === "visible") {

From 085095044928ae0229ada145c4c25896ee42dc2d Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 20:34:00 -0700
Subject: [PATCH 20/56] chore: :art: format

---
 packages/backend/test/e2e/users.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 672080d9af..721ffc2f59 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -43,8 +43,7 @@ describe("ユーザー", () => {
 			roles: any[];
 		};
 
-	type MeDetailed = UserDetailedNotMe &
-		misskey.entities.MeDetailed
+	type MeDetailed = UserDetailedNotMe & misskey.entities.MeDetailed;
 
 	type User = MeDetailed & { token: string };
 

From fe5834e4703c7d80ddddc89d2a497fbd6b2df5f8 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 20:46:54 -0700
Subject: [PATCH 21/56] docs: :memo: rudamentary sea-orm-cli instructions

---
 packages/backend/native-utils/migration/README.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/packages/backend/native-utils/migration/README.md b/packages/backend/native-utils/migration/README.md
index b3ea53eb44..b3c34dfbd4 100644
--- a/packages/backend/native-utils/migration/README.md
+++ b/packages/backend/native-utils/migration/README.md
@@ -1,3 +1,15 @@
+# Making migrations
+
+- Install `sea-orm-cli`
+  ```sh
+  cargo install sea-orm-cli
+```
+
+- Generate
+	```sh
+	sea-orm-cli migrate generate ****
+	```
+
 # Running Migrator CLI
 
 - Generate a new migration file

From 9fa71f37d7684688f71b6738d0e58025fca442ab Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Wed, 14 Jun 2023 20:47:49 -0700
Subject: [PATCH 22/56] docs: :memo: sea orm migration "Setting Up Migration"
 doc link

---
 packages/backend/native-utils/migration/README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/backend/native-utils/migration/README.md b/packages/backend/native-utils/migration/README.md
index b3c34dfbd4..4ef153d6e6 100644
--- a/packages/backend/native-utils/migration/README.md
+++ b/packages/backend/native-utils/migration/README.md
@@ -1,5 +1,7 @@
 # Making migrations
 
+For more information, please read https://www.sea-ql.org/SeaORM/docs/migration/setting-up-migration/
+
 - Install `sea-orm-cli`
   ```sh
   cargo install sea-orm-cli

From 55902208e76e6e10ca6e037e58be2e55ea8abeff Mon Sep 17 00:00:00 2001
From: Kainoa Kanter <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 03:49:17 +0000
Subject: [PATCH 23/56] =?UTF-8?q?docs:=20=F0=9F=93=9D=20fix=20formatting?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/native-utils/migration/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/native-utils/migration/README.md b/packages/backend/native-utils/migration/README.md
index 4ef153d6e6..1ac338d743 100644
--- a/packages/backend/native-utils/migration/README.md
+++ b/packages/backend/native-utils/migration/README.md
@@ -5,7 +5,7 @@ For more information, please read https://www.sea-ql.org/SeaORM/docs/migration/s
 - Install `sea-orm-cli`
   ```sh
   cargo install sea-orm-cli
-```
+  ```
 
 - Generate
 	```sh

From fbce5d819fb19182568f4348e9a2aa4e6b54bc11 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 14:53:21 -0700
Subject: [PATCH 24/56] style: :lipstick: margin on user card follow btn

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

diff --git a/packages/client/src/components/MkUserInfo.vue b/packages/client/src/components/MkUserInfo.vue
index f53aa7b8ef..2207363ee7 100644
--- a/packages/client/src/components/MkUserInfo.vue
+++ b/packages/client/src/components/MkUserInfo.vue
@@ -148,6 +148,7 @@ defineProps<{
 		position: absolute;
 		top: 8px;
 		right: 8px;
+		margin-bottom: 1rem;
 	}
 }
 </style>

From 46af585cf78f192acd912ce13f8813b4a2a7f38e Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 16:12:32 -0700
Subject: [PATCH 25/56] =?UTF-8?q?feat:=20=F0=9F=94=92=20Improve=202FA/keyp?=
 =?UTF-8?q?ass=20experience?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Tamania <tamaina@hotmail.co.jp>
Co-authored-by: Syuilo <syuilotan@yahoo.co.jp>
---
 locales/ar-SA.yml                             |   4 +-
 locales/bn-BD.yml                             |   4 +-
 locales/ca-ES.yml                             |   4 +-
 locales/cs-CZ.yml                             |   4 +-
 locales/de-DE.yml                             |   4 +-
 locales/en-US.yml                             |  26 +-
 locales/es-ES.yml                             |   4 +-
 locales/fr-FR.yml                             |   4 +-
 locales/id-ID.yml                             |   4 +-
 locales/it-IT.yml                             |   2 +-
 locales/ja-JP.yml                             |  31 +-
 locales/ko-KR.yml                             |   4 +-
 locales/pl-PL.yml                             |   4 +-
 locales/ru-RU.yml                             |   4 +-
 locales/sk-SK.yml                             |   4 +-
 locales/uk-UA.yml                             |   2 +-
 locales/vi-VN.yml                             |   4 +-
 locales/zh-CN.yml                             |   4 +-
 locales/zh-TW.yml                             |   4 +-
 package.json                                  |   2 +-
 packages/backend/package.json                 |   3 +-
 packages/backend/src/server/api/endpoints.ts  |   2 +
 .../src/server/api/endpoints/i/2fa/done.ts    |  23 +-
 .../server/api/endpoints/i/2fa/key-done.ts    |   2 +-
 .../api/endpoints/i/2fa/password-less.ts      |  41 +-
 .../server/api/endpoints/i/2fa/register.ts    |  17 +-
 .../server/api/endpoints/i/2fa/remove-key.ts  |  18 +
 .../server/api/endpoints/i/2fa/unregister.ts  |  11 +-
 .../server/api/endpoints/i/2fa/update-key.ts  |  58 ++
 .../backend/src/server/api/private/signin.ts  |  18 +-
 packages/calckey-js/src/api.types.ts          |   1 +
 packages/client/src/components/MkDialog.vue   |  62 ++-
 packages/client/src/components/MkSignin.vue   |   7 +-
 packages/client/src/components/form/input.vue |   4 +-
 .../client/src/components/form/select.vue     | 277 +++++-----
 packages/client/src/os.ts                     | 146 +++--
 .../src/pages/settings/2fa.qrdialog.vue       |  96 ++++
 packages/client/src/pages/settings/2fa.vue    | 518 +++++++++---------
 .../client/src/pages/settings/security.vue    |  26 +-
 packages/client/src/style.scss                |  28 +
 pnpm-lock.yaml                                |  38 +-
 41 files changed, 937 insertions(+), 582 deletions(-)
 create mode 100644 packages/backend/src/server/api/endpoints/i/2fa/update-key.ts
 create mode 100644 packages/client/src/pages/settings/2fa.qrdialog.vue

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 3ce83ebcc8..7e97a99ebc 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -1049,8 +1049,8 @@ _tutorial:
   step6_4: "Now go, explore, and have fun!"
 _2fa:
   alreadyRegistered: "سجلت سلفًا جهازًا للاستيثاق بعاملين."
-  registerDevice: "سجّل جهازًا جديدًا"
-  registerKey: "تسجيل مفتاح أمان جديد"
+  registerTOTP: "سجّل جهازًا جديدًا"
+  registerSecurityKey: "تسجيل مفتاح أمان جديد"
   step1: "أولًا ثبّت تطبيق استيثاق على جهازك (مثل {a} و{b})."
   step2: "امسح رمز الاستجابة السريعة الموجد على الشاشة."
   step3: "أدخل الرمز الموجود في تطبيقك لإكمال التثبيت."
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index f08e4bfc2d..e3fbf8cb9b 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -1130,8 +1130,8 @@ _tutorial:
   step6_4: "Now go, explore, and have fun!"
 _2fa:
   alreadyRegistered: "আপনি ইতিমধ্যে একটি 2-ফ্যাক্টর অথেনটিকেশন ডিভাইস নিবন্ধন করেছেন৷"
-  registerDevice: "নতুন ডিভাইস নিবন্ধন করুন"
-  registerKey: "সিকিউরিটি কী নিবন্ধন করুন"
+  registerTOTP: "নতুন ডিভাইস নিবন্ধন করুন"
+  registerSecurityKey: "সিকিউরিটি কী নিবন্ধন করুন"
   step1: "প্রথমে, আপনার ডিভাইসে {a} বা {b} এর মতো একটি অথেনটিকেশন অ্যাপ ইনস্টল করুন৷"
   step2: "এরপরে, অ্যাপের সাহায্যে প্রদর্শিত QR কোডটি স্ক্যান করুন।"
   step2Url: "ডেস্কটপ অ্যাপে, নিম্নলিখিত URL লিখুন:"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index b57fd1c0f5..20e67a0971 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -319,13 +319,13 @@ _sfx:
 _2fa:
   step2Url: "També pots inserir aquest enllaç i utilitzes una aplicació d'escriptori:"
   alreadyRegistered: Ja heu registrat un dispositiu d'autenticació de dos factors.
-  registerDevice: Registrar un dispositiu nou
+  registerTOTP: Registrar un dispositiu nou
   securityKeyInfo: A més de l'autenticació d'empremta digital o PIN, també podeu configurar
     l'autenticació mitjançant claus de seguretat de maquinari compatibles amb FIDO2
     per protegir encara més el vostre compte.
   step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquest
     token d'inici de sessió.
-  registerKey: Registra una clau de seguretat
+  registerSecurityKey: Registra una clau de seguretat
   step1: En primer lloc, instal·la una aplicació d'autenticació (com ara {a} o {b})
     al dispositiu.
   step2: A continuació, escaneja el codi QR que es mostra en aquesta pantalla.
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 8b502762fe..1fe16135e6 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -698,8 +698,8 @@ _time:
   minute: "Minut"
   hour: "Hodin"
 _2fa:
-  registerDevice: "Přidat zařízení"
-  registerKey: "Přidat bezpečnostní klíč"
+  registerTOTP: "Přidat zařízení"
+  registerSecurityKey: "Přidat bezpečnostní klíč"
 _weekday:
   sunday: "Neděle"
   monday: "Pondělí"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 5d0459395c..e8615b6f58 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1371,8 +1371,8 @@ _tutorial:
 _2fa:
   alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung
     registriert."
-  registerDevice: "Neues Gerät registrieren"
-  registerKey: "Neuen Sicherheitsschlüssel registrieren"
+  registerTOTP: "Neues Gerät registrieren"
+  registerSecurityKey: "Neuen Sicherheitsschlüssel 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."
diff --git a/locales/en-US.yml b/locales/en-US.yml
index c560011e73..b2bc4e7146 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1487,16 +1487,28 @@ _tutorial:
   step6_4: "Now go, explore, and have fun!"
 _2fa:
   alreadyRegistered: "You have already registered a 2-factor authentication device."
-  registerDevice: "Register a new device"
-  registerKey: "Register a security key"
+  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."
   step2Url: "You can also enter this URL if you're using a desktop program:"
+  step3Title: "Enter an authentication code"
   step3: "Enter the token provided by your app to finish setup."
   step4: "From now on, any future login attempts will ask for such a login token."
-  securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup
-    authentication via hardware security keys that support FIDO2 to further secure
-    your account."
+  securityKeyNotSupported: "Your browser does not support security keys."
+  registerTOTPBeforeKey: "Please set up an authenticator app to register a security or pass key."
+  securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup authentication via hardware security keys that support FIDO2 to further secure your account."
+  chromePasskeyNotSupported: "Chrome passkeys are currently not supported."
+  registerSecurityKey: "Register a security or pass key"
+  securityKeyName: "Enter a key name"
+  tapSecurityKey: "Please follow your browser to register the security or pass key"
+  removeKey: "Remove security key"
+  removeKeyConfirm: "Really delete the {name} key?"
+  whyTOTPOnlyRenew: "The authenticator app cannot be removed as long as a security key is registered."
+  renewTOTP: "Reconfigure authenticator app"
+  renewTOTPConfirm: "This will cause verification codes from your previous app to stop working"
+  renewTOTPOk: "Reconfigure"
+  renewTOTPCancel: "Cancel"
 _permissions:
   "read:account": "View your account information"
   "write:account": "Edit your account information"
@@ -2058,3 +2070,7 @@ _experiments:
   postImportsCaption: "Allows users to import their posts from past Calckey,\
     \ Misskey, Mastodon, Akkoma, and Pleroma accounts. It may cause slowdowns during\
     \ load if your queue is bottlenecked."
+
+_dialog:
+  charactersExceeded: "Max characters exceeded! Current: {current}/Limit: {max}"
+  charactersBelow: "Not enough characters! Current: {current}/Limit: {min}"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 2dc40f3597..a4016b7bb7 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -1331,8 +1331,8 @@ _tutorial:
   step6_4: "¡Ahora ve, explora y diviértete!"
 _2fa:
   alreadyRegistered: "Ya has completado la configuración."
-  registerDevice: "Registrar dispositivo"
-  registerKey: "Registrar clave"
+  registerTOTP: "Registrar dispositivo"
+  registerSecurityKey: "Registrar clave"
   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."
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index dad0956880..2e6b1400f9 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1262,8 +1262,8 @@ _tutorial:
   step6_4: "Maintenant, allez-y, explorez et amusez-vous !"
 _2fa:
   alreadyRegistered: "Configuration déjà achevée."
-  registerDevice: "Ajouter un nouvel appareil"
-  registerKey: "Enregistrer une clef"
+  registerTOTP: "Ajouter un nouvel appareil"
+  registerSecurityKey: "Enregistrer une clef"
   step1: "Tout d'abord, installez une application d'authentification, telle que {a}\
     \ ou {b}, sur votre appareil."
   step2: "Ensuite, scannez le code QR affiché sur l’écran."
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index f9859c2c72..17bebe99cf 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -1254,8 +1254,8 @@ _tutorial:
   step7_3: "Semoga berhasil dan bersenang-senanglah! \U0001F680"
 _2fa:
   alreadyRegistered: "Kamu telah mendaftarkan perangkat otentikasi dua faktor."
-  registerDevice: "Daftarkan perangkat baru"
-  registerKey: "Daftarkan kunci keamanan baru"
+  registerTOTP: "Daftarkan perangkat baru"
+  registerSecurityKey: "Daftarkan kunci keamanan baru"
   step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat\
     \ kamu."
   step2: "Lalu, pindai kode QR yang ada di layar."
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 44434eb264..7e39e7746c 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -1139,7 +1139,7 @@ _tutorial:
     Questo però lo fa! È un po' complicato, ma ci riuscirete in poco tempo"
   step6_4: "Ora andate, esplorate e divertitevi!"
 _2fa:
-  registerDevice: "Aggiungi dispositivo"
+  registerTOTP: "Aggiungi dispositivo"
 _permissions:
   "read:account": "Visualizzare le informazioni dell'account"
   "write:account": "Modificare le informazioni dell'account"
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0ddde1a139..29462842da 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1314,14 +1314,28 @@ _tutorial:
   step6_4: "これで完了です。お楽しみください!"
 _2fa:
   alreadyRegistered: "既に設定は完了しています。"
-  registerDevice: "デバイスを登録"
-  registerKey: "キーを登録"
+  registerTOTP: "認証アプリの設定を開始"
   step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。"
   step2: "次に、表示されているQRコードをアプリでスキャンします。"
-  step2Url: "デスクトップアプリでは次のURLを入力します:"
-  step3: "アプリに表示されているトークンを入力して完了です。"
-  step4: "これからログインするときも、同じようにトークンを入力します。"
-  securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキーもしくは端末の指紋認証やPINを使用してログインするように設定できます。"
+  step2Click: "QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。"
+  step2Url: "デスクトップアプリでは次のURIを入力します:"
+  step3Title: "確認コードを入力"
+  step3: "アプリに表示されている確認コード(トークン)を入力して完了です。"
+  step4: "これからログインするときも、同じように確認コードを入力します。"
+  securityKeyNotSupported: "お使いのブラウザはセキュリティキーに対応していません。"
+  registerTOTPBeforeKey: "セキュリティキー・パスキーを登録するには、まず認証アプリの設定を行なってください。"
+  securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキー、端末の生体認証やPINロック、パスキーといった、WebAuthn由来の鍵を登録します。"
+  chromePasskeyNotSupported: "Chromeのパスキーは現在サポートしていません。"
+  registerSecurityKey: "セキュリティキー・パスキーを登録する"
+  securityKeyName: "キーの名前を入力"
+  tapSecurityKey: "ブラウザの指示に従い、セキュリティキーやパスキーを登録してください"
+  removeKey: "セキュリティキーを削除"
+  removeKeyConfirm: "{name}を削除しますか?"
+  whyTOTPOnlyRenew: "セキュリティキーが登録されている場合、認証アプリの設定は解除できません。"
+  renewTOTP: "認証アプリを再設定"
+  renewTOTPConfirm: "今までの認証アプリの確認コードは使用できなくなります"
+  renewTOTPOk: "再設定する"
+  renewTOTPCancel: "やめておく"
 _permissions:
   "read:account": "アカウントの情報を見る"
   "write:account": "アカウントの情報を変更する"
@@ -1882,7 +1896,7 @@ sendModMail: モデレーションノートを送る
 deleted: 削除済み
 editNote: 投稿を編集
 edited: 編集済み
-signupsDisabled: 
+signupsDisabled:
   現在、このサーバーでは新規登録が一般開放されていません。招待コードをお持ちの場合には、以下の欄に入力してください。招待コードをお持ちでない場合にも、新規登録を開放している他のサーバーには入れますよ!
 findOtherInstance: 他のサーバーを探す
 newer: 新しい投稿
@@ -1898,3 +1912,6 @@ antennasDesc: "アンテナでは指定した条件に合致する投稿が表
 expandOnNoteClickDesc: オフの場合、右クリックメニューか日付をクリックすることで開けます。
 expandOnNoteClick: クリックで投稿の詳細を開く
 clipsDesc: クリップは分類と共有ができるブックマークです。各投稿のメニューからクリップを作成できます。
+_dialog:
+  charactersExceeded: "最大文字数を超えています! 現在 {current} / 制限 {max}"
+  charactersBelow: "最小文字数を下回っています! 現在 {current} / 制限 {min}"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 7143cf2f98..2c8e548bde 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -1179,8 +1179,8 @@ _time:
   day: "일"
 _2fa:
   alreadyRegistered: "이미 설정이 완료되었습니다."
-  registerDevice: "디바이스 등록"
-  registerKey: "키를 등록"
+  registerTOTP: "디바이스 등록"
+  registerSecurityKey: "키를 등록"
   step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다."
   step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
   step2Url: "데스크톱 앱에서는 다음 URL을 입력하세요:"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index e2ca8b1db8..fa5ec88d8c 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -1260,8 +1260,8 @@ _tutorial:
   step6_4: "A teraz idź, odkrywaj i baw się dobrze!"
 _2fa:
   alreadyRegistered: "Zarejestrowałeś już urządzenie do uwierzytelniania dwuskładnikowego."
-  registerDevice: "Zarejestruj nowe urządzenie"
-  registerKey: "Zarejestruj klucz bezpieczeństwa"
+  registerTOTP: "Zarejestruj nowe urządzenie"
+  registerSecurityKey: "Zarejestruj klucz bezpieczeństwa"
   step1: "Najpierw, zainstaluj aplikację uwierzytelniającą (taką jak {a} lub {b})
     na swoim urządzeniu."
   step2: "Następnie, zeskanuje kod QR z ekranu."
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 01b21b0fcb..fe90f23d9b 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -1249,8 +1249,8 @@ _tutorial:
   step6_4: "Теперь идите, изучайте и развлекайтесь!"
 _2fa:
   alreadyRegistered: "Двухфакторная аутентификация уже настроена."
-  registerDevice: "Зарегистрируйте ваше устройство"
-  registerKey: "Зарегистрировать ключ"
+  registerTOTP: "Зарегистрируйте ваше устройство"
+  registerSecurityKey: "Зарегистрировать ключ"
   step1: "Прежде всего, установите на устройство приложение для аутентификации, например,\
     \ {a} или {b}."
   step2: "Далее отсканируйте отображаемый QR-код при помощи приложения."
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 462f34ed2d..dce23d7558 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -1196,8 +1196,8 @@ _tutorial:
   step6_4: "Now go, explore, and have fun!"
 _2fa:
   alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikačné zariadenie."
-  registerDevice: "Registrovať nové zariadenie"
-  registerKey: "Registrovať bezpečnostný kľúč"
+  registerTOTP: "Registrovať nové zariadenie"
+  registerSecurityKey: "Registrovať bezpečnostný kľúč"
   step1: "Najprv si nainštalujte autentifikačnú aplikáciu (napríklad {a} alebo {b}) na svoje zariadenie."
   step2: "Potom, naskenujte QR kód zobrazený na obrazovke."
   step2Url: "Do aplikácie zadajte nasledujúcu URL adresu:"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 549dce666c..712c0fd03e 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -959,7 +959,7 @@ _tutorial:
   step6_3: "Кожен сервер працює по-своєму, і не на всіх серверах працює Calckey. Але цей працює! Це трохи складно, але ви швидко розберетеся"
   step6_4: "Тепер ідіть, вивчайте і розважайтеся!"
 _2fa:
-  registerKey: "Зареєструвати новий ключ безпеки"
+  registerSecurityKey: "Зареєструвати новий ключ безпеки"
 _permissions:
   "read:account": "Переглядати дані профілю"
   "write:account": "Змінити дані акаунту"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 4b254fe943..ddd79084fc 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1201,8 +1201,8 @@ _tutorial:
   step6_4: "Now go, explore, and have fun!"
 _2fa:
   alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
-  registerDevice: "Đăng ký một thiết bị"
-  registerKey: "Đăng ký một mã bảo vệ"
+  registerTOTP: "Đăng ký một thiết bị"
+  registerSecurityKey: "Đăng ký một mã bảo vệ"
   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."
   step2Url: "Bạn cũng có thể nhập URL này nếu sử dụng một chương trình máy tính:"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 5359cb6efb..a6c9ec5a1e 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -1210,8 +1210,8 @@ _tutorial:
   step6_4: "现在去学习并享受乐趣!"
 _2fa:
   alreadyRegistered: "此设备已被注册"
-  registerDevice: "注册设备"
-  registerKey: "注册密钥"
+  registerTOTP: "注册设备"
+  registerSecurityKey: "注册密钥"
   step1: "首先,在您的设备上安装验证应用,例如{a}或{b}。"
   step2: "然后,扫描屏幕上显示的二维码。"
   step2Url: "在桌面应用程序中输入以下URL:"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 61b5afbe63..3b552ffd29 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -1219,8 +1219,8 @@ _tutorial:
   step6_4: "現在開始探索吧!"
 _2fa:
   alreadyRegistered: "你已註冊過一個雙重認證的裝置。"
-  registerDevice: "註冊裝置"
-  registerKey: "註冊鍵"
+  registerTOTP: "註冊裝置"
+  registerSecurityKey: "註冊鍵"
   step1: "首先,在您的設備上安裝二步驗證程式,例如{a}或{b}。"
   step2: "然後,掃描螢幕上的QR code。"
   step2Url: "在桌面版應用中,請輸入以下的URL:"
diff --git a/package.json b/package.json
index b2c90463b8..42a18a33c1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "calckey",
-	"version": "14.0.0-dev46",
+	"version": "14.0.0-dev51",
 	"codename": "aqua",
 	"repository": {
 		"type": "git",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index cf04b3bf72..2a19b916cf 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -101,6 +101,7 @@
 		"nsfwjs": "2.4.2",
 		"oauth": "^0.10.0",
 		"os-utils": "0.0.14",
+		"otpauth": "^9.1.2",
 		"parse5": "7.1.2",
 		"pg": "8.11.0",
 		"private-ip": "2.3.4",
@@ -123,7 +124,6 @@
 		"semver": "7.5.1",
 		"sharp": "0.32.1",
 		"sonic-channel": "^1.3.1",
-		"speakeasy": "2.0.0",
 		"stringz": "2.1.0",
 		"summaly": "2.7.0",
 		"syslog-pro": "1.0.0",
@@ -181,7 +181,6 @@
 		"@types/semver": "7.5.0",
 		"@types/sharp": "0.31.1",
 		"@types/sinonjs__fake-timers": "8.1.2",
-		"@types/speakeasy": "2.0.7",
 		"@types/tinycolor2": "1.4.3",
 		"@types/tmp": "0.2.3",
 		"@types/uuid": "8.3.4",
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index e0b513298d..2cb3b30d10 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -174,6 +174,7 @@ import * as ep___i_2fa_keyDone from "./endpoints/i/2fa/key-done.js";
 import * as ep___i_2fa_passwordLess from "./endpoints/i/2fa/password-less.js";
 import * as ep___i_2fa_registerKey from "./endpoints/i/2fa/register-key.js";
 import * as ep___i_2fa_register from "./endpoints/i/2fa/register.js";
+import * as ep___i_2fa_updateKey from "./endpoints/i/2fa/update-key.js";
 import * as ep___i_2fa_removeKey from "./endpoints/i/2fa/remove-key.js";
 import * as ep___i_2fa_unregister from "./endpoints/i/2fa/unregister.js";
 import * as ep___i_apps from "./endpoints/i/apps.js";
@@ -528,6 +529,7 @@ const eps = [
 	["i/2fa/password-less", ep___i_2fa_passwordLess],
 	["i/2fa/register-key", ep___i_2fa_registerKey],
 	["i/2fa/register", ep___i_2fa_register],
+	["i/2fa/update-key", ep___i_2fa_updateKey],
 	["i/2fa/remove-key", ep___i_2fa_removeKey],
 	["i/2fa/unregister", ep___i_2fa_unregister],
 	["i/apps", ep___i_apps],
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts
index 1e9892f03b..05d57d2821 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts
@@ -1,6 +1,7 @@
-import * as speakeasy from "speakeasy";
+import { publishMainStream } from "@/services/stream.js";
+import * as OTPAuth from "otpauth";
 import define from "../../../define.js";
-import { UserProfiles } from "@/models/index.js";
+import { Users, UserProfiles } from "@/models/index.js";
 
 export const meta = {
 	requireCredential: true,
@@ -25,13 +26,14 @@ export default define(meta, paramDef, async (ps, user) => {
 		throw new Error("二段階認証の設定が開始されていません");
 	}
 
-	const verified = (speakeasy as any).totp.verify({
-		secret: profile.twoFactorTempSecret,
-		encoding: "base32",
-		token: token,
+	const delta = OTPAuth.TOTP.validate({
+		secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret),
+		digits: 6,
+		token,
+		window: 1,
 	});
 
-	if (!verified) {
+	if (delta === null) {
 		throw new Error("not verified");
 	}
 
@@ -39,4 +41,11 @@ export default define(meta, paramDef, async (ps, user) => {
 		twoFactorSecret: profile.twoFactorTempSecret,
 		twoFactorEnabled: true,
 	});
+
+	const iObj = await Users.pack(user.id, user, {
+		detail: true,
+		includeSecrets: true,
+	});
+
+	publishMainStream(user.id, "meUpdated", iObj);
 });
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 f0581de4b4..34660c6f2e 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
@@ -28,7 +28,7 @@ export const paramDef = {
 		attestationObject: { type: "string" },
 		password: { type: "string" },
 		challengeId: { type: "string" },
-		name: { type: "string" },
+		name: { type: "string", minLength: 1, maxLength: 30 },
 	},
 	required: [
 		"clientDataJSON",
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts
index 11b2e9a2e3..b9f3426804 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts
@@ -1,10 +1,20 @@
 import define from "../../../define.js";
-import { UserProfiles } from "@/models/index.js";
+import { Users, UserProfiles, UserSecurityKeys } from "@/models/index.js";
+import { publishMainStream } from "@/services/stream.js";
+import { ApiError } from "../../../error.js";
 
 export const meta = {
 	requireCredential: true,
 
 	secure: true,
+
+	errors: {
+		noKey: {
+			message: "No security key.",
+			code: "NO_SECURITY_KEY",
+			id: "f9c54d7f-d4c2-4d3c-9a8g-a70daac86512",
+		},
+	},
 } as const;
 
 export const paramDef = {
@@ -16,7 +26,36 @@ export const paramDef = {
 } as const;
 
 export default define(meta, paramDef, async (ps, user) => {
+	if (ps.value === true) {
+		// セキュリティキーがなければパスワードレスを有効にはできない
+		const keyCount = await UserSecurityKeys.count({
+			where: {
+				userId: user.id,
+			},
+			select: {
+				id: true,
+				name: true,
+				lastUsed: true,
+			},
+		});
+
+		if (keyCount === 0) {
+			await UserProfiles.update(user.id, {
+				usePasswordLessLogin: false,
+			});
+
+			throw new ApiError(meta.errors.noKey);
+		}
+	}
+
 	await UserProfiles.update(user.id, {
 		usePasswordLessLogin: ps.value,
 	});
+
+	const iObj = await Users.pack(user.id, user, {
+		detail: true,
+		includeSecrets: true,
+	});
+
+	publishMainStream(user.id, "meUpdated", iObj);
 });
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
index 533035bc91..cf391ca2fd 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
@@ -1,4 +1,4 @@
-import * as speakeasy from "speakeasy";
+import * as OTPAuth from "otpauth";
 import * as QRCode from "qrcode";
 import config from "@/config/index.js";
 import { UserProfiles } from "@/models/index.js";
@@ -30,25 +30,24 @@ export default define(meta, paramDef, async (ps, user) => {
 	}
 
 	// Generate user's secret key
-	const secret = speakeasy.generateSecret({
-		length: 32,
-	});
+	const secret = new OTPAuth.Secret();
 
 	await UserProfiles.update(user.id, {
 		twoFactorTempSecret: secret.base32,
 	});
 
 	// Get the data URL of the authenticator URL
-	const url = speakeasy.otpauthURL({
-		secret: secret.base32,
-		encoding: "base32",
+	const totp = new OTPAuth.TOTP({
+		secret,
+		digits: 6,
 		label: user.username,
 		issuer: config.host,
 	});
-	const dataUrl = await QRCode.toDataURL(url);
+	const url = totp.toString();
+	const qr = await QRCode.toDataURL(url);
 
 	return {
-		qr: dataUrl,
+		qr,
 		url,
 		secret: secret.base32,
 		label: user.username,
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
index 862c971e75..d91c8f214b 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
@@ -34,6 +34,24 @@ export default define(meta, paramDef, async (ps, user) => {
 		id: ps.credentialId,
 	});
 
+	// 使われているキーがなくなったらパスワードレスログインをやめる
+	const keyCount = await UserSecurityKeys.count({
+		where: {
+			userId: user.id,
+		},
+		select: {
+			id: true,
+			name: true,
+			lastUsed: true,
+		},
+	});
+
+	if (keyCount === 0) {
+		await UserProfiles.update(me.id, {
+			usePasswordLessLogin: false,
+		});
+	}
+
 	// Publish meUpdated event
 	publishMainStream(
 		user.id,
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
index 57d57ff65a..54f1422d4c 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
@@ -1,5 +1,6 @@
+import { publishMainStream } from "@/services/stream.js";
 import define from "../../../define.js";
-import { UserProfiles } from "@/models/index.js";
+import { Users, UserProfiles } from "@/models/index.js";
 import { comparePassword } from "@/misc/password.js";
 
 export const meta = {
@@ -29,5 +30,13 @@ export default define(meta, paramDef, async (ps, user) => {
 	await UserProfiles.update(user.id, {
 		twoFactorSecret: null,
 		twoFactorEnabled: false,
+		usePasswordLessLogin: false,
 	});
+
+	const iObj = await Users.pack(user.id, user, {
+		detail: true,
+		includeSecrets: true,
+	});
+
+	publishMainStream(user.id, "meUpdated", iObj);
 });
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts
new file mode 100644
index 0000000000..7587ec780e
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts
@@ -0,0 +1,58 @@
+import { publishMainStream } from "@/services/stream.js";
+import define from "../../../define.js";
+import { Users, UserSecurityKeys } from "@/models/index.js";
+import { ApiError } from "../../../error.js";
+
+export const meta = {
+	requireCredential: true,
+
+	secure: true,
+
+	errors: {
+		noSuchKey: {
+			message: "No such key.",
+			code: "NO_SUCH_KEY",
+			id: "f9c5467f-d492-4d3c-9a8g-a70dacc86512",
+		},
+
+		accessDenied: {
+			message: "You do not have edit privilege of the channel.",
+			code: "ACCESS_DENIED",
+			id: "1fb7cb09-d46a-4fff-b8df-057708cce513",
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: "object",
+	properties: {
+		name: { type: "string", minLength: 1, maxLength: 30 },
+		credentialId: { type: "string" },
+	},
+	required: ["name", "credentialId"],
+} as const;
+
+export default define(meta, paramDef, async (ps, user) => {
+	const key = await UserSecurityKeys.findOneBy({
+		id: ps.credentialId,
+	});
+
+	if (key == null) {
+		throw new ApiError(meta.errors.noSuchKey);
+	}
+
+	if (key.userId !== user.id) {
+		throw new ApiError(meta.errors.accessDenied);
+	}
+
+	await UserSecurityKeys.update(key.id, {
+		name: ps.name,
+	});
+
+	const iObj = await Users.pack(user.id, user, {
+		detail: true,
+		includeSecrets: true,
+	});
+
+	publishMainStream(user.id, "meUpdated", iObj);
+});
diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts
index ef5b137813..06d801a953 100644
--- a/packages/backend/src/server/api/private/signin.ts
+++ b/packages/backend/src/server/api/private/signin.ts
@@ -1,5 +1,5 @@
 import type Koa from "koa";
-import * as speakeasy from "speakeasy";
+import * as OTPAuth from "otpauth";
 import signin from "../common/signin.js";
 import config from "@/config/index.js";
 import {
@@ -136,14 +136,18 @@ export default async (ctx: Koa.Context) => {
 			return;
 		}
 
-		const verified = (speakeasy as any).totp.verify({
-			secret: profile.twoFactorSecret,
-			encoding: "base32",
-			token: token,
-			window: 2,
+		if (profile.twoFactorSecret == null) {
+			throw new Error("Attempted 2FA signin without 2FA enabled.");
+		}
+
+		const delta = OTPAuth.TOTP.validate({
+			secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret),
+			digits: 6,
+			token,
+			window: 1,
 		});
 
-		if (verified) {
+		if (delta != null) {
 			signin(ctx, user);
 			return;
 		} else {
diff --git a/packages/calckey-js/src/api.types.ts b/packages/calckey-js/src/api.types.ts
index 9f16056da1..626bdaad02 100644
--- a/packages/calckey-js/src/api.types.ts
+++ b/packages/calckey-js/src/api.types.ts
@@ -725,6 +725,7 @@ export type Endpoints = {
 	"i/2fa/password-less": { req: TODO; res: TODO };
 	"i/2fa/register-key": { req: TODO; res: TODO };
 	"i/2fa/register": { req: TODO; res: TODO };
+	"i/2fa/update-key": { req: TODO; res: TODO };
 	"i/2fa/remove-key": { req: TODO; res: TODO };
 	"i/2fa/unregister": { req: TODO; res: TODO };
 
diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue
index 20f0b9a74d..f870005eff 100644
--- a/packages/client/src/components/MkDialog.vue
+++ b/packages/client/src/components/MkDialog.vue
@@ -53,12 +53,15 @@
 			>
 				<Mfm :text="i18n.ts.password" />
 			</header>
-			<div v-if="text" :class="$style.text"><Mfm :text="text" /></div>
+			<div v-if="text" :class="$style.text">
+				<Mfm :text="text" />
+			</div>
 			<MkInput
 				ref="inputEl"
 				v-if="input && input.type !== 'paragraph'"
 				v-model="inputValue"
 				autofocus
+				:autocomplete="input.autocomplete"
 				:type="input.type == 'search' ? 'search' : input.type || 'text'"
 				:placeholder="input.placeholder || undefined"
 				@keydown="onInputKeydown"
@@ -69,6 +72,22 @@
 				<template v-if="input.type === 'password'" #prefix
 					><i class="ph-password ph-bold ph-lg"></i
 				></template>
+				<template #caption>
+					<span
+						v-if="
+							okButtonDisabled &&
+							disabledReason === 'charactersExceeded'
+						"
+						v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"
+					/>
+					<span
+						v-else-if="
+							okButtonDisabled &&
+							disabledReason === 'charactersBelow'
+						"
+						v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"
+					/>
+				</template>
 				<template v-if="input.type === 'search'" #suffix>
 					<button
 						class="_buttonIcon"
@@ -118,6 +137,7 @@
 						inline
 						primary
 						:autofocus="!input && !select"
+						:disabled="okButtonDisabled"
 						@click="ok"
 						>{{
 							showCancelButton || input || select
@@ -139,8 +159,8 @@
 						primary
 						:autofocus="!input && !select"
 						@click="ok"
-						>{{ i18n.ts.yes }}</MkButton
-					>
+						>{{ i18n.ts.yes }}
+					</MkButton>
 					<MkButton
 						v-if="showCancelButton || input || select"
 						inline
@@ -182,7 +202,10 @@ import * as Acct from "calckey-js/built/acct";
 type Input = {
 	type: HTMLInputElement["type"];
 	placeholder?: string | null;
-	default: any | null;
+	autocomplete?: string;
+	default: string | number | null;
+	minLength?: number;
+	maxLength?: number;
 };
 
 type Select = {
@@ -245,8 +268,35 @@ const emit = defineEmits<{
 
 const modal = shallowRef<InstanceType<typeof MkModal>>();
 
-const inputValue = ref(props.input?.default || "");
-const selectedValue = ref(props.select?.default || null);
+const inputValue = ref<string | number | null>(props.input?.default ?? null);
+const selectedValue = ref(props.select?.default ?? null);
+
+let disabledReason = $ref<null | "charactersExceeded" | "charactersBelow">(
+	null
+);
+const okButtonDisabled = $computed<boolean>(() => {
+	if (props.input) {
+		if (props.input.minLength) {
+			if (
+				(inputValue.value || inputValue.value === "") &&
+				(inputValue.value as string).length < props.input.minLength
+			) {
+				disabledReason = "charactersBelow";
+				return true;
+			}
+		}
+		if (props.input.maxLength) {
+			if (
+				inputValue.value &&
+				(inputValue.value as string).length > props.input.maxLength
+			) {
+				disabledReason = "charactersExceeded";
+				return true;
+			}
+		}
+	}
+	return false;
+});
 
 const inputEl = ref<typeof MkInput>();
 
diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index c95d89bd1d..d3dbbe943d 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -39,6 +39,7 @@
 					:placeholder="i18n.ts.password"
 					type="password"
 					:with-password-toggle="true"
+					autocomplete="current-password"
 					required
 					data-cy-signin-password
 				>
@@ -90,6 +91,7 @@
 						v-model="password"
 						type="password"
 						:with-password-toggle="true"
+						autocomplete="current-password"
 						required
 					>
 						<template #label>{{ i18n.ts.password }}</template>
@@ -101,7 +103,7 @@
 						v-model="token"
 						type="text"
 						pattern="^[0-9]{6}$"
-						autocomplete="off"
+						autocomplete="one-time-code"
 						:spellcheck="false"
 						required
 					>
@@ -383,10 +385,11 @@ function showSuspendedDialog() {
 			margin: 0 auto 0 auto;
 			width: 64px;
 			height: 64px;
-			background: #ddd;
+			background: var(--accentedBg);
 			background-position: center;
 			background-size: cover;
 			border-radius: 100%;
+			transition: background-image 0.2s ease-in;
 		}
 	}
 }
diff --git a/packages/client/src/components/form/input.vue b/packages/client/src/components/form/input.vue
index 8a6a1dd933..55d69f7416 100644
--- a/packages/client/src/components/form/input.vue
+++ b/packages/client/src/components/form/input.vue
@@ -61,7 +61,7 @@ import { useInterval } from "@/scripts/use-interval";
 import { i18n } from "@/i18n";
 
 const props = defineProps<{
-	modelValue: string | number;
+	modelValue: string | number | null;
 	type?:
 		| "text"
 		| "number"
@@ -77,7 +77,7 @@ const props = defineProps<{
 	pattern?: string;
 	placeholder?: string;
 	autofocus?: boolean;
-	autocomplete?: boolean;
+	autocomplete?: string;
 	spellcheck?: boolean;
 	step?: any;
 	datalist?: string[];
diff --git a/packages/client/src/components/form/select.vue b/packages/client/src/components/form/select.vue
index e62decad21..db4bbb2880 100644
--- a/packages/client/src/components/form/select.vue
+++ b/packages/client/src/components/form/select.vue
@@ -1,38 +1,39 @@
 <template>
 	<div class="vblkjoeq">
-		<label>
-			<div class="label"><slot name="label"></slot></div>
-			<div
-				ref="container"
-				class="input"
-				:class="{ inline, disabled, focused }"
-				@click.prevent="onClick"
-				tabindex="-1"
+		<div class="label" @click="focus"><slot name="label"></slot></div>
+		<div
+			ref="container"
+			class="input"
+			:class="{ inline, disabled, focused }"
+			@mousedown.prevent="show"
+		>
+			<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
+			<select
+				ref="inputEl"
+				v-model="v"
+				v-adaptive-border
+				class="select"
+				:disabled="disabled"
+				:required="required"
+				:readonly="readonly"
+				:placeholder="placeholder"
+				@focus="focused = true"
+				@blur="focused = false"
+				@input="onInput"
 			>
-				<div ref="prefixEl" class="prefix">
-					<slot name="prefix"></slot>
-				</div>
-				<select
-					ref="inputEl"
-					v-model="v"
-					v-adaptive-border
-					class="select"
-					:disabled="disabled"
-					:required="required"
-					:readonly="readonly"
-					:placeholder="placeholder"
-					@focus="focused = true"
-					@blur="focused = false"
-					@input="onInput"
-				>
-					<slot></slot>
-				</select>
-				<div ref="suffixEl" class="suffix">
-					<i class="ph-caret-down ph-bold ph-lg"></i>
-				</div>
+				<slot></slot>
+			</select>
+			<div ref="suffixEl" class="suffix">
+				<i
+					class="ph-caret-down ph-bold ph-lg"
+					:class="[
+						$style.chevron,
+						{ [$style.chevronOpening]: opening },
+					]"
+				></i>
 			</div>
-			<div class="caption"><slot name="caption"></slot></div>
-		</label>
+		</div>
+		<div class="caption"><slot name="caption"></slot></div>
 
 		<MkButton v-if="manualSave && changed" primary @click="updated"
 			><i class="ph-floppy-disk-back ph-bold ph-lg"></i>
@@ -44,7 +45,6 @@
 <script lang="ts" setup>
 import {
 	onMounted,
-	onUnmounted,
 	nextTick,
 	ref,
 	watch,
@@ -59,7 +59,7 @@ import { useInterval } from "@/scripts/use-interval";
 import { i18n } from "@/i18n";
 
 const props = defineProps<{
-	modelValue: string;
+	modelValue: string | null;
 	required?: boolean;
 	readonly?: boolean;
 	disabled?: boolean;
@@ -73,7 +73,7 @@ const props = defineProps<{
 
 const emit = defineEmits<{
 	(ev: "change", _ev: KeyboardEvent): void;
-	(ev: "update:modelValue", value: string): void;
+	(ev: "update:modelValue", value: string | null): void;
 }>();
 
 const slots = useSlots();
@@ -81,6 +81,7 @@ const slots = useSlots();
 const { modelValue, autofocus } = toRefs(props);
 const v = ref(modelValue.value);
 const focused = ref(false);
+const opening = ref(false);
 const changed = ref(false);
 const invalid = ref(false);
 const filled = computed(() => v.value !== "" && v.value != null);
@@ -88,7 +89,7 @@ const inputEl = ref(null);
 const prefixEl = ref(null);
 const suffixEl = ref(null);
 const container = ref(null);
-const height = props.small ? 36 : props.large ? 40 : 38;
+const height = props.small ? 33 : props.large ? 39 : 36;
 
 const focus = () => inputEl.value.focus();
 const onInput = (ev) => {
@@ -145,8 +146,9 @@ onMounted(() => {
 	});
 });
 
-const onClick = (ev: MouseEvent) => {
+function show(ev: MouseEvent) {
 	focused.value = true;
+	opening.value = true;
 
 	const menu = [];
 	let options = slots.default!();
@@ -154,7 +156,7 @@ const onClick = (ev: MouseEvent) => {
 	const pushOption = (option: VNode) => {
 		menu.push({
 			text: option.children,
-			active: v.value === option.props.value,
+			active: computed(() => v.value === option.props.value),
 			action: () => {
 				v.value = option.props.value;
 			},
@@ -188,127 +190,136 @@ const onClick = (ev: MouseEvent) => {
 
 	os.popupMenu(menu, container.value, {
 		width: container.value.offsetWidth,
+		onClosing: () => {
+			opening.value = false;
+		},
 	}).then(() => {
 		focused.value = false;
 	});
-};
+}
 </script>
 
 <style lang="scss" scoped>
 .vblkjoeq {
-	> label {
-		> .label {
-			font-size: 0.85em;
-			padding: 0 0 8px 0;
-			user-select: none;
+	> .label {
+		font-size: 0.85em;
+		padding: 0 0 8px 0;
+		user-select: none;
 
-			&:empty {
-				display: none;
-			}
+		&:empty {
+			display: none;
 		}
+	}
 
-		> .caption {
-			font-size: 0.85em;
-			padding: 8px 0 0 0;
-			color: var(--fgTransparentWeak);
+	> .caption {
+		font-size: 0.85em;
+		padding: 8px 0 0 0;
+		color: var(--fgTransparentWeak);
 
-			&:empty {
-				display: none;
-			}
+		&:empty {
+			display: none;
 		}
+	}
 
-		> .input {
-			position: relative;
-			cursor: pointer;
-			margin-left: 0.2rem;
-			margin-right: 0.2rem;
-
-			&:hover {
-				> .select {
-					border-color: var(--inputBorderHover) !important;
-				}
-			}
+	> .input {
+		position: relative;
+		cursor: pointer;
 
+		&:hover {
 			> .select {
-				appearance: none;
-				-webkit-appearance: none;
-				display: block;
-				height: v-bind("height + 'px'");
-				width: 100%;
-				margin: 0;
-				padding: 0 12px;
-				font: inherit;
-				font-weight: normal;
-				font-size: 1em;
-				color: var(--fg);
-				background: var(--panel);
-				border: solid 1px var(--panel);
-				border-radius: 6px;
-				outline: none;
-				box-shadow: none;
-				box-sizing: border-box;
-				cursor: pointer;
-				transition: border-color 0.1s ease-out;
-				pointer-events: none;
-				user-select: none;
+				border-color: var(--inputBorderHover) !important;
+			}
+		}
+
+		> .select {
+			appearance: none;
+			-webkit-appearance: none;
+			display: block;
+			height: v-bind("height + 'px'");
+			width: 100%;
+			margin: 0;
+			padding: 0 12px;
+			font: inherit;
+			font-weight: normal;
+			font-size: 1em;
+			color: var(--fg);
+			background: var(--panel);
+			border: solid 1px var(--panel);
+			border-radius: 6px;
+			outline: none;
+			box-shadow: none;
+			box-sizing: border-box;
+			cursor: pointer;
+			transition: border-color 0.1s ease-out;
+			pointer-events: none;
+			user-select: none;
+		}
+
+		> .prefix,
+		> .suffix {
+			display: flex;
+			align-items: center;
+			position: absolute;
+			z-index: 1;
+			top: 0;
+			padding: 0 12px;
+			font-size: 1em;
+			height: v-bind("height + 'px'");
+			pointer-events: none;
+
+			&:empty {
+				display: none;
 			}
 
-			> .prefix,
-			> .suffix {
-				display: flex;
-				align-items: center;
-				position: absolute;
-				z-index: 1;
-				top: 0;
-				padding: 0 12px;
-				font-size: 1em;
-				height: v-bind("height + 'px'");
-				pointer-events: none;
-
-				&:empty {
-					display: none;
-				}
-
-				> * {
-					display: inline-block;
-					min-width: 16px;
-					max-width: 150px;
-					overflow: hidden;
-					white-space: nowrap;
-					text-overflow: ellipsis;
-				}
-			}
-
-			> .prefix {
-				left: 0;
-				padding-right: 6px;
-			}
-
-			> .suffix {
-				right: 0;
-				padding-left: 6px;
-			}
-
-			&.inline {
+			> * {
 				display: inline-block;
-				margin: 0;
+				min-width: 16px;
+				max-width: 150px;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
 			}
+		}
 
-			&.focused {
-				> select {
-					border-color: var(--accent) !important;
-				}
+		> .prefix {
+			left: 0;
+			padding-right: 6px;
+		}
+
+		> .suffix {
+			right: 0;
+			padding-left: 6px;
+		}
+
+		&.inline {
+			display: inline-block;
+			margin: 0;
+		}
+
+		&.focused {
+			> select {
+				border-color: var(--accent) !important;
 			}
+		}
 
-			&.disabled {
-				opacity: 0.7;
+		&.disabled {
+			opacity: 0.7;
 
-				&,
-				* {
-					cursor: not-allowed !important;
-				}
+			&,
+			* {
+				cursor: not-allowed !important;
 			}
 		}
 	}
 }
 </style>
+
+<style lang="scss" module>
+.chevron {
+	transition: transform 0.1s ease-out;
+}
+
+.chevronOpening {
+	transform: rotateX(180deg);
+}
+</style>
diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts
index 6a96b83fea..376d5184e5 100644
--- a/packages/client/src/os.ts
+++ b/packages/client/src/os.ts
@@ -22,7 +22,7 @@ const apiClient = new Misskey.api.APIClient({
 export const api = ((
 	endpoint: string,
 	data: Record<string, any> = {},
-	token?: string | null | undefined,
+	token?: string | null | undefined
 ) => {
 	pendingApiRequestsCount.value++;
 
@@ -36,13 +36,16 @@ export const api = ((
 		: undefined;
 
 	const promise = new Promise((resolve, reject) => {
-		fetch(endpoint.indexOf("://") > -1 ? endpoint : `${apiUrl}/${endpoint}`, {
-			method: "POST",
-			body: JSON.stringify(data),
-			credentials: "omit",
-			cache: "no-cache",
-			headers: authorization ? { authorization } : {},
-		})
+		fetch(
+			endpoint.indexOf("://") > -1 ? endpoint : `${apiUrl}/${endpoint}`,
+			{
+				method: "POST",
+				body: JSON.stringify(data),
+				credentials: "omit",
+				cache: "no-cache",
+				headers: authorization ? { authorization } : {},
+			}
+		)
 			.then(async (res) => {
 				const body = res.status === 204 ? null : await res.json();
 
@@ -65,7 +68,7 @@ export const api = ((
 export const apiGet = ((
 	endpoint: string,
 	data: Record<string, any> = {},
-	token?: string | null | undefined,
+	token?: string | null | undefined
 ) => {
 	pendingApiRequestsCount.value++;
 
@@ -110,7 +113,7 @@ export const apiGet = ((
 export const apiWithDialog = ((
 	endpoint: string,
 	data: Record<string, any> = {},
-	token?: string | null | undefined,
+	token?: string | null | undefined
 ) => {
 	const promise = api(endpoint, data, token);
 	promiseDialog(promise, null, (err) => {
@@ -127,7 +130,7 @@ export function promiseDialog<T extends Promise<any>>(
 	promise: T,
 	onSuccess?: ((res: any) => void) | null,
 	onFailure?: ((err: Error) => void) | null,
-	text?: string,
+	text?: string
 ): T {
 	const showing = ref(true);
 	const success = ref(false);
@@ -165,7 +168,7 @@ export function promiseDialog<T extends Promise<any>>(
 			text: text,
 		},
 		{},
-		"closed",
+		"closed"
 	);
 
 	return promise;
@@ -186,7 +189,7 @@ const zIndexes = {
 	high: 3000000,
 };
 export function claimZIndex(
-	priority: "low" | "middle" | "high" = "low",
+	priority: "low" | "middle" | "high" = "low"
 ): number {
 	zIndexes[priority] += 100;
 	return zIndexes[priority];
@@ -201,7 +204,7 @@ export async function popup(
 	component: Component,
 	props: Record<string, any>,
 	events = {},
-	disposeEvent?: string,
+	disposeEvent?: string
 ) {
 	markRaw(component);
 
@@ -242,7 +245,7 @@ export function pageWindow(path: string) {
 			initialPath: path,
 		},
 		{},
-		"closed",
+		"closed"
 	);
 }
 
@@ -257,7 +260,7 @@ export function modalPageWindow(path: string) {
 			initialPath: path,
 		},
 		{},
-		"closed",
+		"closed"
 	);
 }
 
@@ -268,7 +271,7 @@ export function toast(message: string) {
 			message,
 		},
 		{},
-		"closed",
+		"closed"
 	);
 }
 
@@ -289,7 +292,7 @@ export function alert(props: {
 					resolve();
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -313,7 +316,7 @@ export function confirm(props: {
 					resolve(result ? result : { canceled: true });
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -340,7 +343,7 @@ export function yesno(props: {
 					resolve(result ? result : { canceled: true });
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -350,7 +353,10 @@ export function inputText(props: {
 	title?: string | null;
 	text?: string | null;
 	placeholder?: string | null;
+	autocomplete?: string;
 	default?: string | null;
+	minLength?: number;
+	maxLength?: number;
 }): Promise<
 	| { canceled: true; result: undefined }
 	| {
@@ -360,19 +366,17 @@ export function inputText(props: {
 > {
 	return new Promise((resolve, reject) => {
 		popup(
-			defineAsyncComponent({
-				loader: () => import("@/components/MkDialog.vue"),
-				loadingComponent: MkWaitingDialog,
-				delay: 1000,
-			}),
+			MkDialog,
 			{
-				type: props.type,
 				title: props.title,
 				text: props.text,
 				input: {
 					type: props.type,
 					placeholder: props.placeholder,
+					autocomplete: props.autocomplete,
 					default: props.default,
+					minLength: props.minLength,
+					maxLength: props.maxLength,
 				},
 			},
 			{
@@ -380,7 +384,7 @@ export function inputText(props: {
 					resolve(result ? result : { canceled: true });
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -418,7 +422,7 @@ export function inputParagraph(props: {
 					resolve(result ? result : { canceled: true });
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -428,6 +432,7 @@ export function inputNumber(props: {
 	text?: string | null;
 	placeholder?: string | null;
 	default?: number | null;
+	autocomplete?: string;
 }): Promise<
 	| { canceled: true; result: undefined }
 	| {
@@ -448,6 +453,7 @@ export function inputNumber(props: {
 				input: {
 					type: "number",
 					placeholder: props.placeholder,
+					autocomplete: props.autocomplete,
 					default: props.default,
 				},
 			},
@@ -456,7 +462,7 @@ export function inputNumber(props: {
 					resolve(result ? result : { canceled: true });
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -475,11 +481,7 @@ export function inputDate(props: {
 > {
 	return new Promise((resolve, reject) => {
 		popup(
-			defineAsyncComponent({
-				loader: () => import("@/components/MkDialog.vue"),
-				loadingComponent: MkWaitingDialog,
-				delay: 1000,
-			}),
+			MkDialog,
 			{
 				title: props.title,
 				text: props.text,
@@ -492,13 +494,16 @@ export function inputDate(props: {
 			{
 				done: (result) => {
 					resolve(
-						(result && isFinite(new Date(result.result)))
-							? { result: new Date(result.result), canceled: false }
-							: { canceled: true },
+						result
+							? {
+									result: new Date(result.result),
+									canceled: false,
+							  }
+							: { canceled: true }
 					);
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -524,7 +529,7 @@ export function select<C = any>(
 					}[];
 				}[];
 		  }
-	),
+	)
 ): Promise<
 	| { canceled: true; result: undefined }
 	| {
@@ -534,11 +539,7 @@ export function select<C = any>(
 > {
 	return new Promise((resolve, reject) => {
 		popup(
-			defineAsyncComponent({
-				loader: () => import("@/components/MkDialog.vue"),
-				loadingComponent: MkWaitingDialog,
-				delay: 1000,
-			}),
+			MkDialog,
 			{
 				title: props.title,
 				text: props.text,
@@ -553,23 +554,19 @@ export function select<C = any>(
 					resolve(result ? result : { canceled: true });
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
 
-export function success() {
+export function success(): Promise<void> {
 	return new Promise((resolve, reject) => {
 		const showing = ref(true);
 		window.setTimeout(() => {
 			showing.value = false;
 		}, 1000);
 		popup(
-			defineAsyncComponent({
-				loader: () => import("@/components/MkWaitingDialog.vue"),
-				loadingComponent: MkWaitingDialog,
-				delay: 1000,
-			}),
+			MkWaitingDialog,
 			{
 				success: true,
 				showing: showing,
@@ -577,20 +574,16 @@ export function success() {
 			{
 				done: () => resolve(),
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
 
-export function waiting() {
+export function waiting(): Promise<void> {
 	return new Promise((resolve, reject) => {
 		const showing = ref(true);
 		popup(
-			defineAsyncComponent({
-				loader: () => import("@/components/MkWaitingDialog.vue"),
-				loadingComponent: MkWaitingDialog,
-				delay: 1000,
-			}),
+			MkWaitingDialog,
 			{
 				success: false,
 				showing: showing,
@@ -598,7 +591,7 @@ export function waiting() {
 			{
 				done: () => resolve(),
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -617,7 +610,7 @@ export function form(title, form) {
 					resolve(result);
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -636,7 +629,7 @@ export async function selectUser() {
 					resolve(user);
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -655,7 +648,7 @@ export async function selectInstance(): Promise<Misskey.entities.Instance> {
 					resolve(instance);
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -679,7 +672,7 @@ export async function selectDriveFile(multiple: boolean) {
 					}
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -703,7 +696,7 @@ export async function selectDriveFolder(multiple: boolean) {
 					}
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -725,7 +718,7 @@ export async function pickEmoji(src: HTMLElement | null, opts) {
 					resolve(emoji);
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -734,7 +727,7 @@ export async function cropImage(
 	image: Misskey.entities.DriveFile,
 	options: {
 		aspectRatio: number;
-	},
+	}
 ): Promise<Misskey.entities.DriveFile> {
 	return new Promise((resolve, reject) => {
 		popup(
@@ -752,7 +745,7 @@ export async function cropImage(
 					resolve(x);
 				},
 			},
-			"closed",
+			"closed"
 		);
 	});
 }
@@ -767,7 +760,7 @@ let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null;
 export async function openEmojiPicker(
 	src?: HTMLElement,
 	opts,
-	initialTextarea: typeof activeTextarea,
+	initialTextarea: typeof activeTextarea
 ) {
 	if (openingEmojiPicker) return;
 
@@ -783,13 +776,14 @@ export async function openEmojiPicker(
 	const observer = new MutationObserver((records) => {
 		for (const record of records) {
 			for (const node of Array.from(record.addedNodes).filter(
-				(node) => node instanceof HTMLElement,
+				(node) => node instanceof HTMLElement
 			) as HTMLElement[]) {
 				const textareas = node.querySelectorAll("textarea, input");
 				for (const textarea of Array.from(textareas).filter(
-					(textarea) => textarea.dataset.preventEmojiInsert == null,
+					(textarea) => textarea.dataset.preventEmojiInsert == null
 				)) {
-					if (document.activeElement === textarea) activeTextarea = textarea;
+					if (document.activeElement === textarea)
+						activeTextarea = textarea;
 					textarea.addEventListener("focus", () => {
 						activeTextarea = textarea;
 					});
@@ -827,7 +821,7 @@ export async function openEmojiPicker(
 				openingEmojiPicker = null;
 				observer.disconnect();
 			},
-		},
+		}
 	);
 }
 
@@ -839,7 +833,7 @@ export function popupMenu(
 		width?: number;
 		viaKeyboard?: boolean;
 		noReturnFocus?: boolean;
-	},
+	}
 ) {
 	return new Promise((resolve, reject) => {
 		let dispose;
@@ -862,7 +856,7 @@ export function popupMenu(
 					resolve();
 					dispose();
 				},
-			},
+			}
 		).then((res) => {
 			dispose = res.dispose;
 		});
@@ -871,7 +865,7 @@ export function popupMenu(
 
 export function contextMenu(
 	items: MenuItem[] | Ref<MenuItem[]>,
-	ev: MouseEvent,
+	ev: MouseEvent
 ) {
 	ev.preventDefault();
 	return new Promise((resolve, reject) => {
@@ -891,7 +885,7 @@ export function contextMenu(
 					resolve();
 					dispose();
 				},
-			},
+			}
 		).then((res) => {
 			dispose = res.dispose;
 		});
diff --git a/packages/client/src/pages/settings/2fa.qrdialog.vue b/packages/client/src/pages/settings/2fa.qrdialog.vue
new file mode 100644
index 0000000000..59b2875d72
--- /dev/null
+++ b/packages/client/src/pages/settings/2fa.qrdialog.vue
@@ -0,0 +1,96 @@
+<template>
+	<MkModal
+		ref="dialogEl"
+		:prefer-type="'dialog'"
+		:z-priority="'low'"
+		@click="cancel"
+		@close="cancel"
+		@closed="emit('closed')"
+	>
+		<div :class="$style.root" class="_gaps_m">
+			<I18n :src="i18n.ts._2fa.step1" tag="div">
+				<template #a>
+					<a
+						href="https://authpass.app/"
+						rel="noopener"
+						target="_blank"
+						class="_link"
+						>AuthPass</a
+					>
+				</template>
+				<template #b>
+					<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>
+			<MkKeyValue :copy="twoFactorData.url">
+				<template #key>{{ i18n.ts._2fa.step2Url }}</template>
+				<template #value>{{ twoFactorData.url }}</template>
+			</MkKeyValue>
+			<div class="_buttons">
+				<MkButton primary @click="ok">{{ i18n.ts.next }}</MkButton>
+				<MkButton @click="cancel">{{ i18n.ts.cancel }}</MkButton>
+			</div>
+		</div>
+	</MkModal>
+</template>
+
+<script lang="ts" setup>
+import MkButton from "@/components/MkButton.vue";
+import MkModal from "@/components/MkModal.vue";
+import MkKeyValue from "@/components/MkKeyValue.vue";
+import { i18n } from "@/i18n";
+
+defineProps<{
+	twoFactorData: {
+		qr: string;
+		url: string;
+	};
+}>();
+
+const emit = defineEmits<{
+	(ev: "ok"): void;
+	(ev: "cancel"): void;
+	(ev: "closed"): void;
+}>();
+
+const cancel = () => {
+	emit("cancel");
+	emit("closed");
+};
+
+const ok = () => {
+	emit("ok");
+	emit("closed");
+};
+</script>
+
+<style lang="scss" module>
+.root {
+	position: relative;
+	margin: auto;
+	padding: 32px;
+	min-width: 320px;
+	max-width: calc(100svw - 64px);
+	box-sizing: border-box;
+	background: var(--panel);
+	border-radius: var(--radius);
+}
+
+.qr {
+	width: 20em;
+	max-width: 100%;
+}
+</style>
diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index a1af86b698..2a3d8d6e0a 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -1,300 +1,310 @@
 <template>
-	<div>
-		<MkButton
-			v-if="!twoFactorData && !$i.twoFactorEnabled"
-			@click="register"
-			>{{ i18n.ts._2fa.registerDevice }}</MkButton
-		>
-		<template v-if="$i.twoFactorEnabled">
-			<p>{{ i18n.ts._2fa.alreadyRegistered }}</p>
-			<MkButton @click="unregister">{{ i18n.ts.unregister }}</MkButton>
-		</template>
+	<FormSection :first="first">
+		<template #label>{{ i18n.ts["2fa"] }}</template>
 
-		<template v-if="supportsCredentials && $i.twoFactorEnabled">
-			<hr class="totp-method-sep" />
-
-			<h2 class="heading">{{ i18n.ts.securityKey }}</h2>
-			<p>{{ i18n.ts._2fa.securityKeyInfo }}</p>
-			<div class="key-list">
-				<div v-for="key in $i.securityKeysList" class="key">
-					<h3>{{ key.name }}</h3>
-					<div class="last-used">
-						{{ i18n.ts.lastUsed }}<MkTime :time="key.lastUsed" />
-					</div>
-					<MkButton @click="unregisterKey(key)">{{
+		<div v-if="$i" class="_gaps_s">
+			<MkFolder>
+				<template #icon
+					><i class="ph-shield-check ph-bold ph-lg"></i
+				></template>
+				<template #label>{{ i18n.ts.totp }}</template>
+				<template #caption>{{ i18n.ts.totpDescription }}</template>
+				<div v-if="$i.twoFactorEnabled" class="_gaps_s">
+					<div v-text="i18n.ts._2fa.alreadyRegistered" />
+					<template v-if="$i.securityKeysList.length > 0">
+						<MkButton @click="renewTOTP">{{
+							i18n.ts._2fa.renewTOTP
+						}}</MkButton>
+						<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
+					</template>
+					<MkButton v-else @click="unregisterTOTP">{{
 						i18n.ts.unregister
 					}}</MkButton>
 				</div>
-			</div>
+
+				<MkButton
+					v-else-if="!twoFactorData && !$i.twoFactorEnabled"
+					@click="registerTOTP"
+					>{{ i18n.ts._2fa.registerTOTP }}</MkButton
+				>
+			</MkFolder>
+
+			<MkFolder>
+				<template #icon><i class="ph-key ph-bold ph-lg"></i></template>
+				<template #label>{{ i18n.ts.securityKeyAndPasskey }}</template>
+				<div class="_gaps_s">
+					<MkInfo>
+						{{ i18n.ts._2fa.securityKeyInfo }}<br />
+						<br />
+						{{ i18n.ts._2fa.chromePasskeyNotSupported }}
+					</MkInfo>
+
+					<MkInfo v-if="!supportsCredentials" warn>
+						{{ i18n.ts._2fa.securityKeyNotSupported }}
+					</MkInfo>
+
+					<MkInfo
+						v-else-if="supportsCredentials && !$i.twoFactorEnabled"
+						warn
+					>
+						{{ i18n.ts._2fa.registerTOTPBeforeKey }}
+					</MkInfo>
+
+					<template v-else>
+						<MkButton primary @click="addSecurityKey">{{
+							i18n.ts._2fa.registerSecurityKey
+						}}</MkButton>
+						<MkFolder
+							v-for="key in $i.securityKeysList"
+							:key="key.id"
+						>
+							<template #label>{{ key.name }}</template>
+							<template #suffix
+								><I18n :src="i18n.ts.lastUsedAt"
+									><template #t
+										><MkTime
+											:time="
+												key.lastUsed
+											" /></template></I18n
+							></template>
+							<div class="_buttons">
+								<MkButton @click="renameKey(key)"
+									><i
+										class="ph-pencil-line ph-bold ph-lg"
+									></i>
+									{{ i18n.ts.rename }}</MkButton
+								>
+								<MkButton danger @click="unregisterKey(key)"
+									><i class="ph-trash ph-bold ph-lg"></i>
+									{{ i18n.ts.unregister }}</MkButton
+								>
+							</div>
+						</MkFolder>
+					</template>
+				</div>
+			</MkFolder>
 
 			<MkSwitch
-				v-if="$i.securityKeysList.length > 0"
-				v-model="usePasswordLessLogin"
-				@update:modelValue="updatePasswordLessLogin"
-				>{{ i18n.ts.passwordLessLogin }}</MkSwitch
+				:disabled="
+					!$i.twoFactorEnabled || $i.securityKeysList.length === 0
+				"
+				:modelValue="usePasswordLessLogin"
+				@update:modelValue="(v) => updatePasswordLessLogin(v)"
 			>
-
-			<MkInfo
-				v-if="registration && registration.error"
-				style="margin-bottom: 1rem"
-				warn
-				>{{ i18n.ts.error }}: {{ registration.error }}</MkInfo
-			>
-			<MkButton
-				v-if="!registration || registration.error"
-				@click="addSecurityKey"
-				>{{ i18n.ts._2fa.registerKey }}</MkButton
-			>
-
-			<ol v-if="registration && !registration.error">
-				<li v-if="registration.stage >= 0">
-					{{ i18n.ts.tapSecurityKey }}
-					<i
-						v-if="registration.saving && registration.stage == 0"
-						class="ph-circle-notch ph-bold ph-lg fa-pulse ph-fw ph-lg"
-					></i>
-				</li>
-				<li v-if="registration.stage >= 1">
-					<MkForm
-						:disabled="
-							registration.stage != 1 || registration.saving
-						"
-					>
-						<MkInput v-model="keyName" :max="30">
-							<template #label>{{
-								i18n.ts.securityKeyName
-							}}</template>
-						</MkInput>
-						<MkButton
-							:disabled="keyName.length == 0"
-							@click="registerKey"
-							>{{ i18n.ts.registerSecurityKey }}</MkButton
-						>
-						<i
-							v-if="
-								registration.saving && registration.stage == 1
-							"
-							class="ph-circle-notch ph-bold ph-lg fa-pulse ph-fw ph-lg"
-						></i>
-					</MkForm>
-				</li>
-			</ol>
-		</template>
-		<div v-if="twoFactorData && !$i.twoFactorEnabled">
-			<ol style="margin: 0; padding: 0 0 0 1em">
-				<li>
-					<I18n :src="i18n.ts._2fa.step1" tag="span">
-						<template #a>
-							<a
-								href="https://authpass.app/"
-								rel="noopener"
-								target="_blank"
-								class="_link"
-								>AuthPass</a
-							>
-						</template>
-						<template #b>
-							<a
-								href="https://support.google.com/accounts/answer/1066447"
-								rel="noopener"
-								target="_blank"
-								class="_link"
-								>Google Authenticator</a
-							>
-						</template>
-					</I18n>
-				</li>
-				<li>
-					{{ i18n.ts._2fa.step2 }}<br /><img
-						:src="twoFactorData.qr"
-					/>
-					<p>
-						{{ i18n.ts._2fa.step2Url }}<br />{{ twoFactorData.url }}
-					</p>
-				</li>
-				<li>
-					{{ i18n.ts._2fa.step3 }}<br />
-					<MkInput
-						v-model="token"
-						type="text"
-						pattern="^[0-9]{6}$"
-						autocomplete="off"
-						:spellcheck="false"
-						><template #label>{{
-							i18n.ts.token
-						}}</template></MkInput
-					>
-					<MkButton primary @click="submit">{{
-						i18n.ts.done
-					}}</MkButton>
-				</li>
-			</ol>
-			<MkInfo>{{ i18n.ts._2fa.step4 }}</MkInfo>
+				<template #label>{{ i18n.ts.passwordLessLogin }}</template>
+				<template #caption>{{
+					i18n.ts.passwordLessLoginDescription
+				}}</template>
+			</MkSwitch>
 		</div>
-	</div>
+	</FormSection>
 </template>
 
 <script lang="ts" setup>
-import { ref } from "vue";
+import { ref, defineAsyncComponent } from "vue";
 import { hostname } from "@/config";
 import { byteify, hexify, stringify } from "@/scripts/2fa";
 import MkButton from "@/components/MkButton.vue";
 import MkInfo from "@/components/MkInfo.vue";
-import MkInput from "@/components/form/input.vue";
-import MkSwitch from "@/components/form/switch.vue";
+import MkSwitch from "@/components/MkSwitch.vue";
+import FormSection from "@/components/form/section.vue";
+import MkFolder from "@/components/MkFolder.vue";
 import * as os from "@/os";
 import { $i } from "@/account";
 import { i18n } from "@/i18n";
 
+// メモ: 各エンドポイントはmeUpdatedを発行するため、refreshAccountは不要
+
+withDefaults(
+	defineProps<{
+		first?: boolean;
+	}>(),
+	{
+		first: false,
+	}
+);
+
 const twoFactorData = ref<any>(null);
 const supportsCredentials = ref(!!navigator.credentials);
-const usePasswordLessLogin = ref($i!.usePasswordLessLogin);
-const registration = ref<any>(null);
-const keyName = ref("");
-const token = ref(null);
+const usePasswordLessLogin = $computed(() => $i!.usePasswordLessLogin);
 
-function register() {
-	os.inputText({
-		title: i18n.ts.password,
+async function registerTOTP() {
+	const password = await os.inputText({
+		title: i18n.ts._2fa.registerTOTP,
+		text: i18n.ts.currentPassword,
 		type: "password",
-	}).then(({ canceled, result: password }) => {
-		if (canceled) return;
-		os.api("i/2fa/register", {
-			password: password,
-		}).then((data) => {
-			twoFactorData.value = data;
-		});
+		autocomplete: "current-password",
+	});
+	if (password.canceled) return;
+
+	const twoFactorData = await os.apiWithDialog("i/2fa/register", {
+		password: password.result,
+	});
+
+	const qrdialog = await new Promise<boolean>((res) => {
+		os.popup(
+			defineAsyncComponent(() => import("./2fa.qrdialog.vue")),
+			{
+				twoFactorData,
+			},
+			{
+				ok: () => res(true),
+				cancel: () => res(false),
+			},
+			"closed"
+		);
+	});
+	if (!qrdialog) return;
+
+	const token = await os.inputNumber({
+		title: i18n.ts._2fa.step3Title,
+		text: i18n.ts._2fa.step3,
+		autocomplete: "one-time-code",
+	});
+	if (token.canceled) return;
+
+	await os.apiWithDialog("i/2fa/done", {
+		token: token.result.toString(),
+	});
+
+	await os.alert({
+		type: "success",
+		text: i18n.ts._2fa.step4,
 	});
 }
 
-function unregister() {
+function unregisterTOTP() {
 	os.inputText({
 		title: i18n.ts.password,
 		type: "password",
+		autocomplete: "current-password",
 	}).then(({ canceled, result: password }) => {
 		if (canceled) return;
-		os.api("i/2fa/unregister", {
+		os.apiWithDialog("i/2fa/unregister", {
 			password: password,
-		})
-			.then(() => {
-				usePasswordLessLogin.value = false;
-				updatePasswordLessLogin();
-			})
-			.then(() => {
-				os.success();
-				$i!.twoFactorEnabled = false;
-			});
-	});
-}
-
-function submit() {
-	os.api("i/2fa/done", {
-		token: token.value,
-	})
-		.then(() => {
-			os.success();
-			$i!.twoFactorEnabled = true;
-		})
-		.catch((err) => {
+		}).catch((error) => {
 			os.alert({
 				type: "error",
-				text: err,
+				text: error,
 			});
 		});
+	});
 }
 
-function registerKey() {
-	registration.value.saving = true;
-	os.api("i/2fa/key-done", {
-		password: registration.value.password,
-		name: keyName.value,
-		challengeId: registration.value.challengeId,
+function renewTOTP() {
+	os.confirm({
+		type: "question",
+		title: i18n.ts._2fa.renewTOTP,
+		text: i18n.ts._2fa.renewTOTPConfirm,
+		okText: i18n.ts._2fa.renewTOTPOk,
+		cancelText: i18n.ts._2fa.renewTOTPCancel,
+	}).then(({ canceled }) => {
+		if (canceled) return;
+		registerTOTP();
+	});
+}
+
+async function unregisterKey(key) {
+	const confirm = await os.confirm({
+		type: "question",
+		title: i18n.ts._2fa.removeKey,
+		text: i18n.t("_2fa.removeKeyConfirm", { name: key.name }),
+	});
+	if (confirm.canceled) return;
+
+	const password = await os.inputText({
+		title: i18n.ts.password,
+		type: "password",
+		autocomplete: "current-password",
+	});
+	if (password.canceled) return;
+
+	await os.apiWithDialog("i/2fa/remove-key", {
+		password: password.result,
+		credentialId: key.id,
+	});
+	os.success();
+}
+
+async function renameKey(key) {
+	const name = await os.inputText({
+		title: i18n.ts.rename,
+		default: key.name,
+		type: "text",
+		minLength: 1,
+		maxLength: 30,
+	});
+	if (name.canceled) return;
+
+	await os.apiWithDialog("i/2fa/update-key", {
+		name: name.result,
+		credentialId: key.id,
+	});
+}
+
+async function addSecurityKey() {
+	const password = await os.inputText({
+		title: i18n.ts.password,
+		type: "password",
+		autocomplete: "current-password",
+	});
+	if (password.canceled) return;
+
+	const challenge: any = await os.apiWithDialog("i/2fa/register-key", {
+		password: password.result,
+	});
+
+	const name = await os.inputText({
+		title: i18n.ts._2fa.registerSecurityKey,
+		text: i18n.ts._2fa.securityKeyName,
+		type: "text",
+		minLength: 1,
+		maxLength: 30,
+	});
+	if (name.canceled) return;
+
+	const webAuthnCreation = navigator.credentials.create({
+		publicKey: {
+			challenge: byteify(challenge.challenge, "base64"),
+			rp: {
+				id: hostname,
+				name: "Misskey",
+			},
+			user: {
+				id: byteify($i!.id, "ascii"),
+				name: $i!.username,
+				displayName: $i!.name,
+			},
+			pubKeyCredParams: [{ alg: -7, type: "public-key" }],
+			timeout: 60000,
+			attestation: "direct",
+		},
+	}) as Promise<
+		| (PublicKeyCredential & { response: AuthenticatorAttestationResponse })
+		| null
+	>;
+
+	const credential = await os.promiseDialog(
+		webAuthnCreation,
+		null,
+		() => {}, // ユーザーのキャンセルはrejectなのでエラーダイアログを出さない
+		i18n.ts._2fa.tapSecurityKey
+	);
+	if (!credential) return;
+
+	await os.apiWithDialog("i/2fa/key-done", {
+		password: password.result,
+		name: name.result,
+		challengeId: challenge.challengeId,
 		// we convert each 16 bits to a string to serialise
-		clientDataJSON: stringify(
-			registration.value.credential.response.clientDataJSON
-		),
-		attestationObject: hexify(
-			registration.value.credential.response.attestationObject
-		),
-	}).then((key) => {
-		registration.value = null;
-		key!.lastUsed = new Date();
-		os.success();
+		clientDataJSON: stringify(credential.response.clientDataJSON),
+		attestationObject: hexify(credential.response.attestationObject),
 	});
 }
 
-function unregisterKey(key) {
-	os.inputText({
-		title: i18n.ts.password,
-		type: "password",
-	}).then(({ canceled, result: password }) => {
-		if (canceled) return;
-		return os
-			.api("i/2fa/remove-key", {
-				password,
-				credentialId: key.id,
-			})
-			.then(() => {
-				usePasswordLessLogin.value = false;
-				updatePasswordLessLogin();
-			})
-			.then(() => {
-				os.success();
-			});
-	});
-}
-
-function addSecurityKey() {
-	os.inputText({
-		title: i18n.ts.password,
-		type: "password",
-	}).then(({ canceled, result: password }) => {
-		if (canceled) return;
-		os.api("i/2fa/register-key", {
-			password,
-		})
-			.then((reg) => {
-				registration.value = {
-					password,
-					challengeId: reg!.challengeId,
-					stage: 0,
-					publicKeyOptions: {
-						challenge: byteify(reg!.challenge, "base64"),
-						rp: {
-							id: hostname,
-							name: "Calckey",
-						},
-						user: {
-							id: byteify($i!.id, "ascii"),
-							name: $i!.username,
-							displayName: $i!.name,
-						},
-						pubKeyCredParams: [{ alg: -7, type: "public-key" }],
-						timeout: 60000,
-						attestation: "direct",
-					},
-					saving: true,
-				};
-				return navigator.credentials.create({
-					publicKey: registration.value.publicKeyOptions,
-				});
-			})
-			.then((credential) => {
-				registration.value.credential = credential;
-				registration.value.saving = false;
-				registration.value.stage = 1;
-			})
-			.catch((err) => {
-				console.warn("Error while registering?", err);
-				registration.value.error = err.message;
-				registration.value.stage = -1;
-			});
-	});
-}
-
-async function updatePasswordLessLogin() {
-	await os.api("i/2fa/password-less", {
-		value: !!usePasswordLessLogin.value,
+async function updatePasswordLessLogin(value: boolean) {
+	await os.apiWithDialog("i/2fa/password-less", {
+		value,
 	});
 }
 </script>
diff --git a/packages/client/src/pages/settings/security.vue b/packages/client/src/pages/settings/security.vue
index be4f275f16..eef27cfdcb 100644
--- a/packages/client/src/pages/settings/security.vue
+++ b/packages/client/src/pages/settings/security.vue
@@ -2,15 +2,12 @@
 	<div class="_formRoot">
 		<FormSection>
 			<template #label>{{ i18n.ts.password }}</template>
-			<FormButton primary @click="change()">{{
+			<MkButton primary @click="change()">{{
 				i18n.ts.changePassword
-			}}</FormButton>
+			}}</MkButton>
 		</FormSection>
 
-		<FormSection>
-			<template #label>{{ i18n.ts.twoStepAuthentication }}</template>
-			<X2fa />
-		</FormSection>
+		<X2fa />
 
 		<FormSection>
 			<template #label>{{ i18n.ts.signinHistory }}</template>
@@ -43,9 +40,9 @@
 
 		<FormSection>
 			<FormSlot>
-				<FormButton danger @click="regenerateToken"
+				<MkButton danger @click="regenerateToken"
 					><i class="ph-arrows-clockwise ph-bold ph-lg"></i>
-					{{ i18n.ts.regenerateLoginToken }}</FormButton
+					{{ i18n.ts.regenerateLoginToken }}</MkButton
 				>
 				<template #caption>{{
 					i18n.ts.regenerateLoginTokenDescription
@@ -59,7 +56,7 @@
 import X2fa from "./2fa.vue";
 import FormSection from "@/components/form/section.vue";
 import FormSlot from "@/components/form/slot.vue";
-import FormButton from "@/components/MkButton.vue";
+import MkButton from "@/components/MkButton.vue";
 import MkPagination from "@/components/MkPagination.vue";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
@@ -70,11 +67,12 @@ const pagination = {
 	limit: 5,
 };
 
-async function change(): Promise<void> {
+async function change() {
 	const { canceled: canceled1, result: currentPassword } = await os.inputText(
 		{
 			title: i18n.ts.currentPassword,
 			type: "password",
+			autocomplete: "current-password",
 		}
 	);
 	if (canceled1) return;
@@ -82,12 +80,14 @@ async function change(): Promise<void> {
 	const { canceled: canceled2, result: newPassword } = await os.inputText({
 		title: i18n.ts.newPassword,
 		type: "password",
+		autocomplete: "new-password",
 	});
 	if (canceled2) return;
 
 	const { canceled: canceled3, result: newPassword2 } = await os.inputText({
 		title: i18n.ts.newPasswordRetype,
 		type: "password",
+		autocomplete: "new-password",
 	});
 	if (canceled3) return;
 
@@ -105,13 +105,13 @@ async function change(): Promise<void> {
 	});
 }
 
-function regenerateToken(): void {
+function regenerateToken() {
 	os.inputText({
 		title: i18n.ts.password,
 		type: "password",
 	}).then(({ canceled, result: password }) => {
 		if (canceled) return;
-		os.api("i/regenerate_token", {
+		os.api("i/regenerate-token", {
 			password: password,
 		});
 	});
@@ -129,7 +129,7 @@ definePageMetadata({
 
 <style lang="scss" scoped>
 .timnmucd {
-	padding: 16px;
+	padding: 12px;
 
 	&:first-child {
 		border-top-left-radius: 6px;
diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss
index 38870cc561..0f3f841b94 100644
--- a/packages/client/src/style.scss
+++ b/packages/client/src/style.scss
@@ -287,6 +287,34 @@ hr {
 	}
 }
 
+._panel {
+	background: var(--panel);
+	border-radius: var(--radius);
+	overflow: clip;
+}
+
+._margin {
+	margin: var(--margin) 0;
+}
+
+._gaps_m {
+	display: flex;
+	flex-direction: column;
+	gap: 1.5em;
+}
+
+._gaps_s {
+	display: flex;
+	flex-direction: column;
+	gap: 0.75em;
+}
+
+._gaps {
+	display: flex;
+	flex-direction: column;
+	gap: var(--margin);
+}
+
 ._inputs {
 	display: flex;
 	margin: 32px 0;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e98ca7feeb..2ecab716a2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -294,6 +294,9 @@ importers:
       os-utils:
         specifier: 0.0.14
         version: 0.0.14
+      otpauth:
+        specifier: ^9.1.2
+        version: 9.1.2
       parse5:
         specifier: 7.1.2
         version: 7.1.2
@@ -360,9 +363,6 @@ importers:
       sonic-channel:
         specifier: ^1.3.1
         version: 1.3.1
-      speakeasy:
-        specifier: 2.0.0
-        version: 2.0.0
       stringz:
         specifier: 2.1.0
         version: 2.1.0
@@ -536,9 +536,6 @@ importers:
       '@types/sinonjs__fake-timers':
         specifier: 8.1.2
         version: 8.1.2
-      '@types/speakeasy':
-        specifier: 2.0.7
-        version: 2.0.7
       '@types/tinycolor2':
         specifier: 1.4.3
         version: 1.4.3
@@ -2649,6 +2646,7 @@ packages:
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [android]
+    requiresBuild: true
     dependencies:
       '@swc/wasm': 1.2.130
 
@@ -2755,6 +2753,7 @@ packages:
 
   /@swc/wasm@1.2.130:
     resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
+    requiresBuild: true
 
   /@syuilo/aiscript@0.11.1:
     resolution: {integrity: sha512-chwOIA3yLUKvOB0G611hjLArKTeOWNmTm3lHERSaDW1d+dS6do56naX6Lkwy2UpnwWC0qzeNSgg35elk6t2gZg==}
@@ -3641,12 +3640,6 @@ packages:
     resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
     dev: true
 
-  /@types/speakeasy@2.0.7:
-    resolution: {integrity: sha512-JEcOhN2SQCoX86ZfiZEe8px84sVJtivBXMZfOVyARTYEj0hrwwbj1nF0FwEL3nJSoEV6uTbcdLllMKBgAYHWCQ==}
-    dependencies:
-      '@types/node': 18.11.18
-    dev: true
-
   /@types/stack-utils@2.0.1:
     resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
     dev: true
@@ -4893,10 +4886,6 @@ packages:
   /balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
-  /base32.js@0.0.1:
-    resolution: {integrity: sha512-EGHIRiegFa62/SsA1J+Xs2tIzludPdzM064N9wjbiEgHnGnJ1V0WEpA4pEwCYT5nDvZk3ubf0shqaCS7k6xeUQ==}
-    dev: false
-
   /base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
@@ -10060,6 +10049,10 @@ packages:
     resolution: {integrity: sha512-emiQ05haY9CRj1Ho/LiuCqr/+8RgJuWdiHYNglIg2Qjfz0n+pnUq9I2QHplXuOMO2EnAW1oCGC1++aU5VoWSlw==}
     dev: false
 
+  /jssha@3.3.0:
+    resolution: {integrity: sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w==}
+    dev: false
+
   /jstransformer@1.0.0:
     resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==}
     dependencies:
@@ -11676,6 +11669,12 @@ packages:
     resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
     dev: true
 
+  /otpauth@9.1.2:
+    resolution: {integrity: sha512-iI5nlVvMFP3aTPdjG/fnC4mhVJ/KZOSnBrvo/VnYHUwlTp9jVLjAe2B3i3pyCH+3/E5jYQRSvuHk/8oas3870g==}
+    dependencies:
+      jssha: 3.3.0
+    dev: false
+
   /p-cancelable@2.1.1:
     resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
     engines: {node: '>=8'}
@@ -13731,13 +13730,6 @@ packages:
     resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==}
     dev: true
 
-  /speakeasy@2.0.0:
-    resolution: {integrity: sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==}
-    engines: {node: '>= 0.10.0'}
-    dependencies:
-      base32.js: 0.0.1
-    dev: false
-
   /split-string@3.1.0:
     resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
     engines: {node: '>=0.10.0'}

From e915604b3226d09318789af6be21fd063fd4d5dd Mon Sep 17 00:00:00 2001
From: Namekuji <nmkj@users.noreply.hosted.weblate.org>
Date: Wed, 14 Jun 2023 16:27:18 +0000
Subject: [PATCH 26/56] chore: Translated using Weblate (Japanese)

Currently translated at 100.0% (1787 of 1787 strings)

Translation: Calckey/locales
Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/
---
 locales/ja-JP.yml | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0ddde1a139..f4f3b21613 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1878,7 +1878,7 @@ _experiments:
   postImportsCaption: 
     ユーザーが過去の投稿をCalckey・Misskey・Mastodon・Akkoma・Pleromaからインポートすることを許可します。キューが溜まっているときにインポートするとサーバーに負荷がかかる可能性があります。
   enablePostImports: 投稿のインポートを有効にする
-sendModMail: モデレーションノートを送る
+sendModMail: モデレーション通知を送る
 deleted: 削除済み
 editNote: 投稿を編集
 edited: 編集済み
@@ -1898,3 +1898,19 @@ antennasDesc: "アンテナでは指定した条件に合致する投稿が表
 expandOnNoteClickDesc: オフの場合、右クリックメニューか日付をクリックすることで開けます。
 expandOnNoteClick: クリックで投稿の詳細を開く
 clipsDesc: クリップは分類と共有ができるブックマークです。各投稿のメニューからクリップを作成できます。
+_filters:
+  followersOnly: フォロワーのみ
+  fromUser: ユーザーを指定
+  withFile: 添付ファイルあり
+  fromDomain: ドメインを指定
+  notesBefore: 指定の日付以前
+  notesAfter: 指定の日付以降
+  followingOnly: フォロー中のみ
+isModerator: モデレーター
+audio: 音声
+image: 画像
+video: 動画
+isBot: このアカウントはBotです
+isLocked: このアカウントのフォローは承認制です
+isAdmin: 管理者
+isPatron: Calckey 後援者

From 0504c17b93b3b66cacca4ee180a14500d26359d7 Mon Sep 17 00:00:00 2001
From: Kenny Hui <kenny.mh.hui@outlook.com>
Date: Thu, 15 Jun 2023 09:03:06 +0000
Subject: [PATCH 27/56] chore: Translated using Weblate (Chinese (Traditional))

Currently translated at 95.6% (1709 of 1787 strings)

Translation: Calckey/locales
Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/
---
 locales/zh-TW.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 61b5afbe63..6e83c79334 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -1840,3 +1840,9 @@ subscribePushNotification: 啟用推送通知
 unsubscribePushNotification: 禁用推送通知
 pushNotificationAlreadySubscribed: 推送通知已經啟用
 recommendedInstancesDescription: 以每行分隔的推薦服務器出現在推薦的時間軸中。 不要添加 `https://`,只添加域名。
+searchPlaceholder: 搜尋 Calckey
+cw: 內容警告
+selectChannel: 選擇一個頻道
+newer: 較新
+older: 較舊
+jumpToPrevious: 跳到上一個

From 1dbe44b2112593449c15f7034e866343dbd0a09c Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 16:21:51 -0700
Subject: [PATCH 28/56] fix: :bug: sonic logged connection despite not existing

---
 packages/backend/src/db/sonic.ts | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/db/sonic.ts b/packages/backend/src/db/sonic.ts
index 590c479247..032982f083 100644
--- a/packages/backend/src/db/sonic.ts
+++ b/packages/backend/src/db/sonic.ts
@@ -5,8 +5,6 @@ import config from "@/config/index.js";
 
 const logger = dbLogger.createSubLogger("sonic", "gray", false);
 
-logger.info("Connecting to Sonic");
-
 const handlers = (type: string): SonicChannel.Handlers => ({
 	connected: () => {
 		logger.succ(`Connected to Sonic ${type}`);
@@ -28,6 +26,10 @@ const handlers = (type: string): SonicChannel.Handlers => ({
 const hasConfig =
 	config.sonic && (config.sonic.host || config.sonic.port || config.sonic.auth);
 
+if (hasConfig) {
+	logger.info("Connecting to Sonic");
+}
+
 const host = hasConfig ? config.sonic.host ?? "localhost" : "";
 const port = hasConfig ? config.sonic.port ?? 1491 : 0;
 const auth = hasConfig ? config.sonic.auth ?? "SecretPassword" : "";

From ef37b360edafeac22701ef6bea740eb71358d881 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 16:25:31 -0700
Subject: [PATCH 29/56] fix: :ambulance: fix switch import

---
 packages/client/src/pages/settings/2fa.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index 2a3d8d6e0a..88a68f6859 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -106,7 +106,7 @@ import { hostname } from "@/config";
 import { byteify, hexify, stringify } from "@/scripts/2fa";
 import MkButton from "@/components/MkButton.vue";
 import MkInfo from "@/components/MkInfo.vue";
-import MkSwitch from "@/components/MkSwitch.vue";
+import MkSwitch from "@/components/form/switch.vue";
 import FormSection from "@/components/form/section.vue";
 import MkFolder from "@/components/MkFolder.vue";
 import * as os from "@/os";

From 16b2eda924f76852b400482ba6ea7511cb8d2418 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 16:34:56 -0700
Subject: [PATCH 30/56] style: :lipstick: 2fa dialog styling

---
 packages/client/src/pages/settings/2fa.qrdialog.vue | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/packages/client/src/pages/settings/2fa.qrdialog.vue b/packages/client/src/pages/settings/2fa.qrdialog.vue
index 59b2875d72..4e45860b1b 100644
--- a/packages/client/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/client/src/pages/settings/2fa.qrdialog.vue
@@ -35,11 +35,13 @@
 			<a :href="twoFactorData.url"
 				><img :class="$style.qr" :src="twoFactorData.qr"
 			/></a>
-			<MkKeyValue :copy="twoFactorData.url">
-				<template #key>{{ i18n.ts._2fa.step2Url }}</template>
-				<template #value>{{ twoFactorData.url }}</template>
-			</MkKeyValue>
-			<div class="_buttons">
+			<div style="max-width: 600px;">
+				<MkKeyValue :copy="twoFactorData.url">
+					<template #key>{{ i18n.ts._2fa.step2Url }}</template>
+					<template #value>{{ twoFactorData.url }}</template>
+				</MkKeyValue>
+			</div>
+			<div class="_flexList">
 				<MkButton primary @click="ok">{{ i18n.ts.next }}</MkButton>
 				<MkButton @click="cancel">{{ i18n.ts.cancel }}</MkButton>
 			</div>

From 892a9ad0b6906f885f17621f1bff2edb3ef5b8e3 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 16:42:24 -0700
Subject: [PATCH 31/56] feat: :lipstick: button icons for security

---
 .../src/pages/settings/2fa.qrdialog.vue       |  2 +-
 packages/client/src/pages/settings/2fa.vue    | 21 +++++++++++--------
 .../client/src/pages/settings/security.vue    |  2 +-
 3 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/packages/client/src/pages/settings/2fa.qrdialog.vue b/packages/client/src/pages/settings/2fa.qrdialog.vue
index 4e45860b1b..f6e6e35953 100644
--- a/packages/client/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/client/src/pages/settings/2fa.qrdialog.vue
@@ -35,7 +35,7 @@
 			<a :href="twoFactorData.url"
 				><img :class="$style.qr" :src="twoFactorData.qr"
 			/></a>
-			<div style="max-width: 600px;">
+			<div style="max-width: 600px">
 				<MkKeyValue :copy="twoFactorData.url">
 					<template #key>{{ i18n.ts._2fa.step2Url }}</template>
 					<template #value>{{ twoFactorData.url }}</template>
diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index 88a68f6859..45915d965d 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -12,14 +12,16 @@
 				<div v-if="$i.twoFactorEnabled" class="_gaps_s">
 					<div v-text="i18n.ts._2fa.alreadyRegistered" />
 					<template v-if="$i.securityKeysList.length > 0">
-						<MkButton @click="renewTOTP">{{
-							i18n.ts._2fa.renewTOTP
-						}}</MkButton>
+						<MkButton @click="renewTOTP"
+							><i class="ph-shield-check ph-bold ph-lg"></i
+							>{{ i18n.ts._2fa.renewTOTP }}</MkButton
+						>
 						<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
 					</template>
-					<MkButton v-else @click="unregisterTOTP">{{
-						i18n.ts.unregister
-					}}</MkButton>
+					<MkButton v-else @click="unregisterTOTP"
+						><i class="ph-shield-slash ph-bold ph-lg"></i
+						>{{ i18n.ts.unregister }}</MkButton
+					>
 				</div>
 
 				<MkButton
@@ -51,9 +53,10 @@
 					</MkInfo>
 
 					<template v-else>
-						<MkButton primary @click="addSecurityKey">{{
-							i18n.ts._2fa.registerSecurityKey
-						}}</MkButton>
+						<MkButton primary @click="addSecurityKey"
+							><i class="ph-key ph-bold ph-lg"></i
+							>{{ i18n.ts._2fa.registerSecurityKey }}</MkButton
+						>
 						<MkFolder
 							v-for="key in $i.securityKeysList"
 							:key="key.id"
diff --git a/packages/client/src/pages/settings/security.vue b/packages/client/src/pages/settings/security.vue
index eef27cfdcb..88ef2ba119 100644
--- a/packages/client/src/pages/settings/security.vue
+++ b/packages/client/src/pages/settings/security.vue
@@ -1,6 +1,6 @@
 <template>
 	<div class="_formRoot">
-		<FormSection>
+		<FormSection style="border: none !important">
 			<template #label>{{ i18n.ts.password }}</template>
 			<MkButton primary @click="change()">{{
 				i18n.ts.changePassword

From 905d80898a9fad2719b71bfa3aeef4f7f82516df Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 16:50:04 -0700
Subject: [PATCH 32/56] fix: :lipstick: fix sign-in 2fa token style

---
 packages/client/src/components/MkSignin.vue | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index d3dbbe943d..634d5932e8 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -107,16 +107,15 @@
 						:spellcheck="false"
 						required
 					>
-						<template #label>{{ i18n.ts.token }}</template>
-						<template #prefix
-							><i class="ph-poker-chip ph-bold ph-lg"></i
-						></template>
+						<template #prefix>
+							<i class="ph-poker-chip ph-bold ph-lg"></i>
+						</template>
 					</MkInput>
 					<MkButton
 						type="submit"
 						:disabled="signing"
 						primary
-						style="margin: 0 auto"
+						style="margin: 1rem auto auto"
 						>{{
 							signing ? i18n.ts.loggingIn : i18n.ts.login
 						}}</MkButton

From 7220fede8ca577e92c07b1b60f69c3e09591f0a4 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 19:32:27 -0700
Subject: [PATCH 33/56] feat: :sparkles: 2FA input dialog

---
 packages/client/package.json                  |  1 +
 packages/client/src/components/MkSignin.vue   | 41 ++++++++++++++-----
 .../src/pages/settings/2fa.qrdialog.vue       |  2 +
 packages/client/src/pages/settings/2fa.vue    | 27 +++++++++---
 pnpm-lock.yaml                                | 12 ++++++
 5 files changed, 68 insertions(+), 15 deletions(-)

diff --git a/packages/client/package.json b/packages/client/package.json
index 2a1dadeb40..9816f69631 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -87,6 +87,7 @@
 		"vue-isyourpasswordsafe": "^2.0.0",
 		"vue-plyr": "^7.0.0",
 		"vue-prism-editor": "2.0.0-alpha.2",
+		"vue3-otp-input": "^0.4.1",
 		"vuedraggable": "4.1.0"
 	}
 }
diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 634d5932e8..13da5d7ab2 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -99,18 +99,15 @@
 							><i class="ph-lock ph-bold ph-lg"></i
 						></template>
 					</MkInput>
-					<MkInput
+					<vue3-otp-input
+						input-classes="otp-input"
+						inputType="number"
+						:num-inputs="6"
 						v-model="token"
-						type="text"
-						pattern="^[0-9]{6}$"
-						autocomplete="one-time-code"
-						:spellcheck="false"
+						:should-auto-focus="true"
+						@on-complete="onSubmit"
 						required
-					>
-						<template #prefix>
-							<i class="ph-poker-chip ph-bold ph-lg"></i>
-						</template>
-					</MkInput>
+					/>
 					<MkButton
 						type="submit"
 						:disabled="signing"
@@ -159,6 +156,7 @@
 </template>
 
 <script lang="ts" setup>
+import Vue3OtpInput from "vue3-otp-input";
 import { defineAsyncComponent } from "vue";
 import { toUnicode } from "punycode/";
 import MkButton from "@/components/MkButton.vue";
@@ -392,4 +390,27 @@ function showSuspendedDialog() {
 		}
 	}
 }
+
+.otp-input {
+  width: 40px;
+  height: 40px;
+  padding: 5px;
+  margin: 0 10px;
+  font-size: 20px;
+  border-radius: 4px;
+  border: 2px solid var(--accent);
+	background-color: var(--accentedBg);
+  text-align: center;
+}
+.otp-input.is-complete {
+	border-color: var(--success) !important;
+}
+.otp-input.error {
+  border-color: var(--error) !important;
+}
+.otp-input::-webkit-inner-spin-button,
+.otp-input::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
 </style>
diff --git a/packages/client/src/pages/settings/2fa.qrdialog.vue b/packages/client/src/pages/settings/2fa.qrdialog.vue
index f6e6e35953..76d962e217 100644
--- a/packages/client/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/client/src/pages/settings/2fa.qrdialog.vue
@@ -94,5 +94,7 @@ const ok = () => {
 .qr {
 	width: 20em;
 	max-width: 100%;
+	border-radius: 10px;
+	border: 3px solid var(--accent);
 }
 </style>
diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue
index 45915d965d..56a4cca0f4 100644
--- a/packages/client/src/pages/settings/2fa.vue
+++ b/packages/client/src/pages/settings/2fa.vue
@@ -5,7 +5,10 @@
 		<div v-if="$i" class="_gaps_s">
 			<MkFolder>
 				<template #icon
-					><i class="ph-shield-check ph-bold ph-lg"></i
+					><i
+						class="ph-shield-check ph-bold ph-lg"
+						style="margin-right: 0.5rem"
+					></i
 				></template>
 				<template #label>{{ i18n.ts.totp }}</template>
 				<template #caption>{{ i18n.ts.totpDescription }}</template>
@@ -13,13 +16,19 @@
 					<div v-text="i18n.ts._2fa.alreadyRegistered" />
 					<template v-if="$i.securityKeysList.length > 0">
 						<MkButton @click="renewTOTP"
-							><i class="ph-shield-check ph-bold ph-lg"></i
+							><i
+								class="ph-shield-check ph-bold ph-lg"
+								style="margin-right: 0.5rem"
+							></i
 							>{{ i18n.ts._2fa.renewTOTP }}</MkButton
 						>
 						<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
 					</template>
 					<MkButton v-else @click="unregisterTOTP"
-						><i class="ph-shield-slash ph-bold ph-lg"></i
+						><i
+							class="ph-shield-slash ph-bold ph-lg"
+							style="margin-right: 0.5rem"
+						></i
 						>{{ i18n.ts.unregister }}</MkButton
 					>
 				</div>
@@ -32,7 +41,12 @@
 			</MkFolder>
 
 			<MkFolder>
-				<template #icon><i class="ph-key ph-bold ph-lg"></i></template>
+				<template #icon
+					><i
+						class="ph-key ph-bold ph-lg"
+						style="margin-right: 0.5rem"
+					></i
+				></template>
 				<template #label>{{ i18n.ts.securityKeyAndPasskey }}</template>
 				<div class="_gaps_s">
 					<MkInfo>
@@ -54,7 +68,10 @@
 
 					<template v-else>
 						<MkButton primary @click="addSecurityKey"
-							><i class="ph-key ph-bold ph-lg"></i
+							><i
+								class="ph-key ph-bold ph-lg"
+								style="margin-right: 0.5rem"
+							></i
 							>{{ i18n.ts._2fa.registerSecurityKey }}</MkButton
 						>
 						<MkFolder
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2ecab716a2..e1c51be057 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -906,6 +906,9 @@ importers:
       vue-prism-editor:
         specifier: 2.0.0-alpha.2
         version: 2.0.0-alpha.2(vue@3.3.4)
+      vue3-otp-input:
+        specifier: ^0.4.1
+        version: 0.4.1(vue@3.3.4)
       vuedraggable:
         specifier: 4.1.0
         version: 4.1.0(vue@3.3.4)
@@ -15213,6 +15216,15 @@ packages:
       vue: 3.3.4
     dev: true
 
+  /vue3-otp-input@0.4.1(vue@3.3.4):
+    resolution: {integrity: sha512-wVl9i3DcWlO0C7fBI9V+RIP3crm/1tY72fuhvb3YM2JfbLoYofB96aPl5AgFhA0Cse5bQEMYtIvOeiqW3rfbAw==}
+    engines: {node: '>=16.0.0', npm: '>=8.0.0'}
+    peerDependencies:
+      vue: ^3.0.*
+    dependencies:
+      vue: 3.3.4
+    dev: true
+
   /vue@2.7.14:
     resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
     dependencies:

From 9c16c879de883fbdc294f3889dc4fb190be91287 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 19:52:51 -0700
Subject: [PATCH 34/56] fix: :bug: 2FA dialog

---
 packages/client/src/components/MkSignin.vue   | 28 ++-----------------
 .../src/pages/settings/2fa.qrdialog.vue       |  2 +-
 packages/client/src/style.scss                | 24 ++++++++++++++++
 3 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 13da5d7ab2..0cd4a7c6ef 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -100,8 +100,9 @@
 						></template>
 					</MkInput>
 					<vue3-otp-input
-						input-classes="otp-input"
-						inputType="number"
+						input-classes="_otp_input"
+						inputType="letter-numeric"
+						separator=""
 						:num-inputs="6"
 						v-model="token"
 						:should-auto-focus="true"
@@ -390,27 +391,4 @@ function showSuspendedDialog() {
 		}
 	}
 }
-
-.otp-input {
-  width: 40px;
-  height: 40px;
-  padding: 5px;
-  margin: 0 10px;
-  font-size: 20px;
-  border-radius: 4px;
-  border: 2px solid var(--accent);
-	background-color: var(--accentedBg);
-  text-align: center;
-}
-.otp-input.is-complete {
-	border-color: var(--success) !important;
-}
-.otp-input.error {
-  border-color: var(--error) !important;
-}
-.otp-input::-webkit-inner-spin-button,
-.otp-input::-webkit-outer-spin-button {
-  -webkit-appearance: none;
-  margin: 0;
-}
 </style>
diff --git a/packages/client/src/pages/settings/2fa.qrdialog.vue b/packages/client/src/pages/settings/2fa.qrdialog.vue
index 76d962e217..5f14d069eb 100644
--- a/packages/client/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/client/src/pages/settings/2fa.qrdialog.vue
@@ -35,7 +35,7 @@
 			<a :href="twoFactorData.url"
 				><img :class="$style.qr" :src="twoFactorData.qr"
 			/></a>
-			<div style="max-width: 600px">
+			<div style="max-width: 610px">
 				<MkKeyValue :copy="twoFactorData.url">
 					<template #key>{{ i18n.ts._2fa.step2Url }}</template>
 					<template #value>{{ twoFactorData.url }}</template>
diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss
index 0f3f841b94..f1973c679c 100644
--- a/packages/client/src/style.scss
+++ b/packages/client/src/style.scss
@@ -798,6 +798,30 @@ hr {
 	}
 }
 
+._otp_input {
+  width: 40px;
+  height: 40px;
+  padding: 5px;
+  margin: 1rem 7px auto;
+  font-size: 20px;
+  border-radius: 4px;
+  border: 2px solid var(--accent);
+	background-color: var(--accentedBg);
+	color: var(--fg);
+  text-align: center;
+}
+._otp_input.is-complete {
+	border-color: var(--success) !important;
+}
+._otp_input.error {
+  border-color: var(--error) !important;
+}
+._otp_input::-webkit-inner-spin-button,
+._otp_input::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
+
 @media(prefers-reduced-motion: no-preference) {
 	@keyframes scaleIn {
 		from {

From b148338b78df0116b07a0da676d599ed5ff5de53 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 20:06:47 -0700
Subject: [PATCH 35/56] fix: :bug: use correct 2fa value

---
 packages/client/src/components/MkSignin.vue | 6 ++++++
 packages/client/src/style.scss              | 7 ++-----
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 0cd4a7c6ef..0c8fffcaf0 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -106,6 +106,7 @@
 						:num-inputs="6"
 						v-model="token"
 						:should-auto-focus="true"
+						@on-change="updateToken"
 						@on-complete="onSubmit"
 						required
 					/>
@@ -158,6 +159,7 @@
 
 <script lang="ts" setup>
 import Vue3OtpInput from "vue3-otp-input";
+import { ref } from "vue";
 import { defineAsyncComponent } from "vue";
 import { toUnicode } from "punycode/";
 import MkButton from "@/components/MkButton.vue";
@@ -183,6 +185,10 @@ let queryingKey = $ref(false);
 let hCaptchaResponse = $ref(null);
 let reCaptchaResponse = $ref(null);
 
+const updateToken = (value: string) => {
+	token = value;
+};
+
 const meta = $computed(() => instance);
 
 const emit = defineEmits<{
diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss
index f1973c679c..7bf18abfea 100644
--- a/packages/client/src/style.scss
+++ b/packages/client/src/style.scss
@@ -799,8 +799,8 @@ hr {
 }
 
 ._otp_input {
-  width: 40px;
-  height: 40px;
+  width: 30px;
+  height: 30px;
   padding: 5px;
   margin: 1rem 7px auto;
   font-size: 20px;
@@ -810,9 +810,6 @@ hr {
 	color: var(--fg);
   text-align: center;
 }
-._otp_input.is-complete {
-	border-color: var(--success) !important;
-}
 ._otp_input.error {
   border-color: var(--error) !important;
 }

From f50f8830a873eb4c2c456adeb07a3b064528d367 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 22:15:16 -0700
Subject: [PATCH 36/56] refactor: :arrow_up: use custom version of
 vue3-otp-input

This enables the use of number inputs without the slider
---
 packages/client/package.json                |  2 +-
 packages/client/src/components/MkSignin.vue |  2 +-
 pnpm-lock.yaml                              | 25 ++++++++++++---------
 3 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/packages/client/package.json b/packages/client/package.json
index 9816f69631..d024277ddf 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -87,7 +87,7 @@
 		"vue-isyourpasswordsafe": "^2.0.0",
 		"vue-plyr": "^7.0.0",
 		"vue-prism-editor": "2.0.0-alpha.2",
-		"vue3-otp-input": "^0.4.1",
+		"vue3-otp-input": "github:thatonecalculator/vue3-otp-input",
 		"vuedraggable": "4.1.0"
 	}
 }
diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 0c8fffcaf0..70c7ecd59c 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -101,7 +101,7 @@
 					</MkInput>
 					<vue3-otp-input
 						input-classes="_otp_input"
-						inputType="letter-numeric"
+						inputType="number"
 						separator=""
 						:num-inputs="6"
 						v-model="token"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e1c51be057..d4e70ef0f3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -907,8 +907,8 @@ importers:
         specifier: 2.0.0-alpha.2
         version: 2.0.0-alpha.2(vue@3.3.4)
       vue3-otp-input:
-        specifier: ^0.4.1
-        version: 0.4.1(vue@3.3.4)
+        specifier: github:thatonecalculator/vue3-otp-input
+        version: github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14(vue@3.3.4)
       vuedraggable:
         specifier: 4.1.0
         version: 4.1.0(vue@3.3.4)
@@ -15216,15 +15216,6 @@ packages:
       vue: 3.3.4
     dev: true
 
-  /vue3-otp-input@0.4.1(vue@3.3.4):
-    resolution: {integrity: sha512-wVl9i3DcWlO0C7fBI9V+RIP3crm/1tY72fuhvb3YM2JfbLoYofB96aPl5AgFhA0Cse5bQEMYtIvOeiqW3rfbAw==}
-    engines: {node: '>=16.0.0', npm: '>=8.0.0'}
-    peerDependencies:
-      vue: ^3.0.*
-    dependencies:
-      vue: 3.3.4
-    dev: true
-
   /vue@2.7.14:
     resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
     dependencies:
@@ -15906,3 +15897,15 @@ packages:
       rangetouch: 2.0.1
       url-polyfill: 1.1.12
     dev: true
+
+  github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14(vue@3.3.4):
+    resolution: {tarball: https://codeload.github.com/thatonecalculator/vue3-otp-input/tar.gz/098b24b224661884983a5035ef3b245a519dff14}
+    id: github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14
+    name: vue3-otp-input
+    version: 0.4.1
+    engines: {node: '>=16.0.0', npm: '>=8.0.0'}
+    peerDependencies:
+      vue: ^3.0.*
+    dependencies:
+      vue: 3.3.4
+    dev: true

From 074e6333069bbb84e59e408d31534ebeda9eb700 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Thu, 15 Jun 2023 22:16:21 -0700
Subject: [PATCH 37/56] fix: :adhesive_bandage: convert numeric input to string

---
 packages/client/src/components/MkSignin.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 70c7ecd59c..3d2e05dfae 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -186,7 +186,7 @@ let hCaptchaResponse = $ref(null);
 let reCaptchaResponse = $ref(null);
 
 const updateToken = (value: string) => {
-	token = value;
+	token = value.toString();
 };
 
 const meta = $computed(() => instance);

From 837a45bd98bd2ef519341eed18be37694c4b05c0 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Fri, 16 Jun 2023 00:13:41 -0700
Subject: [PATCH 38/56] refactor: :safety_vest: replace js-yaml with yaml

Technically mitigates CVE-2023-2251, but users never input YAML to Calckey. Still, this does no harm, and it's a good idea to keep dependencies like these up-to-date, as js-yaml was last updated 2 years ago.
---
 locales/index.js                    | 108 ++++++++++++++++------------
 package.json                        |   4 +-
 packages/backend/package.json       |   5 +-
 packages/backend/src/config/load.ts |   4 +-
 pnpm-lock.yaml                      |  24 +++----
 5 files changed, 81 insertions(+), 64 deletions(-)

diff --git a/locales/index.js b/locales/index.js
index 7399bb5a18..0f84c02b43 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -2,59 +2,79 @@
  * Languages Loader
  */
 
-const fs = require('fs');
-const yaml = require('js-yaml');
-let languages = []
-let languages_custom = []
+const fs = require("fs");
+const yaml = require("yaml");
 
-const merge = (...args) => args.reduce((a, c) => ({
-	...a,
-	...c,
-	...Object.entries(a)
-		.filter(([k]) => c && typeof c[k] === 'object')
-		.reduce((a, [k, v]) => (a[k] = merge(v, c[k]), a), {})
-}), {});
+const languages = [];
+const languages_custom = [];
 
+const merge = (...args) =>
+	args.reduce(
+		(a, c) => ({
+			...a,
+			...c,
+			...Object.entries(a)
+				.filter(([k]) => c && typeof c[k] === "object")
+				.reduce((a, [k, v]) => ((a[k] = merge(v, c[k])), a), {}),
+		}),
+		{}
+	);
 
 fs.readdirSync(__dirname).forEach((file) => {
-	if (file.includes('.yml')){
-		file = file.slice(0, file.indexOf('.'))
-		languages.push(file);
+	if (file.includes(".yml")) {
+		locale = file.slice(0, file.indexOf("."));
+		languages.push(locale);
 	}
-})
+});
 
-fs.readdirSync(__dirname + '/../custom/locales').forEach((file) => {
-	if (file.includes('.yml')){
-		file = file.slice(0, file.indexOf('.'))
-		languages_custom.push(file);
+fs.readdirSync(`${__dirname}/../custom/locales`).forEach((file) => {
+	if (file.includes(".yml")) {
+		customLocale = file.slice(0, file.indexOf("."));
+		languages_custom.push(customLocale);
 	}
-})
+});
 
 const primaries = {
-	'en': 'US',
-	'ja': 'JP',
-	'zh': 'CN',
+	en: "US",
+	ja: "JP",
+	zh: "CN",
 };
 
-// 何故か文字列にバックスペース文字が混入することがあり、YAMLが壊れるので取り除く
-const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
+const locales = languages.reduce(
+	(a, c) =>
+		(a[c] = yaml.parse(fs.readFileSync(`${__dirname}/${c}.yml`, "utf-8"))) ||
+		{},
+	a
+);
+const locales_custom = languages_custom.reduce(
+	(a, c) =>
+		(a[c] = yaml.parse(
+			fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, "utf-8")
+		)) || {},
+	a
+);
+Object.assign(locales, locales_custom);
 
-const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {});
-const locales_custom = languages_custom.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, 'utf-8'))) || {}, a), {});
-Object.assign(locales, locales_custom)
-
-module.exports = Object.entries(locales)
-	.reduce((a, [k ,v]) => (a[k] = (() => {
-		const [lang] = k.split('-');
-		switch (k) {
-			case 'ja-JP': return v;
-			case 'ja-KS':
-			case 'en-US': return merge(locales['ja-JP'], v);
-			default: return merge(
-				locales['ja-JP'],
-				locales['en-US'],
-				locales[`${lang}-${primaries[lang]}`] || {},
-				v
-			);
-		}
-	})(), a), {});
+module.exports = Object.entries(locales).reduce(
+	(a, [k, v]) => (
+		(a[k] = (() => {
+			const [lang] = k.split("-");
+			switch (k) {
+				case "ja-JP":
+					return v;
+				case "ja-KS":
+				case "en-US":
+					return merge(locales["ja-JP"], v);
+				default:
+					return merge(
+						locales["ja-JP"],
+						locales["en-US"],
+						locales[`${lang}-${primaries[lang]}`] || {},
+						v
+					);
+			}
+		})()),
+		a
+	),
+	{}
+);
diff --git a/package.json b/package.json
index 42a18a33c1..381884dcb8 100644
--- a/package.json
+++ b/package.json
@@ -40,8 +40,8 @@
 		"@bull-board/ui": "5.2.0",
 		"@napi-rs/cli": "^2.16.1",
 		"@tensorflow/tfjs": "^3.21.0",
-		"js-yaml": "4.1.0",
-		"seedrandom": "^3.0.5"
+		"seedrandom": "^3.0.5",
+		"yaml": "^2.3.1"
 	},
 	"devDependencies": {
 		"@types/gulp": "4.0.10",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 2a19b916cf..86ad0bace1 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -75,7 +75,6 @@
 		"ioredis": "5.3.2",
 		"ip-cidr": "3.0.11",
 		"is-svg": "4.3.2",
-		"js-yaml": "4.1.0",
 		"jsdom": "20.0.3",
 		"jsonld": "8.2.0",
 		"jsrsasign": "10.6.1",
@@ -137,7 +136,8 @@
 		"uuid": "9.0.0",
 		"web-push": "3.6.1",
 		"websocket": "1.0.34",
-		"xev": "3.0.2"
+		"xev": "3.0.2",
+		"yaml": "^2.3.1"
 	},
 	"devDependencies": {
 		"@swc/cli": "^0.1.62",
@@ -148,7 +148,6 @@
 		"@types/cbor": "6.0.0",
 		"@types/escape-regexp": "0.0.1",
 		"@types/fluent-ffmpeg": "2.1.20",
-		"@types/js-yaml": "4.0.5",
 		"@types/jsdom": "20.0.1",
 		"@types/jsonld": "1.5.8",
 		"@types/jsrsasign": "10.5.4",
diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts
index 9b8ee5edbb..2ffe570b46 100644
--- a/packages/backend/src/config/load.ts
+++ b/packages/backend/src/config/load.ts
@@ -5,7 +5,7 @@
 import * as fs from "node:fs";
 import { fileURLToPath } from "node:url";
 import { dirname } from "node:path";
-import * as yaml from "js-yaml";
+import { parse } from "yaml";
 import type { Source, Mixin } from "./types.js";
 
 const _filename = fileURLToPath(import.meta.url);
@@ -32,7 +32,7 @@ export default function load() {
 			"utf-8",
 		),
 	);
-	const config = yaml.load(fs.readFileSync(path, "utf-8")) as Source;
+	const config = parse(fs.readFileSync(path, "utf-8")) as Source;
 
 	const mixin = {} as Mixin;
 
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e1c51be057..f2eef2ca45 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,12 +23,12 @@ importers:
       '@tensorflow/tfjs':
         specifier: ^3.21.0
         version: 3.21.0(seedrandom@3.0.5)
-      js-yaml:
-        specifier: 4.1.0
-        version: 4.1.0
       seedrandom:
         specifier: ^3.0.5
         version: 3.0.5
+      yaml:
+        specifier: ^2.3.1
+        version: 2.3.1
     devDependencies:
       '@types/gulp':
         specifier: 4.0.10
@@ -216,9 +216,6 @@ importers:
       is-svg:
         specifier: 4.3.2
         version: 4.3.2
-      js-yaml:
-        specifier: 4.1.0
-        version: 4.1.0
       jsdom:
         specifier: 20.0.3
         version: 20.0.3
@@ -405,6 +402,9 @@ importers:
       xev:
         specifier: 3.0.2
         version: 3.0.2
+      yaml:
+        specifier: ^2.3.1
+        version: 2.3.1
     optionalDependencies:
       '@swc/core-android-arm64':
         specifier: 1.3.11
@@ -437,9 +437,6 @@ importers:
       '@types/fluent-ffmpeg':
         specifier: 2.1.20
         version: 2.1.20
-      '@types/js-yaml':
-        specifier: 4.0.5
-        version: 4.0.5
       '@types/jsdom':
         specifier: 20.0.1
         version: 20.0.1
@@ -3277,10 +3274,6 @@ packages:
       pretty-format: 27.5.1
     dev: true
 
-  /@types/js-yaml@4.0.5:
-    resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==}
-    dev: true
-
   /@types/jsdom@20.0.1:
     resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==}
     dependencies:
@@ -15738,6 +15731,11 @@ packages:
     resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
     dev: false
 
+  /yaml@2.3.1:
+    resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==}
+    engines: {node: '>= 14'}
+    dev: false
+
   /yargs-parser@18.1.3:
     resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
     engines: {node: '>=6'}

From 0c9cce7c7c74631cf7316e771469ea25b0fbef3e Mon Sep 17 00:00:00 2001
From: Namekuji <nmkj@wahh.foo>
Date: Thu, 15 Jun 2023 20:59:17 -0400
Subject: [PATCH 39/56] fix aode-relay compatibility

---
 .../src/server/api/mastodon/endpoints/meta.ts |  2 +-
 packages/backend/src/services/note/create.ts  | 33 ++++++++++++++++---
 packages/backend/src/services/relay.ts        | 26 +++++++++++----
 3 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/packages/backend/src/server/api/mastodon/endpoints/meta.ts b/packages/backend/src/server/api/mastodon/endpoints/meta.ts
index d362d1b9e5..c68a09585f 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/meta.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/meta.ts
@@ -12,7 +12,7 @@ export async function getInstance(response: Entity.Instance) {
 		uri: response.uri,
 		title: response.title || "Calckey",
 		short_description:
-			response.description.substring(0, 50) || "See real server website",
+			response.description?.substring(0, 50) || "See real server website",
 		description:
 			response.description ||
 			"This is a vanilla Calckey Instance. It doesnt seem to have a description. BTW you are using the Mastodon api to access this server :)",
diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index ab3f365705..35548a174b 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -36,6 +36,7 @@ import {
 	ChannelFollowings,
 	Blockings,
 	NoteThreadMutings,
+	Relays,
 } from "@/models/index.js";
 import type { DriveFile } from "@/models/entities/drive-file.js";
 import type { App } from "@/models/entities/app.js";
@@ -56,7 +57,7 @@ import { checkHitAntenna } from "@/misc/check-hit-antenna.js";
 import { getWordHardMute } from "@/misc/check-word-mute.js";
 import { addNoteToAntenna } from "../add-note-to-antenna.js";
 import { countSameRenotes } from "@/misc/count-same-renotes.js";
-import { deliverToRelays } from "../relay.js";
+import { deliverToRelays, getCachedRelays } from "../relay.js";
 import type { Channel } from "@/models/entities/channel.js";
 import { normalizeForSearch } from "@/misc/normalize-for-search.js";
 import { getAntennas } from "@/misc/antenna-cache.js";
@@ -72,6 +73,7 @@ import meilisearch from "../../db/meilisearch.js";
 const mutedWordsCache = new Cache<
 	{ userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[]
 >(1000 * 60 * 5);
+const publishedNoteCache = new Cache<boolean>(1000 * 10);
 
 type NotificationType = "reply" | "renote" | "quote" | "mention";
 
@@ -165,6 +167,7 @@ export default async (
 		isSilenced: User["isSilenced"];
 		createdAt: User["createdAt"];
 		isBot: User["isBot"];
+		inbox?: User["inbox"];
 	},
 	data: Option,
 	silent = false,
@@ -453,7 +456,29 @@ export default async (
 			}
 
 			if (!dontFederateInitially) {
-				publishNotesStream(note);
+				const relays = await getCachedRelays();
+				if (!note.uri) {
+					publishNotesStream(note);
+				} else if (
+					data.renote?.uri &&
+					publishedNoteCache.get(data.renote.uri) !== true &&
+					user.inbox &&
+					relays.map((relay) => relay.inbox).includes(user.inbox)
+				) {
+					const uri = data.renote.uri;
+					publishNotesStream(data.renote);
+					publishedNoteCache.set(uri, true);
+					setTimeout(() => {
+						publishedNoteCache.delete(uri);
+					}, 1000 * 10);
+				} else if (note.uri && publishedNoteCache.get(note.uri) !== true) {
+					const uri = note.uri;
+					publishNotesStream(note);
+					publishedNoteCache.set(uri, true);
+					setTimeout(() => {
+						publishedNoteCache.delete(uri);
+					}, 1000 * 10);
+				}
 			}
 			if (note.replyId != null) {
 				// Only provide the reply note id here as the recipient may not be authorized to see the note.
@@ -524,7 +549,6 @@ export default async (
 						nm.push(data.renote.userId, type);
 					}
 				}
-
 				// Fetch watchers
 				nmRelatedPromises.push(
 					notifyToWatchersOfRenotee(data.renote, user, nm, type),
@@ -537,8 +561,9 @@ export default async (
 					});
 					publishMainStream(data.renote.userId, "renote", packedRenote);
 
+					const renote = data.renote;
 					const webhooks = (await getActiveWebhooks()).filter(
-						(x) => x.userId === data.renote!.userId && x.on.includes("renote"),
+						(x) => x.userId === renote.userId && x.on.includes("renote"),
 					);
 					for (const webhook of webhooks) {
 						webhookDeliver(webhook, "renote", {
diff --git a/packages/backend/src/services/relay.ts b/packages/backend/src/services/relay.ts
index 244e05c030..bec4b1f86b 100644
--- a/packages/backend/src/services/relay.ts
+++ b/packages/backend/src/services/relay.ts
@@ -37,7 +37,7 @@ export async function addRelay(inbox: string) {
 	}).then((x) => Relays.findOneByOrFail(x.identifiers[0]));
 
 	const relayActor = await getRelayActor();
-	const follow = await renderFollowRelay(relay, relayActor);
+	const follow = renderFollowRelay(relay, relayActor);
 	const activity = renderActivity(follow);
 	deliver(relayActor, activity, relay.inbox);
 
@@ -60,6 +60,7 @@ export async function removeRelay(inbox: string) {
 	deliver(relayActor, activity, relay.inbox);
 
 	await Relays.delete(relay.id);
+	await updateRelaysCache();
 }
 
 export async function listRelay() {
@@ -67,14 +68,31 @@ export async function listRelay() {
 	return relays;
 }
 
+export async function getCachedRelays(): Promise<Relay[]> {
+	return await relaysCache.fetch(null, () =>
+		Relays.findBy({
+			status: "accepted",
+		}),
+	);
+}
+
 export async function relayAccepted(id: string) {
 	const result = await Relays.update(id, {
 		status: "accepted",
 	});
 
+	await updateRelaysCache();
+
 	return JSON.stringify(result);
 }
 
+async function updateRelaysCache() {
+	const relays = await Relays.findBy({
+		status: "accepted",
+	});
+	relaysCache.set(null, relays);
+}
+
 export async function relayRejected(id: string) {
 	const result = await Relays.update(id, {
 		status: "rejected",
@@ -89,11 +107,7 @@ export async function deliverToRelays(
 ) {
 	if (activity == null) return;
 
-	const relays = await relaysCache.fetch(null, () =>
-		Relays.findBy({
-			status: "accepted",
-		}),
-	);
+	const relays = await getCachedRelays();
 	if (relays.length === 0) return;
 
 	// TODO

From 6236661d6233a3f48fd9c3a2cf7bfc03a4850a08 Mon Sep 17 00:00:00 2001
From: Namekuji <nmkj@wahh.foo>
Date: Thu, 15 Jun 2023 21:56:24 -0400
Subject: [PATCH 40/56] use redis

---
 packages/backend/src/services/note/create.ts | 31 ++++++++++----------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index 35548a174b..9a7d057eec 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -69,11 +69,11 @@ import { db } from "@/db/postgre.js";
 import { getActiveWebhooks } from "@/misc/webhook-cache.js";
 import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
 import meilisearch from "../../db/meilisearch.js";
+import { redisClient } from "@/db/redis.js";
 
 const mutedWordsCache = new Cache<
 	{ userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[]
 >(1000 * 60 * 5);
-const publishedNoteCache = new Cache<boolean>(1000 * 10);
 
 type NotificationType = "reply" | "renote" | "quote" | "mention";
 
@@ -457,27 +457,28 @@ export default async (
 
 			if (!dontFederateInitially) {
 				const relays = await getCachedRelays();
+				const boostedByRelay =
+					!!user.inbox &&
+					relays.map((relay) => relay.inbox).includes(user.inbox);
+
 				if (!note.uri) {
 					publishNotesStream(note);
 				} else if (
+					boostedByRelay &&
 					data.renote?.uri &&
-					publishedNoteCache.get(data.renote.uri) !== true &&
-					user.inbox &&
-					relays.map((relay) => relay.inbox).includes(user.inbox)
+					(await redisClient.exists(`publishedNote:${data.renote.uri}`)) === 0
 				) {
-					const uri = data.renote.uri;
 					publishNotesStream(data.renote);
-					publishedNoteCache.set(uri, true);
-					setTimeout(() => {
-						publishedNoteCache.delete(uri);
-					}, 1000 * 10);
-				} else if (note.uri && publishedNoteCache.get(note.uri) !== true) {
-					const uri = note.uri;
+					const key = `publishedNote:${data.renote.uri}`;
+					await redisClient.set(key, 1, "EX", 10);
+				} else if (
+					!boostedByRelay &&
+					note.uri &&
+					(await redisClient.exists(`publishedNote:${note.uri}`)) === 0
+				) {
+					const key = `publishedNote:${note.uri}`;
 					publishNotesStream(note);
-					publishedNoteCache.set(uri, true);
-					setTimeout(() => {
-						publishedNoteCache.delete(uri);
-					}, 1000 * 10);
+					await redisClient.set(key, 1, "EX", 10);
 				}
 			}
 			if (note.replyId != null) {

From fda6a1b1d2d833488c7b0ea4901f8df4961e712d Mon Sep 17 00:00:00 2001
From: Namekuji <nmkj@wahh.foo>
Date: Fri, 16 Jun 2023 02:48:57 -0400
Subject: [PATCH 41/56] wait a bit more

---
 packages/backend/src/services/note/create.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index 9a7d057eec..3423ef01d1 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -470,7 +470,7 @@ export default async (
 				) {
 					publishNotesStream(data.renote);
 					const key = `publishedNote:${data.renote.uri}`;
-					await redisClient.set(key, 1, "EX", 10);
+					await redisClient.set(key, 1, "EX", 30);
 				} else if (
 					!boostedByRelay &&
 					note.uri &&
@@ -478,7 +478,7 @@ export default async (
 				) {
 					const key = `publishedNote:${note.uri}`;
 					publishNotesStream(note);
-					await redisClient.set(key, 1, "EX", 10);
+					await redisClient.set(key, 1, "EX", 30);
 				}
 			}
 			if (note.replyId != null) {

From 2b1e16d48991cb8c9bfe15043a297ab1c783b863 Mon Sep 17 00:00:00 2001
From: Namekuji <nmkj@wahh.foo>
Date: Fri, 16 Jun 2023 03:45:53 -0400
Subject: [PATCH 42/56] add comments

---
 packages/backend/src/services/note/create.ts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index 3423ef01d1..b493ae5e08 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -457,17 +457,21 @@ export default async (
 
 			if (!dontFederateInitially) {
 				const relays = await getCachedRelays();
+				// Some relays (e.g., aode-relay) deliver posts by boosting them as
+				// Announce activities. In that case, user is the relay's actor.
 				const boostedByRelay =
 					!!user.inbox &&
 					relays.map((relay) => relay.inbox).includes(user.inbox);
 
 				if (!note.uri) {
+					// Publish if the post is local
 					publishNotesStream(note);
 				} else if (
 					boostedByRelay &&
 					data.renote?.uri &&
 					(await redisClient.exists(`publishedNote:${data.renote.uri}`)) === 0
 				) {
+					// Publish if the post was boosted by a relay and not yet published.
 					publishNotesStream(data.renote);
 					const key = `publishedNote:${data.renote.uri}`;
 					await redisClient.set(key, 1, "EX", 30);
@@ -476,6 +480,9 @@ export default async (
 					note.uri &&
 					(await redisClient.exists(`publishedNote:${note.uri}`)) === 0
 				) {
+					// Publish if the post came directly from a remote server, or from a
+					// relay that doesn't boost the post (e.g, YUKIMOCHI Activity-Relay),
+					// and not yet published.
 					const key = `publishedNote:${note.uri}`;
 					publishNotesStream(note);
 					await redisClient.set(key, 1, "EX", 30);

From c5ccd68c08b940d186ea13ec9f78c0173ed5d662 Mon Sep 17 00:00:00 2001
From: Namekuji <nmkj@wahh.foo>
Date: Fri, 16 Jun 2023 04:17:32 -0400
Subject: [PATCH 43/56] remove unused import

---
 packages/backend/src/services/note/create.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index b493ae5e08..9696c3ccaa 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -36,7 +36,6 @@ import {
 	ChannelFollowings,
 	Blockings,
 	NoteThreadMutings,
-	Relays,
 } from "@/models/index.js";
 import type { DriveFile } from "@/models/entities/drive-file.js";
 import type { App } from "@/models/entities/app.js";

From aa46f5f1bb9e2d5170079282081977a8048b5392 Mon Sep 17 00:00:00 2001
From: Namekuji <nmkj@wahh.foo>
Date: Fri, 16 Jun 2023 04:36:11 -0400
Subject: [PATCH 44/56] skip if actor is not found or has been already deleted

---
 .../backend/src/queue/processors/db/delete-account.ts     | 4 +---
 .../backend/src/remote/activitypub/kernel/delete/actor.ts | 8 +++++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/packages/backend/src/queue/processors/db/delete-account.ts b/packages/backend/src/queue/processors/db/delete-account.ts
index a356ca7abf..1cd7642ba5 100644
--- a/packages/backend/src/queue/processors/db/delete-account.ts
+++ b/packages/backend/src/queue/processors/db/delete-account.ts
@@ -17,9 +17,7 @@ export async function deleteAccount(
 	logger.info(`Deleting account of ${job.data.user.id} ...`);
 
 	const user = await Users.findOneBy({ id: job.data.user.id });
-	if (user == null) {
-		return;
-	}
+	if (!user) return;
 
 	{
 		// Delete notes
diff --git a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts
index 3571135aa5..83c6442dde 100644
--- a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts
+++ b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts
@@ -15,9 +15,11 @@ export async function deleteActor(
 		return `skip: delete actor ${actor.uri} !== ${uri}`;
 	}
 
-	const user = await Users.findOneByOrFail({ id: actor.id });
-	if (user.isDeleted) {
-		logger.info("skip: already deleted");
+	const user = await Users.findOneBy({ id: actor.id });
+	if (!user) {
+		return `skip: actor ${actor.id} not found in the local database`;
+	} else if (user.isDeleted) {
+		return `skip: user ${user.id} already deleted`;
 	}
 
 	const job = await createDeleteAccountJob(actor);

From 3aab4c05e7c26a4a9f90dfcaf04f0235e260fc96 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Fri, 16 Jun 2023 10:16:19 -0700
Subject: [PATCH 45/56] fix: :bug: display punishments on desktop

---
 packages/client/src/pages/user/home.vue | 63 ++++++++++++++-----------
 1 file changed, 36 insertions(+), 27 deletions(-)

diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue
index d02b6f665e..4fe2966ce5 100644
--- a/packages/client/src/pages/user/home.vue
+++ b/packages/client/src/pages/user/home.vue
@@ -28,12 +28,38 @@
 							></div>
 							<div class="fade"></div>
 							<div class="title">
-								<div class="nameCollumn">
+								<div class="nameColumn">
 									<MkUserName
 										class="name"
 										:user="user"
 										:nowrap="true"
 									/>
+									<div v-if="$i?.isModerator || $i?.isAdmin">
+										<span
+											v-if="user.isSilenced"
+											style="
+												color: var(--warn);
+												padding: 5px;
+											"
+										>
+											<i
+												class="ph-warning ph-bold ph-lg"
+											></i>
+											{{ i18n.ts.silenced }}
+										</span>
+										<span
+											v-if="user.isSuspended"
+											style="
+												color: var(--error);
+												padding: 5px;
+											"
+										>
+											<i
+												class="ph-warning ph-bold ph-lg"
+											></i>
+											{{ i18n.ts.suspended }}
+										</span>
+									</div>
 									<span
 										v-if="
 											$i &&
@@ -98,7 +124,7 @@
 							:show-indicator="true"
 						/>
 						<div class="title">
-							<div class="nameCollumn">
+							<div class="nameColumn">
 								<MkUserName
 									class="name"
 									:user="user"
@@ -113,20 +139,20 @@
 									class="followed"
 									>{{ i18n.ts.followsYou }}</span
 								>
-								<div
-									v-if="$i?.isModerator || $i?.isAdmin"
-									class="punishments"
-								>
+								<div v-if="$i?.isModerator || $i?.isAdmin">
 									<span
-										class="punished"
 										v-if="user.isSilenced"
+										style="color: var(--warn); padding: 5px"
 									>
 										<i class="ph-warning ph-bold ph-lg"></i>
 										{{ i18n.ts.silenced }}
 									</span>
 									<span
-										class="punished"
 										v-if="user.isSuspended"
+										style="
+											color: var(--error);
+											padding: 5px;
+										"
 									>
 										<i class="ph-warning ph-bold ph-lg"></i>
 										{{ i18n.ts.suspended }}
@@ -516,23 +542,6 @@ onUnmounted(() => {
 						border-radius: 6px;
 					}
 
-					> .punishments {
-						display: flex;
-						gap: 1rem;
-						margin-top: 0.5rem;
-
-						> .punished {
-							padding: 10px;
-							color: var(--infoWarnBg);
-							background-color: var(--infoWarnFg);
-							border-radius: 10px;
-
-							> i {
-								margin-right: 4px;
-							}
-						}
-					}
-
 					> .title {
 						position: absolute;
 						bottom: 0;
@@ -542,7 +551,7 @@ onUnmounted(() => {
 						box-sizing: border-box;
 						color: #fff;
 
-						> .nameCollumn {
+						> .nameColumn {
 							display: block;
 							> .name {
 								margin: 0;
@@ -650,7 +659,7 @@ onUnmounted(() => {
 					font-weight: bold;
 					border-bottom: solid 0.5px var(--divider);
 
-					> .nameCollumn {
+					> .nameColumn {
 						display: block;
 						> .name {
 							margin: 0;

From 35445f8591c6f5a5b6602b1588a243500e1e409f Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Fri, 16 Jun 2023 14:09:42 -0700
Subject: [PATCH 46/56] Revert "refactor: :safety_vest: replace js-yaml with
 yaml"

This reverts commit 837a45bd98bd2ef519341eed18be37694c4b05c0.
---
 locales/index.js                    | 108 ++++++++++++----------------
 package.json                        |   4 +-
 packages/backend/package.json       |   5 +-
 packages/backend/src/config/load.ts |   4 +-
 pnpm-lock.yaml                      |  24 ++++---
 5 files changed, 64 insertions(+), 81 deletions(-)

diff --git a/locales/index.js b/locales/index.js
index 0f84c02b43..7399bb5a18 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -2,79 +2,59 @@
  * Languages Loader
  */
 
-const fs = require("fs");
-const yaml = require("yaml");
+const fs = require('fs');
+const yaml = require('js-yaml');
+let languages = []
+let languages_custom = []
 
-const languages = [];
-const languages_custom = [];
+const merge = (...args) => args.reduce((a, c) => ({
+	...a,
+	...c,
+	...Object.entries(a)
+		.filter(([k]) => c && typeof c[k] === 'object')
+		.reduce((a, [k, v]) => (a[k] = merge(v, c[k]), a), {})
+}), {});
 
-const merge = (...args) =>
-	args.reduce(
-		(a, c) => ({
-			...a,
-			...c,
-			...Object.entries(a)
-				.filter(([k]) => c && typeof c[k] === "object")
-				.reduce((a, [k, v]) => ((a[k] = merge(v, c[k])), a), {}),
-		}),
-		{}
-	);
 
 fs.readdirSync(__dirname).forEach((file) => {
-	if (file.includes(".yml")) {
-		locale = file.slice(0, file.indexOf("."));
-		languages.push(locale);
+	if (file.includes('.yml')){
+		file = file.slice(0, file.indexOf('.'))
+		languages.push(file);
 	}
-});
+})
 
-fs.readdirSync(`${__dirname}/../custom/locales`).forEach((file) => {
-	if (file.includes(".yml")) {
-		customLocale = file.slice(0, file.indexOf("."));
-		languages_custom.push(customLocale);
+fs.readdirSync(__dirname + '/../custom/locales').forEach((file) => {
+	if (file.includes('.yml')){
+		file = file.slice(0, file.indexOf('.'))
+		languages_custom.push(file);
 	}
-});
+})
 
 const primaries = {
-	en: "US",
-	ja: "JP",
-	zh: "CN",
+	'en': 'US',
+	'ja': 'JP',
+	'zh': 'CN',
 };
 
-const locales = languages.reduce(
-	(a, c) =>
-		(a[c] = yaml.parse(fs.readFileSync(`${__dirname}/${c}.yml`, "utf-8"))) ||
-		{},
-	a
-);
-const locales_custom = languages_custom.reduce(
-	(a, c) =>
-		(a[c] = yaml.parse(
-			fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, "utf-8")
-		)) || {},
-	a
-);
-Object.assign(locales, locales_custom);
+// 何故か文字列にバックスペース文字が混入することがあり、YAMLが壊れるので取り除く
+const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
 
-module.exports = Object.entries(locales).reduce(
-	(a, [k, v]) => (
-		(a[k] = (() => {
-			const [lang] = k.split("-");
-			switch (k) {
-				case "ja-JP":
-					return v;
-				case "ja-KS":
-				case "en-US":
-					return merge(locales["ja-JP"], v);
-				default:
-					return merge(
-						locales["ja-JP"],
-						locales["en-US"],
-						locales[`${lang}-${primaries[lang]}`] || {},
-						v
-					);
-			}
-		})()),
-		a
-	),
-	{}
-);
+const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {});
+const locales_custom = languages_custom.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, 'utf-8'))) || {}, a), {});
+Object.assign(locales, locales_custom)
+
+module.exports = Object.entries(locales)
+	.reduce((a, [k ,v]) => (a[k] = (() => {
+		const [lang] = k.split('-');
+		switch (k) {
+			case 'ja-JP': return v;
+			case 'ja-KS':
+			case 'en-US': return merge(locales['ja-JP'], v);
+			default: return merge(
+				locales['ja-JP'],
+				locales['en-US'],
+				locales[`${lang}-${primaries[lang]}`] || {},
+				v
+			);
+		}
+	})(), a), {});
diff --git a/package.json b/package.json
index 381884dcb8..42a18a33c1 100644
--- a/package.json
+++ b/package.json
@@ -40,8 +40,8 @@
 		"@bull-board/ui": "5.2.0",
 		"@napi-rs/cli": "^2.16.1",
 		"@tensorflow/tfjs": "^3.21.0",
-		"seedrandom": "^3.0.5",
-		"yaml": "^2.3.1"
+		"js-yaml": "4.1.0",
+		"seedrandom": "^3.0.5"
 	},
 	"devDependencies": {
 		"@types/gulp": "4.0.10",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 86ad0bace1..2a19b916cf 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -75,6 +75,7 @@
 		"ioredis": "5.3.2",
 		"ip-cidr": "3.0.11",
 		"is-svg": "4.3.2",
+		"js-yaml": "4.1.0",
 		"jsdom": "20.0.3",
 		"jsonld": "8.2.0",
 		"jsrsasign": "10.6.1",
@@ -136,8 +137,7 @@
 		"uuid": "9.0.0",
 		"web-push": "3.6.1",
 		"websocket": "1.0.34",
-		"xev": "3.0.2",
-		"yaml": "^2.3.1"
+		"xev": "3.0.2"
 	},
 	"devDependencies": {
 		"@swc/cli": "^0.1.62",
@@ -148,6 +148,7 @@
 		"@types/cbor": "6.0.0",
 		"@types/escape-regexp": "0.0.1",
 		"@types/fluent-ffmpeg": "2.1.20",
+		"@types/js-yaml": "4.0.5",
 		"@types/jsdom": "20.0.1",
 		"@types/jsonld": "1.5.8",
 		"@types/jsrsasign": "10.5.4",
diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts
index 2ffe570b46..9b8ee5edbb 100644
--- a/packages/backend/src/config/load.ts
+++ b/packages/backend/src/config/load.ts
@@ -5,7 +5,7 @@
 import * as fs from "node:fs";
 import { fileURLToPath } from "node:url";
 import { dirname } from "node:path";
-import { parse } from "yaml";
+import * as yaml from "js-yaml";
 import type { Source, Mixin } from "./types.js";
 
 const _filename = fileURLToPath(import.meta.url);
@@ -32,7 +32,7 @@ export default function load() {
 			"utf-8",
 		),
 	);
-	const config = parse(fs.readFileSync(path, "utf-8")) as Source;
+	const config = yaml.load(fs.readFileSync(path, "utf-8")) as Source;
 
 	const mixin = {} as Mixin;
 
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c91365d22b..d4e70ef0f3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,12 +23,12 @@ importers:
       '@tensorflow/tfjs':
         specifier: ^3.21.0
         version: 3.21.0(seedrandom@3.0.5)
+      js-yaml:
+        specifier: 4.1.0
+        version: 4.1.0
       seedrandom:
         specifier: ^3.0.5
         version: 3.0.5
-      yaml:
-        specifier: ^2.3.1
-        version: 2.3.1
     devDependencies:
       '@types/gulp':
         specifier: 4.0.10
@@ -216,6 +216,9 @@ importers:
       is-svg:
         specifier: 4.3.2
         version: 4.3.2
+      js-yaml:
+        specifier: 4.1.0
+        version: 4.1.0
       jsdom:
         specifier: 20.0.3
         version: 20.0.3
@@ -402,9 +405,6 @@ importers:
       xev:
         specifier: 3.0.2
         version: 3.0.2
-      yaml:
-        specifier: ^2.3.1
-        version: 2.3.1
     optionalDependencies:
       '@swc/core-android-arm64':
         specifier: 1.3.11
@@ -437,6 +437,9 @@ importers:
       '@types/fluent-ffmpeg':
         specifier: 2.1.20
         version: 2.1.20
+      '@types/js-yaml':
+        specifier: 4.0.5
+        version: 4.0.5
       '@types/jsdom':
         specifier: 20.0.1
         version: 20.0.1
@@ -3274,6 +3277,10 @@ packages:
       pretty-format: 27.5.1
     dev: true
 
+  /@types/js-yaml@4.0.5:
+    resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==}
+    dev: true
+
   /@types/jsdom@20.0.1:
     resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==}
     dependencies:
@@ -15722,11 +15729,6 @@ packages:
     resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
     dev: false
 
-  /yaml@2.3.1:
-    resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==}
-    engines: {node: '>= 14'}
-    dev: false
-
   /yargs-parser@18.1.3:
     resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
     engines: {node: '>=6'}

From 54bdfd6e1d7d9cab9f00eadca528c534d7337450 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Fri, 16 Jun 2023 14:11:33 -0700
Subject: [PATCH 47/56] Revert "refactor: :arrow_up: use custom version of
 vue3-otp-input"

This reverts commit f50f8830a873eb4c2c456adeb07a3b064528d367.
---
 packages/client/package.json                |  2 +-
 packages/client/src/components/MkSignin.vue |  2 +-
 pnpm-lock.yaml                              | 25 +++++++++------------
 3 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/packages/client/package.json b/packages/client/package.json
index d024277ddf..9816f69631 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -87,7 +87,7 @@
 		"vue-isyourpasswordsafe": "^2.0.0",
 		"vue-plyr": "^7.0.0",
 		"vue-prism-editor": "2.0.0-alpha.2",
-		"vue3-otp-input": "github:thatonecalculator/vue3-otp-input",
+		"vue3-otp-input": "^0.4.1",
 		"vuedraggable": "4.1.0"
 	}
 }
diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index 3d2e05dfae..f2c83d5774 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -101,7 +101,7 @@
 					</MkInput>
 					<vue3-otp-input
 						input-classes="_otp_input"
-						inputType="number"
+						inputType="letter-numeric"
 						separator=""
 						:num-inputs="6"
 						v-model="token"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d4e70ef0f3..e1c51be057 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -907,8 +907,8 @@ importers:
         specifier: 2.0.0-alpha.2
         version: 2.0.0-alpha.2(vue@3.3.4)
       vue3-otp-input:
-        specifier: github:thatonecalculator/vue3-otp-input
-        version: github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14(vue@3.3.4)
+        specifier: ^0.4.1
+        version: 0.4.1(vue@3.3.4)
       vuedraggable:
         specifier: 4.1.0
         version: 4.1.0(vue@3.3.4)
@@ -15216,6 +15216,15 @@ packages:
       vue: 3.3.4
     dev: true
 
+  /vue3-otp-input@0.4.1(vue@3.3.4):
+    resolution: {integrity: sha512-wVl9i3DcWlO0C7fBI9V+RIP3crm/1tY72fuhvb3YM2JfbLoYofB96aPl5AgFhA0Cse5bQEMYtIvOeiqW3rfbAw==}
+    engines: {node: '>=16.0.0', npm: '>=8.0.0'}
+    peerDependencies:
+      vue: ^3.0.*
+    dependencies:
+      vue: 3.3.4
+    dev: true
+
   /vue@2.7.14:
     resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
     dependencies:
@@ -15897,15 +15906,3 @@ packages:
       rangetouch: 2.0.1
       url-polyfill: 1.1.12
     dev: true
-
-  github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14(vue@3.3.4):
-    resolution: {tarball: https://codeload.github.com/thatonecalculator/vue3-otp-input/tar.gz/098b24b224661884983a5035ef3b245a519dff14}
-    id: github.com/thatonecalculator/vue3-otp-input/098b24b224661884983a5035ef3b245a519dff14
-    name: vue3-otp-input
-    version: 0.4.1
-    engines: {node: '>=16.0.0', npm: '>=8.0.0'}
-    peerDependencies:
-      vue: ^3.0.*
-    dependencies:
-      vue: 3.3.4
-    dev: true

From 0127cc6ac36036bc63084f141aad2f00acd435dc Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Fri, 16 Jun 2023 14:12:21 -0700
Subject: [PATCH 48/56] refactor: :coffin: unused import

---
 packages/client/src/components/MkSignin.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/client/src/components/MkSignin.vue b/packages/client/src/components/MkSignin.vue
index f2c83d5774..27de73c49d 100644
--- a/packages/client/src/components/MkSignin.vue
+++ b/packages/client/src/components/MkSignin.vue
@@ -159,7 +159,6 @@
 
 <script lang="ts" setup>
 import Vue3OtpInput from "vue3-otp-input";
-import { ref } from "vue";
 import { defineAsyncComponent } from "vue";
 import { toUnicode } from "punycode/";
 import MkButton from "@/components/MkButton.vue";

From 3d6d90a137c27a34306f0f9cd73e703ffc50c1ed Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Fri, 16 Jun 2023 14:14:47 -0700
Subject: [PATCH 49/56] refactor: :art: locale loader

---
 locales/index.js | 117 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 74 insertions(+), 43 deletions(-)

diff --git a/locales/index.js b/locales/index.js
index 7399bb5a18..ac8ba19cfa 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -2,59 +2,90 @@
  * Languages Loader
  */
 
-const fs = require('fs');
-const yaml = require('js-yaml');
-let languages = []
-let languages_custom = []
-
-const merge = (...args) => args.reduce((a, c) => ({
-	...a,
-	...c,
-	...Object.entries(a)
-		.filter(([k]) => c && typeof c[k] === 'object')
-		.reduce((a, [k, v]) => (a[k] = merge(v, c[k]), a), {})
-}), {});
+const fs = require("fs");
+const yaml = require("js-yaml");
+const languages = [];
+const languages_custom = [];
 
+const merge = (...args) =>
+	args.reduce(
+		(a, c) => ({
+			...a,
+			...c,
+			...Object.entries(a)
+				.filter(([k]) => c && typeof c[k] === "object")
+				.reduce((a, [k, v]) => ((a[k] = merge(v, c[k])), a), {}),
+		}),
+		{},
+	);
 
 fs.readdirSync(__dirname).forEach((file) => {
-	if (file.includes('.yml')){
-		file = file.slice(0, file.indexOf('.'))
-		languages.push(file);
+	if (file.includes(".yml")) {
+		locale = file.slice(0, file.indexOf("."));
+		languages.push(locale);
 	}
-})
+});
 
-fs.readdirSync(__dirname + '/../custom/locales').forEach((file) => {
-	if (file.includes('.yml')){
-		file = file.slice(0, file.indexOf('.'))
-		languages_custom.push(file);
+fs.readdirSync(__dirname + "/../custom/locales").forEach((file) => {
+	if (file.includes(".yml")) {
+		customLocale = file.slice(0, file.indexOf("."));
+		languages_custom.push(customLocale);
 	}
-})
+});
 
 const primaries = {
-	'en': 'US',
-	'ja': 'JP',
-	'zh': 'CN',
+	en: "US",
+	ja: "JP",
+	zh: "CN",
 };
 
 // 何故か文字列にバックスペース文字が混入することがあり、YAMLが壊れるので取り除く
-const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
+const clean = (text) =>
+	text.replace(new RegExp(String.fromCodePoint(0x08), "g"), "");
 
-const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {});
-const locales_custom = languages_custom.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, 'utf-8'))) || {}, a), {});
-Object.assign(locales, locales_custom)
+const locales = languages.reduce(
+	(a, c) => (
+		(a[c] =
+			yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, "utf-8"))) ||
+			{}),
+		a
+	),
+	{},
+);
+const locales_custom = languages_custom.reduce(
+	(a, c) => (
+		(a[c] =
+			yaml.load(
+				clean(
+					fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, "utf-8"),
+				),
+			) || {}),
+		a
+	),
+	{},
+);
+Object.assign(locales, locales_custom);
 
-module.exports = Object.entries(locales)
-	.reduce((a, [k ,v]) => (a[k] = (() => {
-		const [lang] = k.split('-');
-		switch (k) {
-			case 'ja-JP': return v;
-			case 'ja-KS':
-			case 'en-US': return merge(locales['ja-JP'], v);
-			default: return merge(
-				locales['ja-JP'],
-				locales['en-US'],
-				locales[`${lang}-${primaries[lang]}`] || {},
-				v
-			);
-		}
-	})(), a), {});
+module.exports = Object.entries(locales).reduce(
+	(a, [k, v]) => (
+		(a[k] = (() => {
+			const [lang] = k.split("-");
+			switch (k) {
+				case "ja-JP":
+					return v;
+				case "ja-KS":
+				case "en-US":
+					return merge(locales["ja-JP"], v);
+				default:
+					return merge(
+						locales["ja-JP"],
+						locales["en-US"],
+						locales[`${lang}-${primaries[lang]}`] || {},
+						v,
+					);
+			}
+		})()),
+		a
+	),
+	{},
+);

From 51a7bab9ff574e3dffe503eb0eaf5fbaf5050111 Mon Sep 17 00:00:00 2001
From: ThatOneCalculator <kainoa@t1c.dev>
Date: Fri, 16 Jun 2023 14:19:42 -0700
Subject: [PATCH 50/56] fix

---
 locales/index.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/locales/index.js b/locales/index.js
index ac8ba19cfa..62e55e7e5c 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -21,15 +21,15 @@ const merge = (...args) =>
 
 fs.readdirSync(__dirname).forEach((file) => {
 	if (file.includes(".yml")) {
-		locale = file.slice(0, file.indexOf("."));
-		languages.push(locale);
+		file = file.slice(0, file.indexOf("."));
+		languages.push(file);
 	}
 });
 
 fs.readdirSync(__dirname + "/../custom/locales").forEach((file) => {
 	if (file.includes(".yml")) {
-		customLocale = file.slice(0, file.indexOf("."));
-		languages_custom.push(customLocale);
+		file = file.slice(0, file.indexOf("."));
+		languages_custom.push(file);
 	}
 });
 

From b4f75069508101cfc1da057da7dea19c82b902ce Mon Sep 17 00:00:00 2001
From: Freeplay <freeplay@duck.com>
Date: Fri, 16 Jun 2023 22:05:36 -0400
Subject: [PATCH 51/56] Fix mini follow button label + its position in user
 cards

---
 .../client/src/components/MkFollowButton.vue  | 20 ++++++++++++-------
 packages/client/src/components/MkUserInfo.vue | 12 +++++------
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/packages/client/src/components/MkFollowButton.vue b/packages/client/src/components/MkFollowButton.vue
index bff393fddf..9dc5af4f63 100644
--- a/packages/client/src/components/MkFollowButton.vue
+++ b/packages/client/src/components/MkFollowButton.vue
@@ -20,16 +20,17 @@
 		:disabled="wait"
 		@click.stop="onClick"
 		:aria-label="`${state} ${user.name || user.username}`"
+		v-tooltip="full ? null : `${state} ${user.name || user.username}`"
 	>
 		<template v-if="!wait">
 			<template v-if="isBlocking">
-				<span v-if="full">{{ (state = i18n.ts.blocked) }}</span
+				<span>{{ (state = i18n.ts.blocked) }}</span
 				><i class="ph-prohibit ph-bold ph-lg"></i>
 			</template>
 			<template
 				v-else-if="hasPendingFollowRequestFromYou && user.isLocked"
 			>
-				<span v-if="full">{{
+				<span>{{
 					(state = i18n.ts.followRequestPending)
 				}}</span
 				><i class="ph-hourglass-medium ph-bold ph-lg"></i>
@@ -38,24 +39,24 @@
 				v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"
 			>
 				<!-- つまりリモートフォローの場合。 -->
-				<span v-if="full">{{ (state = i18n.ts.processing) }}</span
+				<span>{{ (state = i18n.ts.processing) }}</span
 				><i class="ph-circle-notch ph-bold ph-lg fa-pulse"></i>
 			</template>
 			<template v-else-if="isFollowing">
-				<span v-if="full">{{ (state = i18n.ts.unfollow) }}</span
+				<span>{{ (state = i18n.ts.unfollow) }}</span
 				><i class="ph-minus ph-bold ph-lg"></i>
 			</template>
 			<template v-else-if="!isFollowing && user.isLocked">
-				<span v-if="full">{{ (state = i18n.ts.followRequest) }}</span
+				<span>{{ (state = i18n.ts.followRequest) }}</span
 				><i class="ph-plus ph-bold ph-lg"></i>
 			</template>
 			<template v-else-if="!isFollowing && !user.isLocked">
-				<span v-if="full">{{ (state = i18n.ts.follow) }}</span
+				<span>{{ (state = i18n.ts.follow) }}</span
 				><i class="ph-plus ph-bold ph-lg"></i>
 			</template>
 		</template>
 		<template v-else>
-			<span v-if="full">{{ (state = i18n.ts.processing) }}</span
+			<span>{{ (state = i18n.ts.processing) }}</span
 			><i class="ph-circle-notch ph-bold ph-lg fa-pulse ph-fw ph-lg"></i>
 		</template>
 	</button>
@@ -185,6 +186,7 @@ onBeforeUnmount(() => {
 .menu {
 	width: 3em;
 	height: 2em;
+	vertical-align: middle;
 }
 .follow-button {
 	position: relative;
@@ -200,6 +202,7 @@ onBeforeUnmount(() => {
 	height: 2em;
 	border-radius: 100px;
 	background: var(--bg);
+	vertical-align: middle;
 
 	&.full {
 		padding: 0.2em 0.7em;
@@ -215,6 +218,9 @@ onBeforeUnmount(() => {
 
 	&:not(.full) {
 		width: 31px;
+		span {
+			display: none;
+		}
 	}
 
 	&:focus-visible {
diff --git a/packages/client/src/components/MkUserInfo.vue b/packages/client/src/components/MkUserInfo.vue
index 2207363ee7..dc81b2643c 100644
--- a/packages/client/src/components/MkUserInfo.vue
+++ b/packages/client/src/components/MkUserInfo.vue
@@ -45,12 +45,12 @@
 				<MkNumber :value="user.followersCount" />
 			</div>
 		</div>
-		<MkFollowButton
-			v-if="$i && user.id != $i.id"
-			class="koudoku-button"
-			:user="user"
-			full
-		/>
+		<div class="koudoku-button">
+			<MkFollowButton
+				v-if="$i && user.id != $i.id"
+				:user="user"
+			/>
+		</div>
 	</div>
 </template>
 

From be413d5bd2ac460705588573b5dafcfc4e99ba0e Mon Sep 17 00:00:00 2001
From: Freeplay <freeplay@duck.com>
Date: Fri, 16 Jun 2023 22:54:01 -0400
Subject: [PATCH 52/56] Add backgrounds in nav & settings when wallpaper in use

---
 packages/client/src/pages/settings/index.vue |  5 +++++
 packages/client/src/ui/universal.vue         | 17 ++++++++++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/packages/client/src/pages/settings/index.vue b/packages/client/src/pages/settings/index.vue
index 25e8a33318..429186c340 100644
--- a/packages/client/src/pages/settings/index.vue
+++ b/packages/client/src/pages/settings/index.vue
@@ -331,6 +331,11 @@ definePageMetadata(INFO);
 <style lang="scss" scoped>
 .vvcocwet {
 	> .body {
+		.wallpaper & {
+			background: var(--bg);
+			padding: var(--margin);
+			border-radius: var(--radius);
+		}
 		> .nav {
 			.baaadecd {
 				> .info {
diff --git a/packages/client/src/ui/universal.vue b/packages/client/src/ui/universal.vue
index 01141fc61b..cd91c01d92 100644
--- a/packages/client/src/ui/universal.vue
+++ b/packages/client/src/ui/universal.vue
@@ -471,6 +471,21 @@ console.log(mainRouter.currentRoute.value.name);
 	}
 	&.wallpaper {
 		background: var(--wallpaperOverlay);
+
+		:deep(.sidebar .middle) {
+			position: relative;
+			&::before {
+				content: "";
+				position: absolute;
+				inset: -10px 10px;
+				background: var(--bg);
+				border-radius: calc((2.85rem / 2) + 5px);
+				opacity: 1;
+			}
+			> ._button:last-child {
+				margin-bottom: 0 !important;
+			}
+		}
 	}
 
 	&.centered {
@@ -547,7 +562,7 @@ console.log(mainRouter.currentRoute.value.name);
 		&.wallpaper {
 			.contents {
 				background: var(--acrylicBg) !important;
-				backdrop-filter: blur(12px);
+				backdrop-filter: var(--blur, blur(12px));
 			}
 			:deep(.tl),
 			:deep(.notes) {

From cbefe34e9991a19ad0d6b1830f29548ca803bcae Mon Sep 17 00:00:00 2001
From: Freeplay <freeplay@duck.com>
Date: Fri, 16 Jun 2023 23:17:55 -0400
Subject: [PATCH 53/56] Add 'remove your reaction' label

---
 locales/en-US.yml                            | 1 +
 packages/client/src/components/MkNote.vue    | 1 +
 packages/client/src/components/MkNoteSub.vue | 1 +
 3 files changed, 3 insertions(+)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index b2bc4e7146..c0fa8465b0 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -123,6 +123,7 @@ clickToShow: "Click to show"
 sensitive: "NSFW"
 add: "Add"
 reaction: "Reactions"
+removeReaction: "Remove your reaction"
 enableEmojiReactions: "Enable emoji reactions"
 showEmojisInReactionNotifications: "Show emojis in reaction notifications"
 reactionSetting: "Reactions to show in the reaction picker"
diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue
index 19205e0d99..8bf8fae62a 100644
--- a/packages/client/src/components/MkNote.vue
+++ b/packages/client/src/components/MkNote.vue
@@ -190,6 +190,7 @@
 						ref="reactButton"
 						class="button _button reacted"
 						@click="undoReact(appearNote)"
+						v-tooltip.noDelay.bottom="i18n.ts.removeReaction"
 					>
 						<i class="ph-minus ph-bold ph-lg"></i>
 					</button>
diff --git a/packages/client/src/components/MkNoteSub.vue b/packages/client/src/components/MkNoteSub.vue
index 813eab7ae3..62fcf7b349 100644
--- a/packages/client/src/components/MkNoteSub.vue
+++ b/packages/client/src/components/MkNoteSub.vue
@@ -119,6 +119,7 @@
 						ref="reactButton"
 						class="button _button reacted"
 						@click="undoReact(appearNote)"
+						v-tooltip.noDelay.bottom="i18n.ts.removeReaction"
 					>
 						<i class="ph-minus ph-bold ph-lg"></i>
 					</button>

From d62182454a0d20796826fc436f219264abe2c429 Mon Sep 17 00:00:00 2001
From: jolupa <jolupameister@gmail.com>
Date: Fri, 16 Jun 2023 05:26:43 +0000
Subject: [PATCH 54/56] chore: Translated using Weblate (Catalan)

Currently translated at 100.0% (1803 of 1803 strings)

Translation: Calckey/locales
Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/
---
 locales/ca-ES.yml | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 20e67a0971..da20643540 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -325,11 +325,30 @@ _2fa:
     per protegir encara més el vostre compte.
   step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquest
     token d'inici de sessió.
-  registerSecurityKey: Registra una clau de seguretat
+  registerSecurityKey: Registrar una clau de seguretat o d'accés
   step1: En primer lloc, instal·la una aplicació d'autenticació (com ara {a} o {b})
     al dispositiu.
   step2: A continuació, escaneja el codi QR que es mostra en aquesta pantalla.
   step3: Introdueix el token que t'ha proporcionat l'aplicació per finalitzar la configuració.
+  step3Title: Introduïu un codi d'autenticació
+  chromePasskeyNotSupported: Les claus de pas de Chrome actualment no s'admeten.
+  securityKeyName: Introduïu un nom de clau
+  removeKey: Suprimeix la clau de seguretat
+  removeKeyConfirm: Vols suprimir la clau {name}?
+  renewTOTP: Tornar a configurar l'aplicació d'autenticació
+  renewTOTPOk: Reconfigurar
+  renewTOTPCancel: Cancel·lar
+  step2Click: Fer clic en aquest codi QR us permetrà registrar 2FA a la vostra clau
+    de seguretat o aplicació d'autenticació del telèfon.
+  securityKeyNotSupported: El vostre navegador no admet claus de seguretat.
+  registerTOTPBeforeKey: Configureu una aplicació d'autenticació per registrar una
+    clau de seguretat o de passi.
+  tapSecurityKey: Si us plau, seguiu el vostre navegador per registrar la clau de
+    seguretat o d'accés
+  renewTOTPConfirm: Això farà que els codis de verificació de l'aplicació anterior
+    deixin de funcionar
+  whyTOTPOnlyRenew: L’aplicació d’autenticació no es pot eliminar sempre que es hi
+    hagi una clau de seguretat registrada.
 _widgets:
   notifications: "Notificacions"
   timeline: "Línia de temps"
@@ -988,7 +1007,7 @@ avoidMultiCaptchaConfirm: Fent servir diferents sistemes de Captcha pot causar i
 antennas: Antenes
 enableEmojiReactions: Activa reaccions amb emojis
 blockThisInstance: Bloqueja aquest servidor
-registration: Registre
+registration: Registra't
 showEmojisInReactionNotifications: Mostra els emojis a les notificacions de les reaccions
 renoteMute: Silencia els impulsos
 renoteUnmute: Treu el silenci als impulsos
@@ -1035,7 +1054,7 @@ recentlyRegisteredUsers: Usuaris registrats fa poc
 recentlyDiscoveredUsers: Nous suaris descoberts
 administrator: Administrador
 token: Token
-registerSecurityKey: Registra una clau de seguretat
+registerSecurityKey: Registreu una clau de seguretat
 securityKeyName: Nom clau
 lastUsed: Feta servir per última vegada
 unregister: Anul·lar el registre
@@ -2107,3 +2126,7 @@ _filters:
 image: Imatge
 video: Vídeo
 audio: Àudio
+_dialog:
+  charactersExceeded: "S'han superat el màxim de caràcters! Actual: {current}/Límit:
+    {max}"
+  charactersBelow: 'No hi ha caràcters suficients! Corrent: {current}/Limit: {min}'

From 5019d4234552e64b8fcf96e5c5bd8f15aeaf6f32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Al=C3=A9xia?= <alexiacordeiro@tutanota.com>
Date: Fri, 16 Jun 2023 00:50:55 +0000
Subject: [PATCH 55/56] chore: Translated using Weblate (Portuguese (Brazil))

Currently translated at 4.4% (81 of 1803 strings)

Translation: Calckey/locales
Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pt_BR/
---
 locales/pt_BR.yml | 73 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml
index d56a9fcae4..a02b7069e4 100644
--- a/locales/pt_BR.yml
+++ b/locales/pt_BR.yml
@@ -12,3 +12,76 @@ notifications: Notificações
 password: Senha
 forgotPassword: Esqueci a senha
 cancel: Cancelar
+noThankYou: Não, obrigade
+save: Salvar
+enterUsername: Insira nome de usuário
+cw: Aviso de conteúdo
+driveFileDeleteConfirm: Tem a certeza de que pretende apagar o arquivo "{name}"? O
+  arquivo será removido de todas as mensagens que o contenham como anexo.
+deleteAndEdit: Deletar e editar
+import: Importar
+exportRequested: Você pediu uma exportação. Isso pode demorar um pouco. Será adicionado
+  ao seu Drive quando for completo.
+note: Postar
+notes: Postagens
+deleteAndEditConfirm: Você tem certeza que quer deletar esse post e edita-lo? Você
+  vai perder todas as reações, impulsionamentos e respostas dele.
+showLess: Fechar
+importRequested: Você requisitou uma importação. Isso pode demorar um pouco.
+listsDesc: Listas deixam você criar linhas do tempo com usuários específicos. Elas
+  podem ser acessadas pela página de linhas do tempo.
+edited: Editado
+sendMessage: Enviar uma mensagem
+older: antigo
+createList: Criar lista
+loadMore: Carregar mais
+mentions: Menções
+importAndExport: Importar/Exportar Dados
+files: Arquivos
+lists: Listas
+manageLists: Gerenciar listas
+error: Erro
+somethingHappened: Ocorreu um erro
+retry: Tentar novamente
+renotedBy: Impulsionado por {user}
+noNotes: Nenhum post
+noNotifications: Nenhuma notificação
+instance: Servidor
+settings: Configurações
+basicSettings: Configurações Básicas
+otherSettings: Outras Configurações
+openInWindow: Abrir em janela
+profile: Perfil
+noAccountDescription: Esse usuário ainda não escreveu sua bio.
+login: Entrar
+loggingIn: Entrando
+logout: Sair
+signup: Criar conta
+uploading: Enviando...
+users: Usuários
+addUser: Adicione um usuário
+addInstance: Adicionar um servidor
+cantFavorite: Não foi possível adicionar aos marcadores.
+pin: Fixar no perfil
+unpin: Desfixar do perfil
+copyContent: Copiar conteúdos
+copyLink: Copiar link
+delete: Deletar
+deleted: Deletado
+editNote: Editar anotação
+addToList: Adicionar a lista
+copyUsername: Copiar nome de usuário
+searchUser: Procurar por um usuário
+reply: Responder
+jumpToPrevious: Pular para o anterior
+showMore: Mostrar mais
+newer: novo
+youGotNewFollower: seguiu você
+mention: Mencionar
+directNotes: Mensagens diretas
+export: Exportar
+unfollowConfirm: Você tem certez que deseja para de seguir {name}?
+noLists: Você não possui nenhuma lista
+following: Seguindo
+followers: Seguidores
+followsYou: Segue você

From 1abf71f569f228ac3f2befb78e9a1b568aa257ef Mon Sep 17 00:00:00 2001
From: Freeplay <freeplay@duck.com>
Date: Sat, 17 Jun 2023 00:51:38 -0400
Subject: [PATCH 56/56] add faded edges to swiper + shadows :3

---
 .../client/src/components/MkRemoteCaution.vue |  4 ++--
 packages/client/src/pages/timeline.vue        |  1 -
 packages/client/src/style.scss                | 24 ++++++++++++++++++-
 3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/packages/client/src/components/MkRemoteCaution.vue b/packages/client/src/components/MkRemoteCaution.vue
index f8a84e81e2..23a7a731b5 100644
--- a/packages/client/src/components/MkRemoteCaution.vue
+++ b/packages/client/src/components/MkRemoteCaution.vue
@@ -1,5 +1,5 @@
 <template>
-	<div class="jmgmzlwq _block">
+	<div class="caution _block">
 		{{ i18n.ts.remoteUserCaution
 		}}<a
 			class="link"
@@ -20,7 +20,7 @@ defineProps<{
 </script>
 
 <style lang="scss" scoped>
-.jmgmzlwq {
+.caution {
 	padding: 16px;
 	font-size: 90%;
 	background: var(--infoWarnBg);
diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue
index 8aec6496d9..2633d0e0b5 100644
--- a/packages/client/src/pages/timeline.vue
+++ b/packages/client/src/pages/timeline.vue
@@ -354,7 +354,6 @@ onMounted(() => {
 	> .tl {
 		background: none;
 		border-radius: var(--radius);
-		overflow: clip;
 	}
 }
 </style>
diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss
index 7bf18abfea..6855d6f9b3 100644
--- a/packages/client/src/style.scss
+++ b/packages/client/src/style.scss
@@ -107,6 +107,12 @@ html, body {
 	padding: 0;
 }
 
+.swiper {
+	margin-inline: -24px !important;
+	padding-inline: 24px !important;
+	mask: linear-gradient(to right, transparent, black 24px calc(100% - 24px), transparent);
+	-webkit-mask: linear-gradient(to right, transparent, black 24px calc(100% - 24px), transparent);
+}
 .swiper-slide {
 	min-height: 100vh;
 }
@@ -348,7 +354,9 @@ hr {
 }
 
 ._block {
-	@extend ._panel;
+	background: var(--panel);
+	border-radius: var(--radius);
+	overflow: visible;
 
 	& + ._block {
 		margin-top: var(--margin);
@@ -625,6 +633,20 @@ hr {
 	outline: none;
 }
 
+:not(.noGap):not(._block) > {
+	.note-container, .cmuxhskf > section, ._gap > ._block, .noGap, .profile, .card {
+		&:not(.caution)
+		{
+			box-shadow: 0 4px 25px rgba(0,0,0,0.04);
+			border-radius: var(--radius);
+			overflow: clip;
+		}
+	}
+}
+.widgets ._panel {
+	box-shadow: none !important;
+}
+
 ._zoom {
 	transition-duration: 0.5s, 0.5s;
 	transition-property: opacity, transform;