From 27ac3d795e7098124a7eea0dd59f6f1ef8f32394 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 4 Jun 2024 13:14:37 +0900
Subject: [PATCH 001/206] Update about-misskey.vue

---
 packages/frontend/src/pages/about-misskey.vue | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index b55ae220d8..629f00689d 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -102,13 +102,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<template #label>Special thanks</template>
 					<div style="display:grid;grid-template-columns:repeat(auto-fill, minmax(130px, 1fr));grid-gap:24px;align-items:center;">
 						<div>
-							<a style="display: inline-block;" class="masknetwork" title="Mask Network" href="https://mask.io/" target="_blank"><img style="width: 100%;" src="https://misskey-hub.net/sponsors/masknetwork.png" alt="Mask Network"></a>
+							<a style="display: inline-block;" class="masknetwork" title="Mask Network" href="https://mask.io/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/masknetwork.png" alt="Mask Network"></a>
 						</div>
 						<div>
-							<a style="display: inline-block;" class="xserver" title="XServer" href="https://www.xserver.ne.jp/" target="_blank"><img style="width: 100%;" src="https://misskey-hub.net/sponsors/xserver.png" alt="XServer"></a>
+							<a style="display: inline-block;" class="xserver" title="XServer" href="https://www.xserver.ne.jp/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/xserver.png" alt="XServer"></a>
 						</div>
 						<div>
-							<a style="display: inline-block;" class="skeb" title="Skeb" href="https://skeb.jp/" target="_blank"><img style="width: 100%;" src="https://misskey-hub.net/sponsors/skeb.svg" alt="Skeb"></a>
+							<a style="display: inline-block;" class="skeb" title="Skeb" href="https://skeb.jp/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/skeb.svg" alt="Skeb"></a>
+						</div>
+						<div>
+							<a style="display: inline-block;" class="pepabo" title="GMO Pepabo" href="https://pepabo.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/gmo_pepabo.svg" alt="Skeb"></a>
 						</div>
 					</div>
 				</FormSection>

From 43cccaaee9be42fab38eaa9ca04bb5e55b5d8db7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 4 Jun 2024 13:15:35 +0900
Subject: [PATCH 002/206] fix

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

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 629f00689d..cc0394f401 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -111,7 +111,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<a style="display: inline-block;" class="skeb" title="Skeb" href="https://skeb.jp/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/skeb.svg" alt="Skeb"></a>
 						</div>
 						<div>
-							<a style="display: inline-block;" class="pepabo" title="GMO Pepabo" href="https://pepabo.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/gmo_pepabo.svg" alt="Skeb"></a>
+							<a style="display: inline-block;" class="pepabo" title="GMO Pepabo" href="https://pepabo.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/gmo_pepabo.svg" alt="GMO Pepabo"></a>
 						</div>
 					</div>
 				</FormSection>

From d4a8c63264939c4ec36af628f7e1516d2ae60254 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 6 Jun 2024 09:32:04 +0900
Subject: [PATCH 003/206] enhance(backend): sentry integration for job queues

---
 .../src/queue/QueueProcessorService.ts        | 389 +++++++++++-------
 1 file changed, 231 insertions(+), 158 deletions(-)

diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index ce999d9cef..eb1901d069 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -5,6 +5,7 @@
 
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
 import * as Bull from 'bullmq';
+import * as Sentry from '@sentry/node';
 import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import type Logger from '@/logger.js';
@@ -135,199 +136,271 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		}
 
 		//#region system
-		this.systemQueueWorker = new Bull.Worker(QUEUE.SYSTEM, (job) => {
-			switch (job.name) {
-				case 'tickCharts': return this.tickChartsProcessorService.process();
-				case 'resyncCharts': return this.resyncChartsProcessorService.process();
-				case 'cleanCharts': return this.cleanChartsProcessorService.process();
-				case 'aggregateRetention': return this.aggregateRetentionProcessorService.process();
-				case 'checkExpiredMutings': return this.checkExpiredMutingsProcessorService.process();
-				case 'clean': return this.cleanProcessorService.process();
-				default: throw new Error(`unrecognized job type ${job.name} for system`);
-			}
-		}, {
-			...baseQueueOptions(this.config, QUEUE.SYSTEM),
-			autorun: false,
-		});
+		{
+			const processer = (job: Bull.Job) => {
+				switch (job.name) {
+					case 'tickCharts': return this.tickChartsProcessorService.process();
+					case 'resyncCharts': return this.resyncChartsProcessorService.process();
+					case 'cleanCharts': return this.cleanChartsProcessorService.process();
+					case 'aggregateRetention': return this.aggregateRetentionProcessorService.process();
+					case 'checkExpiredMutings': return this.checkExpiredMutingsProcessorService.process();
+					case 'clean': return this.cleanProcessorService.process();
+					default: throw new Error(`unrecognized job type ${job.name} for system`);
+				}
+			};
 
-		const systemLogger = this.logger.createSubLogger('system');
+			this.systemQueueWorker = new Bull.Worker(QUEUE.SYSTEM, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: System: ' + job.name }, () => processer(job));
+				} else {
+					return processer(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.SYSTEM),
+				autorun: false,
+			});
 
-		this.systemQueueWorker
-			.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
-			.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => systemLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-			.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
+			const systemLogger = this.logger.createSubLogger('system');
+
+			this.systemQueueWorker
+				.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('failed', (job, err) => systemLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
+		}
 		//#endregion
 
 		//#region db
-		this.dbQueueWorker = new Bull.Worker(QUEUE.DB, (job) => {
-			switch (job.name) {
-				case 'deleteDriveFiles': return this.deleteDriveFilesProcessorService.process(job);
-				case 'exportCustomEmojis': return this.exportCustomEmojisProcessorService.process(job);
-				case 'exportNotes': return this.exportNotesProcessorService.process(job);
-				case 'exportClips': return this.exportClipsProcessorService.process(job);
-				case 'exportFavorites': return this.exportFavoritesProcessorService.process(job);
-				case 'exportFollowing': return this.exportFollowingProcessorService.process(job);
-				case 'exportMuting': return this.exportMutingProcessorService.process(job);
-				case 'exportBlocking': return this.exportBlockingProcessorService.process(job);
-				case 'exportUserLists': return this.exportUserListsProcessorService.process(job);
-				case 'exportAntennas': return this.exportAntennasProcessorService.process(job);
-				case 'importFollowing': return this.importFollowingProcessorService.process(job);
-				case 'importFollowingToDb': return this.importFollowingProcessorService.processDb(job);
-				case 'importMuting': return this.importMutingProcessorService.process(job);
-				case 'importBlocking': return this.importBlockingProcessorService.process(job);
-				case 'importBlockingToDb': return this.importBlockingProcessorService.processDb(job);
-				case 'importUserLists': return this.importUserListsProcessorService.process(job);
-				case 'importCustomEmojis': return this.importCustomEmojisProcessorService.process(job);
-				case 'importAntennas': return this.importAntennasProcessorService.process(job);
-				case 'deleteAccount': return this.deleteAccountProcessorService.process(job);
-				default: throw new Error(`unrecognized job type ${job.name} for db`);
-			}
-		}, {
-			...baseQueueOptions(this.config, QUEUE.DB),
-			autorun: false,
-		});
+		{
+			const processer = (job: Bull.Job) => {
+				switch (job.name) {
+					case 'deleteDriveFiles': return this.deleteDriveFilesProcessorService.process(job);
+					case 'exportCustomEmojis': return this.exportCustomEmojisProcessorService.process(job);
+					case 'exportNotes': return this.exportNotesProcessorService.process(job);
+					case 'exportClips': return this.exportClipsProcessorService.process(job);
+					case 'exportFavorites': return this.exportFavoritesProcessorService.process(job);
+					case 'exportFollowing': return this.exportFollowingProcessorService.process(job);
+					case 'exportMuting': return this.exportMutingProcessorService.process(job);
+					case 'exportBlocking': return this.exportBlockingProcessorService.process(job);
+					case 'exportUserLists': return this.exportUserListsProcessorService.process(job);
+					case 'exportAntennas': return this.exportAntennasProcessorService.process(job);
+					case 'importFollowing': return this.importFollowingProcessorService.process(job);
+					case 'importFollowingToDb': return this.importFollowingProcessorService.processDb(job);
+					case 'importMuting': return this.importMutingProcessorService.process(job);
+					case 'importBlocking': return this.importBlockingProcessorService.process(job);
+					case 'importBlockingToDb': return this.importBlockingProcessorService.processDb(job);
+					case 'importUserLists': return this.importUserListsProcessorService.process(job);
+					case 'importCustomEmojis': return this.importCustomEmojisProcessorService.process(job);
+					case 'importAntennas': return this.importAntennasProcessorService.process(job);
+					case 'deleteAccount': return this.deleteAccountProcessorService.process(job);
+					default: throw new Error(`unrecognized job type ${job.name} for db`);
+				}
+			};
 
-		const dbLogger = this.logger.createSubLogger('db');
+			this.dbQueueWorker = new Bull.Worker(QUEUE.DB, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: DB: ' + job.name }, () => processer(job));
+				} else {
+					return processer(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.DB),
+				autorun: false,
+			});
 
-		this.dbQueueWorker
-			.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
-			.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => dbLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-			.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
+			const dbLogger = this.logger.createSubLogger('db');
+
+			this.dbQueueWorker
+				.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('failed', (job, err) => dbLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
+		}
 		//#endregion
 
 		//#region deliver
-		this.deliverQueueWorker = new Bull.Worker(QUEUE.DELIVER, (job) => this.deliverProcessorService.process(job), {
-			...baseQueueOptions(this.config, QUEUE.DELIVER),
-			autorun: false,
-			concurrency: this.config.deliverJobConcurrency ?? 128,
-			limiter: {
-				max: this.config.deliverJobPerSec ?? 128,
-				duration: 1000,
-			},
-			settings: {
-				backoffStrategy: httpRelatedBackoff,
-			},
-		});
+		{
+			this.deliverQueueWorker = new Bull.Worker(QUEUE.DELIVER, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: Deliver' }, () => this.deliverProcessorService.process(job));
+				} else {
+					return this.deliverProcessorService.process(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.DELIVER),
+				autorun: false,
+				concurrency: this.config.deliverJobConcurrency ?? 128,
+				limiter: {
+					max: this.config.deliverJobPerSec ?? 128,
+					duration: 1000,
+				},
+				settings: {
+					backoffStrategy: httpRelatedBackoff,
+				},
+			});
 
-		const deliverLogger = this.logger.createSubLogger('deliver');
+			const deliverLogger = this.logger.createSubLogger('deliver');
 
-		this.deliverQueueWorker
-			.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
-			.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
-			.on('failed', (job, err) => deliverLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
-			.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-			.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`));
+			this.deliverQueueWorker
+				.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('failed', (job, err) => deliverLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
+				.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`));
+		}
 		//#endregion
 
 		//#region inbox
-		this.inboxQueueWorker = new Bull.Worker(QUEUE.INBOX, (job) => this.inboxProcessorService.process(job), {
-			...baseQueueOptions(this.config, QUEUE.INBOX),
-			autorun: false,
-			concurrency: this.config.inboxJobConcurrency ?? 16,
-			limiter: {
-				max: this.config.inboxJobPerSec ?? 32,
-				duration: 1000,
-			},
-			settings: {
-				backoffStrategy: httpRelatedBackoff,
-			},
-		});
+		{
+			this.inboxQueueWorker = new Bull.Worker(QUEUE.INBOX, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: Inbox' }, () => this.inboxProcessorService.process(job));
+				} else {
+					return this.inboxProcessorService.process(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.INBOX),
+				autorun: false,
+				concurrency: this.config.inboxJobConcurrency ?? 16,
+				limiter: {
+					max: this.config.inboxJobPerSec ?? 32,
+					duration: 1000,
+				},
+				settings: {
+					backoffStrategy: httpRelatedBackoff,
+				},
+			});
 
-		const inboxLogger = this.logger.createSubLogger('inbox');
+			const inboxLogger = this.logger.createSubLogger('inbox');
 
-		this.inboxQueueWorker
-			.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
-			.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
-			.on('failed', (job, err) => inboxLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-			.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`));
+			this.inboxQueueWorker
+				.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
+				.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
+				.on('failed', (job, err) => inboxLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }))
+				.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`));
+		}
 		//#endregion
 
 		//#region webhook deliver
-		this.webhookDeliverQueueWorker = new Bull.Worker(QUEUE.WEBHOOK_DELIVER, (job) => this.webhookDeliverProcessorService.process(job), {
-			...baseQueueOptions(this.config, QUEUE.WEBHOOK_DELIVER),
-			autorun: false,
-			concurrency: 64,
-			limiter: {
-				max: 64,
-				duration: 1000,
-			},
-			settings: {
-				backoffStrategy: httpRelatedBackoff,
-			},
-		});
+		{
+			this.webhookDeliverQueueWorker = new Bull.Worker(QUEUE.WEBHOOK_DELIVER, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: WebhookDeliver' }, () => this.webhookDeliverProcessorService.process(job));
+				} else {
+					return this.webhookDeliverProcessorService.process(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.WEBHOOK_DELIVER),
+				autorun: false,
+				concurrency: 64,
+				limiter: {
+					max: 64,
+					duration: 1000,
+				},
+				settings: {
+					backoffStrategy: httpRelatedBackoff,
+				},
+			});
 
-		const webhookLogger = this.logger.createSubLogger('webhook');
+			const webhookLogger = this.logger.createSubLogger('webhook');
 
-		this.webhookDeliverQueueWorker
-			.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
-			.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
-			.on('failed', (job, err) => webhookLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
-			.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-			.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`));
+			this.webhookDeliverQueueWorker
+				.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('failed', (job, err) => webhookLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
+				.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`));
+		}
 		//#endregion
 
 		//#region relationship
-		this.relationshipQueueWorker = new Bull.Worker(QUEUE.RELATIONSHIP, (job) => {
-			switch (job.name) {
-				case 'follow': return this.relationshipProcessorService.processFollow(job);
-				case 'unfollow': return this.relationshipProcessorService.processUnfollow(job);
-				case 'block': return this.relationshipProcessorService.processBlock(job);
-				case 'unblock': return this.relationshipProcessorService.processUnblock(job);
-				default: throw new Error(`unrecognized job type ${job.name} for relationship`);
-			}
-		}, {
-			...baseQueueOptions(this.config, QUEUE.RELATIONSHIP),
-			autorun: false,
-			concurrency: this.config.relationshipJobConcurrency ?? 16,
-			limiter: {
-				max: this.config.relationshipJobPerSec ?? 64,
-				duration: 1000,
-			},
-		});
+		{
+			const processer = (job: Bull.Job) => {
+				switch (job.name) {
+					case 'follow': return this.relationshipProcessorService.processFollow(job);
+					case 'unfollow': return this.relationshipProcessorService.processUnfollow(job);
+					case 'block': return this.relationshipProcessorService.processBlock(job);
+					case 'unblock': return this.relationshipProcessorService.processUnblock(job);
+					default: throw new Error(`unrecognized job type ${job.name} for relationship`);
+				}
+			};
 
-		const relationshipLogger = this.logger.createSubLogger('relationship');
+			this.relationshipQueueWorker = new Bull.Worker(QUEUE.RELATIONSHIP, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: Relationship: ' + job.name }, () => processer(job));
+				} else {
+					return processer(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.RELATIONSHIP),
+				autorun: false,
+				concurrency: this.config.relationshipJobConcurrency ?? 16,
+				limiter: {
+					max: this.config.relationshipJobPerSec ?? 64,
+					duration: 1000,
+				},
+			});
 
-		this.relationshipQueueWorker
-			.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`))
-			.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => relationshipLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-			.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`));
+			const relationshipLogger = this.logger.createSubLogger('relationship');
+
+			this.relationshipQueueWorker
+				.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('failed', (job, err) => relationshipLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`));
+		}
 		//#endregion
 
 		//#region object storage
-		this.objectStorageQueueWorker = new Bull.Worker(QUEUE.OBJECT_STORAGE, (job) => {
-			switch (job.name) {
-				case 'deleteFile': return this.deleteFileProcessorService.process(job);
-				case 'cleanRemoteFiles': return this.cleanRemoteFilesProcessorService.process(job);
-				default: throw new Error(`unrecognized job type ${job.name} for objectStorage`);
-			}
-		}, {
-			...baseQueueOptions(this.config, QUEUE.OBJECT_STORAGE),
-			autorun: false,
-			concurrency: 16,
-		});
+		{
+			const processer = (job: Bull.Job) => {
+				switch (job.name) {
+					case 'deleteFile': return this.deleteFileProcessorService.process(job);
+					case 'cleanRemoteFiles': return this.cleanRemoteFilesProcessorService.process(job);
+					default: throw new Error(`unrecognized job type ${job.name} for objectStorage`);
+				}
+			};
 
-		const objectStorageLogger = this.logger.createSubLogger('objectStorage');
+			this.objectStorageQueueWorker = new Bull.Worker(QUEUE.OBJECT_STORAGE, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: ObjectStorage: ' + job.name }, () => processer(job));
+				} else {
+					return processer(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.OBJECT_STORAGE),
+				autorun: false,
+				concurrency: 16,
+			});
 
-		this.objectStorageQueueWorker
-			.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`))
-			.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-			.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`));
+			const objectStorageLogger = this.logger.createSubLogger('objectStorage');
+
+			this.objectStorageQueueWorker
+				.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`));
+		}
 		//#endregion
 
 		//#region ended poll notification
-		this.endedPollNotificationQueueWorker = new Bull.Worker(QUEUE.ENDED_POLL_NOTIFICATION, (job) => this.endedPollNotificationProcessorService.process(job), {
-			...baseQueueOptions(this.config, QUEUE.ENDED_POLL_NOTIFICATION),
-			autorun: false,
-		});
+		{
+			this.endedPollNotificationQueueWorker = new Bull.Worker(QUEUE.ENDED_POLL_NOTIFICATION, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: EndedPollNotification' }, () => this.endedPollNotificationProcessorService.process(job));
+				} else {
+					return this.endedPollNotificationProcessorService.process(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.ENDED_POLL_NOTIFICATION),
+				autorun: false,
+			});
+		}
 		//#endregion
 	}
 

From dbf9e1194bf5b84ec711c14f27c11d2bfeb37f20 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 6 Jun 2024 10:01:50 +0900
Subject: [PATCH 004/206] refactor(backend): remove unused logger option

---
 packages/backend/src/core/LoggerService.ts         |  4 ++--
 .../backend/src/core/chart/ChartLoggerService.ts   |  2 +-
 packages/backend/src/logger.ts                     | 14 +++++---------
 packages/backend/src/server/FileServerService.ts   |  2 +-
 packages/backend/src/server/ServerService.ts       |  2 +-
 5 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts
index 96d9b09992..f102461a50 100644
--- a/packages/backend/src/core/LoggerService.ts
+++ b/packages/backend/src/core/LoggerService.ts
@@ -15,7 +15,7 @@ export class LoggerService {
 	}
 
 	@bindThis
-	public getLogger(domain: string, color?: KEYWORD | undefined, store?: boolean) {
-		return new Logger(domain, color, store);
+	public getLogger(domain: string, color?: KEYWORD | undefined) {
+		return new Logger(domain, color);
 	}
 }
diff --git a/packages/backend/src/core/chart/ChartLoggerService.ts b/packages/backend/src/core/chart/ChartLoggerService.ts
index afc728d564..20815ea968 100644
--- a/packages/backend/src/core/chart/ChartLoggerService.ts
+++ b/packages/backend/src/core/chart/ChartLoggerService.ts
@@ -14,6 +14,6 @@ export class ChartLoggerService {
 	constructor(
 		private loggerService: LoggerService,
 	) {
-		this.logger = this.loggerService.getLogger('chart', 'white', process.env.NODE_ENV !== 'test');
+		this.logger = this.loggerService.getLogger('chart', 'white');
 	}
 }
diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts
index d4705af601..ff5363a425 100644
--- a/packages/backend/src/logger.ts
+++ b/packages/backend/src/logger.ts
@@ -22,31 +22,27 @@ type Level = 'error' | 'success' | 'warning' | 'debug' | 'info';
 export default class Logger {
 	private context: Context;
 	private parentLogger: Logger | null = null;
-	private store: boolean;
 
-	constructor(context: string, color?: KEYWORD, store = true) {
+	constructor(context: string, color?: KEYWORD) {
 		this.context = {
 			name: context,
 			color: color,
 		};
-		this.store = store;
 	}
 
 	@bindThis
-	public createSubLogger(context: string, color?: KEYWORD, store = true): Logger {
-		const logger = new Logger(context, color, store);
+	public createSubLogger(context: string, color?: KEYWORD): Logger {
+		const logger = new Logger(context, color);
 		logger.parentLogger = this;
 		return logger;
 	}
 
 	@bindThis
-	private log(level: Level, message: string, data?: Record<string, any> | null, important = false, subContexts: Context[] = [], store = true): void {
+	private log(level: Level, message: string, data?: Record<string, any> | null, important = false, subContexts: Context[] = []): void {
 		if (envOption.quiet) return;
-		if (!this.store) store = false;
-		if (level === 'debug') store = false;
 
 		if (this.parentLogger) {
-			this.parentLogger.log(level, message, data, important, [this.context].concat(subContexts), store);
+			this.parentLogger.log(level, message, data, important, [this.context].concat(subContexts));
 			return;
 		}
 
diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index 9db3aa1bfb..77a637d895 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -53,7 +53,7 @@ export class FileServerService {
 		private internalStorageService: InternalStorageService,
 		private loggerService: LoggerService,
 	) {
-		this.logger = this.loggerService.getLogger('server', 'gray', false);
+		this.logger = this.loggerService.getLogger('server', 'gray');
 
 		//this.createServer = this.createServer.bind(this);
 	}
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 3572f16627..9c849480f2 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -68,7 +68,7 @@ export class ServerService implements OnApplicationShutdown {
 		private loggerService: LoggerService,
 		private oauth2ProviderService: OAuth2ProviderService,
 	) {
-		this.logger = this.loggerService.getLogger('server', 'gray', false);
+		this.logger = this.loggerService.getLogger('server', 'gray');
 	}
 
 	@bindThis

From 65d19279a2c19c3e6be1c4c50cc9b01c94420d6c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 6 Jun 2024 10:11:43 +0900
Subject: [PATCH 005/206] fix

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

diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts
index 75e1a80cd1..4bc5c799cf 100644
--- a/packages/backend/src/boot/master.ts
+++ b/packages/backend/src/boot/master.ts
@@ -25,7 +25,7 @@ const _dirname = dirname(_filename);
 const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
 
 const logger = new Logger('core', 'cyan');
-const bootLogger = logger.createSubLogger('boot', 'magenta', false);
+const bootLogger = logger.createSubLogger('boot', 'magenta');
 
 const themeColor = chalk.hex('#86b300');
 

From ab69e113f4921462b72f1f352dfefe52b37862f5 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 6 Jun 2024 11:20:54 +0900
Subject: [PATCH 006/206] enhance(backend): improve sentry integration

---
 packages/backend/src/boot/worker.ts           | 23 +++++++++++++++++++
 .../src/queue/QueueProcessorService.ts        |  4 ++--
 .../backend/src/server/api/ApiCallService.ts  | 14 +++++++----
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts
index d4a7cd56e5..5d4a15b29f 100644
--- a/packages/backend/src/boot/worker.ts
+++ b/packages/backend/src/boot/worker.ts
@@ -4,13 +4,36 @@
  */
 
 import cluster from 'node:cluster';
+import * as Sentry from '@sentry/node';
+import { nodeProfilingIntegration } from '@sentry/profiling-node';
 import { envOption } from '@/env.js';
+import { loadConfig } from '@/config.js';
 import { jobQueue, server } from './common.js';
 
 /**
  * Init worker process
  */
 export async function workerMain() {
+	const config = loadConfig();
+
+	if (config.sentryForBackend) {
+		Sentry.init({
+			integrations: [
+				...(config.sentryForBackend.enableNodeProfiling ? [nodeProfilingIntegration()] : []),
+			],
+
+			// Performance Monitoring
+			tracesSampleRate: 1.0, //  Capture 100% of the transactions
+
+			// Set sampling rate for profiling - this is relative to tracesSampleRate
+			profilesSampleRate: 1.0,
+
+			maxBreadcrumbs: 0,
+
+			...config.sentryForBackend.options,
+		});
+	}
+
 	if (envOption.onlyServer) {
 		await server();
 	} else if (envOption.onlyQueue) {
diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index eb1901d069..4f333df791 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -165,7 +165,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.systemQueueWorker
 				.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
 				.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
-				.on('failed', (job, err) => systemLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('failed', (job, err) => systemLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
 				.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
 		}
@@ -214,7 +214,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.dbQueueWorker
 				.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
 				.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
-				.on('failed', (job, err) => dbLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('failed', (job, err) => dbLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
 				.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
 		}
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index 271ef80554..e21a5e90ab 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -93,7 +93,7 @@ export class ApiCallService implements OnApplicationShutdown {
 		}
 	}
 
-	#onExecError(ep: IEndpoint, data: any, err: Error): void {
+	#onExecError(ep: IEndpoint, data: any, err: Error, userId?: MiUser['id']): void {
 		if (err instanceof ApiError || err instanceof AuthenticationError) {
 			throw err;
 		} else {
@@ -108,10 +108,12 @@ export class ApiCallService implements OnApplicationShutdown {
 					id: errId,
 				},
 			});
-			console.error(err, errId);
 
 			if (this.config.sentryForBackend) {
 				Sentry.captureMessage(`Internal error occurred in ${ep.name}: ${err.message}`, {
+					user: {
+						id: userId,
+					},
 					extra: {
 						ep: ep.name,
 						ps: data,
@@ -410,9 +412,13 @@ export class ApiCallService implements OnApplicationShutdown {
 
 		// API invoking
 		if (this.config.sentryForBackend) {
-			return await Sentry.startSpan({ name: 'API: ' + ep.name }, () => ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err)));
+			return await Sentry.startSpan({
+				name: 'API: ' + ep.name,
+			}, () => ep.exec(data, user, token, file, request.ip, request.headers)
+				.catch((err: Error) => this.#onExecError(ep, data, err, user?.id)));
 		} else {
-			return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err));
+			return await ep.exec(data, user, token, file, request.ip, request.headers)
+				.catch((err: Error) => this.#onExecError(ep, data, err, user?.id));
 		}
 	}
 

From a697a7f97b401d1545a7f61694b2704b4d3ac9fc Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 6 Jun 2024 11:38:34 +0900
Subject: [PATCH 007/206] enhance(backend): improve sentry integration

---
 .../src/queue/QueueProcessorService.ts        | 63 ++++++++++++++++---
 1 file changed, 56 insertions(+), 7 deletions(-)

diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index 4f333df791..fdeb6a9518 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -165,7 +165,14 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.systemQueueWorker
 				.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
 				.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
-				.on('failed', (job, err) => systemLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('failed', (job, err) => {
+					systemLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					if (config.sentryForBackend) {
+						Sentry.captureMessage(`Queue: System: ${job?.name ?? '?'}`, {
+							extra: { job, err },
+						});
+					}
+				})
 				.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
 		}
@@ -214,7 +221,14 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.dbQueueWorker
 				.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
 				.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
-				.on('failed', (job, err) => dbLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('failed', (job, err) => {
+					dbLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					if (config.sentryForBackend) {
+						Sentry.captureMessage(`Queue: DB: ${job?.name ?? '?'}`, {
+							extra: { job, err },
+						});
+					}
+				})
 				.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
 		}
@@ -246,7 +260,14 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.deliverQueueWorker
 				.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
 				.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
-				.on('failed', (job, err) => deliverLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
+				.on('failed', (job, err) => {
+					deliverLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
+					if (config.sentryForBackend) {
+						Sentry.captureMessage('Queue: Deliver', {
+							extra: { job, err },
+						});
+					}
+				})
 				.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`));
 		}
@@ -278,7 +299,14 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.inboxQueueWorker
 				.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
 				.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
-				.on('failed', (job, err) => inboxLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }))
+				.on('failed', (job, err) => {
+					inboxLogger.error(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) });
+					if (config.sentryForBackend) {
+						Sentry.captureMessage('Queue: Inbox', {
+							extra: { job, err },
+						});
+					}
+				})
 				.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`));
 		}
@@ -310,7 +338,14 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.webhookDeliverQueueWorker
 				.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
 				.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
-				.on('failed', (job, err) => webhookLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
+				.on('failed', (job, err) => {
+					webhookLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
+					if (config.sentryForBackend) {
+						Sentry.captureMessage('Queue: WebhookDeliver', {
+							extra: { job, err },
+						});
+					}
+				})
 				.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`));
 		}
@@ -349,7 +384,14 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.relationshipQueueWorker
 				.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`))
 				.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`))
-				.on('failed', (job, err) => relationshipLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('failed', (job, err) => {
+					relationshipLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					if (config.sentryForBackend) {
+						Sentry.captureMessage(`Queue: Relationship: ${job?.name ?? '?'}`, {
+							extra: { job, err },
+						});
+					}
+				})
 				.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`));
 		}
@@ -382,7 +424,14 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.objectStorageQueueWorker
 				.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`))
 				.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`))
-				.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+				.on('failed', (job, err) => {
+					objectStorageLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					if (config.sentryForBackend) {
+						Sentry.captureMessage(`Queue: ObjectStorage: ${job?.name ?? '?'}`, {
+							extra: { job, err },
+						});
+					}
+				})
 				.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 				.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`));
 		}

From d55e638a231b380e733dc0ada3c3de410f918a93 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 6 Jun 2024 11:40:11 +0900
Subject: [PATCH 008/206] lint fixes

---
 packages/backend/src/NestLogger.ts | 2 +-
 packages/backend/src/boot/entry.ts | 2 +-
 packages/backend/src/postgres.ts   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/NestLogger.ts b/packages/backend/src/NestLogger.ts
index 80f1f7a024..d0be19664f 100644
--- a/packages/backend/src/NestLogger.ts
+++ b/packages/backend/src/NestLogger.ts
@@ -7,7 +7,7 @@ import { LoggerService } from '@nestjs/common';
 import Logger from '@/logger.js';
 
 const logger = new Logger('core', 'cyan');
-const nestLogger = logger.createSubLogger('nest', 'green', false);
+const nestLogger = logger.createSubLogger('nest', 'green');
 
 export class NestLogger implements LoggerService {
 	/**
diff --git a/packages/backend/src/boot/entry.ts b/packages/backend/src/boot/entry.ts
index 04c6ca9723..25375c3015 100644
--- a/packages/backend/src/boot/entry.ts
+++ b/packages/backend/src/boot/entry.ts
@@ -25,7 +25,7 @@ Error.stackTraceLimit = Infinity;
 EventEmitter.defaultMaxListeners = 128;
 
 const logger = new Logger('core', 'cyan');
-const clusterLogger = logger.createSubLogger('cluster', 'orange', false);
+const clusterLogger = logger.createSubLogger('cluster', 'orange');
 const ev = new Xev();
 
 //#region Events
diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts
index 2d14537bbb..aa2aa5e373 100644
--- a/packages/backend/src/postgres.ts
+++ b/packages/backend/src/postgres.ts
@@ -85,7 +85,7 @@ import { bindThis } from '@/decorators.js';
 
 export const dbLogger = new MisskeyLogger('db');
 
-const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
+const sqlLogger = dbLogger.createSubLogger('sql', 'gray');
 
 class MyCustomLogger implements Logger {
 	@bindThis

From 8f833d742fdb10f088479c78ad489461773a8b81 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 6 Jun 2024 11:51:31 +0900
Subject: [PATCH 009/206] enhance(backend): improve sentry integration

---
 .../backend/src/queue/QueueProcessorService.ts   | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index fdeb6a9518..6a87be021e 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -165,10 +165,10 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.systemQueueWorker
 				.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
 				.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
-				.on('failed', (job, err) => {
+				.on('failed', (job, err: Error) => {
 					systemLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
-						Sentry.captureMessage(`Queue: System: ${job?.name ?? '?'}`, {
+						Sentry.captureMessage(`Queue: System: ${job?.name ?? '?'}: ${err.message}`, {
 							extra: { job, err },
 						});
 					}
@@ -224,7 +224,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				.on('failed', (job, err) => {
 					dbLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
-						Sentry.captureMessage(`Queue: DB: ${job?.name ?? '?'}`, {
+						Sentry.captureMessage(`Queue: DB: ${job?.name ?? '?'}: ${err.message}`, {
 							extra: { job, err },
 						});
 					}
@@ -263,7 +263,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				.on('failed', (job, err) => {
 					deliverLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
 					if (config.sentryForBackend) {
-						Sentry.captureMessage('Queue: Deliver', {
+						Sentry.captureMessage(`Queue: Deliver: ${err.message}`, {
 							extra: { job, err },
 						});
 					}
@@ -302,7 +302,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				.on('failed', (job, err) => {
 					inboxLogger.error(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
-						Sentry.captureMessage('Queue: Inbox', {
+						Sentry.captureMessage(`Queue: Inbox: ${err.message}`, {
 							extra: { job, err },
 						});
 					}
@@ -341,7 +341,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				.on('failed', (job, err) => {
 					webhookLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
 					if (config.sentryForBackend) {
-						Sentry.captureMessage('Queue: WebhookDeliver', {
+						Sentry.captureMessage(`Queue: WebhookDeliver: ${err.message}`, {
 							extra: { job, err },
 						});
 					}
@@ -387,7 +387,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				.on('failed', (job, err) => {
 					relationshipLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
-						Sentry.captureMessage(`Queue: Relationship: ${job?.name ?? '?'}`, {
+						Sentry.captureMessage(`Queue: Relationship: ${job?.name ?? '?'}: ${err.message}`, {
 							extra: { job, err },
 						});
 					}
@@ -427,7 +427,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				.on('failed', (job, err) => {
 					objectStorageLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
-						Sentry.captureMessage(`Queue: ObjectStorage: ${job?.name ?? '?'}`, {
+						Sentry.captureMessage(`Queue: ObjectStorage: ${job?.name ?? '?'}: ${err.message}`, {
 							extra: { job, err },
 						});
 					}

From 00157864e95f4fae0c8aacff16ddac7f322ad68c Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Fri, 7 Jun 2024 09:00:01 +0900
Subject: [PATCH 010/206] =?UTF-8?q?fix(backend):=20=E3=83=81=E3=83=A3?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E7=94=9F=E6=88=90=E6=99=82=E3=81=ABinstance.?=
 =?UTF-8?q?isSuspended=E3=81=8C=E8=AA=AD=E3=81=BE=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=86=E5=95=8F=E9=A1=8C=E3=81=AE=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#13951)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): use sustensionState instead of isSuspended

* Update CHANGELOG.md
---
 CHANGELOG.md                                         | 2 +-
 packages/backend/src/core/chart/charts/federation.ts | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d4ef23d27..3cccb451d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,7 @@
 -
 
 ### Server
--
+- チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 
 
 ## 2024.5.0
diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts
index 5e4555ee96..c2329a2f73 100644
--- a/packages/backend/src/core/chart/charts/federation.ts
+++ b/packages/backend/src/core/chart/charts/federation.ts
@@ -47,7 +47,7 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di
 
 		const suspendedInstancesQuery = this.instancesRepository.createQueryBuilder('instance')
 			.select('instance.host')
-			.where('instance.isSuspended = true');
+			.where('instance.suspensionState != \'none\'');
 
 		const pubsubSubQuery = this.followingsRepository.createQueryBuilder('f')
 			.select('f.followerHost')
@@ -89,7 +89,7 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di
 				.select('COUNT(instance.id)')
 				.where(`instance.host IN (${ subInstancesQuery.getQuery() })`)
 				.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) })
-				.andWhere('instance.isSuspended = false')
+				.andWhere('instance.suspensionState = \'none\'')
 				.andWhere('instance.isNotResponding = false')
 				.getRawOne()
 				.then(x => parseInt(x.count, 10)),
@@ -97,7 +97,7 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di
 				.select('COUNT(instance.id)')
 				.where(`instance.host IN (${ pubInstancesQuery.getQuery() })`)
 				.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) })
-				.andWhere('instance.isSuspended = false')
+				.andWhere('instance.suspensionState = \'none\'')
 				.andWhere('instance.isNotResponding = false')
 				.getRawOne()
 				.then(x => parseInt(x.count, 10)),

From 859271613958cc4a9bc8fcd9616c3146c07a50a4 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 7 Jun 2024 13:15:37 +0900
Subject: [PATCH 011/206] enhance(backend): improve sentry integration

---
 packages/backend/src/queue/QueueProcessorService.ts | 7 +++++++
 packages/backend/src/server/api/ApiCallService.ts   | 1 +
 2 files changed, 8 insertions(+)

diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index 6a87be021e..7bfe1f4caa 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -169,6 +169,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 					systemLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: System: ${job?.name ?? '?'}: ${err.message}`, {
+							level: 'error',
 							extra: { job, err },
 						});
 					}
@@ -225,6 +226,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 					dbLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: DB: ${job?.name ?? '?'}: ${err.message}`, {
+							level: 'error',
 							extra: { job, err },
 						});
 					}
@@ -264,6 +266,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 					deliverLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: Deliver: ${err.message}`, {
+							level: 'error',
 							extra: { job, err },
 						});
 					}
@@ -303,6 +306,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 					inboxLogger.error(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: Inbox: ${err.message}`, {
+							level: 'error',
 							extra: { job, err },
 						});
 					}
@@ -342,6 +346,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 					webhookLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: WebhookDeliver: ${err.message}`, {
+							level: 'error',
 							extra: { job, err },
 						});
 					}
@@ -388,6 +393,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 					relationshipLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: Relationship: ${job?.name ?? '?'}: ${err.message}`, {
+							level: 'error',
 							extra: { job, err },
 						});
 					}
@@ -428,6 +434,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 					objectStorageLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: ObjectStorage: ${job?.name ?? '?'}: ${err.message}`, {
+							level: 'error',
 							extra: { job, err },
 						});
 					}
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index e21a5e90ab..166f9c8675 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -111,6 +111,7 @@ export class ApiCallService implements OnApplicationShutdown {
 
 			if (this.config.sentryForBackend) {
 				Sentry.captureMessage(`Internal error occurred in ${ep.name}: ${err.message}`, {
+					level: 'error',
 					user: {
 						id: userId,
 					},

From e0cf5b24022e22179355cf9439d3cfea6036daba Mon Sep 17 00:00:00 2001
From: Porlam Nicla <84321396+GrapeApple0@users.noreply.github.com>
Date: Fri, 7 Jun 2024 14:46:46 +0900
Subject: [PATCH 012/206] =?UTF-8?q?=E9=85=8D=E4=BF=A1=E5=81=9C=E6=AD=A2?=
 =?UTF-8?q?=E3=81=97=E3=81=9F=E3=82=A4=E3=83=B3=E3=82=B9=E3=82=BF=E3=83=B3?=
 =?UTF-8?q?=E3=82=B9=E4=B8=80=E8=A6=A7=E3=81=8C=E8=A6=8B=E3=82=8C=E3=81=AA?=
 =?UTF-8?q?=E3=81=8F=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#13945)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 配信停止したインスタンス一覧が見れなくなる問題を修正

* Update CHANGELOG.md
---
 CHANGELOG.md                                                  | 2 +-
 .../backend/src/server/api/endpoints/federation/instances.ts  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cccb451d5..f93811c606 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
 ## Unreleased
 
 ### General
--
+- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 
 ### Client
 -
diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts
index 4ef4315fb3..36f4bf5aa6 100644
--- a/packages/backend/src/server/api/endpoints/federation/instances.ts
+++ b/packages/backend/src/server/api/endpoints/federation/instances.ts
@@ -117,9 +117,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			if (typeof ps.suspended === 'boolean') {
 				if (ps.suspended) {
-					query.andWhere('instance.isSuspended = TRUE');
+					query.andWhere('instance.suspensionState != \'none\'');
 				} else {
-					query.andWhere('instance.isSuspended = FALSE');
+					query.andWhere('instance.suspensionState = \'none\'');
 				}
 			}
 

From 61fae45390283aee7ac582aa5303aae863de0f7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 8 Jun 2024 15:34:19 +0900
Subject: [PATCH 013/206] =?UTF-8?q?feat:=20=E9=80=9A=E5=A0=B1=E3=82=92?=
 =?UTF-8?q?=E5=8F=97=E3=81=91=E3=81=9F=E9=9A=9B=E3=81=AB=E3=83=A1=E3=83=BC?=
 =?UTF-8?q?=E3=83=AB=E3=81=BE=E3=81=9F=E3=81=AFWebhook=E3=81=A7=E9=80=9A?=
 =?UTF-8?q?=E7=9F=A5=E3=82=92=E9=80=81=E5=87=BA=E5=87=BA=E6=9D=A5=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#13758)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 通報を受けた際にメールまたはWebhookで通知を送出出来るようにする

* モデログに対応&エンドポイントを単一オブジェクトでのサポートに変更(API経由で大量に作るシチュエーションもないと思うので)

* fix spdx

* fix migration

* fix migration

* fix models

* add e2e webhook

* tweak

* fix modlog

* fix bugs

* add tests and fix bugs

* add tests and fix bugs

* add tests

* fix path

* regenerate locale

* 混入除去

* 混入除去

* add abuseReportResolved

* fix pnpm-lock.yaml

* add abuseReportResolved test

* fix bugs

* fix ui

* add tests

* fix CHANGELOG.md

* add tests

* add RoleService.getModeratorIds tests

* WebhookServiceをUserとSystemに分割

* fix CHANGELOG.md

* fix test

* insertOneを使う用に

* fix

* regenerate locales

* revert version

* separate webhook job queue

* fix

* :art:

* Update QueueProcessorService.ts

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |  94 +++
 locales/ja-JP.yml                             |  27 +
 ...1713656541000-abuse-report-notification.js |  62 ++
 .../core/AbuseReportNotificationService.ts    | 406 ++++++++++
 .../backend/src/core/AbuseReportService.ts    | 128 ++++
 packages/backend/src/core/CoreModule.ts       |  44 +-
 packages/backend/src/core/EmailService.ts     |   2 +
 .../backend/src/core/GlobalEventService.ts    |   4 +
 .../backend/src/core/NoteCreateService.ts     |  12 +-
 packages/backend/src/core/QueueModule.ts      |  38 +-
 packages/backend/src/core/QueueService.ts     |  63 +-
 packages/backend/src/core/RoleService.ts      |  30 +-
 .../backend/src/core/SystemWebhookService.ts  | 233 ++++++
 .../backend/src/core/UserBlockingService.ts   |   6 +-
 .../backend/src/core/UserFollowingService.ts  |  12 +-
 .../backend/src/core/UserWebhookService.ts    |  99 +++
 packages/backend/src/core/WebhookService.ts   |  97 ---
 .../src/core/activitypub/ApInboxService.ts    |  10 +-
 ...eportNotificationRecipientEntityService.ts |  88 +++
 .../entities/SystemWebhookEntityService.ts    |  74 ++
 packages/backend/src/di-symbols.ts            |   2 +
 packages/backend/src/misc/json-schema.ts      |  28 +-
 .../AbuseReportNotificationRecipient.ts       | 100 +++
 .../backend/src/models/RepositoryModule.ts    |  98 ++-
 packages/backend/src/models/SystemWebhook.ts  |  98 +++
 packages/backend/src/models/_.ts              |   6 +
 .../abuse-report-notification-recipient.ts    |  50 ++
 .../src/models/json-schema/system-webhook.ts  |  54 ++
 packages/backend/src/postgres.ts              |   8 +-
 .../backend/src/queue/QueueProcessorModule.ts |   6 +-
 .../src/queue/QueueProcessorService.ts        | 153 ++--
 packages/backend/src/queue/const.ts           |   3 +-
 .../SystemWebhookDeliverProcessorService.ts   |  87 +++
 ... => UserWebhookDeliverProcessorService.ts} |   6 +-
 packages/backend/src/queue/types.ts           |  12 +-
 .../backend/src/server/api/EndpointsModule.ts |  42 +-
 packages/backend/src/server/api/endpoints.ts  |  38 +-
 .../notification-recipient/create.ts          | 122 +++
 .../notification-recipient/delete.ts          |  44 ++
 .../notification-recipient/list.ts            |  55 ++
 .../notification-recipient/show.ts            |  64 ++
 .../notification-recipient/update.ts          | 128 ++++
 .../server/api/endpoints/admin/queue/stats.ts |   5 +-
 .../admin/resolve-abuse-user-report.ts        |  53 +-
 .../endpoints/admin/system-webhook/create.ts  |  85 +++
 .../endpoints/admin/system-webhook/delete.ts  |  44 ++
 .../endpoints/admin/system-webhook/list.ts    |  60 ++
 .../endpoints/admin/system-webhook/show.ts    |  62 ++
 .../endpoints/admin/system-webhook/update.ts  |  91 +++
 .../api/endpoints/users/report-abuse.ts       |  54 +-
 .../src/server/web/ClientServerService.ts     |  17 +-
 packages/backend/src/types.ts                 |  32 +
 .../backend/test/e2e/synalio/abuse-report.ts  | 401 ++++++++++
 .../unit/AbuseReportNotificationService.ts    | 343 +++++++++
 packages/backend/test/unit/RoleService.ts     | 132 +++-
 .../backend/test/unit/SystemWebhookService.ts | 515 +++++++++++++
 packages/frontend/src/components/MkButton.vue |   2 +
 .../frontend/src/components/MkDivider.vue     |  32 +
 packages/frontend/src/components/MkSwitch.vue |   5 +-
 .../components/MkSystemWebhookEditor.impl.ts  |  45 ++
 .../src/components/MkSystemWebhookEditor.vue  | 217 ++++++
 .../notification-recipient.editor.vue         | 307 ++++++++
 .../notification-recipient.item.vue           | 114 +++
 .../abuse-report/notification-recipient.vue   | 176 +++++
 packages/frontend/src/pages/admin/abuses.vue  |  85 ++-
 packages/frontend/src/pages/admin/index.vue   |   5 +
 .../src/pages/admin/modlog.ModLog.vue         |  48 +-
 .../src/pages/admin/system-webhook.item.vue   | 117 +++
 .../src/pages/admin/system-webhook.vue        |  96 +++
 packages/frontend/src/router/definition.ts    |   8 +
 packages/misskey-js/etc/misskey-js.api.md     | 105 ++-
 .../misskey-js/src/autogen/apiClientJSDoc.ts  | 120 +++
 packages/misskey-js/src/autogen/endpoint.ts   |  28 +
 packages/misskey-js/src/autogen/entities.ts   |  18 +
 packages/misskey-js/src/autogen/models.ts     |   2 +
 packages/misskey-js/src/autogen/types.ts      | 693 ++++++++++++++++++
 packages/misskey-js/src/consts.ts             |  26 +
 packages/misskey-js/src/entities.ts           |  19 +-
 79 files changed, 6527 insertions(+), 369 deletions(-)
 create mode 100644 packages/backend/migration/1713656541000-abuse-report-notification.js
 create mode 100644 packages/backend/src/core/AbuseReportNotificationService.ts
 create mode 100644 packages/backend/src/core/AbuseReportService.ts
 create mode 100644 packages/backend/src/core/SystemWebhookService.ts
 create mode 100644 packages/backend/src/core/UserWebhookService.ts
 delete mode 100644 packages/backend/src/core/WebhookService.ts
 create mode 100644 packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts
 create mode 100644 packages/backend/src/core/entities/SystemWebhookEntityService.ts
 create mode 100644 packages/backend/src/models/AbuseReportNotificationRecipient.ts
 create mode 100644 packages/backend/src/models/SystemWebhook.ts
 create mode 100644 packages/backend/src/models/json-schema/abuse-report-notification-recipient.ts
 create mode 100644 packages/backend/src/models/json-schema/system-webhook.ts
 create mode 100644 packages/backend/src/queue/processors/SystemWebhookDeliverProcessorService.ts
 rename packages/backend/src/queue/processors/{WebhookDeliverProcessorService.ts => UserWebhookDeliverProcessorService.ts} (92%)
 create mode 100644 packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/create.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/delete.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/list.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/show.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/update.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/system-webhook/delete.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/system-webhook/list.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/system-webhook/show.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts
 create mode 100644 packages/backend/test/e2e/synalio/abuse-report.ts
 create mode 100644 packages/backend/test/unit/AbuseReportNotificationService.ts
 create mode 100644 packages/backend/test/unit/SystemWebhookService.ts
 create mode 100644 packages/frontend/src/components/MkDivider.vue
 create mode 100644 packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
 create mode 100644 packages/frontend/src/components/MkSystemWebhookEditor.vue
 create mode 100644 packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
 create mode 100644 packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
 create mode 100644 packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
 create mode 100644 packages/frontend/src/pages/admin/system-webhook.item.vue
 create mode 100644 packages/frontend/src/pages/admin/system-webhook.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f93811c606..8b70636d82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## Unreleased
 
 ### General
+- Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 
 ### Client
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 0b1b86d373..acdc1fc421 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -9305,6 +9305,10 @@ export interface Locale extends ILocale {
          * Webhookを作成
          */
         "createWebhook": string;
+        /**
+         * Webhookを編集
+         */
+        "modifyWebhook": string;
         /**
          * 名前
          */
@@ -9351,6 +9355,72 @@ export interface Locale extends ILocale {
              */
             "mention": string;
         };
+        "_systemEvents": {
+            /**
+             * ユーザーから通報があったとき
+             */
+            "abuseReport": string;
+            /**
+             * ユーザーからの通報を処理したとき
+             */
+            "abuseReportResolved": string;
+        };
+        /**
+         * Webhookを削除しますか?
+         */
+        "deleteConfirm": string;
+    };
+    "_abuseReport": {
+        "_notificationRecipient": {
+            /**
+             * 通報の通知先を追加
+             */
+            "createRecipient": string;
+            /**
+             * 通報の通知先を編集
+             */
+            "modifyRecipient": string;
+            /**
+             * 通知先の種類
+             */
+            "recipientType": string;
+            "_recipientType": {
+                /**
+                 * メール
+                 */
+                "mail": string;
+                /**
+                 * Webhook
+                 */
+                "webhook": string;
+                "_captions": {
+                    /**
+                     * モデレーター権限を持つユーザーのメールアドレスに通知を送ります(通報を受けた時のみ)
+                     */
+                    "mail": string;
+                    /**
+                     * 指定したSystemWebhookに通知を送ります(通報を受けた時と通報を解決した時にそれぞれ発信)
+                     */
+                    "webhook": string;
+                };
+            };
+            /**
+             * キーワード
+             */
+            "keywords": string;
+            /**
+             * 通知先ユーザー
+             */
+            "notifiedUser": string;
+            /**
+             * 使用するWebhook
+             */
+            "notifiedWebhook": string;
+            /**
+             * 通知先を削除しますか?
+             */
+            "deleteConfirm": string;
+        };
     };
     "_moderationLogTypes": {
         /**
@@ -9497,6 +9567,30 @@ export interface Locale extends ILocale {
          * ユーザーのバナーを解除
          */
         "unsetUserBanner": string;
+        /**
+         * SystemWebhookを作成
+         */
+        "createSystemWebhook": string;
+        /**
+         * SystemWebhookを更新
+         */
+        "updateSystemWebhook": string;
+        /**
+         * SystemWebhookを削除
+         */
+        "deleteSystemWebhook": string;
+        /**
+         * 通報の通知先を作成
+         */
+        "createAbuseReportNotificationRecipient": string;
+        /**
+         * 通報の通知先を更新
+         */
+        "updateAbuseReportNotificationRecipient": string;
+        /**
+         * 通報の通知先を削除
+         */
+        "deleteAbuseReportNotificationRecipient": string;
     };
     "_fileViewer": {
         /**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index a89cfbd843..3ac1ce82a3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2468,6 +2468,7 @@ _drivecleaner:
 
 _webhookSettings:
   createWebhook: "Webhookを作成"
+  modifyWebhook: "Webhookを編集"
   name: "名前"
   secret: "シークレット"
   events: "Webhookを実行するタイミング"
@@ -2480,6 +2481,26 @@ _webhookSettings:
     renote: "Renoteされたとき"
     reaction: "リアクションがあったとき"
     mention: "メンションされたとき"
+  _systemEvents:
+    abuseReport: "ユーザーから通報があったとき"
+    abuseReportResolved: "ユーザーからの通報を処理したとき"
+  deleteConfirm: "Webhookを削除しますか?"
+
+_abuseReport:
+  _notificationRecipient:
+    createRecipient: "通報の通知先を追加"
+    modifyRecipient: "通報の通知先を編集"
+    recipientType: "通知先の種類"
+    _recipientType:
+      mail: "メール"
+      webhook: "Webhook"
+      _captions:
+        mail: "モデレーター権限を持つユーザーのメールアドレスに通知を送ります(通報を受けた時のみ)"
+        webhook: "指定したSystemWebhookに通知を送ります(通報を受けた時と通報を解決した時にそれぞれ発信)"
+    keywords: "キーワード"
+    notifiedUser: "通知先ユーザー"
+    notifiedWebhook: "使用するWebhook"
+    deleteConfirm: "通知先を削除しますか?"
 
 _moderationLogTypes:
   createRole: "ロールを作成"
@@ -2518,6 +2539,12 @@ _moderationLogTypes:
   deleteAvatarDecoration: "アイコンデコレーションを削除"
   unsetUserAvatar: "ユーザーのアイコンを解除"
   unsetUserBanner: "ユーザーのバナーを解除"
+  createSystemWebhook: "SystemWebhookを作成"
+  updateSystemWebhook: "SystemWebhookを更新"
+  deleteSystemWebhook: "SystemWebhookを削除"
+  createAbuseReportNotificationRecipient: "通報の通知先を作成"
+  updateAbuseReportNotificationRecipient: "通報の通知先を更新"
+  deleteAbuseReportNotificationRecipient: "通報の通知先を削除"
 
 _fileViewer:
   title: "ファイルの詳細"
diff --git a/packages/backend/migration/1713656541000-abuse-report-notification.js b/packages/backend/migration/1713656541000-abuse-report-notification.js
new file mode 100644
index 0000000000..4a754f81e2
--- /dev/null
+++ b/packages/backend/migration/1713656541000-abuse-report-notification.js
@@ -0,0 +1,62 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class AbuseReportNotification1713656541000 {
+	name = 'AbuseReportNotification1713656541000'
+
+	async up(queryRunner) {
+		await queryRunner.query(`
+			CREATE TABLE "system_webhook" (
+				"id" varchar(32) NOT NULL,
+				"isActive" boolean NOT NULL DEFAULT true,
+				"updatedAt" timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
+				"latestSentAt" timestamp with time zone NULL DEFAULT NULL,
+				"latestStatus" integer NULL DEFAULT NULL,
+				"name" varchar(255) NOT NULL,
+				"on" varchar(128) [] NOT NULL DEFAULT '{}'::character varying[],
+				"url" varchar(1024) NOT NULL,
+				"secret" varchar(1024) NOT NULL,
+				CONSTRAINT "PK_system_webhook_id" PRIMARY KEY ("id")
+			);
+			CREATE INDEX "IDX_system_webhook_isActive" ON "system_webhook" ("isActive");
+			CREATE INDEX "IDX_system_webhook_on" ON "system_webhook" USING gin ("on");
+
+			CREATE TABLE "abuse_report_notification_recipient" (
+				"id" varchar(32) NOT NULL,
+				"isActive" boolean NOT NULL DEFAULT true,
+				"updatedAt" timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
+				"name" varchar(255) NOT NULL,
+				"method" varchar(64) NOT NULL,
+				"userId" varchar(32) NULL DEFAULT NULL,
+				"systemWebhookId" varchar(32) NULL DEFAULT NULL,
+				CONSTRAINT "PK_abuse_report_notification_recipient_id" PRIMARY KEY ("id"),
+				CONSTRAINT "FK_abuse_report_notification_recipient_userId1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION,
+				CONSTRAINT "FK_abuse_report_notification_recipient_userId2" FOREIGN KEY ("userId") REFERENCES "user_profile"("userId") ON DELETE CASCADE ON UPDATE NO ACTION,
+				CONSTRAINT "FK_abuse_report_notification_recipient_systemWebhookId" FOREIGN KEY ("systemWebhookId") REFERENCES "system_webhook"("id") ON DELETE CASCADE ON UPDATE NO ACTION
+			);
+			CREATE INDEX "IDX_abuse_report_notification_recipient_isActive" ON "abuse_report_notification_recipient" ("isActive");
+			CREATE INDEX "IDX_abuse_report_notification_recipient_method" ON "abuse_report_notification_recipient" ("method");
+			CREATE INDEX "IDX_abuse_report_notification_recipient_userId" ON "abuse_report_notification_recipient" ("userId");
+			CREATE INDEX "IDX_abuse_report_notification_recipient_systemWebhookId" ON "abuse_report_notification_recipient" ("systemWebhookId");
+		`);
+	}
+
+	async down(queryRunner) {
+		await queryRunner.query(`
+			ALTER TABLE "abuse_report_notification_recipient" DROP CONSTRAINT "FK_abuse_report_notification_recipient_userId1";
+			ALTER TABLE "abuse_report_notification_recipient" DROP CONSTRAINT "FK_abuse_report_notification_recipient_userId2";
+			ALTER TABLE "abuse_report_notification_recipient" DROP CONSTRAINT "FK_abuse_report_notification_recipient_systemWebhookId";
+			DROP INDEX "IDX_abuse_report_notification_recipient_isActive";
+			DROP INDEX "IDX_abuse_report_notification_recipient_method";
+			DROP INDEX "IDX_abuse_report_notification_recipient_userId";
+			DROP INDEX "IDX_abuse_report_notification_recipient_systemWebhookId";
+			DROP TABLE "abuse_report_notification_recipient";
+
+			DROP INDEX "IDX_system_webhook_isActive";
+			DROP INDEX "IDX_system_webhook_on";
+			DROP TABLE "system_webhook";
+		`);
+	}
+}
diff --git a/packages/backend/src/core/AbuseReportNotificationService.ts b/packages/backend/src/core/AbuseReportNotificationService.ts
new file mode 100644
index 0000000000..df752afcd8
--- /dev/null
+++ b/packages/backend/src/core/AbuseReportNotificationService.ts
@@ -0,0 +1,406 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable, type OnApplicationShutdown } from '@nestjs/common';
+import { Brackets, In, IsNull, Not } from 'typeorm';
+import * as Redis from 'ioredis';
+import sanitizeHtml from 'sanitize-html';
+import { DI } from '@/di-symbols.js';
+import { bindThis } from '@/decorators.js';
+import { GlobalEvents, GlobalEventService } from '@/core/GlobalEventService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
+import type {
+	AbuseReportNotificationRecipientRepository,
+	MiAbuseReportNotificationRecipient,
+	MiAbuseUserReport,
+	MiUser,
+} from '@/models/_.js';
+import { EmailService } from '@/core/EmailService.js';
+import { MetaService } from '@/core/MetaService.js';
+import { RoleService } from '@/core/RoleService.js';
+import { RecipientMethod } from '@/models/AbuseReportNotificationRecipient.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+import { IdService } from './IdService.js';
+
+@Injectable()
+export class AbuseReportNotificationService implements OnApplicationShutdown {
+	constructor(
+		@Inject(DI.abuseReportNotificationRecipientRepository)
+		private abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository,
+		@Inject(DI.redisForSub)
+		private redisForSub: Redis.Redis,
+		private idService: IdService,
+		private roleService: RoleService,
+		private systemWebhookService: SystemWebhookService,
+		private emailService: EmailService,
+		private metaService: MetaService,
+		private moderationLogService: ModerationLogService,
+		private globalEventService: GlobalEventService,
+	) {
+		this.redisForSub.on('message', this.onMessage);
+	}
+
+	/**
+	 * 管理者用Redisイベントを用いて{@link abuseReports}の内容を管理者各位に通知する.
+	 * 通知先ユーザは{@link RoleService.getModeratorIds}の取得結果に依る.
+	 *
+	 * @see RoleService.getModeratorIds
+	 * @see GlobalEventService.publishAdminStream
+	 */
+	@bindThis
+	public async notifyAdminStream(abuseReports: MiAbuseUserReport[]) {
+		if (abuseReports.length <= 0) {
+			return;
+		}
+
+		const moderatorIds = await this.roleService.getModeratorIds(true, true);
+
+		for (const moderatorId of moderatorIds) {
+			for (const abuseReport of abuseReports) {
+				this.globalEventService.publishAdminStream(
+					moderatorId,
+					'newAbuseUserReport',
+					{
+						id: abuseReport.id,
+						targetUserId: abuseReport.targetUserId,
+						reporterId: abuseReport.reporterId,
+						comment: abuseReport.comment,
+					},
+				);
+			}
+		}
+	}
+
+	/**
+	 * Mailを用いて{@link abuseReports}の内容を管理者各位に通知する.
+	 * メールアドレスの送信先は以下の通り.
+	 * - モデレータ権限所有者ユーザ(設定画面からメールアドレスの設定を行っているユーザに限る)
+	 * - metaテーブルに設定されているメールアドレス
+	 *
+	 * @see EmailService.sendEmail
+	 */
+	@bindThis
+	public async notifyMail(abuseReports: MiAbuseUserReport[]) {
+		if (abuseReports.length <= 0) {
+			return;
+		}
+
+		const recipientEMailAddresses = await this.fetchEMailRecipients().then(it => it
+			.filter(it => it.isActive && it.userProfile?.emailVerified)
+			.map(it => it.userProfile?.email)
+			.filter(isNotNull),
+		);
+
+		// 送信先の鮮度を保つため、毎回取得する
+		const meta = await this.metaService.fetch(true);
+		recipientEMailAddresses.push(
+			...(meta.email ? [meta.email] : []),
+		);
+
+		if (recipientEMailAddresses.length <= 0) {
+			return;
+		}
+
+		for (const mailAddress of recipientEMailAddresses) {
+			await Promise.all(
+				abuseReports.map(it => {
+					// TODO: 送信処理はJobQueue化したい
+					return this.emailService.sendEmail(
+						mailAddress,
+						'New Abuse Report',
+						sanitizeHtml(it.comment),
+						sanitizeHtml(it.comment),
+					);
+				}),
+			);
+		}
+	}
+
+	/**
+	 * SystemWebhookを用いて{@link abuseReports}の内容を管理者各位に通知する.
+	 * ここではJobQueueへのエンキューのみを行うため、即時実行されない.
+	 *
+	 * @see SystemWebhookService.enqueueSystemWebhook
+	 */
+	@bindThis
+	public async notifySystemWebhook(
+		abuseReports: MiAbuseUserReport[],
+		type: 'abuseReport' | 'abuseReportResolved',
+	) {
+		if (abuseReports.length <= 0) {
+			return;
+		}
+
+		const recipientWebhookIds = await this.fetchWebhookRecipients()
+			.then(it => it
+				.filter(it => it.isActive && it.systemWebhookId && it.method === 'webhook')
+				.map(it => it.systemWebhookId)
+				.filter(isNotNull));
+		for (const webhookId of recipientWebhookIds) {
+			await Promise.all(
+				abuseReports.map(it => {
+					return this.systemWebhookService.enqueueSystemWebhook(
+						webhookId,
+						type,
+						it,
+					);
+				}),
+			);
+		}
+	}
+
+	/**
+	 * 通報の通知先一覧を取得する.
+	 *
+	 * @param {Object} [params] クエリの取得条件
+	 * @param {Object} [params.method] 取得する通知先の通知方法
+	 * @param {Object} [opts] 動作時の詳細なオプション
+	 * @param {boolean} [opts.removeUnauthorized] 副作用としてモデレータ権限を持たない送信先ユーザをDBから削除するかどうか(default: true)
+	 * @param {boolean} [opts.joinUser] 通知先のユーザ情報をJOINするかどうか(default: false)
+	 * @param {boolean} [opts.joinSystemWebhook] 通知先のSystemWebhook情報をJOINするかどうか(default: false)
+	 * @see removeUnauthorizedRecipientUsers
+	 */
+	@bindThis
+	public async fetchRecipients(
+		params?: {
+			ids?: MiAbuseReportNotificationRecipient['id'][],
+			method?: RecipientMethod[],
+		},
+		opts?: {
+			removeUnauthorized?: boolean,
+			joinUser?: boolean,
+			joinSystemWebhook?: boolean,
+		},
+	): Promise<MiAbuseReportNotificationRecipient[]> {
+		const query = this.abuseReportNotificationRecipientRepository.createQueryBuilder('recipient');
+
+		if (opts?.joinUser) {
+			query.innerJoinAndSelect('user', 'user', 'recipient.userId = user.id');
+			query.innerJoinAndSelect('recipient.userProfile', 'userProfile');
+		}
+
+		if (opts?.joinSystemWebhook) {
+			query.innerJoinAndSelect('recipient.systemWebhook', 'systemWebhook');
+		}
+
+		if (params?.ids) {
+			query.andWhere({ id: In(params.ids) });
+		}
+
+		if (params?.method) {
+			query.andWhere(new Brackets(qb => {
+				if (params.method?.includes('email')) {
+					qb.orWhere({ method: 'email', userId: Not(IsNull()) });
+				}
+				if (params.method?.includes('webhook')) {
+					qb.orWhere({ method: 'webhook', userId: IsNull() });
+				}
+			}));
+		}
+
+		const recipients = await query.getMany();
+		if (recipients.length <= 0) {
+			return [];
+		}
+
+		// アサイン有効期限切れはイベントで拾えないので、このタイミングでチェック及び削除(オプション)
+		return (opts?.removeUnauthorized ?? true)
+			? await this.removeUnauthorizedRecipientUsers(recipients)
+			: recipients;
+	}
+
+	/**
+	 * EMailの通知先一覧を取得する.
+	 * リレーション先の{@link MiUser}および{@link MiUserProfile}も同時に取得する.
+	 *
+	 * @param {Object} [opts]
+	 * @param {boolean} [opts.removeUnauthorized] 副作用としてモデレータ権限を持たない送信先ユーザをDBから削除するかどうか(default: true)
+	 * @see removeUnauthorizedRecipientUsers
+	 */
+	@bindThis
+	public async fetchEMailRecipients(opts?: {
+		removeUnauthorized?: boolean
+	}): Promise<MiAbuseReportNotificationRecipient[]> {
+		return this.fetchRecipients({ method: ['email'] }, { joinUser: true, ...opts });
+	}
+
+	/**
+	 * Webhookの通知先一覧を取得する.
+	 * リレーション先の{@link MiSystemWebhook}も同時に取得する.
+	 */
+	@bindThis
+	public fetchWebhookRecipients(): Promise<MiAbuseReportNotificationRecipient[]> {
+		return this.fetchRecipients({ method: ['webhook'] }, { joinSystemWebhook: true });
+	}
+
+	/**
+	 * 通知先を作成する.
+	 */
+	@bindThis
+	public async createRecipient(
+		params: {
+			isActive: MiAbuseReportNotificationRecipient['isActive'];
+			name: MiAbuseReportNotificationRecipient['name'];
+			method: MiAbuseReportNotificationRecipient['method'];
+			userId: MiAbuseReportNotificationRecipient['userId'];
+			systemWebhookId: MiAbuseReportNotificationRecipient['systemWebhookId'];
+		},
+		updater: MiUser,
+	): Promise<MiAbuseReportNotificationRecipient> {
+		const id = this.idService.gen();
+		await this.abuseReportNotificationRecipientRepository.insert({
+			...params,
+			id,
+		});
+
+		const created = await this.abuseReportNotificationRecipientRepository.findOneByOrFail({ id: id });
+
+		this.moderationLogService
+			.log(updater, 'createAbuseReportNotificationRecipient', {
+				recipientId: id,
+				recipient: created,
+			})
+			.then();
+
+		return created;
+	}
+
+	/**
+	 * 通知先を更新する.
+	 */
+	@bindThis
+	public async updateRecipient(
+		params: {
+			id: MiAbuseReportNotificationRecipient['id'];
+			isActive: MiAbuseReportNotificationRecipient['isActive'];
+			name: MiAbuseReportNotificationRecipient['name'];
+			method: MiAbuseReportNotificationRecipient['method'];
+			userId: MiAbuseReportNotificationRecipient['userId'];
+			systemWebhookId: MiAbuseReportNotificationRecipient['systemWebhookId'];
+		},
+		updater: MiUser,
+	): Promise<MiAbuseReportNotificationRecipient> {
+		const beforeEntity = await this.abuseReportNotificationRecipientRepository.findOneByOrFail({ id: params.id });
+
+		await this.abuseReportNotificationRecipientRepository.update(params.id, {
+			isActive: params.isActive,
+			updatedAt: new Date(),
+			name: params.name,
+			method: params.method,
+			userId: params.userId,
+			systemWebhookId: params.systemWebhookId,
+		});
+
+		const afterEntity = await this.abuseReportNotificationRecipientRepository.findOneByOrFail({ id: params.id });
+
+		this.moderationLogService
+			.log(updater, 'updateAbuseReportNotificationRecipient', {
+				recipientId: params.id,
+				before: beforeEntity,
+				after: afterEntity,
+			})
+			.then();
+
+		return afterEntity;
+	}
+
+	/**
+	 * 通知先を削除する.
+	 */
+	@bindThis
+	public async deleteRecipient(
+		id: MiAbuseReportNotificationRecipient['id'],
+		updater: MiUser,
+	) {
+		const entity = await this.abuseReportNotificationRecipientRepository.findBy({ id });
+
+		await this.abuseReportNotificationRecipientRepository.delete(id);
+
+		this.moderationLogService
+			.log(updater, 'deleteAbuseReportNotificationRecipient', {
+				recipientId: id,
+				recipient: entity,
+			})
+			.then();
+	}
+
+	/**
+	 * モデレータ権限を持たない(*1)通知先ユーザを削除する.
+	 *
+	 * *1: 以下の両方を満たすものの事を言う
+	 * - 通知先にユーザIDが設定されている
+	 * - 付与ロールにモデレータ権限がない or アサインの有効期限が切れている
+	 *
+	 * @param recipients 通知先一覧の配列
+	 * @returns {@lisk recipients}からモデレータ権限を持たない通知先を削除した配列
+	 */
+	@bindThis
+	private async removeUnauthorizedRecipientUsers(recipients: MiAbuseReportNotificationRecipient[]): Promise<MiAbuseReportNotificationRecipient[]> {
+		const userRecipients = recipients.filter(it => it.userId !== null);
+		const recipientUserIds = new Set(userRecipients.map(it => it.userId).filter(isNotNull));
+		if (recipientUserIds.size <= 0) {
+			// ユーザが通知先として設定されていない場合、この関数での処理を行うべきレコードが無い
+			return recipients;
+		}
+
+		// モデレータ権限の有無で通知先設定を振り分ける
+		const authorizedUserIds = await this.roleService.getModeratorIds(true, true);
+		const authorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
+		const unauthorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
+		for (const recipient of userRecipients) {
+			// eslint-disable-next-line
+			if (authorizedUserIds.includes(recipient.userId!)) {
+				authorizedUserRecipients.push(recipient);
+			} else {
+				unauthorizedUserRecipients.push(recipient);
+			}
+		}
+
+		// モデレータ権限を持たない通知先をDBから削除する
+		if (unauthorizedUserRecipients.length > 0) {
+			await this.abuseReportNotificationRecipientRepository.delete(unauthorizedUserRecipients.map(it => it.id));
+		}
+		const nonUserRecipients = recipients.filter(it => it.userId === null);
+		return [...nonUserRecipients, ...authorizedUserRecipients].sort((a, b) => a.id.localeCompare(b.id));
+	}
+
+	@bindThis
+	private async onMessage(_: string, data: string): Promise<void> {
+		const obj = JSON.parse(data);
+		if (obj.channel !== 'internal') {
+			return;
+		}
+
+		const { type } = obj.message as GlobalEvents['internal']['payload'];
+		switch (type) {
+			case 'roleUpdated':
+			case 'roleDeleted':
+			case 'userRoleUnassigned': {
+				// 場合によってはキャッシュ更新よりも先にここが呼ばれてしまう可能性があるのでnextTickで遅延実行
+				process.nextTick(async () => {
+					const recipients = await this.abuseReportNotificationRecipientRepository.findBy({
+						userId: Not(IsNull()),
+					});
+					await this.removeUnauthorizedRecipientUsers(recipients);
+				});
+				break;
+			}
+			default: {
+				break;
+			}
+		}
+	}
+
+	@bindThis
+	public dispose(): void {
+		this.redisForSub.off('message', this.onMessage);
+	}
+
+	@bindThis
+	public onApplicationShutdown(signal?: string | undefined): void {
+		this.dispose();
+	}
+}
diff --git a/packages/backend/src/core/AbuseReportService.ts b/packages/backend/src/core/AbuseReportService.ts
new file mode 100644
index 0000000000..69c51509ba
--- /dev/null
+++ b/packages/backend/src/core/AbuseReportService.ts
@@ -0,0 +1,128 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { In } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import { bindThis } from '@/decorators.js';
+import type { AbuseUserReportsRepository, MiAbuseUserReport, MiUser, UsersRepository } from '@/models/_.js';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+import { QueueService } from '@/core/QueueService.js';
+import { InstanceActorService } from '@/core/InstanceActorService.js';
+import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { IdService } from './IdService.js';
+
+@Injectable()
+export class AbuseReportService {
+	constructor(
+		@Inject(DI.abuseUserReportsRepository)
+		private abuseUserReportsRepository: AbuseUserReportsRepository,
+		@Inject(DI.usersRepository)
+		private usersRepository: UsersRepository,
+		private idService: IdService,
+		private abuseReportNotificationService: AbuseReportNotificationService,
+		private queueService: QueueService,
+		private instanceActorService: InstanceActorService,
+		private apRendererService: ApRendererService,
+		private moderationLogService: ModerationLogService,
+	) {
+	}
+
+	/**
+	 * ユーザからの通報をDBに記録し、その内容を下記の手段で管理者各位に通知する.
+	 * - 管理者用Redisイベント
+	 * - EMail(モデレータ権限所有者ユーザ+metaテーブルに設定されているメールアドレス)
+	 * - SystemWebhook
+	 *
+	 * @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
+	 * @see AbuseReportNotificationService.notify
+	 */
+	@bindThis
+	public async report(params: {
+		targetUserId: MiAbuseUserReport['targetUserId'],
+		targetUserHost: MiAbuseUserReport['targetUserHost'],
+		reporterId: MiAbuseUserReport['reporterId'],
+		reporterHost: MiAbuseUserReport['reporterHost'],
+		comment: string,
+	}[]) {
+		const entities = params.map(param => {
+			return {
+				id: this.idService.gen(),
+				targetUserId: param.targetUserId,
+				targetUserHost: param.targetUserHost,
+				reporterId: param.reporterId,
+				reporterHost: param.reporterHost,
+				comment: param.comment,
+			};
+		});
+
+		const reports = Array.of<MiAbuseUserReport>();
+		for (const entity of entities) {
+			const report = await this.abuseUserReportsRepository.insertOne(entity);
+			reports.push(report);
+		}
+
+		return Promise.all([
+			this.abuseReportNotificationService.notifyAdminStream(reports),
+			this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReport'),
+			this.abuseReportNotificationService.notifyMail(reports),
+		]);
+	}
+
+	/**
+	 * 通報を解決し、その内容を下記の手段で管理者各位に通知する.
+	 * - SystemWebhook
+	 *
+	 * @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
+	 * @param operator 通報を処理したユーザ
+	 * @see AbuseReportNotificationService.notify
+	 */
+	@bindThis
+	public async resolve(
+		params: {
+			reportId: string;
+			forward: boolean;
+		}[],
+		operator: MiUser,
+	) {
+		const paramsMap = new Map(params.map(it => [it.reportId, it]));
+		const reports = await this.abuseUserReportsRepository.findBy({
+			id: In(params.map(it => it.reportId)),
+		});
+
+		for (const report of reports) {
+			// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+			const ps = paramsMap.get(report.id)!;
+
+			await this.abuseUserReportsRepository.update(report.id, {
+				resolved: true,
+				assigneeId: operator.id,
+				forwarded: ps.forward && report.targetUserHost !== null,
+			});
+
+			if (ps.forward && report.targetUserHost != null) {
+				const actor = await this.instanceActorService.getInstanceActor();
+				const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
+
+				// eslint-disable-next-line
+				const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
+				const contextAssignedFlag = this.apRendererService.addContext(flag);
+				this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
+			}
+
+			this.moderationLogService
+				.log(operator, 'resolveAbuseReport', {
+					reportId: report.id,
+					report: report,
+					forwarded: ps.forward && report.targetUserHost !== null,
+				})
+				.then();
+		}
+
+		return this.abuseUserReportsRepository.findBy({ id: In(reports.map(it => it.id)) })
+			.then(reports => this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReportResolved'));
+	}
+}
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index be80df6f1c..b5b34487ec 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -5,6 +5,13 @@
 
 import { Module } from '@nestjs/common';
 import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
+import { AbuseReportService } from '@/core/AbuseReportService.js';
+import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
+import {
+	AbuseReportNotificationRecipientEntityService,
+} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
 import { AccountMoveService } from './AccountMoveService.js';
 import { AccountUpdateService } from './AccountUpdateService.js';
 import { AiService } from './AiService.js';
@@ -56,7 +63,7 @@ import { UserMutingService } from './UserMutingService.js';
 import { UserSuspendService } from './UserSuspendService.js';
 import { UserAuthService } from './UserAuthService.js';
 import { VideoProcessingService } from './VideoProcessingService.js';
-import { WebhookService } from './WebhookService.js';
+import { UserWebhookService } from './UserWebhookService.js';
 import { ProxyAccountService } from './ProxyAccountService.js';
 import { UtilityService } from './UtilityService.js';
 import { FileInfoService } from './FileInfoService.js';
@@ -144,6 +151,8 @@ import type { Provider } from '@nestjs/common';
 
 //#region 文字列ベースでのinjection用(循環参照対応のため)
 const $LoggerService: Provider = { provide: 'LoggerService', useExisting: LoggerService };
+const $AbuseReportService: Provider = { provide: 'AbuseReportService', useExisting: AbuseReportService };
+const $AbuseReportNotificationService: Provider = { provide: 'AbuseReportNotificationService', useExisting: AbuseReportNotificationService };
 const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService };
 const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
 const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
@@ -196,7 +205,8 @@ const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting
 const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService };
 const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService };
 const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService };
-const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService };
+const $UserWebhookService: Provider = { provide: 'UserWebhookService', useExisting: UserWebhookService };
+const $SystemWebhookService: Provider = { provide: 'SystemWebhookService', useExisting: SystemWebhookService };
 const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
 const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService };
 const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
@@ -225,6 +235,7 @@ const $ChartManagementService: Provider = { provide: 'ChartManagementService', u
 
 const $AbuseUserReportEntityService: Provider = { provide: 'AbuseUserReportEntityService', useExisting: AbuseUserReportEntityService };
 const $AnnouncementEntityService: Provider = { provide: 'AnnouncementEntityService', useExisting: AnnouncementEntityService };
+const $AbuseReportNotificationRecipientEntityService: Provider = { provide: 'AbuseReportNotificationRecipientEntityService', useExisting: AbuseReportNotificationRecipientEntityService };
 const $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useExisting: AntennaEntityService };
 const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting: AppEntityService };
 const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService };
@@ -258,6 +269,7 @@ const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', u
 const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService };
 const $ReversiGameEntityService: Provider = { provide: 'ReversiGameEntityService', useExisting: ReversiGameEntityService };
 const $MetaEntityService: Provider = { provide: 'MetaEntityService', useExisting: MetaEntityService };
+const $SystemWebhookEntityService: Provider = { provide: 'SystemWebhookEntityService', useExisting: SystemWebhookEntityService };
 
 const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService };
 const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService };
@@ -285,6 +297,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 	],
 	providers: [
 		LoggerService,
+		AbuseReportService,
+		AbuseReportNotificationService,
 		AccountMoveService,
 		AccountUpdateService,
 		AiService,
@@ -337,7 +351,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserSuspendService,
 		UserAuthService,
 		VideoProcessingService,
-		WebhookService,
+		UserWebhookService,
+		SystemWebhookService,
 		UtilityService,
 		FileInfoService,
 		SearchService,
@@ -366,6 +381,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 
 		AbuseUserReportEntityService,
 		AnnouncementEntityService,
+		AbuseReportNotificationRecipientEntityService,
 		AntennaEntityService,
 		AppEntityService,
 		AuthSessionEntityService,
@@ -399,6 +415,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		RoleEntityService,
 		ReversiGameEntityService,
 		MetaEntityService,
+		SystemWebhookEntityService,
 
 		ApAudienceService,
 		ApDbResolverService,
@@ -422,6 +439,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 
 		//#region 文字列ベースでのinjection用(循環参照対応のため)
 		$LoggerService,
+		$AbuseReportService,
+		$AbuseReportNotificationService,
 		$AccountMoveService,
 		$AccountUpdateService,
 		$AiService,
@@ -474,7 +493,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserSuspendService,
 		$UserAuthService,
 		$VideoProcessingService,
-		$WebhookService,
+		$UserWebhookService,
+		$SystemWebhookService,
 		$UtilityService,
 		$FileInfoService,
 		$SearchService,
@@ -503,6 +523,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 
 		$AbuseUserReportEntityService,
 		$AnnouncementEntityService,
+		$AbuseReportNotificationRecipientEntityService,
 		$AntennaEntityService,
 		$AppEntityService,
 		$AuthSessionEntityService,
@@ -536,6 +557,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$RoleEntityService,
 		$ReversiGameEntityService,
 		$MetaEntityService,
+		$SystemWebhookEntityService,
 
 		$ApAudienceService,
 		$ApDbResolverService,
@@ -560,6 +582,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 	exports: [
 		QueueModule,
 		LoggerService,
+		AbuseReportService,
+		AbuseReportNotificationService,
 		AccountMoveService,
 		AccountUpdateService,
 		AiService,
@@ -612,7 +636,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserSuspendService,
 		UserAuthService,
 		VideoProcessingService,
-		WebhookService,
+		UserWebhookService,
+		SystemWebhookService,
 		UtilityService,
 		FileInfoService,
 		SearchService,
@@ -640,6 +665,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 
 		AbuseUserReportEntityService,
 		AnnouncementEntityService,
+		AbuseReportNotificationRecipientEntityService,
 		AntennaEntityService,
 		AppEntityService,
 		AuthSessionEntityService,
@@ -673,6 +699,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		RoleEntityService,
 		ReversiGameEntityService,
 		MetaEntityService,
+		SystemWebhookEntityService,
 
 		ApAudienceService,
 		ApDbResolverService,
@@ -696,6 +723,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 
 		//#region 文字列ベースでのinjection用(循環参照対応のため)
 		$LoggerService,
+		$AbuseReportService,
+		$AbuseReportNotificationService,
 		$AccountMoveService,
 		$AccountUpdateService,
 		$AiService,
@@ -748,7 +777,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserSuspendService,
 		$UserAuthService,
 		$VideoProcessingService,
-		$WebhookService,
+		$UserWebhookService,
+		$SystemWebhookService,
 		$UtilityService,
 		$FileInfoService,
 		$SearchService,
@@ -776,6 +806,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 
 		$AbuseUserReportEntityService,
 		$AnnouncementEntityService,
+		$AbuseReportNotificationRecipientEntityService,
 		$AntennaEntityService,
 		$AppEntityService,
 		$AuthSessionEntityService,
@@ -809,6 +840,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$RoleEntityService,
 		$ReversiGameEntityService,
 		$MetaEntityService,
+		$SystemWebhookEntityService,
 
 		$ApAudienceService,
 		$ApDbResolverService,
diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index 08f8f80a6e..435dbbae28 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -16,6 +16,7 @@ import type { UserProfilesRepository } from '@/models/_.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
 import { HttpRequestService } from '@/core/HttpRequestService.js';
+import { QueueService } from '@/core/QueueService.js';
 
 @Injectable()
 export class EmailService {
@@ -32,6 +33,7 @@ export class EmailService {
 		private loggerService: LoggerService,
 		private utilityService: UtilityService,
 		private httpRequestService: HttpRequestService,
+		private queueService: QueueService,
 	) {
 		this.logger = this.loggerService.getLogger('email');
 	}
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index 90efd63f3a..a70743bed2 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -18,6 +18,7 @@ import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
 import type { MiSignin } from '@/models/Signin.js';
 import type { MiPage } from '@/models/Page.js';
 import type { MiWebhook } from '@/models/Webhook.js';
+import type { MiSystemWebhook } from '@/models/SystemWebhook.js';
 import type { MiMeta } from '@/models/Meta.js';
 import { MiAvatarDecoration, MiReversiGame, MiRole, MiRoleAssignment } from '@/models/_.js';
 import type { Packed } from '@/misc/json-schema.js';
@@ -227,6 +228,9 @@ export interface InternalEventTypes {
 	webhookCreated: MiWebhook;
 	webhookDeleted: MiWebhook;
 	webhookUpdated: MiWebhook;
+	systemWebhookCreated: MiSystemWebhook;
+	systemWebhookDeleted: MiSystemWebhook;
+	systemWebhookUpdated: MiSystemWebhook;
 	antennaCreated: MiAntenna;
 	antennaDeleted: MiAntenna;
 	antennaUpdated: MiAntenna;
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index e5580f36d1..0c9de117d2 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -38,7 +38,7 @@ import InstanceChart from '@/core/chart/charts/instance.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { NotificationService } from '@/core/NotificationService.js';
-import { WebhookService } from '@/core/WebhookService.js';
+import { UserWebhookService } from '@/core/UserWebhookService.js';
 import { HashtagService } from '@/core/HashtagService.js';
 import { AntennaService } from '@/core/AntennaService.js';
 import { QueueService } from '@/core/QueueService.js';
@@ -205,7 +205,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		private federatedInstanceService: FederatedInstanceService,
 		private hashtagService: HashtagService,
 		private antennaService: AntennaService,
-		private webhookService: WebhookService,
+		private webhookService: UserWebhookService,
 		private featuredService: FeaturedService,
 		private remoteUserResolveService: RemoteUserResolveService,
 		private apDeliverManagerService: ApDeliverManagerService,
@@ -606,7 +606,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 			this.webhookService.getActiveWebhooks().then(webhooks => {
 				webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note'));
 				for (const webhook of webhooks) {
-					this.queueService.webhookDeliver(webhook, 'note', {
+					this.queueService.userWebhookDeliver(webhook, 'note', {
 						note: noteObj,
 					});
 				}
@@ -633,7 +633,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 						const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply'));
 						for (const webhook of webhooks) {
-							this.queueService.webhookDeliver(webhook, 'reply', {
+							this.queueService.userWebhookDeliver(webhook, 'reply', {
 								note: noteObj,
 							});
 						}
@@ -656,7 +656,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 					const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote'));
 					for (const webhook of webhooks) {
-						this.queueService.webhookDeliver(webhook, 'renote', {
+						this.queueService.userWebhookDeliver(webhook, 'renote', {
 							note: noteObj,
 						});
 					}
@@ -788,7 +788,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 			const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention'));
 			for (const webhook of webhooks) {
-				this.queueService.webhookDeliver(webhook, 'mention', {
+				this.queueService.userWebhookDeliver(webhook, 'mention', {
 					note: detailPackedNote,
 				});
 			}
diff --git a/packages/backend/src/core/QueueModule.ts b/packages/backend/src/core/QueueModule.ts
index 216734e9e5..b10b8e5899 100644
--- a/packages/backend/src/core/QueueModule.ts
+++ b/packages/backend/src/core/QueueModule.ts
@@ -7,10 +7,17 @@ import { Inject, Module, OnApplicationShutdown } from '@nestjs/common';
 import * as Bull from 'bullmq';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
-import { QUEUE, baseQueueOptions } from '@/queue/const.js';
+import { baseQueueOptions, QUEUE } from '@/queue/const.js';
 import { allSettled } from '@/misc/promise-tracker.js';
+import {
+	DeliverJobData,
+	EndedPollNotificationJobData,
+	InboxJobData,
+	RelationshipJobData,
+	UserWebhookDeliverJobData,
+	SystemWebhookDeliverJobData,
+} from '../queue/types.js';
 import type { Provider } from '@nestjs/common';
-import type { DeliverJobData, InboxJobData, EndedPollNotificationJobData, WebhookDeliverJobData, RelationshipJobData } from '../queue/types.js';
 
 export type SystemQueue = Bull.Queue<Record<string, unknown>>;
 export type EndedPollNotificationQueue = Bull.Queue<EndedPollNotificationJobData>;
@@ -19,7 +26,8 @@ export type InboxQueue = Bull.Queue<InboxJobData>;
 export type DbQueue = Bull.Queue;
 export type RelationshipQueue = Bull.Queue<RelationshipJobData>;
 export type ObjectStorageQueue = Bull.Queue;
-export type WebhookDeliverQueue = Bull.Queue<WebhookDeliverJobData>;
+export type UserWebhookDeliverQueue = Bull.Queue<UserWebhookDeliverJobData>;
+export type SystemWebhookDeliverQueue = Bull.Queue<SystemWebhookDeliverJobData>;
 
 const $system: Provider = {
 	provide: 'queue:system',
@@ -63,9 +71,15 @@ const $objectStorage: Provider = {
 	inject: [DI.config],
 };
 
-const $webhookDeliver: Provider = {
-	provide: 'queue:webhookDeliver',
-	useFactory: (config: Config) => new Bull.Queue(QUEUE.WEBHOOK_DELIVER, baseQueueOptions(config, QUEUE.WEBHOOK_DELIVER)),
+const $userWebhookDeliver: Provider = {
+	provide: 'queue:userWebhookDeliver',
+	useFactory: (config: Config) => new Bull.Queue(QUEUE.USER_WEBHOOK_DELIVER, baseQueueOptions(config, QUEUE.USER_WEBHOOK_DELIVER)),
+	inject: [DI.config],
+};
+
+const $systemWebhookDeliver: Provider = {
+	provide: 'queue:systemWebhookDeliver',
+	useFactory: (config: Config) => new Bull.Queue(QUEUE.SYSTEM_WEBHOOK_DELIVER, baseQueueOptions(config, QUEUE.SYSTEM_WEBHOOK_DELIVER)),
 	inject: [DI.config],
 };
 
@@ -80,7 +94,8 @@ const $webhookDeliver: Provider = {
 		$db,
 		$relationship,
 		$objectStorage,
-		$webhookDeliver,
+		$userWebhookDeliver,
+		$systemWebhookDeliver,
 	],
 	exports: [
 		$system,
@@ -90,7 +105,8 @@ const $webhookDeliver: Provider = {
 		$db,
 		$relationship,
 		$objectStorage,
-		$webhookDeliver,
+		$userWebhookDeliver,
+		$systemWebhookDeliver,
 	],
 })
 export class QueueModule implements OnApplicationShutdown {
@@ -102,7 +118,8 @@ export class QueueModule implements OnApplicationShutdown {
 		@Inject('queue:db') public dbQueue: DbQueue,
 		@Inject('queue:relationship') public relationshipQueue: RelationshipQueue,
 		@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
-		@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue,
+		@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
+		@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
 	) {}
 
 	public async dispose(): Promise<void> {
@@ -117,7 +134,8 @@ export class QueueModule implements OnApplicationShutdown {
 			this.dbQueue.close(),
 			this.relationshipQueue.close(),
 			this.objectStorageQueue.close(),
-			this.webhookDeliverQueue.close(),
+			this.userWebhookDeliverQueue.close(),
+			this.systemWebhookDeliverQueue.close(),
 		]);
 	}
 
diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index c258a22927..80827a500b 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -8,15 +8,33 @@ import { Inject, Injectable } from '@nestjs/common';
 import type { IActivity } from '@/core/activitypub/type.js';
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import type { MiWebhook, webhookEventTypes } from '@/models/Webhook.js';
+import type { MiSystemWebhook, SystemWebhookEventType } from '@/models/SystemWebhook.js';
 import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
-import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
-import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
+import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
+import type {
+	DbJobData,
+	DeliverJobData,
+	RelationshipJobData,
+	SystemWebhookDeliverJobData,
+	ThinUser,
+	UserWebhookDeliverJobData,
+} from '../queue/types.js';
+import type {
+	DbQueue,
+	DeliverQueue,
+	EndedPollNotificationQueue,
+	InboxQueue,
+	ObjectStorageQueue,
+	RelationshipQueue,
+	SystemQueue,
+	UserWebhookDeliverQueue,
+	SystemWebhookDeliverQueue,
+} from './QueueModule.js';
 import type httpSignature from '@peertube/http-signature';
 import type * as Bull from 'bullmq';
-import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
 
 @Injectable()
 export class QueueService {
@@ -31,7 +49,8 @@ export class QueueService {
 		@Inject('queue:db') public dbQueue: DbQueue,
 		@Inject('queue:relationship') public relationshipQueue: RelationshipQueue,
 		@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
-		@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue,
+		@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
+		@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
 	) {
 		this.systemQueue.add('tickCharts', {
 		}, {
@@ -431,9 +450,13 @@ export class QueueService {
 		});
 	}
 
+	/**
+	 * @see UserWebhookDeliverJobData
+	 * @see WebhookDeliverProcessorService
+	 */
 	@bindThis
-	public webhookDeliver(webhook: MiWebhook, type: typeof webhookEventTypes[number], content: unknown) {
-		const data = {
+	public userWebhookDeliver(webhook: MiWebhook, type: typeof webhookEventTypes[number], content: unknown) {
+		const data: UserWebhookDeliverJobData = {
 			type,
 			content,
 			webhookId: webhook.id,
@@ -444,7 +467,33 @@ export class QueueService {
 			eventId: randomUUID(),
 		};
 
-		return this.webhookDeliverQueue.add(webhook.id, data, {
+		return this.userWebhookDeliverQueue.add(webhook.id, data, {
+			attempts: 4,
+			backoff: {
+				type: 'custom',
+			},
+			removeOnComplete: true,
+			removeOnFail: true,
+		});
+	}
+
+	/**
+	 * @see SystemWebhookDeliverJobData
+	 * @see WebhookDeliverProcessorService
+	 */
+	@bindThis
+	public systemWebhookDeliver(webhook: MiSystemWebhook, type: SystemWebhookEventType, content: unknown) {
+		const data: SystemWebhookDeliverJobData = {
+			type,
+			content,
+			webhookId: webhook.id,
+			to: webhook.url,
+			secret: webhook.secret,
+			createdAt: Date.now(),
+			eventId: randomUUID(),
+		};
+
+		return this.systemWebhookDeliverQueue.add(webhook.id, data, {
 			attempts: 4,
 			backoff: {
 				type: 'custom',
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index d6eea70297..e2ebecb99f 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -410,14 +410,32 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 	}
 
 	@bindThis
-	public async getModeratorIds(includeAdmins = true): Promise<MiUser['id'][]> {
+	public async getModeratorIds(includeAdmins = true, excludeExpire = false): Promise<MiUser['id'][]> {
 		const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
-		const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator);
-		const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
-			roleId: In(moderatorRoles.map(r => r.id)),
-		}) : [];
+		const moderatorRoles = includeAdmins
+			? roles.filter(r => r.isModerator || r.isAdministrator)
+			: roles.filter(r => r.isModerator);
+
 		// TODO: isRootなアカウントも含める
-		return assigns.map(a => a.userId);
+		const assigns = moderatorRoles.length > 0
+			? await this.roleAssignmentsRepository.findBy({ roleId: In(moderatorRoles.map(r => r.id)) })
+			: [];
+
+		const now = Date.now();
+		const result = [
+			// Setを経由して重複を除去(ユーザIDは重複する可能性があるので)
+			...new Set(
+				assigns
+					.filter(it =>
+						(excludeExpire)
+							? (it.expiresAt == null || it.expiresAt.getTime() > now)
+							: true,
+					)
+					.map(a => a.userId),
+			),
+		];
+
+		return result.sort((x, y) => x.localeCompare(y));
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/SystemWebhookService.ts b/packages/backend/src/core/SystemWebhookService.ts
new file mode 100644
index 0000000000..bc6851f788
--- /dev/null
+++ b/packages/backend/src/core/SystemWebhookService.ts
@@ -0,0 +1,233 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import * as Redis from 'ioredis';
+import type { MiUser, SystemWebhooksRepository } from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { bindThis } from '@/decorators.js';
+import { GlobalEvents, GlobalEventService } from '@/core/GlobalEventService.js';
+import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWebhook.js';
+import { IdService } from '@/core/IdService.js';
+import { QueueService } from '@/core/QueueService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { LoggerService } from '@/core/LoggerService.js';
+import Logger from '@/logger.js';
+import type { OnApplicationShutdown } from '@nestjs/common';
+
+@Injectable()
+export class SystemWebhookService implements OnApplicationShutdown {
+	private logger: Logger;
+	private activeSystemWebhooksFetched = false;
+	private activeSystemWebhooks: MiSystemWebhook[] = [];
+
+	constructor(
+		@Inject(DI.redisForSub)
+		private redisForSub: Redis.Redis,
+		@Inject(DI.systemWebhooksRepository)
+		private systemWebhooksRepository: SystemWebhooksRepository,
+		private idService: IdService,
+		private queueService: QueueService,
+		private moderationLogService: ModerationLogService,
+		private loggerService: LoggerService,
+		private globalEventService: GlobalEventService,
+	) {
+		this.redisForSub.on('message', this.onMessage);
+		this.logger = this.loggerService.getLogger('webhook');
+	}
+
+	@bindThis
+	public async fetchActiveSystemWebhooks() {
+		if (!this.activeSystemWebhooksFetched) {
+			this.activeSystemWebhooks = await this.systemWebhooksRepository.findBy({
+				isActive: true,
+			});
+			this.activeSystemWebhooksFetched = true;
+		}
+
+		return this.activeSystemWebhooks;
+	}
+
+	/**
+	 * SystemWebhook の一覧を取得する.
+	 */
+	@bindThis
+	public async fetchSystemWebhooks(params?: {
+		ids?: MiSystemWebhook['id'][];
+		isActive?: MiSystemWebhook['isActive'];
+		on?: MiSystemWebhook['on'];
+	}): Promise<MiSystemWebhook[]> {
+		const query = this.systemWebhooksRepository.createQueryBuilder('systemWebhook');
+		if (params) {
+			if (params.ids && params.ids.length > 0) {
+				query.andWhere('systemWebhook.id IN (:...ids)', { ids: params.ids });
+			}
+			if (params.isActive !== undefined) {
+				query.andWhere('systemWebhook.isActive = :isActive', { isActive: params.isActive });
+			}
+			if (params.on && params.on.length > 0) {
+				query.andWhere(':on <@ systemWebhook.on', { on: params.on });
+			}
+		}
+
+		return query.getMany();
+	}
+
+	/**
+	 * SystemWebhook を作成する.
+	 */
+	@bindThis
+	public async createSystemWebhook(
+		params: {
+			isActive: MiSystemWebhook['isActive'];
+			name: MiSystemWebhook['name'];
+			on: MiSystemWebhook['on'];
+			url: MiSystemWebhook['url'];
+			secret: MiSystemWebhook['secret'];
+		},
+		updater: MiUser,
+	): Promise<MiSystemWebhook> {
+		const id = this.idService.gen();
+		await this.systemWebhooksRepository.insert({
+			...params,
+			id,
+		});
+
+		const webhook = await this.systemWebhooksRepository.findOneByOrFail({ id });
+		this.globalEventService.publishInternalEvent('systemWebhookCreated', webhook);
+		this.moderationLogService
+			.log(updater, 'createSystemWebhook', {
+				systemWebhookId: webhook.id,
+				webhook: webhook,
+			})
+			.then();
+
+		return webhook;
+	}
+
+	/**
+	 * SystemWebhook を更新する.
+	 */
+	@bindThis
+	public async updateSystemWebhook(
+		params: {
+			id: MiSystemWebhook['id'];
+			isActive: MiSystemWebhook['isActive'];
+			name: MiSystemWebhook['name'];
+			on: MiSystemWebhook['on'];
+			url: MiSystemWebhook['url'];
+			secret: MiSystemWebhook['secret'];
+		},
+		updater: MiUser,
+	): Promise<MiSystemWebhook> {
+		const beforeEntity = await this.systemWebhooksRepository.findOneByOrFail({ id: params.id });
+		await this.systemWebhooksRepository.update(beforeEntity.id, {
+			updatedAt: new Date(),
+			isActive: params.isActive,
+			name: params.name,
+			on: params.on,
+			url: params.url,
+			secret: params.secret,
+		});
+
+		const afterEntity = await this.systemWebhooksRepository.findOneByOrFail({ id: beforeEntity.id });
+		this.globalEventService.publishInternalEvent('systemWebhookUpdated', afterEntity);
+		this.moderationLogService
+			.log(updater, 'updateSystemWebhook', {
+				systemWebhookId: beforeEntity.id,
+				before: beforeEntity,
+				after: afterEntity,
+			})
+			.then();
+
+		return afterEntity;
+	}
+
+	/**
+	 * SystemWebhook を削除する.
+	 */
+	@bindThis
+	public async deleteSystemWebhook(id: MiSystemWebhook['id'], updater: MiUser) {
+		const webhook = await this.systemWebhooksRepository.findOneByOrFail({ id });
+		await this.systemWebhooksRepository.delete(id);
+
+		this.globalEventService.publishInternalEvent('systemWebhookDeleted', webhook);
+		this.moderationLogService
+			.log(updater, 'deleteSystemWebhook', {
+				systemWebhookId: webhook.id,
+				webhook,
+			})
+			.then();
+	}
+
+	/**
+	 * SystemWebhook をWebhook配送キューに追加する
+	 * @see QueueService.systemWebhookDeliver
+	 */
+	@bindThis
+	public async enqueueSystemWebhook(webhook: MiSystemWebhook | MiSystemWebhook['id'], type: SystemWebhookEventType, content: unknown) {
+		const webhookEntity = typeof webhook === 'string'
+			? (await this.fetchActiveSystemWebhooks()).find(a => a.id === webhook)
+			: webhook;
+		if (!webhookEntity || !webhookEntity.isActive) {
+			this.logger.info(`Webhook is not active or not found : ${webhook}`);
+			return;
+		}
+
+		if (!webhookEntity.on.includes(type)) {
+			this.logger.info(`Webhook ${webhookEntity.id} is not listening to ${type}`);
+			return;
+		}
+
+		return this.queueService.systemWebhookDeliver(webhookEntity, type, content);
+	}
+
+	@bindThis
+	private async onMessage(_: string, data: string): Promise<void> {
+		const obj = JSON.parse(data);
+		if (obj.channel !== 'internal') {
+			return;
+		}
+
+		const { type, body } = obj.message as GlobalEvents['internal']['payload'];
+		switch (type) {
+			case 'systemWebhookCreated': {
+				if (body.isActive) {
+					this.activeSystemWebhooks.push(MiSystemWebhook.deserialize(body));
+				}
+				break;
+			}
+			case 'systemWebhookUpdated': {
+				if (body.isActive) {
+					const i = this.activeSystemWebhooks.findIndex(a => a.id === body.id);
+					if (i > -1) {
+						this.activeSystemWebhooks[i] = MiSystemWebhook.deserialize(body);
+					} else {
+						this.activeSystemWebhooks.push(MiSystemWebhook.deserialize(body));
+					}
+				} else {
+					this.activeSystemWebhooks = this.activeSystemWebhooks.filter(a => a.id !== body.id);
+				}
+				break;
+			}
+			case 'systemWebhookDeleted': {
+				this.activeSystemWebhooks = this.activeSystemWebhooks.filter(a => a.id !== body.id);
+				break;
+			}
+			default:
+				break;
+		}
+	}
+
+	@bindThis
+	public dispose(): void {
+		this.redisForSub.off('message', this.onMessage);
+	}
+
+	@bindThis
+	public onApplicationShutdown(signal?: string | undefined): void {
+		this.dispose();
+	}
+}
diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts
index 96f389b54c..2f1310b8ef 100644
--- a/packages/backend/src/core/UserBlockingService.ts
+++ b/packages/backend/src/core/UserBlockingService.ts
@@ -16,7 +16,7 @@ import Logger from '@/logger.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { LoggerService } from '@/core/LoggerService.js';
-import { WebhookService } from '@/core/WebhookService.js';
+import { UserWebhookService } from '@/core/UserWebhookService.js';
 import { bindThis } from '@/decorators.js';
 import { CacheService } from '@/core/CacheService.js';
 import { UserFollowingService } from '@/core/UserFollowingService.js';
@@ -46,7 +46,7 @@ export class UserBlockingService implements OnModuleInit {
 		private idService: IdService,
 		private queueService: QueueService,
 		private globalEventService: GlobalEventService,
-		private webhookService: WebhookService,
+		private webhookService: UserWebhookService,
 		private apRendererService: ApRendererService,
 		private loggerService: LoggerService,
 	) {
@@ -121,7 +121,7 @@ export class UserBlockingService implements OnModuleInit {
 
 				const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
 				for (const webhook of webhooks) {
-					this.queueService.webhookDeliver(webhook, 'unfollow', {
+					this.queueService.userWebhookDeliver(webhook, 'unfollow', {
 						user: packed,
 					});
 				}
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index 406ea04031..267a6a3f1b 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -16,7 +16,7 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js
 import type { Packed } from '@/misc/json-schema.js';
 import InstanceChart from '@/core/chart/charts/instance.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
-import { WebhookService } from '@/core/WebhookService.js';
+import { UserWebhookService } from '@/core/UserWebhookService.js';
 import { NotificationService } from '@/core/NotificationService.js';
 import { DI } from '@/di-symbols.js';
 import type { FollowingsRepository, FollowRequestsRepository, InstancesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
@@ -82,7 +82,7 @@ export class UserFollowingService implements OnModuleInit {
 		private metaService: MetaService,
 		private notificationService: NotificationService,
 		private federatedInstanceService: FederatedInstanceService,
-		private webhookService: WebhookService,
+		private webhookService: UserWebhookService,
 		private apRendererService: ApRendererService,
 		private accountMoveService: AccountMoveService,
 		private fanoutTimelineService: FanoutTimelineService,
@@ -331,7 +331,7 @@ export class UserFollowingService implements OnModuleInit {
 
 				const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
 				for (const webhook of webhooks) {
-					this.queueService.webhookDeliver(webhook, 'follow', {
+					this.queueService.userWebhookDeliver(webhook, 'follow', {
 						user: packed,
 					});
 				}
@@ -345,7 +345,7 @@ export class UserFollowingService implements OnModuleInit {
 
 				const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === followee.id && x.on.includes('followed'));
 				for (const webhook of webhooks) {
-					this.queueService.webhookDeliver(webhook, 'followed', {
+					this.queueService.userWebhookDeliver(webhook, 'followed', {
 						user: packed,
 					});
 				}
@@ -398,7 +398,7 @@ export class UserFollowingService implements OnModuleInit {
 
 				const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
 				for (const webhook of webhooks) {
-					this.queueService.webhookDeliver(webhook, 'unfollow', {
+					this.queueService.userWebhookDeliver(webhook, 'unfollow', {
 						user: packed,
 					});
 				}
@@ -740,7 +740,7 @@ export class UserFollowingService implements OnModuleInit {
 
 		const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow'));
 		for (const webhook of webhooks) {
-			this.queueService.webhookDeliver(webhook, 'unfollow', {
+			this.queueService.userWebhookDeliver(webhook, 'unfollow', {
 				user: packedFollowee,
 			});
 		}
diff --git a/packages/backend/src/core/UserWebhookService.ts b/packages/backend/src/core/UserWebhookService.ts
new file mode 100644
index 0000000000..e96bfeea95
--- /dev/null
+++ b/packages/backend/src/core/UserWebhookService.ts
@@ -0,0 +1,99 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import * as Redis from 'ioredis';
+import type { WebhooksRepository } from '@/models/_.js';
+import type { MiWebhook } from '@/models/Webhook.js';
+import { DI } from '@/di-symbols.js';
+import { bindThis } from '@/decorators.js';
+import { GlobalEvents } from '@/core/GlobalEventService.js';
+import type { OnApplicationShutdown } from '@nestjs/common';
+
+@Injectable()
+export class UserWebhookService implements OnApplicationShutdown {
+	private activeWebhooksFetched = false;
+	private activeWebhooks: MiWebhook[] = [];
+
+	constructor(
+		@Inject(DI.redisForSub)
+		private redisForSub: Redis.Redis,
+		@Inject(DI.webhooksRepository)
+		private webhooksRepository: WebhooksRepository,
+	) {
+		this.redisForSub.on('message', this.onMessage);
+	}
+
+	@bindThis
+	public async getActiveWebhooks() {
+		if (!this.activeWebhooksFetched) {
+			this.activeWebhooks = await this.webhooksRepository.findBy({
+				active: true,
+			});
+			this.activeWebhooksFetched = true;
+		}
+
+		return this.activeWebhooks;
+	}
+
+	@bindThis
+	private async onMessage(_: string, data: string): Promise<void> {
+		const obj = JSON.parse(data);
+		if (obj.channel !== 'internal') {
+			return;
+		}
+
+		const { type, body } = obj.message as GlobalEvents['internal']['payload'];
+		switch (type) {
+			case 'webhookCreated': {
+				if (body.active) {
+					this.activeWebhooks.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
+						...body,
+						latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
+						user: null, // joinなカラムは通常取ってこないので
+					});
+				}
+				break;
+			}
+			case 'webhookUpdated': {
+				if (body.active) {
+					const i = this.activeWebhooks.findIndex(a => a.id === body.id);
+					if (i > -1) {
+						this.activeWebhooks[i] = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
+							...body,
+							latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
+							user: null, // joinなカラムは通常取ってこないので
+						};
+					} else {
+						this.activeWebhooks.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
+							...body,
+							latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
+							user: null, // joinなカラムは通常取ってこないので
+						});
+					}
+				} else {
+					this.activeWebhooks = this.activeWebhooks.filter(a => a.id !== body.id);
+				}
+				break;
+			}
+			case 'webhookDeleted': {
+				this.activeWebhooks = this.activeWebhooks.filter(a => a.id !== body.id);
+				break;
+			}
+			default:
+				break;
+		}
+	}
+
+	@bindThis
+	public dispose(): void {
+		this.redisForSub.off('message', this.onMessage);
+	}
+
+	@bindThis
+	public onApplicationShutdown(signal?: string | undefined): void {
+		this.dispose();
+	}
+}
diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts
deleted file mode 100644
index 6be34977b0..0000000000
--- a/packages/backend/src/core/WebhookService.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { Inject, Injectable } from '@nestjs/common';
-import * as Redis from 'ioredis';
-import type { WebhooksRepository } from '@/models/_.js';
-import type { MiWebhook } from '@/models/Webhook.js';
-import { DI } from '@/di-symbols.js';
-import { bindThis } from '@/decorators.js';
-import type { GlobalEvents } from '@/core/GlobalEventService.js';
-import type { OnApplicationShutdown } from '@nestjs/common';
-
-@Injectable()
-export class WebhookService implements OnApplicationShutdown {
-	private webhooksFetched = false;
-	private webhooks: MiWebhook[] = [];
-
-	constructor(
-		@Inject(DI.redisForSub)
-		private redisForSub: Redis.Redis,
-
-		@Inject(DI.webhooksRepository)
-		private webhooksRepository: WebhooksRepository,
-	) {
-		//this.onMessage = this.onMessage.bind(this);
-		this.redisForSub.on('message', this.onMessage);
-	}
-
-	@bindThis
-	public async getActiveWebhooks() {
-		if (!this.webhooksFetched) {
-			this.webhooks = await this.webhooksRepository.findBy({
-				active: true,
-			});
-			this.webhooksFetched = true;
-		}
-
-		return this.webhooks;
-	}
-
-	@bindThis
-	private async onMessage(_: string, data: string): Promise<void> {
-		const obj = JSON.parse(data);
-
-		if (obj.channel === 'internal') {
-			const { type, body } = obj.message as GlobalEvents['internal']['payload'];
-			switch (type) {
-				case 'webhookCreated':
-					if (body.active) {
-						this.webhooks.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
-							...body,
-							latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
-							user: null, // joinなカラムは通常取ってこないので
-						});
-					}
-					break;
-				case 'webhookUpdated':
-					if (body.active) {
-						const i = this.webhooks.findIndex(a => a.id === body.id);
-						if (i > -1) {
-							this.webhooks[i] = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
-								...body,
-								latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
-								user: null, // joinなカラムは通常取ってこないので
-							};
-						} else {
-							this.webhooks.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい
-								...body,
-								latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
-								user: null, // joinなカラムは通常取ってこないので
-							});
-						}
-					} else {
-						this.webhooks = this.webhooks.filter(a => a.id !== body.id);
-					}
-					break;
-				case 'webhookDeleted':
-					this.webhooks = this.webhooks.filter(a => a.id !== body.id);
-					break;
-				default:
-					break;
-			}
-		}
-	}
-
-	@bindThis
-	public dispose(): void {
-		this.redisForSub.off('message', this.onMessage);
-	}
-
-	@bindThis
-	public onApplicationShutdown(signal?: string | undefined): void {
-		this.dispose();
-	}
-}
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index d0d206760c..de3178b482 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -29,6 +29,7 @@ import { bindThis } from '@/decorators.js';
 import type { MiRemoteUser } from '@/models/User.js';
 import { isNotNull } from '@/misc/is-not-null.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { AbuseReportService } from '@/core/AbuseReportService.js';
 import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
 import { ApNoteService } from './models/ApNoteService.js';
 import { ApLoggerService } from './ApLoggerService.js';
@@ -57,9 +58,6 @@ export class ApInboxService {
 		@Inject(DI.followingsRepository)
 		private followingsRepository: FollowingsRepository,
 
-		@Inject(DI.abuseUserReportsRepository)
-		private abuseUserReportsRepository: AbuseUserReportsRepository,
-
 		@Inject(DI.followRequestsRepository)
 		private followRequestsRepository: FollowRequestsRepository,
 
@@ -68,6 +66,7 @@ export class ApInboxService {
 		private utilityService: UtilityService,
 		private idService: IdService,
 		private metaService: MetaService,
+		private abuseReportService: AbuseReportService,
 		private userFollowingService: UserFollowingService,
 		private apAudienceService: ApAudienceService,
 		private reactionService: ReactionService,
@@ -545,14 +544,13 @@ export class ApInboxService {
 		});
 		if (users.length < 1) return 'skip';
 
-		await this.abuseUserReportsRepository.insert({
-			id: this.idService.gen(),
+		await this.abuseReportService.report([{
 			targetUserId: users[0].id,
 			targetUserHost: users[0].host,
 			reporterId: actor.id,
 			reporterHost: actor.host,
 			comment: `${activity.content}\n${JSON.stringify(uris, null, 2)}`,
-		});
+		}]);
 
 		return 'ok';
 	}
diff --git a/packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts b/packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts
new file mode 100644
index 0000000000..6819afafd9
--- /dev/null
+++ b/packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts
@@ -0,0 +1,88 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { In } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import type { AbuseReportNotificationRecipientRepository, MiAbuseReportNotificationRecipient } from '@/models/_.js';
+import { bindThis } from '@/decorators.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { Packed } from '@/misc/json-schema.js';
+import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
+
+@Injectable()
+export class AbuseReportNotificationRecipientEntityService {
+	constructor(
+		@Inject(DI.abuseReportNotificationRecipientRepository)
+		private abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository,
+		private userEntityService: UserEntityService,
+		private systemWebhookEntityService: SystemWebhookEntityService,
+	) {
+	}
+
+	@bindThis
+	public async pack(
+		src: MiAbuseReportNotificationRecipient['id'] | MiAbuseReportNotificationRecipient,
+		opts?: {
+			users: Map<string, Packed<'UserLite'>>,
+			webhooks: Map<string, Packed<'SystemWebhook'>>,
+		},
+	): Promise<Packed<'AbuseReportNotificationRecipient'>> {
+		const recipient = typeof src === 'object'
+			? src
+			: await this.abuseReportNotificationRecipientRepository.findOneByOrFail({ id: src });
+		const user = recipient.userId
+			? (opts?.users.get(recipient.userId) ?? await this.userEntityService.pack<'UserLite'>(recipient.userId))
+			: undefined;
+		const webhook = recipient.systemWebhookId
+			? (opts?.webhooks.get(recipient.systemWebhookId) ?? await this.systemWebhookEntityService.pack(recipient.systemWebhookId))
+			: undefined;
+
+		return {
+			id: recipient.id,
+			isActive: recipient.isActive,
+			updatedAt: recipient.updatedAt.toISOString(),
+			name: recipient.name,
+			method: recipient.method,
+			userId: recipient.userId ?? undefined,
+			user: user,
+			systemWebhookId: recipient.systemWebhookId ?? undefined,
+			systemWebhook: webhook,
+		};
+	}
+
+	@bindThis
+	public async packMany(
+		src: MiAbuseReportNotificationRecipient['id'][] | MiAbuseReportNotificationRecipient[],
+	): Promise<Packed<'AbuseReportNotificationRecipient'>[]> {
+		const objs = src.filter((it): it is MiAbuseReportNotificationRecipient => typeof it === 'object');
+		const ids = src.filter((it): it is MiAbuseReportNotificationRecipient['id'] => typeof it === 'string');
+		if (ids.length > 0) {
+			objs.push(
+				...await this.abuseReportNotificationRecipientRepository.findBy({ id: In(ids) }),
+			);
+		}
+
+		const userIds = objs.map(it => it.userId).filter(isNotNull);
+		const users: Map<string, Packed<'UserLite'>> = (userIds.length > 0)
+			? await this.userEntityService.packMany(userIds)
+				.then(it => new Map(it.map(it => [it.id, it])))
+			: new Map();
+
+		const systemWebhookIds = objs.map(it => it.systemWebhookId).filter(isNotNull);
+		const systemWebhooks: Map<string, Packed<'SystemWebhook'>> = (systemWebhookIds.length > 0)
+			? await this.systemWebhookEntityService.packMany(systemWebhookIds)
+				.then(it => new Map(it.map(it => [it.id, it])))
+			: new Map();
+
+		return Promise
+			.all(
+				objs.map(it => this.pack(it, { users: users, webhooks: systemWebhooks })),
+			)
+			.then(it => it.sort((a, b) => a.id.localeCompare(b.id)));
+	}
+}
+
diff --git a/packages/backend/src/core/entities/SystemWebhookEntityService.ts b/packages/backend/src/core/entities/SystemWebhookEntityService.ts
new file mode 100644
index 0000000000..e18734091c
--- /dev/null
+++ b/packages/backend/src/core/entities/SystemWebhookEntityService.ts
@@ -0,0 +1,74 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { In } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import type { MiSystemWebhook, SystemWebhooksRepository } from '@/models/_.js';
+import { bindThis } from '@/decorators.js';
+import { Packed } from '@/misc/json-schema.js';
+
+@Injectable()
+export class SystemWebhookEntityService {
+	constructor(
+		@Inject(DI.systemWebhooksRepository)
+		private systemWebhooksRepository: SystemWebhooksRepository,
+	) {
+	}
+
+	@bindThis
+	public async pack(
+		src: MiSystemWebhook['id'] | MiSystemWebhook,
+		opts?: {
+			webhooks: Map<string, MiSystemWebhook>
+		},
+	): Promise<Packed<'SystemWebhook'>> {
+		const webhook = typeof src === 'object'
+			? src
+			: opts?.webhooks.get(src) ?? await this.systemWebhooksRepository.findOneByOrFail({ id: src });
+
+		return {
+			id: webhook.id,
+			isActive: webhook.isActive,
+			updatedAt: webhook.updatedAt.toISOString(),
+			latestSentAt: webhook.latestSentAt?.toISOString() ?? null,
+			latestStatus: webhook.latestStatus,
+			name: webhook.name,
+			on: webhook.on,
+			url: webhook.url,
+			secret: webhook.secret,
+		};
+	}
+
+	@bindThis
+	public async packMany(src: MiSystemWebhook['id'][] | MiSystemWebhook[]): Promise<Packed<'SystemWebhook'>[]> {
+		if (src.length === 0) {
+			return [];
+		}
+
+		const webhooks = Array.of<MiSystemWebhook>();
+		webhooks.push(
+			...src.filter((it): it is MiSystemWebhook => typeof it === 'object'),
+		);
+
+		const ids = src.filter((it): it is MiSystemWebhook['id'] => typeof it === 'string');
+		if (ids.length > 0) {
+			webhooks.push(
+				...await this.systemWebhooksRepository.findBy({ id: In(ids) }),
+			);
+		}
+
+		return Promise
+			.all(
+				webhooks.map(x =>
+					this.pack(x, {
+						webhooks: new Map(webhooks.map(x => [x.id, x])),
+					}),
+				),
+			)
+			.then(it => it.sort((a, b) => a.id.localeCompare(b.id)));
+	}
+}
+
diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts
index 919f4794a3..271082b4ff 100644
--- a/packages/backend/src/di-symbols.ts
+++ b/packages/backend/src/di-symbols.ts
@@ -49,6 +49,7 @@ export const DI = {
 	swSubscriptionsRepository: Symbol('swSubscriptionsRepository'),
 	hashtagsRepository: Symbol('hashtagsRepository'),
 	abuseUserReportsRepository: Symbol('abuseUserReportsRepository'),
+	abuseReportNotificationRecipientRepository: Symbol('abuseReportNotificationRecipientRepository'),
 	registrationTicketsRepository: Symbol('registrationTicketsRepository'),
 	authSessionsRepository: Symbol('authSessionsRepository'),
 	accessTokensRepository: Symbol('accessTokensRepository'),
@@ -70,6 +71,7 @@ export const DI = {
 	channelFavoritesRepository: Symbol('channelFavoritesRepository'),
 	registryItemsRepository: Symbol('registryItemsRepository'),
 	webhooksRepository: Symbol('webhooksRepository'),
+	systemWebhooksRepository: Symbol('systemWebhooksRepository'),
 	adsRepository: Symbol('adsRepository'),
 	passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'),
 	retentionAggregationsRepository: Symbol('retentionAggregationsRepository'),
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index 41e5bfe9e4..a721b8663c 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -4,12 +4,12 @@
  */
 
 import {
-	packedUserLiteSchema,
-	packedUserDetailedNotMeOnlySchema,
 	packedMeDetailedOnlySchema,
-	packedUserDetailedNotMeSchema,
 	packedMeDetailedSchema,
+	packedUserDetailedNotMeOnlySchema,
+	packedUserDetailedNotMeSchema,
 	packedUserDetailedSchema,
+	packedUserLiteSchema,
 	packedUserSchema,
 } from '@/models/json-schema/user.js';
 import { packedNoteSchema } from '@/models/json-schema/note.js';
@@ -25,7 +25,7 @@ import { packedBlockingSchema } from '@/models/json-schema/blocking.js';
 import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js';
 import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
 import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js';
-import { packedPageSchema, packedPageBlockSchema } from '@/models/json-schema/page.js';
+import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js';
 import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js';
 import { packedChannelSchema } from '@/models/json-schema/channel.js';
 import { packedAntennaSchema } from '@/models/json-schema/antenna.js';
@@ -38,25 +38,27 @@ import { packedFlashSchema } from '@/models/json-schema/flash.js';
 import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
 import { packedSigninSchema } from '@/models/json-schema/signin.js';
 import {
-	packedRoleLiteSchema,
-	packedRoleSchema,
-	packedRolePoliciesSchema,
+	packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
 	packedRoleCondFormulaLogicsSchema,
-	packedRoleCondFormulaValueNot,
-	packedRoleCondFormulaValueIsLocalOrRemoteSchema,
 	packedRoleCondFormulaValueAssignedRoleSchema,
 	packedRoleCondFormulaValueCreatedSchema,
-	packedRoleCondFormulaFollowersOrFollowingOrNotesSchema,
+	packedRoleCondFormulaValueIsLocalOrRemoteSchema,
+	packedRoleCondFormulaValueNot,
 	packedRoleCondFormulaValueSchema,
 	packedRoleCondFormulaValueUserSettingBooleanSchema,
+	packedRoleLiteSchema,
+	packedRolePoliciesSchema,
+	packedRoleSchema,
 } from '@/models/json-schema/role.js';
 import { packedAdSchema } from '@/models/json-schema/ad.js';
-import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
+import { packedReversiGameDetailedSchema, packedReversiGameLiteSchema } from '@/models/json-schema/reversi-game.js';
 import {
-	packedMetaLiteSchema,
 	packedMetaDetailedOnlySchema,
 	packedMetaDetailedSchema,
+	packedMetaLiteSchema,
 } from '@/models/json-schema/meta.js';
+import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js';
+import { packedAbuseReportNotificationRecipientSchema } from '@/models/json-schema/abuse-report-notification-recipient.js';
 
 export const refs = {
 	UserLite: packedUserLiteSchema,
@@ -111,6 +113,8 @@ export const refs = {
 	MetaLite: packedMetaLiteSchema,
 	MetaDetailedOnly: packedMetaDetailedOnlySchema,
 	MetaDetailed: packedMetaDetailedSchema,
+	SystemWebhook: packedSystemWebhookSchema,
+	AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema,
 };
 
 export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
diff --git a/packages/backend/src/models/AbuseReportNotificationRecipient.ts b/packages/backend/src/models/AbuseReportNotificationRecipient.ts
new file mode 100644
index 0000000000..fbff880afc
--- /dev/null
+++ b/packages/backend/src/models/AbuseReportNotificationRecipient.ts
@@ -0,0 +1,100 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
+import { MiSystemWebhook } from '@/models/SystemWebhook.js';
+import { MiUserProfile } from '@/models/UserProfile.js';
+import { id } from './util/id.js';
+import { MiUser } from './User.js';
+
+/**
+ * 通報受信時に通知を送信する方法.
+ */
+export type RecipientMethod = 'email' | 'webhook';
+
+@Entity('abuse_report_notification_recipient')
+export class MiAbuseReportNotificationRecipient {
+	@PrimaryColumn(id())
+	public id: string;
+
+	/**
+	 * 有効かどうか.
+	 */
+	@Index()
+	@Column('boolean', {
+		default: true,
+	})
+	public isActive: boolean;
+
+	/**
+	 * 更新日時.
+	 */
+	@Column('timestamp with time zone', {
+		default: () => 'CURRENT_TIMESTAMP',
+	})
+	public updatedAt: Date;
+
+	/**
+	 * 通知設定名.
+	 */
+	@Column('varchar', {
+		length: 255,
+	})
+	public name: string;
+
+	/**
+	 * 通知方法.
+	 */
+	@Index()
+	@Column('varchar', {
+		length: 64,
+	})
+	public method: RecipientMethod;
+
+	/**
+	 * 通知先のユーザID.
+	 */
+	@Index()
+	@Column({
+		...id(),
+		nullable: true,
+	})
+	public userId: MiUser['id'] | null;
+
+	/**
+	 * 通知先のユーザ.
+	 */
+	@ManyToOne(type => MiUser, {
+		onDelete: 'CASCADE',
+	})
+	@JoinColumn({ name: 'userId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId1' })
+	public user: MiUser | null;
+
+	/**
+	 * 通知先のユーザプロフィール.
+	 */
+	@ManyToOne(type => MiUserProfile, {})
+	@JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' })
+	public userProfile: MiUserProfile | null;
+
+	/**
+	 * 通知先のシステムWebhookId.
+	 */
+	@Index()
+	@Column({
+		...id(),
+		nullable: true,
+	})
+	public systemWebhookId: string | null;
+
+	/**
+	 * 通知先のシステムWebhook.
+	 */
+	@ManyToOne(type => MiSystemWebhook, {
+		onDelete: 'CASCADE',
+	})
+	@JoinColumn()
+	public systemWebhook: MiSystemWebhook | null;
+}
diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts
index d3062d6b36..ea0f88baba 100644
--- a/packages/backend/src/models/RepositoryModule.ts
+++ b/packages/backend/src/models/RepositoryModule.ts
@@ -3,11 +3,83 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import type { Provider } from '@nestjs/common';
 import { Module } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import { MiRepository, MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, MiBubbleGameRecord, MiReversiGame, miRepository } from './_.js';
+import {
+	MiAbuseReportNotificationRecipient,
+	MiAbuseUserReport,
+	MiAccessToken,
+	MiAd,
+	MiAnnouncement,
+	MiAnnouncementRead,
+	MiAntenna,
+	MiApp,
+	MiAuthSession,
+	MiAvatarDecoration,
+	MiBlocking,
+	MiBubbleGameRecord,
+	MiChannel,
+	MiChannelFavorite,
+	MiChannelFollowing,
+	MiClip,
+	MiClipFavorite,
+	MiClipNote,
+	MiDriveFile,
+	MiDriveFolder,
+	MiEmoji,
+	MiFlash,
+	MiFlashLike,
+	MiFollowing,
+	MiFollowRequest,
+	MiGalleryLike,
+	MiGalleryPost,
+	MiHashtag,
+	MiInstance,
+	MiMeta,
+	MiModerationLog,
+	MiMuting,
+	MiNote,
+	MiNoteFavorite,
+	MiNoteReaction,
+	MiNoteThreadMuting,
+	MiNoteUnread,
+	MiPage,
+	MiPageLike,
+	MiPasswordResetRequest,
+	MiPoll,
+	MiPollVote,
+	MiPromoNote,
+	MiPromoRead,
+	MiRegistrationTicket,
+	MiRegistryItem,
+	MiRelay,
+	MiRenoteMuting,
+	MiRepository,
+	miRepository,
+	MiRetentionAggregation,
+	MiReversiGame,
+	MiRole,
+	MiRoleAssignment,
+	MiSignin,
+	MiSwSubscription,
+	MiSystemWebhook,
+	MiUsedUsername,
+	MiUser,
+	MiUserIp,
+	MiUserKeypair,
+	MiUserList,
+	MiUserListFavorite,
+	MiUserListMembership,
+	MiUserMemo,
+	MiUserNotePining,
+	MiUserPending,
+	MiUserProfile,
+	MiUserPublickey,
+	MiUserSecurityKey,
+	MiWebhook
+} from './_.js';
 import type { DataSource } from 'typeorm';
-import type { Provider } from '@nestjs/common';
 
 const $usersRepository: Provider = {
 	provide: DI.usersRepository,
@@ -225,6 +297,12 @@ const $abuseUserReportsRepository: Provider = {
 	inject: [DI.db],
 };
 
+const $abuseReportNotificationRecipientRepository: Provider = {
+	provide: DI.abuseReportNotificationRecipientRepository,
+	useFactory: (db: DataSource) => db.getRepository(MiAbuseReportNotificationRecipient),
+	inject: [DI.db],
+};
+
 const $registrationTicketsRepository: Provider = {
 	provide: DI.registrationTicketsRepository,
 	useFactory: (db: DataSource) => db.getRepository(MiRegistrationTicket).extend(miRepository as MiRepository<MiRegistrationTicket>),
@@ -351,6 +429,12 @@ const $webhooksRepository: Provider = {
 	inject: [DI.db],
 };
 
+const $systemWebhooksRepository: Provider = {
+	provide: DI.systemWebhooksRepository,
+	useFactory: (db: DataSource) => db.getRepository(MiSystemWebhook),
+	inject: [DI.db],
+};
+
 const $adsRepository: Provider = {
 	provide: DI.adsRepository,
 	useFactory: (db: DataSource) => db.getRepository(MiAd).extend(miRepository as MiRepository<MiAd>),
@@ -412,8 +496,7 @@ const $reversiGamesRepository: Provider = {
 };
 
 @Module({
-	imports: [
-	],
+	imports: [],
 	providers: [
 		$usersRepository,
 		$notesRepository,
@@ -451,6 +534,7 @@ const $reversiGamesRepository: Provider = {
 		$swSubscriptionsRepository,
 		$hashtagsRepository,
 		$abuseUserReportsRepository,
+		$abuseReportNotificationRecipientRepository,
 		$registrationTicketsRepository,
 		$authSessionsRepository,
 		$accessTokensRepository,
@@ -472,6 +556,7 @@ const $reversiGamesRepository: Provider = {
 		$channelFavoritesRepository,
 		$registryItemsRepository,
 		$webhooksRepository,
+		$systemWebhooksRepository,
 		$adsRepository,
 		$passwordResetRequestsRepository,
 		$retentionAggregationsRepository,
@@ -520,6 +605,7 @@ const $reversiGamesRepository: Provider = {
 		$swSubscriptionsRepository,
 		$hashtagsRepository,
 		$abuseUserReportsRepository,
+		$abuseReportNotificationRecipientRepository,
 		$registrationTicketsRepository,
 		$authSessionsRepository,
 		$accessTokensRepository,
@@ -541,6 +627,7 @@ const $reversiGamesRepository: Provider = {
 		$channelFavoritesRepository,
 		$registryItemsRepository,
 		$webhooksRepository,
+		$systemWebhooksRepository,
 		$adsRepository,
 		$passwordResetRequestsRepository,
 		$retentionAggregationsRepository,
@@ -553,4 +640,5 @@ const $reversiGamesRepository: Provider = {
 		$reversiGamesRepository,
 	],
 })
-export class RepositoryModule {}
+export class RepositoryModule {
+}
diff --git a/packages/backend/src/models/SystemWebhook.ts b/packages/backend/src/models/SystemWebhook.ts
new file mode 100644
index 0000000000..86fb323d1d
--- /dev/null
+++ b/packages/backend/src/models/SystemWebhook.ts
@@ -0,0 +1,98 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Column, Entity, Index, PrimaryColumn } from 'typeorm';
+import { Serialized } from '@/types.js';
+import { id } from './util/id.js';
+
+export const systemWebhookEventTypes = [
+	// ユーザからの通報を受けたとき
+	'abuseReport',
+	// 通報を処理したとき
+	'abuseReportResolved',
+] as const;
+export type SystemWebhookEventType = typeof systemWebhookEventTypes[number];
+
+@Entity('system_webhook')
+export class MiSystemWebhook {
+	@PrimaryColumn(id())
+	public id: string;
+
+	/**
+	 * 有効かどうか.
+	 */
+	@Index('IDX_system_webhook_isActive', { synchronize: false })
+	@Column('boolean', {
+		default: true,
+	})
+	public isActive: boolean;
+
+	/**
+	 * 更新日時.
+	 */
+	@Column('timestamp with time zone', {
+		default: () => 'CURRENT_TIMESTAMP',
+	})
+	public updatedAt: Date;
+
+	/**
+	 * 最後に送信された日時.
+	 */
+	@Column('timestamp with time zone', {
+		nullable: true,
+	})
+	public latestSentAt: Date | null;
+
+	/**
+	 * 最後に送信されたステータスコード
+	 */
+	@Column('integer', {
+		nullable: true,
+	})
+	public latestStatus: number | null;
+
+	/**
+	 * 通知設定名.
+	 */
+	@Column('varchar', {
+		length: 255,
+	})
+	public name: string;
+
+	/**
+	 * イベント種別.
+	 */
+	@Index('IDX_system_webhook_on', { synchronize: false })
+	@Column('varchar', {
+		length: 128,
+		array: true,
+		default: '{}',
+	})
+	public on: SystemWebhookEventType[];
+
+	/**
+	 * Webhook送信先のURL.
+	 */
+	@Column('varchar', {
+		length: 1024,
+	})
+	public url: string;
+
+	/**
+	 * Webhook検証用の値.
+	 */
+	@Column('varchar', {
+		length: 1024,
+	})
+	public secret: string;
+
+	static deserialize(obj: Serialized<MiSystemWebhook>): MiSystemWebhook {
+		return {
+			...obj,
+			updatedAt: new Date(obj.updatedAt),
+			latestSentAt: obj.latestSentAt ? new Date(obj.latestSentAt) : null,
+		};
+	}
+}
diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts
index 2e6a41586e..d366ce48d0 100644
--- a/packages/backend/src/models/_.ts
+++ b/packages/backend/src/models/_.ts
@@ -11,6 +11,7 @@ import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transfor
 import { ObjectUtils } from 'typeorm/util/ObjectUtils.js';
 import { OrmUtils } from 'typeorm/util/OrmUtils.js';
 import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
+import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
 import { MiAccessToken } from '@/models/AccessToken.js';
 import { MiAd } from '@/models/Ad.js';
 import { MiAnnouncement } from '@/models/Announcement.js';
@@ -68,6 +69,7 @@ import { MiUserPublickey } from '@/models/UserPublickey.js';
 import { MiUserSecurityKey } from '@/models/UserSecurityKey.js';
 import { MiUserMemo } from '@/models/UserMemo.js';
 import { MiWebhook } from '@/models/Webhook.js';
+import { MiSystemWebhook } from '@/models/SystemWebhook.js';
 import { MiChannel } from '@/models/Channel.js';
 import { MiRetentionAggregation } from '@/models/RetentionAggregation.js';
 import { MiRole } from '@/models/Role.js';
@@ -144,6 +146,7 @@ export const miRepository = {
 
 export {
 	MiAbuseUserReport,
+	MiAbuseReportNotificationRecipient,
 	MiAccessToken,
 	MiAd,
 	MiAnnouncement,
@@ -201,6 +204,7 @@ export {
 	MiUserPublickey,
 	MiUserSecurityKey,
 	MiWebhook,
+	MiSystemWebhook,
 	MiChannel,
 	MiRetentionAggregation,
 	MiRole,
@@ -213,6 +217,7 @@ export {
 };
 
 export type AbuseUserReportsRepository = Repository<MiAbuseUserReport> & MiRepository<MiAbuseUserReport>;
+export type AbuseReportNotificationRecipientRepository = Repository<MiAbuseReportNotificationRecipient> & MiRepository<MiAbuseReportNotificationRecipient>;
 export type AccessTokensRepository = Repository<MiAccessToken> & MiRepository<MiAccessToken>;
 export type AdsRepository = Repository<MiAd> & MiRepository<MiAd>;
 export type AnnouncementsRepository = Repository<MiAnnouncement> & MiRepository<MiAnnouncement>;
@@ -270,6 +275,7 @@ export type UserProfilesRepository = Repository<MiUserProfile> & MiRepository<Mi
 export type UserPublickeysRepository = Repository<MiUserPublickey> & MiRepository<MiUserPublickey>;
 export type UserSecurityKeysRepository = Repository<MiUserSecurityKey> & MiRepository<MiUserSecurityKey>;
 export type WebhooksRepository = Repository<MiWebhook> & MiRepository<MiWebhook>;
+export type SystemWebhooksRepository = Repository<MiSystemWebhook> & MiRepository<MiWebhook>;
 export type ChannelsRepository = Repository<MiChannel> & MiRepository<MiChannel>;
 export type RetentionAggregationsRepository = Repository<MiRetentionAggregation> & MiRepository<MiRetentionAggregation>;
 export type RolesRepository = Repository<MiRole> & MiRepository<MiRole>;
diff --git a/packages/backend/src/models/json-schema/abuse-report-notification-recipient.ts b/packages/backend/src/models/json-schema/abuse-report-notification-recipient.ts
new file mode 100644
index 0000000000..6215f0f5a2
--- /dev/null
+++ b/packages/backend/src/models/json-schema/abuse-report-notification-recipient.ts
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const packedAbuseReportNotificationRecipientSchema = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		isActive: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		updatedAt: {
+			type: 'string',
+			format: 'date-time',
+			optional: false, nullable: false,
+		},
+		name: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		method: {
+			type: 'string',
+			optional: false, nullable: false,
+			enum: ['email', 'webhook'],
+		},
+		userId: {
+			type: 'string',
+			optional: true, nullable: false,
+		},
+		user: {
+			type: 'object',
+			optional: true, nullable: false,
+			ref: 'UserLite',
+		},
+		systemWebhookId: {
+			type: 'string',
+			optional: true, nullable: false,
+		},
+		systemWebhook: {
+			type: 'object',
+			optional: true, nullable: false,
+			ref: 'SystemWebhook',
+		},
+	},
+} as const;
diff --git a/packages/backend/src/models/json-schema/system-webhook.ts b/packages/backend/src/models/json-schema/system-webhook.ts
new file mode 100644
index 0000000000..d83065a743
--- /dev/null
+++ b/packages/backend/src/models/json-schema/system-webhook.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { systemWebhookEventTypes } from '@/models/SystemWebhook.js';
+
+export const packedSystemWebhookSchema = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		isActive: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		updatedAt: {
+			type: 'string',
+			format: 'date-time',
+			optional: false, nullable: false,
+		},
+		latestSentAt: {
+			type: 'string',
+			format: 'date-time',
+			optional: false, nullable: true,
+		},
+		latestStatus: {
+			type: 'number',
+			optional: false, nullable: true,
+		},
+		name: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		on: {
+			type: 'array',
+			items: {
+				type: 'string',
+				optional: false, nullable: false,
+				enum: systemWebhookEventTypes,
+			},
+		},
+		url: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		secret: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+	},
+} as const;
diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts
index aa2aa5e373..251a03c303 100644
--- a/packages/backend/src/postgres.ts
+++ b/packages/backend/src/postgres.ts
@@ -5,13 +5,12 @@
 
 // https://github.com/typeorm/typeorm/issues/2400
 import pg from 'pg';
-pg.types.setTypeParser(20, Number);
-
 import { DataSource, Logger } from 'typeorm';
 import * as highlight from 'cli-highlight';
 import { entities as charts } from '@/core/chart/entities.js';
 
 import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
+import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
 import { MiAccessToken } from '@/models/AccessToken.js';
 import { MiAd } from '@/models/Ad.js';
 import { MiAnnouncement } from '@/models/Announcement.js';
@@ -69,6 +68,7 @@ import { MiUserProfile } from '@/models/UserProfile.js';
 import { MiUserPublickey } from '@/models/UserPublickey.js';
 import { MiUserSecurityKey } from '@/models/UserSecurityKey.js';
 import { MiWebhook } from '@/models/Webhook.js';
+import { MiSystemWebhook } from '@/models/SystemWebhook.js';
 import { MiChannel } from '@/models/Channel.js';
 import { MiRetentionAggregation } from '@/models/RetentionAggregation.js';
 import { MiRole } from '@/models/Role.js';
@@ -83,6 +83,8 @@ import { Config } from '@/config.js';
 import MisskeyLogger from '@/logger.js';
 import { bindThis } from '@/decorators.js';
 
+pg.types.setTypeParser(20, Number);
+
 export const dbLogger = new MisskeyLogger('db');
 
 const sqlLogger = dbLogger.createSubLogger('sql', 'gray');
@@ -167,6 +169,7 @@ export const entities = [
 	MiHashtag,
 	MiSwSubscription,
 	MiAbuseUserReport,
+	MiAbuseReportNotificationRecipient,
 	MiRegistrationTicket,
 	MiSignin,
 	MiModerationLog,
@@ -185,6 +188,7 @@ export const entities = [
 	MiPasswordResetRequest,
 	MiUserPending,
 	MiWebhook,
+	MiSystemWebhook,
 	MiUserIp,
 	MiRetentionAggregation,
 	MiRole,
diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts
index 8086158997..a1fd38fcc5 100644
--- a/packages/backend/src/queue/QueueProcessorModule.ts
+++ b/packages/backend/src/queue/QueueProcessorModule.ts
@@ -11,7 +11,8 @@ import { QueueProcessorService } from './QueueProcessorService.js';
 import { DeliverProcessorService } from './processors/DeliverProcessorService.js';
 import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js';
 import { InboxProcessorService } from './processors/InboxProcessorService.js';
-import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js';
+import { UserWebhookDeliverProcessorService } from './processors/UserWebhookDeliverProcessorService.js';
+import { SystemWebhookDeliverProcessorService } from './processors/SystemWebhookDeliverProcessorService.js';
 import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js';
 import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js';
 import { CleanProcessorService } from './processors/CleanProcessorService.js';
@@ -71,7 +72,8 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor
 		DeleteFileProcessorService,
 		CleanRemoteFilesProcessorService,
 		RelationshipProcessorService,
-		WebhookDeliverProcessorService,
+		UserWebhookDeliverProcessorService,
+		SystemWebhookDeliverProcessorService,
 		EndedPollNotificationProcessorService,
 		DeliverProcessorService,
 		InboxProcessorService,
diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index 7bfe1f4caa..7bd74f3210 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -10,7 +10,8 @@ import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import type Logger from '@/logger.js';
 import { bindThis } from '@/decorators.js';
-import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js';
+import { UserWebhookDeliverProcessorService } from './processors/UserWebhookDeliverProcessorService.js';
+import { SystemWebhookDeliverProcessorService } from './processors/SystemWebhookDeliverProcessorService.js';
 import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js';
 import { DeliverProcessorService } from './processors/DeliverProcessorService.js';
 import { InboxProcessorService } from './processors/InboxProcessorService.js';
@@ -76,7 +77,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 	private dbQueueWorker: Bull.Worker;
 	private deliverQueueWorker: Bull.Worker;
 	private inboxQueueWorker: Bull.Worker;
-	private webhookDeliverQueueWorker: Bull.Worker;
+	private userWebhookDeliverQueueWorker: Bull.Worker;
+	private systemWebhookDeliverQueueWorker: Bull.Worker;
 	private relationshipQueueWorker: Bull.Worker;
 	private objectStorageQueueWorker: Bull.Worker;
 	private endedPollNotificationQueueWorker: Bull.Worker;
@@ -86,7 +88,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		private config: Config,
 
 		private queueLoggerService: QueueLoggerService,
-		private webhookDeliverProcessorService: WebhookDeliverProcessorService,
+		private userWebhookDeliverProcessorService: UserWebhookDeliverProcessorService,
+		private systemWebhookDeliverProcessorService: SystemWebhookDeliverProcessorService,
 		private endedPollNotificationProcessorService: EndedPollNotificationProcessorService,
 		private deliverProcessorService: DeliverProcessorService,
 		private inboxProcessorService: InboxProcessorService,
@@ -160,13 +163,13 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				autorun: false,
 			});
 
-			const systemLogger = this.logger.createSubLogger('system');
+			const logger = this.logger.createSubLogger('system');
 
 			this.systemQueueWorker
-				.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
-				.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('active', (job) => logger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) id=${job.id}`))
 				.on('failed', (job, err: Error) => {
-					systemLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					logger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: System: ${job?.name ?? '?'}: ${err.message}`, {
 							level: 'error',
@@ -174,8 +177,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 						});
 					}
 				})
-				.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-				.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
 		}
 		//#endregion
 
@@ -217,13 +220,13 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				autorun: false,
 			});
 
-			const dbLogger = this.logger.createSubLogger('db');
+			const logger = this.logger.createSubLogger('db');
 
 			this.dbQueueWorker
-				.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
-				.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('active', (job) => logger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) id=${job.id}`))
 				.on('failed', (job, err) => {
-					dbLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					logger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: DB: ${job?.name ?? '?'}: ${err.message}`, {
 							level: 'error',
@@ -231,8 +234,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 						});
 					}
 				})
-				.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-				.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
 		}
 		//#endregion
 
@@ -257,13 +260,13 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				},
 			});
 
-			const deliverLogger = this.logger.createSubLogger('deliver');
+			const logger = this.logger.createSubLogger('deliver');
 
 			this.deliverQueueWorker
-				.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
-				.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('active', (job) => logger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
 				.on('failed', (job, err) => {
-					deliverLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
+					logger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: Deliver: ${err.message}`, {
 							level: 'error',
@@ -271,8 +274,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 						});
 					}
 				})
-				.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-				.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`));
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
 		}
 		//#endregion
 
@@ -297,13 +300,13 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				},
 			});
 
-			const inboxLogger = this.logger.createSubLogger('inbox');
+			const logger = this.logger.createSubLogger('inbox');
 
 			this.inboxQueueWorker
-				.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
-				.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
+				.on('active', (job) => logger.debug(`active ${getJobInfo(job, true)}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
 				.on('failed', (job, err) => {
-					inboxLogger.error(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) });
+					logger.error(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: Inbox: ${err.message}`, {
 							level: 'error',
@@ -311,21 +314,21 @@ export class QueueProcessorService implements OnApplicationShutdown {
 						});
 					}
 				})
-				.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-				.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`));
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
 		}
 		//#endregion
 
-		//#region webhook deliver
+		//#region user-webhook deliver
 		{
-			this.webhookDeliverQueueWorker = new Bull.Worker(QUEUE.WEBHOOK_DELIVER, (job) => {
+			this.userWebhookDeliverQueueWorker = new Bull.Worker(QUEUE.USER_WEBHOOK_DELIVER, (job) => {
 				if (this.config.sentryForBackend) {
-					return Sentry.startSpan({ name: 'Queue: WebhookDeliver' }, () => this.webhookDeliverProcessorService.process(job));
+					return Sentry.startSpan({ name: 'Queue: UserWebhookDeliver' }, () => this.userWebhookDeliverProcessorService.process(job));
 				} else {
-					return this.webhookDeliverProcessorService.process(job);
+					return this.userWebhookDeliverProcessorService.process(job);
 				}
 			}, {
-				...baseQueueOptions(this.config, QUEUE.WEBHOOK_DELIVER),
+				...baseQueueOptions(this.config, QUEUE.USER_WEBHOOK_DELIVER),
 				autorun: false,
 				concurrency: 64,
 				limiter: {
@@ -337,22 +340,62 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				},
 			});
 
-			const webhookLogger = this.logger.createSubLogger('webhook');
+			const logger = this.logger.createSubLogger('user-webhook');
 
-			this.webhookDeliverQueueWorker
-				.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
-				.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
+			this.userWebhookDeliverQueueWorker
+				.on('active', (job) => logger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
 				.on('failed', (job, err) => {
-					webhookLogger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
+					logger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
 					if (config.sentryForBackend) {
-						Sentry.captureMessage(`Queue: WebhookDeliver: ${err.message}`, {
+						Sentry.captureMessage(`Queue: UserWebhookDeliver: ${err.message}`, {
 							level: 'error',
 							extra: { job, err },
 						});
 					}
 				})
-				.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-				.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`));
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
+		}
+		//#endregion
+
+		//#region system-webhook deliver
+		{
+			this.systemWebhookDeliverQueueWorker = new Bull.Worker(QUEUE.SYSTEM_WEBHOOK_DELIVER, (job) => {
+				if (this.config.sentryForBackend) {
+					return Sentry.startSpan({ name: 'Queue: SystemWebhookDeliver' }, () => this.systemWebhookDeliverProcessorService.process(job));
+				} else {
+					return this.systemWebhookDeliverProcessorService.process(job);
+				}
+			}, {
+				...baseQueueOptions(this.config, QUEUE.SYSTEM_WEBHOOK_DELIVER),
+				autorun: false,
+				concurrency: 16,
+				limiter: {
+					max: 16,
+					duration: 1000,
+				},
+				settings: {
+					backoffStrategy: httpRelatedBackoff,
+				},
+			});
+
+			const logger = this.logger.createSubLogger('system-webhook');
+
+			this.systemWebhookDeliverQueueWorker
+				.on('active', (job) => logger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
+				.on('failed', (job, err) => {
+					logger.error(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`);
+					if (config.sentryForBackend) {
+						Sentry.captureMessage(`Queue: SystemWebhookDeliver: ${err.message}`, {
+							level: 'error',
+							extra: { job, err },
+						});
+					}
+				})
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
 		}
 		//#endregion
 
@@ -384,13 +427,13 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				},
 			});
 
-			const relationshipLogger = this.logger.createSubLogger('relationship');
+			const logger = this.logger.createSubLogger('relationship');
 
 			this.relationshipQueueWorker
-				.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`))
-				.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('active', (job) => logger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) id=${job.id}`))
 				.on('failed', (job, err) => {
-					relationshipLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					logger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: Relationship: ${job?.name ?? '?'}: ${err.message}`, {
 							level: 'error',
@@ -398,8 +441,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 						});
 					}
 				})
-				.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-				.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`));
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
 		}
 		//#endregion
 
@@ -425,13 +468,13 @@ export class QueueProcessorService implements OnApplicationShutdown {
 				concurrency: 16,
 			});
 
-			const objectStorageLogger = this.logger.createSubLogger('objectStorage');
+			const logger = this.logger.createSubLogger('objectStorage');
 
 			this.objectStorageQueueWorker
-				.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`))
-				.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`))
+				.on('active', (job) => logger.debug(`active id=${job.id}`))
+				.on('completed', (job, result) => logger.debug(`completed(${result}) id=${job.id}`))
 				.on('failed', (job, err) => {
-					objectStorageLogger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
+					logger.error(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) });
 					if (config.sentryForBackend) {
 						Sentry.captureMessage(`Queue: ObjectStorage: ${job?.name ?? '?'}: ${err.message}`, {
 							level: 'error',
@@ -439,8 +482,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 						});
 					}
 				})
-				.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) }))
-				.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`));
+				.on('error', (err: Error) => logger.error(`error ${err.stack}`, { e: renderError(err) }))
+				.on('stalled', (jobId) => logger.warn(`stalled id=${jobId}`));
 		}
 		//#endregion
 
@@ -467,7 +510,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.dbQueueWorker.run(),
 			this.deliverQueueWorker.run(),
 			this.inboxQueueWorker.run(),
-			this.webhookDeliverQueueWorker.run(),
+			this.userWebhookDeliverQueueWorker.run(),
+			this.systemWebhookDeliverQueueWorker.run(),
 			this.relationshipQueueWorker.run(),
 			this.objectStorageQueueWorker.run(),
 			this.endedPollNotificationQueueWorker.run(),
@@ -481,7 +525,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			this.dbQueueWorker.close(),
 			this.deliverQueueWorker.close(),
 			this.inboxQueueWorker.close(),
-			this.webhookDeliverQueueWorker.close(),
+			this.userWebhookDeliverQueueWorker.close(),
+			this.systemWebhookDeliverQueueWorker.close(),
 			this.relationshipQueueWorker.close(),
 			this.objectStorageQueueWorker.close(),
 			this.endedPollNotificationQueueWorker.close(),
diff --git a/packages/backend/src/queue/const.ts b/packages/backend/src/queue/const.ts
index 132e916612..67f689b618 100644
--- a/packages/backend/src/queue/const.ts
+++ b/packages/backend/src/queue/const.ts
@@ -14,7 +14,8 @@ export const QUEUE = {
 	DB: 'db',
 	RELATIONSHIP: 'relationship',
 	OBJECT_STORAGE: 'objectStorage',
-	WEBHOOK_DELIVER: 'webhookDeliver',
+	USER_WEBHOOK_DELIVER: 'userWebhookDeliver',
+	SYSTEM_WEBHOOK_DELIVER: 'systemWebhookDeliver',
 };
 
 export function baseQueueOptions(config: Config, queueName: typeof QUEUE[keyof typeof QUEUE]): Bull.QueueOptions {
diff --git a/packages/backend/src/queue/processors/SystemWebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/SystemWebhookDeliverProcessorService.ts
new file mode 100644
index 0000000000..f6bef52684
--- /dev/null
+++ b/packages/backend/src/queue/processors/SystemWebhookDeliverProcessorService.ts
@@ -0,0 +1,87 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import * as Bull from 'bullmq';
+import { DI } from '@/di-symbols.js';
+import type { SystemWebhooksRepository } from '@/models/_.js';
+import type { Config } from '@/config.js';
+import type Logger from '@/logger.js';
+import { HttpRequestService } from '@/core/HttpRequestService.js';
+import { StatusError } from '@/misc/status-error.js';
+import { bindThis } from '@/decorators.js';
+import { QueueLoggerService } from '../QueueLoggerService.js';
+import { SystemWebhookDeliverJobData } from '../types.js';
+
+@Injectable()
+export class SystemWebhookDeliverProcessorService {
+	private logger: Logger;
+
+	constructor(
+		@Inject(DI.config)
+		private config: Config,
+
+		@Inject(DI.systemWebhooksRepository)
+		private systemWebhooksRepository: SystemWebhooksRepository,
+
+		private httpRequestService: HttpRequestService,
+		private queueLoggerService: QueueLoggerService,
+	) {
+		this.logger = this.queueLoggerService.logger.createSubLogger('webhook');
+	}
+
+	@bindThis
+	public async process(job: Bull.Job<SystemWebhookDeliverJobData>): Promise<string> {
+		try {
+			this.logger.debug(`delivering ${job.data.webhookId}`);
+
+			const res = await this.httpRequestService.send(job.data.to, {
+				method: 'POST',
+				headers: {
+					'User-Agent': 'Misskey-Hooks',
+					'X-Misskey-Host': this.config.host,
+					'X-Misskey-Hook-Id': job.data.webhookId,
+					'X-Misskey-Hook-Secret': job.data.secret,
+					'Content-Type': 'application/json',
+				},
+				body: JSON.stringify({
+					server: this.config.url,
+					hookId: job.data.webhookId,
+					eventId: job.data.eventId,
+					createdAt: job.data.createdAt,
+					type: job.data.type,
+					body: job.data.content,
+				}),
+			});
+
+			this.systemWebhooksRepository.update({ id: job.data.webhookId }, {
+				latestSentAt: new Date(),
+				latestStatus: res.status,
+			});
+
+			return 'Success';
+		} catch (res) {
+			this.logger.error(res as Error);
+
+			this.systemWebhooksRepository.update({ id: job.data.webhookId }, {
+				latestSentAt: new Date(),
+				latestStatus: res instanceof StatusError ? res.statusCode : 1,
+			});
+
+			if (res instanceof StatusError) {
+				// 4xx
+				if (!res.isRetryable) {
+					throw new Bull.UnrecoverableError(`${res.statusCode} ${res.statusMessage}`);
+				}
+
+				// 5xx etc.
+				throw new Error(`${res.statusCode} ${res.statusMessage}`);
+			} else {
+				// DNS error, socket error, timeout ...
+				throw res;
+			}
+		}
+	}
+}
diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/UserWebhookDeliverProcessorService.ts
similarity index 92%
rename from packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts
rename to packages/backend/src/queue/processors/UserWebhookDeliverProcessorService.ts
index 8c260c0137..9ec630ef70 100644
--- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/UserWebhookDeliverProcessorService.ts
@@ -13,10 +13,10 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
 import { StatusError } from '@/misc/status-error.js';
 import { bindThis } from '@/decorators.js';
 import { QueueLoggerService } from '../QueueLoggerService.js';
-import type { WebhookDeliverJobData } from '../types.js';
+import { UserWebhookDeliverJobData } from '../types.js';
 
 @Injectable()
-export class WebhookDeliverProcessorService {
+export class UserWebhookDeliverProcessorService {
 	private logger: Logger;
 
 	constructor(
@@ -33,7 +33,7 @@ export class WebhookDeliverProcessorService {
 	}
 
 	@bindThis
-	public async process(job: Bull.Job<WebhookDeliverJobData>): Promise<string> {
+	public async process(job: Bull.Job<UserWebhookDeliverJobData>): Promise<string> {
 		try {
 			this.logger.debug(`delivering ${job.data.webhookId}`);
 
diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts
index ce57ba745e..a4077a0547 100644
--- a/packages/backend/src/queue/types.ts
+++ b/packages/backend/src/queue/types.ts
@@ -106,7 +106,17 @@ export type EndedPollNotificationJobData = {
 	noteId: MiNote['id'];
 };
 
-export type WebhookDeliverJobData = {
+export type SystemWebhookDeliverJobData = {
+	type: string;
+	content: unknown;
+	webhookId: MiWebhook['id'];
+	to: string;
+	secret: string;
+	createdAt: number;
+	eventId: string;
+};
+
+export type UserWebhookDeliverJobData = {
 	type: string;
 	content: unknown;
 	webhookId: MiWebhook['id'];
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index c645f4bcc6..41576bedaa 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -6,8 +6,13 @@
 import { Module } from '@nestjs/common';
 
 import { CoreModule } from '@/core/CoreModule.js';
-import * as ep___admin_meta from './endpoints/admin/meta.js';
+import * as ep___admin_abuseReport_notificationRecipient_list from '@/server/api/endpoints/admin/abuse-report/notification-recipient/list.js';
+import * as ep___admin_abuseReport_notificationRecipient_show from '@/server/api/endpoints/admin/abuse-report/notification-recipient/show.js';
+import * as ep___admin_abuseReport_notificationRecipient_create from '@/server/api/endpoints/admin/abuse-report/notification-recipient/create.js';
+import * as ep___admin_abuseReport_notificationRecipient_update from '@/server/api/endpoints/admin/abuse-report/notification-recipient/update.js';
+import * as ep___admin_abuseReport_notificationRecipient_delete from '@/server/api/endpoints/admin/abuse-report/notification-recipient/delete.js';
 import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
+import * as ep___admin_meta from './endpoints/admin/meta.js';
 import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
 import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
 import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
@@ -82,6 +87,11 @@ import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js';
 import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js';
 import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
 import * as ep___admin_roles_users from './endpoints/admin/roles/users.js';
+import * as ep___admin_systemWebhook_create from './endpoints/admin/system-webhook/create.js';
+import * as ep___admin_systemWebhook_delete from './endpoints/admin/system-webhook/delete.js';
+import * as ep___admin_systemWebhook_list from './endpoints/admin/system-webhook/list.js';
+import * as ep___admin_systemWebhook_show from './endpoints/admin/system-webhook/show.js';
+import * as ep___admin_systemWebhook_update from './endpoints/admin/system-webhook/update.js';
 import * as ep___announcements from './endpoints/announcements.js';
 import * as ep___announcements_show from './endpoints/announcements/show.js';
 import * as ep___antennas_create from './endpoints/antennas/create.js';
@@ -381,6 +391,11 @@ import type { Provider } from '@nestjs/common';
 
 const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_meta.default };
 const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default };
+const $admin_abuseReport_notificationRecipient_list: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/list', useClass: ep___admin_abuseReport_notificationRecipient_list.default };
+const $admin_abuseReport_notificationRecipient_show: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/show', useClass: ep___admin_abuseReport_notificationRecipient_show.default };
+const $admin_abuseReport_notificationRecipient_create: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/create', useClass: ep___admin_abuseReport_notificationRecipient_create.default };
+const $admin_abuseReport_notificationRecipient_update: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/update', useClass: ep___admin_abuseReport_notificationRecipient_update.default };
+const $admin_abuseReport_notificationRecipient_delete: Provider = { provide: 'ep:admin/abuse-report/notification-recipient/delete', useClass: ep___admin_abuseReport_notificationRecipient_delete.default };
 const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default };
 const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default };
 const $admin_accounts_findByEmail: Provider = { provide: 'ep:admin/accounts/find-by-email', useClass: ep___admin_accounts_findByEmail.default };
@@ -455,6 +470,11 @@ const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useCla
 const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default };
 const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default };
 const $admin_roles_users: Provider = { provide: 'ep:admin/roles/users', useClass: ep___admin_roles_users.default };
+const $admin_systemWebhook_create: Provider = { provide: 'ep:admin/system-webhook/create', useClass: ep___admin_systemWebhook_create.default };
+const $admin_systemWebhook_delete: Provider = { provide: 'ep:admin/system-webhook/delete', useClass: ep___admin_systemWebhook_delete.default };
+const $admin_systemWebhook_list: Provider = { provide: 'ep:admin/system-webhook/list', useClass: ep___admin_systemWebhook_list.default };
+const $admin_systemWebhook_show: Provider = { provide: 'ep:admin/system-webhook/show', useClass: ep___admin_systemWebhook_show.default };
+const $admin_systemWebhook_update: Provider = { provide: 'ep:admin/system-webhook/update', useClass: ep___admin_systemWebhook_update.default };
 const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default };
 const $announcements_show: Provider = { provide: 'ep:announcements/show', useClass: ep___announcements_show.default };
 const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default };
@@ -758,6 +778,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		ApiLoggerService,
 		$admin_meta,
 		$admin_abuseUserReports,
+		$admin_abuseReport_notificationRecipient_list,
+		$admin_abuseReport_notificationRecipient_show,
+		$admin_abuseReport_notificationRecipient_create,
+		$admin_abuseReport_notificationRecipient_update,
+		$admin_abuseReport_notificationRecipient_delete,
 		$admin_accounts_create,
 		$admin_accounts_delete,
 		$admin_accounts_findByEmail,
@@ -832,6 +857,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$admin_roles_unassign,
 		$admin_roles_updateDefaultPolicies,
 		$admin_roles_users,
+		$admin_systemWebhook_create,
+		$admin_systemWebhook_delete,
+		$admin_systemWebhook_list,
+		$admin_systemWebhook_show,
+		$admin_systemWebhook_update,
 		$announcements,
 		$announcements_show,
 		$antennas_create,
@@ -1129,6 +1159,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 	exports: [
 		$admin_meta,
 		$admin_abuseUserReports,
+		$admin_abuseReport_notificationRecipient_list,
+		$admin_abuseReport_notificationRecipient_show,
+		$admin_abuseReport_notificationRecipient_create,
+		$admin_abuseReport_notificationRecipient_update,
+		$admin_abuseReport_notificationRecipient_delete,
 		$admin_accounts_create,
 		$admin_accounts_delete,
 		$admin_accounts_findByEmail,
@@ -1203,6 +1238,11 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
 		$admin_roles_unassign,
 		$admin_roles_updateDefaultPolicies,
 		$admin_roles_users,
+		$admin_systemWebhook_create,
+		$admin_systemWebhook_delete,
+		$admin_systemWebhook_list,
+		$admin_systemWebhook_show,
+		$admin_systemWebhook_update,
 		$announcements,
 		$announcements_show,
 		$antennas_create,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index a38c62f35a..3dfb7fdad4 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -6,8 +6,18 @@
 import { permissions } from 'misskey-js';
 import type { KeyOf, Schema } from '@/misc/json-schema.js';
 
-import * as ep___admin_meta from './endpoints/admin/meta.js';
+import * as ep___admin_abuseReport_notificationRecipient_list
+	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/list.js';
+import * as ep___admin_abuseReport_notificationRecipient_show
+	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/show.js';
+import * as ep___admin_abuseReport_notificationRecipient_create
+	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/create.js';
+import * as ep___admin_abuseReport_notificationRecipient_update
+	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/update.js';
+import * as ep___admin_abuseReport_notificationRecipient_delete
+	from '@/server/api/endpoints/admin/abuse-report/notification-recipient/delete.js';
 import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
+import * as ep___admin_meta from './endpoints/admin/meta.js';
 import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
 import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
 import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js';
@@ -44,7 +54,8 @@ import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-c
 import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js';
 import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js';
 import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js';
-import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
+import * as ep___admin_federation_refreshRemoteInstanceMetadata
+	from './endpoints/admin/federation/refresh-remote-instance-metadata.js';
 import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js';
 import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js';
 import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js';
@@ -82,6 +93,11 @@ import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js';
 import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js';
 import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
 import * as ep___admin_roles_users from './endpoints/admin/roles/users.js';
+import * as ep___admin_systemWebhook_create from './endpoints/admin/system-webhook/create.js';
+import * as ep___admin_systemWebhook_delete from './endpoints/admin/system-webhook/delete.js';
+import * as ep___admin_systemWebhook_list from './endpoints/admin/system-webhook/list.js';
+import * as ep___admin_systemWebhook_show from './endpoints/admin/system-webhook/show.js';
+import * as ep___admin_systemWebhook_update from './endpoints/admin/system-webhook/update.js';
 import * as ep___announcements from './endpoints/announcements.js';
 import * as ep___announcements_show from './endpoints/announcements/show.js';
 import * as ep___antennas_create from './endpoints/antennas/create.js';
@@ -379,6 +395,11 @@ import * as ep___reversi_verify from './endpoints/reversi/verify.js';
 const eps = [
 	['admin/meta', ep___admin_meta],
 	['admin/abuse-user-reports', ep___admin_abuseUserReports],
+	['admin/abuse-report/notification-recipient/list', ep___admin_abuseReport_notificationRecipient_list],
+	['admin/abuse-report/notification-recipient/show', ep___admin_abuseReport_notificationRecipient_show],
+	['admin/abuse-report/notification-recipient/create', ep___admin_abuseReport_notificationRecipient_create],
+	['admin/abuse-report/notification-recipient/update', ep___admin_abuseReport_notificationRecipient_update],
+	['admin/abuse-report/notification-recipient/delete', ep___admin_abuseReport_notificationRecipient_delete],
 	['admin/accounts/create', ep___admin_accounts_create],
 	['admin/accounts/delete', ep___admin_accounts_delete],
 	['admin/accounts/find-by-email', ep___admin_accounts_findByEmail],
@@ -453,6 +474,11 @@ const eps = [
 	['admin/roles/unassign', ep___admin_roles_unassign],
 	['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies],
 	['admin/roles/users', ep___admin_roles_users],
+	['admin/system-webhook/create', ep___admin_systemWebhook_create],
+	['admin/system-webhook/delete', ep___admin_systemWebhook_delete],
+	['admin/system-webhook/list', ep___admin_systemWebhook_list],
+	['admin/system-webhook/show', ep___admin_systemWebhook_show],
+	['admin/system-webhook/update', ep___admin_systemWebhook_update],
 	['announcements', ep___announcements],
 	['announcements/show', ep___announcements_show],
 	['antennas/create', ep___antennas_create],
@@ -873,8 +899,12 @@ export interface IEndpoint {
 const endpoints: IEndpoint[] = (eps as [string, any]).map(([name, ep]) => {
 	return {
 		name: name,
-		get meta() { return ep.meta ?? {}; },
-		get params() { return ep.paramDef; },
+		get meta() {
+			return ep.meta ?? {};
+		},
+		get params() {
+			return ep.paramDef;
+		},
 	};
 });
 
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/create.ts b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/create.ts
new file mode 100644
index 0000000000..bdfbcba518
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/create.ts
@@ -0,0 +1,122 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { ApiError } from '@/server/api/error.js';
+import {
+	AbuseReportNotificationRecipientEntityService,
+} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+import { DI } from '@/di-symbols.js';
+import type { UserProfilesRepository } from '@/models/_.js';
+
+export const meta = {
+	tags: ['admin', 'abuse-report', 'notification-recipient'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:abuse-report:notification-recipient',
+
+	res: {
+		type: 'object',
+		ref: 'AbuseReportNotificationRecipient',
+	},
+
+	errors: {
+		correlationCheckEmail: {
+			message: 'If "method" is email, "userId" must be set.',
+			code: 'CORRELATION_CHECK_EMAIL',
+			id: '348bb8ae-575a-6fe9-4327-5811999def8f',
+			httpStatusCode: 400,
+		},
+		correlationCheckWebhook: {
+			message: 'If "method" is webhook, "systemWebhookId" must be set.',
+			code: 'CORRELATION_CHECK_WEBHOOK',
+			id: 'b0c15051-de2d-29ef-260c-9585cddd701a',
+			httpStatusCode: 400,
+		},
+		emailAddressNotSet: {
+			message: 'Email address is not set.',
+			code: 'EMAIL_ADDRESS_NOT_SET',
+			id: '7cc1d85e-2f58-fc31-b644-3de8d0d3421f',
+			httpStatusCode: 400,
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		isActive: {
+			type: 'boolean',
+		},
+		name: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 255,
+		},
+		method: {
+			type: 'string',
+			enum: ['email', 'webhook'],
+		},
+		userId: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+		systemWebhookId: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+	},
+	required: [
+		'isActive',
+		'name',
+		'method',
+	],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		@Inject(DI.userProfilesRepository)
+		private userProfilesRepository: UserProfilesRepository,
+		private abuseReportNotificationService: AbuseReportNotificationService,
+		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			if (ps.method === 'email') {
+				const userProfile = await this.userProfilesRepository.findOneBy({ userId: ps.userId });
+				if (!ps.userId || !userProfile) {
+					throw new ApiError(meta.errors.correlationCheckEmail);
+				}
+
+				if (!userProfile.email || !userProfile.emailVerified) {
+					throw new ApiError(meta.errors.emailAddressNotSet);
+				}
+			}
+
+			if (ps.method === 'webhook' && !ps.systemWebhookId) {
+				throw new ApiError(meta.errors.correlationCheckWebhook);
+			}
+
+			const userId = ps.method === 'email' ? ps.userId : null;
+			const systemWebhookId = ps.method === 'webhook' ? ps.systemWebhookId : null;
+			const result = await this.abuseReportNotificationService.createRecipient(
+				{
+					isActive: ps.isActive,
+					name: ps.name,
+					method: ps.method,
+					userId: userId ?? null,
+					systemWebhookId: systemWebhookId ?? null,
+				},
+				me,
+			);
+
+			return this.abuseReportNotificationRecipientEntityService.pack(result);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/delete.ts b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/delete.ts
new file mode 100644
index 0000000000..b6dc44e09c
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/delete.ts
@@ -0,0 +1,44 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+
+export const meta = {
+	tags: ['admin', 'abuse-report', 'notification-recipient'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:abuse-report:notification-recipient',
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+	},
+	required: [
+		'id',
+	],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private abuseReportNotificationService: AbuseReportNotificationService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			await this.abuseReportNotificationService.deleteRecipient(
+				ps.id,
+				me,
+			);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/list.ts b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/list.ts
new file mode 100644
index 0000000000..dad9161a8a
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/list.ts
@@ -0,0 +1,55 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import {
+	AbuseReportNotificationRecipientEntityService,
+} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+
+export const meta = {
+	tags: ['admin', 'abuse-report', 'notification-recipient'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'read:admin:abuse-report:notification-recipient',
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			ref: 'AbuseReportNotificationRecipient',
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		method: {
+			type: 'array',
+			items: {
+				type: 'string',
+				enum: ['email', 'webhook'],
+			},
+		},
+	},
+	required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private abuseReportNotificationService: AbuseReportNotificationService,
+		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
+	) {
+		super(meta, paramDef, async (ps) => {
+			const recipients = await this.abuseReportNotificationService.fetchRecipients({ method: ps.method });
+			return this.abuseReportNotificationRecipientEntityService.packMany(recipients);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/show.ts b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/show.ts
new file mode 100644
index 0000000000..557798f946
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/show.ts
@@ -0,0 +1,64 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import {
+	AbuseReportNotificationRecipientEntityService,
+} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+import { ApiError } from '@/server/api/error.js';
+
+export const meta = {
+	tags: ['admin', 'abuse-report', 'notification-recipient'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'read:admin:abuse-report:notification-recipient',
+
+	res: {
+		type: 'object',
+		ref: 'AbuseReportNotificationRecipient',
+	},
+
+	errors: {
+		noSuchRecipient: {
+			message: 'No such recipient.',
+			code: 'NO_SUCH_RECIPIENT',
+			id: '013de6a8-f757-04cb-4d73-cc2a7e3368e4',
+			kind: 'server',
+			httpStatusCode: 404,
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+	},
+	required: ['id'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private abuseReportNotificationService: AbuseReportNotificationService,
+		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
+	) {
+		super(meta, paramDef, async (ps) => {
+			const recipients = await this.abuseReportNotificationService.fetchRecipients({ ids: [ps.id] });
+			if (recipients.length === 0) {
+				throw new ApiError(meta.errors.noSuchRecipient);
+			}
+
+			return this.abuseReportNotificationRecipientEntityService.pack(recipients[0]);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/update.ts b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/update.ts
new file mode 100644
index 0000000000..bd4b485217
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-report/notification-recipient/update.ts
@@ -0,0 +1,128 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { ApiError } from '@/server/api/error.js';
+import {
+	AbuseReportNotificationRecipientEntityService,
+} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+import { DI } from '@/di-symbols.js';
+import type { UserProfilesRepository } from '@/models/_.js';
+
+export const meta = {
+	tags: ['admin', 'abuse-report', 'notification-recipient'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:abuse-report:notification-recipient',
+
+	res: {
+		type: 'object',
+		ref: 'AbuseReportNotificationRecipient',
+	},
+
+	errors: {
+		correlationCheckEmail: {
+			message: 'If "method" is email, "userId" must be set.',
+			code: 'CORRELATION_CHECK_EMAIL',
+			id: '348bb8ae-575a-6fe9-4327-5811999def8f',
+			httpStatusCode: 400,
+		},
+		correlationCheckWebhook: {
+			message: 'If "method" is webhook, "systemWebhookId" must be set.',
+			code: 'CORRELATION_CHECK_WEBHOOK',
+			id: 'b0c15051-de2d-29ef-260c-9585cddd701a',
+			httpStatusCode: 400,
+		},
+		emailAddressNotSet: {
+			message: 'Email address is not set.',
+			code: 'EMAIL_ADDRESS_NOT_SET',
+			id: '7cc1d85e-2f58-fc31-b644-3de8d0d3421f',
+			httpStatusCode: 400,
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+		isActive: {
+			type: 'boolean',
+		},
+		name: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 255,
+		},
+		method: {
+			type: 'string',
+			enum: ['email', 'webhook'],
+		},
+		userId: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+		systemWebhookId: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+	},
+	required: [
+		'id',
+		'isActive',
+		'name',
+		'method',
+	],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		@Inject(DI.userProfilesRepository)
+		private userProfilesRepository: UserProfilesRepository,
+		private abuseReportNotificationService: AbuseReportNotificationService,
+		private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			if (ps.method === 'email') {
+				const userProfile = await this.userProfilesRepository.findOneBy({ userId: ps.userId });
+				if (!ps.userId || !userProfile) {
+					throw new ApiError(meta.errors.correlationCheckEmail);
+				}
+
+				if (!userProfile.email || !userProfile.emailVerified) {
+					throw new ApiError(meta.errors.emailAddressNotSet);
+				}
+			}
+
+			if (ps.method === 'webhook' && !ps.systemWebhookId) {
+				throw new ApiError(meta.errors.correlationCheckWebhook);
+			}
+
+			const userId = ps.method === 'email' ? ps.userId : null;
+			const systemWebhookId = ps.method === 'webhook' ? ps.systemWebhookId : null;
+			const result = await this.abuseReportNotificationService.updateRecipient(
+				{
+					id: ps.id,
+					isActive: ps.isActive,
+					name: ps.name,
+					method: ps.method,
+					userId: userId ?? null,
+					systemWebhookId: systemWebhookId ?? null,
+				},
+				me,
+			);
+
+			return this.abuseReportNotificationRecipientEntityService.pack(result);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
index 9694b3fa40..d7f9e4eaa3 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js';
+import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, UserWebhookDeliverQueue, SystemWebhookDeliverQueue } from '@/core/QueueModule.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -53,7 +53,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject('queue:inbox') public inboxQueue: InboxQueue,
 		@Inject('queue:db') public dbQueue: DbQueue,
 		@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
-		@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue,
+		@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
+		@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const deliverJobCounts = await this.deliverQueue.getJobCounts();
diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
index 8b0456068b..9b79100fcf 100644
--- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
+++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
@@ -5,12 +5,10 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import type { UsersRepository, AbuseUserReportsRepository } from '@/models/_.js';
-import { InstanceActorService } from '@/core/InstanceActorService.js';
-import { QueueService } from '@/core/QueueService.js';
-import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
+import type { AbuseUserReportsRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
-import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { ApiError } from '@/server/api/error.js';
+import { AbuseReportService } from '@/core/AbuseReportService.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -18,6 +16,16 @@ export const meta = {
 	requireCredential: true,
 	requireModerator: true,
 	kind: 'write:admin:resolve-abuse-user-report',
+
+	errors: {
+		noSuchAbuseReport: {
+			message: 'No such abuse report.',
+			code: 'NO_SUCH_ABUSE_REPORT',
+			id: 'ac3794dd-2ce4-d878-e546-73c60c06b398',
+			kind: 'server',
+			httpStatusCode: 404,
+		},
+	},
 } as const;
 
 export const paramDef = {
@@ -29,47 +37,20 @@ export const paramDef = {
 	required: ['reportId'],
 } as const;
 
-// TODO: ロジックをサービスに切り出す
-
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
-		@Inject(DI.usersRepository)
-		private usersRepository: UsersRepository,
-
 		@Inject(DI.abuseUserReportsRepository)
 		private abuseUserReportsRepository: AbuseUserReportsRepository,
-
-		private queueService: QueueService,
-		private instanceActorService: InstanceActorService,
-		private apRendererService: ApRendererService,
-		private moderationLogService: ModerationLogService,
+		private abuseReportService: AbuseReportService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
-
-			if (report == null) {
-				throw new Error('report not found');
+			if (!report) {
+				throw new ApiError(meta.errors.noSuchAbuseReport);
 			}
 
-			if (ps.forward && report.targetUserHost != null) {
-				const actor = await this.instanceActorService.getInstanceActor();
-				const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
-
-				this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox, false);
-			}
-
-			await this.abuseUserReportsRepository.update(report.id, {
-				resolved: true,
-				assigneeId: me.id,
-				forwarded: ps.forward && report.targetUserHost != null,
-			});
-
-			this.moderationLogService.log(me, 'resolveAbuseReport', {
-				reportId: report.id,
-				report: report,
-				forwarded: ps.forward && report.targetUserHost != null,
-			});
+			await this.abuseReportService.resolve([{ reportId: report.id, forward: ps.forward }], me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts b/packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts
new file mode 100644
index 0000000000..28071e7a33
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/system-webhook/create.ts
@@ -0,0 +1,85 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
+import { systemWebhookEventTypes } from '@/models/SystemWebhook.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+
+export const meta = {
+	tags: ['admin', 'system-webhook'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:system-webhook',
+
+	res: {
+		type: 'object',
+		ref: 'SystemWebhook',
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		isActive: {
+			type: 'boolean',
+		},
+		name: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 255,
+		},
+		on: {
+			type: 'array',
+			items: {
+				type: 'string',
+				enum: systemWebhookEventTypes,
+			},
+		},
+		url: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 1024,
+		},
+		secret: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 1024,
+		},
+	},
+	required: [
+		'isActive',
+		'name',
+		'on',
+		'url',
+		'secret',
+	],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private systemWebhookService: SystemWebhookService,
+		private systemWebhookEntityService: SystemWebhookEntityService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const result = await this.systemWebhookService.createSystemWebhook(
+				{
+					isActive: ps.isActive,
+					name: ps.name,
+					on: ps.on,
+					url: ps.url,
+					secret: ps.secret,
+				},
+				me,
+			);
+
+			return this.systemWebhookEntityService.pack(result);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/system-webhook/delete.ts b/packages/backend/src/server/api/endpoints/admin/system-webhook/delete.ts
new file mode 100644
index 0000000000..9cdfc7e70f
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/system-webhook/delete.ts
@@ -0,0 +1,44 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+
+export const meta = {
+	tags: ['admin', 'system-webhook'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:system-webhook',
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+	},
+	required: [
+		'id',
+	],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private systemWebhookService: SystemWebhookService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			await this.systemWebhookService.deleteSystemWebhook(
+				ps.id,
+				me,
+			);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/system-webhook/list.ts b/packages/backend/src/server/api/endpoints/admin/system-webhook/list.ts
new file mode 100644
index 0000000000..7a440a774e
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/system-webhook/list.ts
@@ -0,0 +1,60 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
+import { systemWebhookEventTypes } from '@/models/SystemWebhook.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+
+export const meta = {
+	tags: ['admin', 'system-webhook'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:system-webhook',
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			ref: 'SystemWebhook',
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		isActive: {
+			type: 'boolean',
+		},
+		on: {
+			type: 'array',
+			items: {
+				type: 'string',
+				enum: systemWebhookEventTypes,
+			},
+		},
+	},
+	required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private systemWebhookService: SystemWebhookService,
+		private systemWebhookEntityService: SystemWebhookEntityService,
+	) {
+		super(meta, paramDef, async (ps) => {
+			const webhooks = await this.systemWebhookService.fetchSystemWebhooks({
+				isActive: ps.isActive,
+				on: ps.on,
+			});
+			return this.systemWebhookEntityService.packMany(webhooks);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/system-webhook/show.ts b/packages/backend/src/server/api/endpoints/admin/system-webhook/show.ts
new file mode 100644
index 0000000000..75862c96a7
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/system-webhook/show.ts
@@ -0,0 +1,62 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
+import { ApiError } from '@/server/api/error.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+
+export const meta = {
+	tags: ['admin', 'system-webhook'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:system-webhook',
+
+	res: {
+		type: 'object',
+		ref: 'SystemWebhook',
+	},
+
+	errors: {
+		noSuchSystemWebhook: {
+			message: 'No such SystemWebhook.',
+			code: 'NO_SUCH_SYSTEM_WEBHOOK',
+			id: '38dd1ffe-04b4-6ff5-d8ba-4e6a6ae22c9d',
+			kind: 'server',
+			httpStatusCode: 404,
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+	},
+	required: ['id'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private systemWebhookService: SystemWebhookService,
+		private systemWebhookEntityService: SystemWebhookEntityService,
+	) {
+		super(meta, paramDef, async (ps) => {
+			const webhooks = await this.systemWebhookService.fetchSystemWebhooks({ ids: [ps.id] });
+			if (webhooks.length === 0) {
+				throw new ApiError(meta.errors.noSuchSystemWebhook);
+			}
+
+			return this.systemWebhookEntityService.pack(webhooks[0]);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts b/packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts
new file mode 100644
index 0000000000..8d68bb8f87
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/system-webhook/update.ts
@@ -0,0 +1,91 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
+import { systemWebhookEventTypes } from '@/models/SystemWebhook.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+
+export const meta = {
+	tags: ['admin', 'system-webhook'],
+
+	requireCredential: true,
+	requireModerator: true,
+	secure: true,
+	kind: 'write:admin:system-webhook',
+
+	res: {
+		type: 'object',
+		ref: 'SystemWebhook',
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			format: 'misskey:id',
+		},
+		isActive: {
+			type: 'boolean',
+		},
+		name: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 255,
+		},
+		on: {
+			type: 'array',
+			items: {
+				type: 'string',
+				enum: systemWebhookEventTypes,
+			},
+		},
+		url: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 1024,
+		},
+		secret: {
+			type: 'string',
+			minLength: 1,
+			maxLength: 1024,
+		},
+	},
+	required: [
+		'id',
+		'isActive',
+		'name',
+		'on',
+		'url',
+		'secret',
+	],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		private systemWebhookService: SystemWebhookService,
+		private systemWebhookEntityService: SystemWebhookEntityService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const result = await this.systemWebhookService.updateSystemWebhook(
+				{
+					id: ps.id,
+					isActive: ps.isActive,
+					name: ps.name,
+					on: ps.on,
+					url: ps.url,
+					secret: ps.secret,
+				},
+				me,
+			);
+
+			return this.systemWebhookEntityService.pack(result);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
index 48e14b68cc..5ff6de37d2 100644
--- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts
+++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
@@ -3,17 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import sanitizeHtml from 'sanitize-html';
-import { Inject, Injectable } from '@nestjs/common';
-import type { AbuseUserReportsRepository } from '@/models/_.js';
-import { IdService } from '@/core/IdService.js';
+import { Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
-import { MetaService } from '@/core/MetaService.js';
-import { EmailService } from '@/core/EmailService.js';
-import { DI } from '@/di-symbols.js';
 import { GetterService } from '@/server/api/GetterService.js';
 import { RoleService } from '@/core/RoleService.js';
+import { AbuseReportService } from '@/core/AbuseReportService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -57,60 +51,32 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
-		@Inject(DI.abuseUserReportsRepository)
-		private abuseUserReportsRepository: AbuseUserReportsRepository,
-
-		private idService: IdService,
-		private metaService: MetaService,
-		private emailService: EmailService,
 		private getterService: GetterService,
 		private roleService: RoleService,
-		private globalEventService: GlobalEventService,
+		private abuseReportService: AbuseReportService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			// Lookup user
-			const user = await this.getterService.getUser(ps.userId).catch(err => {
+			const targetUser = await this.getterService.getUser(ps.userId).catch(err => {
 				if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
 				throw err;
 			});
 
-			if (user.id === me.id) {
+			if (targetUser.id === me.id) {
 				throw new ApiError(meta.errors.cannotReportYourself);
 			}
 
-			if (await this.roleService.isAdministrator(user)) {
+			if (await this.roleService.isAdministrator(targetUser)) {
 				throw new ApiError(meta.errors.cannotReportAdmin);
 			}
 
-			const report = await this.abuseUserReportsRepository.insertOne({
-				id: this.idService.gen(),
-				targetUserId: user.id,
-				targetUserHost: user.host,
+			await this.abuseReportService.report([{
+				targetUserId: targetUser.id,
+				targetUserHost: targetUser.host,
 				reporterId: me.id,
 				reporterHost: null,
 				comment: ps.comment,
-			});
-
-			// Publish event to moderators
-			setImmediate(async () => {
-				const moderators = await this.roleService.getModerators();
-
-				for (const moderator of moderators) {
-					this.globalEventService.publishAdminStream(moderator.id, 'newAbuseUserReport', {
-						id: report.id,
-						targetUserId: report.targetUserId,
-						reporterId: report.reporterId,
-						comment: report.comment,
-					});
-				}
-
-				const meta = await this.metaService.fetch();
-				if (meta.email) {
-					this.emailService.sendEmail(meta.email, 'New abuse report',
-						sanitizeHtml(ps.comment),
-						sanitizeHtml(ps.comment));
-				}
-			});
+			}]);
 		});
 	}
 }
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index ab03489c0d..f55790b636 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -25,7 +25,16 @@ import { getNoteSummary } from '@/misc/get-note-summary.js';
 import { DI } from '@/di-symbols.js';
 import * as Acct from '@/misc/acct.js';
 import { MetaService } from '@/core/MetaService.js';
-import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js';
+import type {
+	DbQueue,
+	DeliverQueue,
+	EndedPollNotificationQueue,
+	InboxQueue,
+	ObjectStorageQueue,
+	SystemQueue,
+	UserWebhookDeliverQueue,
+	SystemWebhookDeliverQueue,
+} from '@/core/QueueModule.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { PageEntityService } from '@/core/entities/PageEntityService.js';
@@ -111,7 +120,8 @@ export class ClientServerService {
 		@Inject('queue:inbox') public inboxQueue: InboxQueue,
 		@Inject('queue:db') public dbQueue: DbQueue,
 		@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
-		@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue,
+		@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
+		@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
 	) {
 		//this.createServer = this.createServer.bind(this);
 	}
@@ -239,7 +249,8 @@ export class ClientServerService {
 				this.inboxQueue,
 				this.dbQueue,
 				this.objectStorageQueue,
-				this.webhookDeliverQueue,
+				this.userWebhookDeliverQueue,
+				this.systemWebhookDeliverQueue,
 			].map(q => new BullMQAdapter(q)),
 			serverAdapter,
 		});
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 929070d0d2..ecbbee4eff 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -90,6 +90,12 @@ export const moderationLogTypes = [
 	'deleteAvatarDecoration',
 	'unsetUserAvatar',
 	'unsetUserBanner',
+	'createSystemWebhook',
+	'updateSystemWebhook',
+	'deleteSystemWebhook',
+	'createAbuseReportNotificationRecipient',
+	'updateAbuseReportNotificationRecipient',
+	'deleteAbuseReportNotificationRecipient',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -282,6 +288,32 @@ export type ModerationLogPayloads = {
 		userHost: string | null;
 		fileId: string;
 	};
+	createSystemWebhook: {
+		systemWebhookId: string;
+		webhook: any;
+	};
+	updateSystemWebhook: {
+		systemWebhookId: string;
+		before: any;
+		after: any;
+	};
+	deleteSystemWebhook: {
+		systemWebhookId: string;
+		webhook: any;
+	};
+	createAbuseReportNotificationRecipient: {
+		recipientId: string;
+		recipient: any;
+	};
+	updateAbuseReportNotificationRecipient: {
+		recipientId: string;
+		before: any;
+		after: any;
+	};
+	deleteAbuseReportNotificationRecipient: {
+		recipientId: string;
+		recipient: any;
+	};
 };
 
 export type Serialized<T> = {
diff --git a/packages/backend/test/e2e/synalio/abuse-report.ts b/packages/backend/test/e2e/synalio/abuse-report.ts
new file mode 100644
index 0000000000..b0cc3d13ec
--- /dev/null
+++ b/packages/backend/test/e2e/synalio/abuse-report.ts
@@ -0,0 +1,401 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { entities } from 'misskey-js';
+import { beforeEach, describe, test } from '@jest/globals';
+import Fastify from 'fastify';
+import { api, randomString, role, signup, startJobQueue, UserToken } from '../../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+
+const WEBHOOK_HOST = 'http://localhost:15080';
+const WEBHOOK_PORT = 15080;
+process.env.NODE_ENV = 'test';
+
+describe('[シナリオ] ユーザ通報', () => {
+	let queue: INestApplicationContext;
+	let admin: entities.SignupResponse;
+	let alice: entities.SignupResponse;
+	let bob: entities.SignupResponse;
+
+	type SystemWebhookPayload = {
+		server: string;
+		hookId: string;
+		eventId: string;
+		createdAt: string;
+		type: string;
+		body: any;
+	}
+
+	// -------------------------------------------------------------------------------------------
+
+	async function captureWebhook<T = SystemWebhookPayload>(postAction: () => Promise<void>): Promise<T> {
+		const fastify = Fastify();
+
+		let timeoutHandle: NodeJS.Timeout | null = null;
+		const result = await new Promise<string>(async (resolve, reject) => {
+			fastify.all('/', async (req, res) => {
+				timeoutHandle && clearTimeout(timeoutHandle);
+
+				const body = JSON.stringify(req.body);
+				res.status(200).send('ok');
+				await fastify.close();
+				resolve(body);
+			});
+
+			await fastify.listen({ port: WEBHOOK_PORT });
+
+			timeoutHandle = setTimeout(async () => {
+				await fastify.close();
+				reject(new Error('timeout'));
+			}, 3000);
+
+			try {
+				await postAction();
+			} catch (e) {
+				await fastify.close();
+				reject(e);
+			}
+		});
+
+		await fastify.close();
+
+		return JSON.parse(result) as T;
+	}
+
+	async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
+		const res = await api(
+			'admin/system-webhook/create',
+			{
+				isActive: true,
+				name: randomString(),
+				on: ['abuseReport'],
+				url: WEBHOOK_HOST,
+				secret: randomString(),
+				...args,
+			},
+			credential ?? admin,
+		);
+		return res.body;
+	}
+
+	async function createAbuseReportNotificationRecipient(args?: Partial<entities.AdminAbuseReportNotificationRecipientCreateRequest>, credential?: UserToken): Promise<entities.AdminAbuseReportNotificationRecipientCreateResponse> {
+		const res = await api(
+			'admin/abuse-report/notification-recipient/create',
+			{
+				isActive: true,
+				name: randomString(),
+				method: 'webhook',
+				...args,
+			},
+			credential ?? admin,
+		);
+		return res.body;
+	}
+
+	async function createAbuseReport(args?: Partial<entities.UsersReportAbuseRequest>, credential?: UserToken): Promise<entities.EmptyResponse> {
+		const res = await api(
+			'users/report-abuse',
+			{
+				userId: alice.id,
+				comment: randomString(),
+				...args,
+			},
+			credential ?? admin,
+		);
+		return res.body;
+	}
+
+	async function resolveAbuseReport(args?: Partial<entities.AdminResolveAbuseUserReportRequest>, credential?: UserToken): Promise<entities.EmptyResponse> {
+		const res = await api(
+			'admin/resolve-abuse-user-report',
+			{
+				reportId: admin.id,
+				...args,
+			},
+			credential ?? admin,
+		);
+		return res.body;
+	}
+
+	// -------------------------------------------------------------------------------------------
+
+	beforeAll(async () => {
+		queue = await startJobQueue();
+		admin = await signup({ username: 'admin' });
+		alice = await signup({ username: 'alice' });
+		bob = await signup({ username: 'bob' });
+
+		await role(admin, { isAdministrator: true });
+	}, 1000 * 60 * 2);
+
+	afterAll(async () => {
+		await queue.close();
+	});
+
+	// -------------------------------------------------------------------------------------------
+
+	describe('SystemWebhook', () => {
+		beforeEach(async () => {
+			const webhooks = await api('admin/system-webhook/list', {}, admin);
+			for (const webhook of webhooks.body) {
+				await api('admin/system-webhook/delete', { id: webhook.id }, admin);
+			}
+		});
+
+		test('通報を受けた -> abuseReportが送出される', async () => {
+			const webhook = await createSystemWebhook({
+				on: ['abuseReport'],
+				isActive: true,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			});
+
+			console.log(JSON.stringify(webhookBody, null, 2));
+
+			expect(webhookBody.hookId).toBe(webhook.id);
+			expect(webhookBody.type).toBe('abuseReport');
+			expect(webhookBody.body.targetUserId).toBe(alice.id);
+			expect(webhookBody.body.reporterId).toBe(bob.id);
+			expect(webhookBody.body.comment).toBe(abuse.comment);
+		});
+
+		test('通報を受けた -> abuseReportが送出される -> 解決 -> abuseReportResolvedが送出される', async () => {
+			const webhook = await createSystemWebhook({
+				on: ['abuseReport', 'abuseReportResolved'],
+				isActive: true,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody1 = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			});
+
+			console.log(JSON.stringify(webhookBody1, null, 2));
+			expect(webhookBody1.hookId).toBe(webhook.id);
+			expect(webhookBody1.type).toBe('abuseReport');
+			expect(webhookBody1.body.targetUserId).toBe(alice.id);
+			expect(webhookBody1.body.reporterId).toBe(bob.id);
+			expect(webhookBody1.body.assigneeId).toBeNull();
+			expect(webhookBody1.body.resolved).toBe(false);
+			expect(webhookBody1.body.comment).toBe(abuse.comment);
+
+			// 解決
+			const webhookBody2 = await captureWebhook(async () => {
+				await resolveAbuseReport({
+					reportId: webhookBody1.body.id,
+					forward: false,
+				}, admin);
+			});
+
+			console.log(JSON.stringify(webhookBody2, null, 2));
+			expect(webhookBody2.hookId).toBe(webhook.id);
+			expect(webhookBody2.type).toBe('abuseReportResolved');
+			expect(webhookBody2.body.targetUserId).toBe(alice.id);
+			expect(webhookBody2.body.reporterId).toBe(bob.id);
+			expect(webhookBody2.body.assigneeId).toBe(admin.id);
+			expect(webhookBody2.body.resolved).toBe(true);
+			expect(webhookBody2.body.comment).toBe(abuse.comment);
+		});
+
+		test('通報を受けた -> abuseReportが未許可の場合は送出されない', async () => {
+			const webhook = await createSystemWebhook({
+				on: [],
+				isActive: true,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			}).catch(e => e.message);
+
+			expect(webhookBody).toBe('timeout');
+		});
+
+		test('通報を受けた -> abuseReportが未許可の場合は送出されない -> 解決 -> abuseReportResolvedが送出される', async () => {
+			const webhook = await createSystemWebhook({
+				on: ['abuseReportResolved'],
+				isActive: true,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody1 = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			}).catch(e => e.message);
+
+			expect(webhookBody1).toBe('timeout');
+
+			const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+			// 解決
+			const webhookBody2 = await captureWebhook(async () => {
+				await resolveAbuseReport({
+					reportId: abuseReportId,
+					forward: false,
+				}, admin);
+			});
+
+			console.log(JSON.stringify(webhookBody2, null, 2));
+			expect(webhookBody2.hookId).toBe(webhook.id);
+			expect(webhookBody2.type).toBe('abuseReportResolved');
+			expect(webhookBody2.body.targetUserId).toBe(alice.id);
+			expect(webhookBody2.body.reporterId).toBe(bob.id);
+			expect(webhookBody2.body.assigneeId).toBe(admin.id);
+			expect(webhookBody2.body.resolved).toBe(true);
+			expect(webhookBody2.body.comment).toBe(abuse.comment);
+		});
+
+		test('通報を受けた -> abuseReportが送出される -> 解決 -> abuseReportResolvedが未許可の場合は送出されない', async () => {
+			const webhook = await createSystemWebhook({
+				on: ['abuseReport'],
+				isActive: true,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody1 = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			});
+
+			console.log(JSON.stringify(webhookBody1, null, 2));
+			expect(webhookBody1.hookId).toBe(webhook.id);
+			expect(webhookBody1.type).toBe('abuseReport');
+			expect(webhookBody1.body.targetUserId).toBe(alice.id);
+			expect(webhookBody1.body.reporterId).toBe(bob.id);
+			expect(webhookBody1.body.assigneeId).toBeNull();
+			expect(webhookBody1.body.resolved).toBe(false);
+			expect(webhookBody1.body.comment).toBe(abuse.comment);
+
+			// 解決
+			const webhookBody2 = await captureWebhook(async () => {
+				await resolveAbuseReport({
+					reportId: webhookBody1.body.id,
+					forward: false,
+				}, admin);
+			}).catch(e => e.message);
+
+			expect(webhookBody2).toBe('timeout');
+		});
+
+		test('通報を受けた -> abuseReportが未許可の場合は送出されない -> 解決 -> abuseReportResolvedが未許可の場合は送出されない', async () => {
+			const webhook = await createSystemWebhook({
+				on: [],
+				isActive: true,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody1 = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			}).catch(e => e.message);
+
+			expect(webhookBody1).toBe('timeout');
+
+			const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+			// 解決
+			const webhookBody2 = await captureWebhook(async () => {
+				await resolveAbuseReport({
+					reportId: abuseReportId,
+					forward: false,
+				}, admin);
+			}).catch(e => e.message);
+
+			expect(webhookBody2).toBe('timeout');
+		});
+
+		test('通報を受けた -> Webhookが無効の場合は送出されない', async () => {
+			const webhook = await createSystemWebhook({
+				on: ['abuseReport', 'abuseReportResolved'],
+				isActive: false,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody1 = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			}).catch(e => e.message);
+
+			expect(webhookBody1).toBe('timeout');
+
+			const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+			// 解決
+			const webhookBody2 = await captureWebhook(async () => {
+				await resolveAbuseReport({
+					reportId: abuseReportId,
+					forward: false,
+				}, admin);
+			}).catch(e => e.message);
+
+			expect(webhookBody2).toBe('timeout');
+		});
+
+		test('通報を受けた -> 通知設定が無効の場合は送出されない', async () => {
+			const webhook = await createSystemWebhook({
+				on: ['abuseReport', 'abuseReportResolved'],
+				isActive: true,
+			});
+			await createAbuseReportNotificationRecipient({ systemWebhookId: webhook.id, isActive: false });
+
+			// 通報(bob -> alice)
+			const abuse = {
+				userId: alice.id,
+				comment: randomString(),
+			};
+			const webhookBody1 = await captureWebhook(async () => {
+				await createAbuseReport(abuse, bob);
+			}).catch(e => e.message);
+
+			expect(webhookBody1).toBe('timeout');
+
+			const abuseReportId = (await api('admin/abuse-user-reports', {}, admin)).body[0].id;
+
+			// 解決
+			const webhookBody2 = await captureWebhook(async () => {
+				await resolveAbuseReport({
+					reportId: abuseReportId,
+					forward: false,
+				}, admin);
+			}).catch(e => e.message);
+
+			expect(webhookBody2).toBe('timeout');
+		});
+	});
+});
diff --git a/packages/backend/test/unit/AbuseReportNotificationService.ts b/packages/backend/test/unit/AbuseReportNotificationService.ts
new file mode 100644
index 0000000000..e971659070
--- /dev/null
+++ b/packages/backend/test/unit/AbuseReportNotificationService.ts
@@ -0,0 +1,343 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { jest } from '@jest/globals';
+import { Test, TestingModule } from '@nestjs/testing';
+import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
+import {
+	AbuseReportNotificationRecipientRepository,
+	MiAbuseReportNotificationRecipient,
+	MiSystemWebhook,
+	MiUser,
+	SystemWebhooksRepository,
+	UserProfilesRepository,
+	UsersRepository,
+} from '@/models/_.js';
+import { DI } from '@/di-symbols.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { IdService } from '@/core/IdService.js';
+import { EmailService } from '@/core/EmailService.js';
+import { RoleService } from '@/core/RoleService.js';
+import { MetaService } from '@/core/MetaService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { RecipientMethod } from '@/models/AbuseReportNotificationRecipient.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+import { randomString } from '../utils.js';
+
+process.env.NODE_ENV = 'test';
+
+describe('AbuseReportNotificationService', () => {
+	let app: TestingModule;
+	let service: AbuseReportNotificationService;
+
+	// --------------------------------------------------------------------------------------
+
+	let usersRepository: UsersRepository;
+	let userProfilesRepository: UserProfilesRepository;
+	let systemWebhooksRepository: SystemWebhooksRepository;
+	let abuseReportNotificationRecipientRepository: AbuseReportNotificationRecipientRepository;
+	let idService: IdService;
+	let roleService: jest.Mocked<RoleService>;
+	let emailService: jest.Mocked<EmailService>;
+	let webhookService: jest.Mocked<SystemWebhookService>;
+
+	// --------------------------------------------------------------------------------------
+
+	let root: MiUser;
+	let alice: MiUser;
+	let bob: MiUser;
+	let systemWebhook1: MiSystemWebhook;
+	let systemWebhook2: MiSystemWebhook;
+
+	// --------------------------------------------------------------------------------------
+
+	async function createUser(data: Partial<MiUser> = {}) {
+		const user = await usersRepository
+			.insert({
+				id: idService.gen(),
+				...data,
+			})
+			.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+
+		await userProfilesRepository.insert({
+			userId: user.id,
+		});
+
+		return user;
+	}
+
+	async function createWebhook(data: Partial<MiSystemWebhook> = {}) {
+		return systemWebhooksRepository
+			.insert({
+				id: idService.gen(),
+				name: randomString(),
+				on: ['abuseReport'],
+				url: 'https://example.com',
+				secret: randomString(),
+				...data,
+			})
+			.then(x => systemWebhooksRepository.findOneByOrFail(x.identifiers[0]));
+	}
+
+	async function createRecipient(data: Partial<MiAbuseReportNotificationRecipient> = {}) {
+		return abuseReportNotificationRecipientRepository
+			.insert({
+				id: idService.gen(),
+				isActive: true,
+				name: randomString(),
+				...data,
+			})
+			.then(x => abuseReportNotificationRecipientRepository.findOneByOrFail(x.identifiers[0]));
+	}
+
+	// --------------------------------------------------------------------------------------
+
+	beforeAll(async () => {
+		app = await Test
+			.createTestingModule({
+				imports: [
+					GlobalModule,
+				],
+				providers: [
+					AbuseReportNotificationService,
+					IdService,
+					{
+						provide: RoleService, useFactory: () => ({ getModeratorIds: jest.fn() }),
+					},
+					{
+						provide: SystemWebhookService, useFactory: () => ({ enqueueSystemWebhook: jest.fn() }),
+					},
+					{
+						provide: EmailService, useFactory: () => ({ sendEmail: jest.fn() }),
+					},
+					{
+						provide: MetaService, useFactory: () => ({ fetch: jest.fn() }),
+					},
+					{
+						provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
+					},
+					{
+						provide: GlobalEventService, useFactory: () => ({ publishAdminStream: jest.fn() }),
+					},
+				],
+			})
+			.compile();
+
+		usersRepository = app.get(DI.usersRepository);
+		userProfilesRepository = app.get(DI.userProfilesRepository);
+		systemWebhooksRepository = app.get(DI.systemWebhooksRepository);
+		abuseReportNotificationRecipientRepository = app.get(DI.abuseReportNotificationRecipientRepository);
+
+		service = app.get(AbuseReportNotificationService);
+		idService = app.get(IdService);
+		roleService = app.get(RoleService) as jest.Mocked<RoleService>;
+		emailService = app.get<EmailService>(EmailService) as jest.Mocked<EmailService>;
+		webhookService = app.get<SystemWebhookService>(SystemWebhookService) as jest.Mocked<SystemWebhookService>;
+
+		app.enableShutdownHooks();
+	});
+
+	beforeEach(async () => {
+		root = await createUser({ username: 'root', usernameLower: 'root', isRoot: true });
+		alice = await createUser({ username: 'alice', usernameLower: 'alice', isRoot: false });
+		bob = await createUser({ username: 'bob', usernameLower: 'bob', isRoot: false });
+		systemWebhook1 = await createWebhook();
+		systemWebhook2 = await createWebhook();
+
+		roleService.getModeratorIds.mockResolvedValue([root.id, alice.id, bob.id]);
+	});
+
+	afterEach(async () => {
+		emailService.sendEmail.mockClear();
+		webhookService.enqueueSystemWebhook.mockClear();
+
+		await usersRepository.delete({});
+		await userProfilesRepository.delete({});
+		await systemWebhooksRepository.delete({});
+		await abuseReportNotificationRecipientRepository.delete({});
+	});
+
+	afterAll(async () => {
+		await app.close();
+	});
+
+	// --------------------------------------------------------------------------------------
+
+	describe('createRecipient', () => {
+		test('作成成功1', async () => {
+			const params = {
+				isActive: true,
+				name: randomString(),
+				method: 'email' as RecipientMethod,
+				userId: alice.id,
+				systemWebhookId: null,
+			};
+
+			const recipient1 = await service.createRecipient(params, root);
+			expect(recipient1).toMatchObject(params);
+		});
+
+		test('作成成功2', async () => {
+			const params = {
+				isActive: true,
+				name: randomString(),
+				method: 'webhook' as RecipientMethod,
+				userId: null,
+				systemWebhookId: systemWebhook1.id,
+			};
+
+			const recipient1 = await service.createRecipient(params, root);
+			expect(recipient1).toMatchObject(params);
+		});
+	});
+
+	describe('updateRecipient', () => {
+		test('更新成功1', async () => {
+			const recipient1 = await createRecipient({
+				method: 'email',
+				userId: alice.id,
+			});
+
+			const params = {
+				id: recipient1.id,
+				isActive: false,
+				name: randomString(),
+				method: 'email' as RecipientMethod,
+				userId: bob.id,
+				systemWebhookId: null,
+			};
+
+			const recipient2 = await service.updateRecipient(params, root);
+			expect(recipient2).toMatchObject(params);
+		});
+
+		test('更新成功2', async () => {
+			const recipient1 = await createRecipient({
+				method: 'webhook',
+				systemWebhookId: systemWebhook1.id,
+			});
+
+			const params = {
+				id: recipient1.id,
+				isActive: false,
+				name: randomString(),
+				method: 'webhook' as RecipientMethod,
+				userId: null,
+				systemWebhookId: systemWebhook2.id,
+			};
+
+			const recipient2 = await service.updateRecipient(params, root);
+			expect(recipient2).toMatchObject(params);
+		});
+	});
+
+	describe('deleteRecipient', () => {
+		test('削除成功1', async () => {
+			const recipient1 = await createRecipient({
+				method: 'email',
+				userId: alice.id,
+			});
+
+			await service.deleteRecipient(recipient1.id, root);
+
+			await expect(abuseReportNotificationRecipientRepository.findOneBy({ id: recipient1.id })).resolves.toBeNull();
+		});
+	});
+
+	describe('fetchRecipients', () => {
+		async function create() {
+			const recipient1 = await createRecipient({
+				method: 'email',
+				userId: alice.id,
+			});
+			const recipient2 = await createRecipient({
+				method: 'email',
+				userId: bob.id,
+			});
+
+			const recipient3 = await createRecipient({
+				method: 'webhook',
+				systemWebhookId: systemWebhook1.id,
+			});
+			const recipient4 = await createRecipient({
+				method: 'webhook',
+				systemWebhookId: systemWebhook2.id,
+			});
+
+			return [recipient1, recipient2, recipient3, recipient4];
+		}
+
+		test('フィルタなし', async () => {
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({});
+			expect(recipients).toEqual([recipient1, recipient2, recipient3, recipient4]);
+		});
+
+		test('フィルタなし(非モデレータは除外される)', async () => {
+			roleService.getModeratorIds.mockClear();
+			roleService.getModeratorIds.mockResolvedValue([root.id, bob.id]);
+
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({});
+			// aliceはモデレータではないので除外される
+			expect(recipients).toEqual([recipient2, recipient3, recipient4]);
+		});
+
+		test('フィルタなし(非モデレータでも除外されないオプション設定)', async () => {
+			roleService.getModeratorIds.mockClear();
+			roleService.getModeratorIds.mockResolvedValue([root.id, bob.id]);
+
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({}, { removeUnauthorized: false });
+			expect(recipients).toEqual([recipient1, recipient2, recipient3, recipient4]);
+		});
+
+		test('emailのみ', async () => {
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({ method: ['email'] });
+			expect(recipients).toEqual([recipient1, recipient2]);
+		});
+
+		test('webhookのみ', async () => {
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({ method: ['webhook'] });
+			expect(recipients).toEqual([recipient3, recipient4]);
+		});
+
+		test('すべて', async () => {
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({ method: ['email', 'webhook'] });
+			expect(recipients).toEqual([recipient1, recipient2, recipient3, recipient4]);
+		});
+
+		test('ID指定', async () => {
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({ ids: [recipient1.id, recipient3.id] });
+			expect(recipients).toEqual([recipient1, recipient3]);
+		});
+
+		test('ID指定(method=emailではないIDが混ざりこまない)', async () => {
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({ ids: [recipient1.id, recipient3.id], method: ['email'] });
+			expect(recipients).toEqual([recipient1]);
+		});
+
+		test('ID指定(method=webhookではないIDが混ざりこまない)', async () => {
+			const [recipient1, recipient2, recipient3, recipient4] = await create();
+
+			const recipients = await service.fetchRecipients({ ids: [recipient1.id, recipient3.id], method: ['webhook'] });
+			expect(recipients).toEqual([recipient3]);
+		});
+	});
+});
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index ec441735d7..69fa4162fb 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -3,8 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-
 process.env.NODE_ENV = 'test';
 
 import { jest } from '@jest/globals';
@@ -13,7 +11,14 @@ import { Test } from '@nestjs/testing';
 import * as lolex from '@sinonjs/fake-timers';
 import { GlobalModule } from '@/GlobalModule.js';
 import { RoleService } from '@/core/RoleService.js';
-import type { MiRole, MiUser, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js';
+import {
+	MiRole,
+	MiRoleAssignment,
+	MiUser,
+	RoleAssignmentsRepository,
+	RolesRepository,
+	UsersRepository,
+} from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { MetaService } from '@/core/MetaService.js';
 import { genAidx } from '@/misc/id/aidx.js';
@@ -23,6 +28,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
 import { NotificationService } from '@/core/NotificationService.js';
 import { RoleCondFormulaValue } from '@/models/Role.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { sleep } from '../utils.js';
 import type { TestingModule } from '@nestjs/testing';
 import type { MockFunctionMetadata } from 'jest-mock';
@@ -39,27 +45,27 @@ describe('RoleService', () => {
 	let notificationService: jest.Mocked<NotificationService>;
 	let clock: lolex.InstalledClock;
 
-	function createUser(data: Partial<MiUser> = {}) {
+	async function createUser(data: Partial<MiUser> = {}) {
 		const un = secureRndstr(16);
-		return usersRepository.insert({
+		const x = await usersRepository.insert({
 			id: genAidx(Date.now()),
 			username: un,
 			usernameLower: un,
 			...data,
-		})
-			.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+		});
+		return await usersRepository.findOneByOrFail(x.identifiers[0]);
 	}
 
-	function createRole(data: Partial<MiRole> = {}) {
-		return rolesRepository.insert({
+	async function createRole(data: Partial<MiRole> = {}) {
+		const x = await rolesRepository.insert({
 			id: genAidx(Date.now()),
 			updatedAt: new Date(),
 			lastUsedAt: new Date(),
 			name: '',
 			description: '',
 			...data,
-		})
-			.then(x => rolesRepository.findOneByOrFail(x.identifiers[0]));
+		});
+		return await rolesRepository.findOneByOrFail(x.identifiers[0]);
 	}
 
 	function createConditionalRole(condFormula: RoleCondFormulaValue, data: Partial<MiRole> = {}) {
@@ -71,6 +77,20 @@ describe('RoleService', () => {
 		});
 	}
 
+	async function assignRole(args: Partial<MiRoleAssignment>) {
+		const id = genAidx(Date.now());
+		const expiresAt = new Date();
+		expiresAt.setDate(expiresAt.getDate() + 1);
+
+		await roleAssignmentsRepository.insert({
+			id,
+			expiresAt,
+			...args,
+		});
+
+		return await roleAssignmentsRepository.findOneByOrFail({ id });
+	}
+
 	function aidx() {
 		return genAidx(Date.now());
 	}
@@ -265,6 +285,96 @@ describe('RoleService', () => {
 		});
 	});
 
+	describe('getModeratorIds', () => {
+		test('includeAdmins = false, excludeExpire = false', async () => {
+			const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
+				createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
+			]);
+
+			const role1 = await createRole({ name: 'admin', isAdministrator: true });
+			const role2 = await createRole({ name: 'moderator', isModerator: true });
+			const role3 = await createRole({ name: 'normal' });
+
+			await Promise.all([
+				assignRole({ userId: adminUser1.id, roleId: role1.id }),
+				assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: modeUser1.id, roleId: role2.id }),
+				assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: normalUser1.id, roleId: role3.id }),
+				assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
+			]);
+
+			const result = await roleService.getModeratorIds(false, false);
+			expect(result).toEqual([modeUser1.id, modeUser2.id]);
+		});
+
+		test('includeAdmins = false, excludeExpire = true', async () => {
+			const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
+				createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
+			]);
+
+			const role1 = await createRole({ name: 'admin', isAdministrator: true });
+			const role2 = await createRole({ name: 'moderator', isModerator: true });
+			const role3 = await createRole({ name: 'normal' });
+
+			await Promise.all([
+				assignRole({ userId: adminUser1.id, roleId: role1.id }),
+				assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: modeUser1.id, roleId: role2.id }),
+				assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: normalUser1.id, roleId: role3.id }),
+				assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
+			]);
+
+			const result = await roleService.getModeratorIds(false, true);
+			expect(result).toEqual([modeUser1.id]);
+		});
+
+		test('includeAdmins = true, excludeExpire = false', async () => {
+			const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
+				createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
+			]);
+
+			const role1 = await createRole({ name: 'admin', isAdministrator: true });
+			const role2 = await createRole({ name: 'moderator', isModerator: true });
+			const role3 = await createRole({ name: 'normal' });
+
+			await Promise.all([
+				assignRole({ userId: adminUser1.id, roleId: role1.id }),
+				assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: modeUser1.id, roleId: role2.id }),
+				assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: normalUser1.id, roleId: role3.id }),
+				assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
+			]);
+
+			const result = await roleService.getModeratorIds(true, false);
+			expect(result).toEqual([adminUser1.id, adminUser2.id, modeUser1.id, modeUser2.id]);
+		});
+
+		test('includeAdmins = true, excludeExpire = true', async () => {
+			const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
+				createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
+			]);
+
+			const role1 = await createRole({ name: 'admin', isAdministrator: true });
+			const role2 = await createRole({ name: 'moderator', isModerator: true });
+			const role3 = await createRole({ name: 'normal' });
+
+			await Promise.all([
+				assignRole({ userId: adminUser1.id, roleId: role1.id }),
+				assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: modeUser1.id, roleId: role2.id }),
+				assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
+				assignRole({ userId: normalUser1.id, roleId: role3.id }),
+				assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
+			]);
+
+			const result = await roleService.getModeratorIds(true, true);
+			expect(result).toEqual([adminUser1.id, modeUser1.id]);
+		});
+	});
+
 	describe('conditional role', () => {
 		test('~かつ~', async () => {
 			const [user1, user2, user3, user4] = await Promise.all([
diff --git a/packages/backend/test/unit/SystemWebhookService.ts b/packages/backend/test/unit/SystemWebhookService.ts
new file mode 100644
index 0000000000..41b7f977ca
--- /dev/null
+++ b/packages/backend/test/unit/SystemWebhookService.ts
@@ -0,0 +1,515 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { afterEach, beforeEach, describe, expect, jest } from '@jest/globals';
+import { Test, TestingModule } from '@nestjs/testing';
+import { MiUser } from '@/models/User.js';
+import { MiSystemWebhook, SystemWebhookEventType } from '@/models/SystemWebhook.js';
+import { SystemWebhooksRepository, UsersRepository } from '@/models/_.js';
+import { IdService } from '@/core/IdService.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { DI } from '@/di-symbols.js';
+import { QueueService } from '@/core/QueueService.js';
+import { LoggerService } from '@/core/LoggerService.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+import { randomString, sleep } from '../utils.js';
+
+describe('SystemWebhookService', () => {
+	let app: TestingModule;
+	let service: SystemWebhookService;
+
+	// --------------------------------------------------------------------------------------
+
+	let usersRepository: UsersRepository;
+	let systemWebhooksRepository: SystemWebhooksRepository;
+	let idService: IdService;
+	let queueService: jest.Mocked<QueueService>;
+
+	// --------------------------------------------------------------------------------------
+
+	let root: MiUser;
+
+	// --------------------------------------------------------------------------------------
+
+	async function createUser(data: Partial<MiUser> = {}) {
+		return await usersRepository
+			.insert({
+				id: idService.gen(),
+				...data,
+			})
+			.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+	}
+
+	async function createWebhook(data: Partial<MiSystemWebhook> = {}) {
+		return systemWebhooksRepository
+			.insert({
+				id: idService.gen(),
+				name: randomString(),
+				on: ['abuseReport'],
+				url: 'https://example.com',
+				secret: randomString(),
+				...data,
+			})
+			.then(x => systemWebhooksRepository.findOneByOrFail(x.identifiers[0]));
+	}
+
+	// --------------------------------------------------------------------------------------
+
+	async function beforeAllImpl() {
+		app = await Test
+			.createTestingModule({
+				imports: [
+					GlobalModule,
+				],
+				providers: [
+					SystemWebhookService,
+					IdService,
+					LoggerService,
+					GlobalEventService,
+					{
+						provide: QueueService, useFactory: () => ({ systemWebhookDeliver: jest.fn() }),
+					},
+					{
+						provide: ModerationLogService, useFactory: () => ({ log: () => Promise.resolve() }),
+					},
+				],
+			})
+			.compile();
+
+		usersRepository = app.get(DI.usersRepository);
+		systemWebhooksRepository = app.get(DI.systemWebhooksRepository);
+
+		service = app.get(SystemWebhookService);
+		idService = app.get(IdService);
+		queueService = app.get(QueueService) as jest.Mocked<QueueService>;
+
+		app.enableShutdownHooks();
+	}
+
+	async function afterAllImpl() {
+		await app.close();
+	}
+
+	async function beforeEachImpl() {
+		root = await createUser({ isRoot: true, username: 'root', usernameLower: 'root' });
+	}
+
+	async function afterEachImpl() {
+		await usersRepository.delete({});
+		await systemWebhooksRepository.delete({});
+	}
+
+	// --------------------------------------------------------------------------------------
+
+	describe('アプリを毎回作り直す必要のないグループ', () => {
+		beforeAll(beforeAllImpl);
+		afterAll(afterAllImpl);
+		beforeEach(beforeEachImpl);
+		afterEach(afterEachImpl);
+
+		describe('fetchSystemWebhooks', () => {
+			test('フィルタなし', async () => {
+				const webhook1 = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+				const webhook2 = await createWebhook({
+					isActive: false,
+					on: ['abuseReport'],
+				});
+				const webhook3 = await createWebhook({
+					isActive: true,
+					on: ['abuseReportResolved'],
+				});
+				const webhook4 = await createWebhook({
+					isActive: false,
+					on: [],
+				});
+
+				const fetchedWebhooks = await service.fetchSystemWebhooks();
+				expect(fetchedWebhooks).toEqual([webhook1, webhook2, webhook3, webhook4]);
+			});
+
+			test('activeのみ', async () => {
+				const webhook1 = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+				const webhook2 = await createWebhook({
+					isActive: false,
+					on: ['abuseReport'],
+				});
+				const webhook3 = await createWebhook({
+					isActive: true,
+					on: ['abuseReportResolved'],
+				});
+				const webhook4 = await createWebhook({
+					isActive: false,
+					on: [],
+				});
+
+				const fetchedWebhooks = await service.fetchSystemWebhooks({ isActive: true });
+				expect(fetchedWebhooks).toEqual([webhook1, webhook3]);
+			});
+
+			test('特定のイベントのみ', async () => {
+				const webhook1 = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+				const webhook2 = await createWebhook({
+					isActive: false,
+					on: ['abuseReport'],
+				});
+				const webhook3 = await createWebhook({
+					isActive: true,
+					on: ['abuseReportResolved'],
+				});
+				const webhook4 = await createWebhook({
+					isActive: false,
+					on: [],
+				});
+
+				const fetchedWebhooks = await service.fetchSystemWebhooks({ on: ['abuseReport'] });
+				expect(fetchedWebhooks).toEqual([webhook1, webhook2]);
+			});
+
+			test('activeな特定のイベントのみ', async () => {
+				const webhook1 = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+				const webhook2 = await createWebhook({
+					isActive: false,
+					on: ['abuseReport'],
+				});
+				const webhook3 = await createWebhook({
+					isActive: true,
+					on: ['abuseReportResolved'],
+				});
+				const webhook4 = await createWebhook({
+					isActive: false,
+					on: [],
+				});
+
+				const fetchedWebhooks = await service.fetchSystemWebhooks({ on: ['abuseReport'], isActive: true });
+				expect(fetchedWebhooks).toEqual([webhook1]);
+			});
+
+			test('ID指定', async () => {
+				const webhook1 = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+				const webhook2 = await createWebhook({
+					isActive: false,
+					on: ['abuseReport'],
+				});
+				const webhook3 = await createWebhook({
+					isActive: true,
+					on: ['abuseReportResolved'],
+				});
+				const webhook4 = await createWebhook({
+					isActive: false,
+					on: [],
+				});
+
+				const fetchedWebhooks = await service.fetchSystemWebhooks({ ids: [webhook1.id, webhook4.id] });
+				expect(fetchedWebhooks).toEqual([webhook1, webhook4]);
+			});
+
+			test('ID指定(他条件とANDになるか見たい)', async () => {
+				const webhook1 = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+				const webhook2 = await createWebhook({
+					isActive: false,
+					on: ['abuseReport'],
+				});
+				const webhook3 = await createWebhook({
+					isActive: true,
+					on: ['abuseReportResolved'],
+				});
+				const webhook4 = await createWebhook({
+					isActive: false,
+					on: [],
+				});
+
+				const fetchedWebhooks = await service.fetchSystemWebhooks({ ids: [webhook1.id, webhook4.id], isActive: false });
+				expect(fetchedWebhooks).toEqual([webhook4]);
+			});
+		});
+
+		describe('createSystemWebhook', () => {
+			test('作成成功	', async () => {
+				const params = {
+					isActive: true,
+					name: randomString(),
+					on: ['abuseReport'] as SystemWebhookEventType[],
+					url: 'https://example.com',
+					secret: randomString(),
+				};
+
+				const webhook = await service.createSystemWebhook(params, root);
+				expect(webhook).toMatchObject(params);
+			});
+		});
+
+		describe('updateSystemWebhook', () => {
+			test('更新成功', async () => {
+				const webhook = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+
+				const params = {
+					id: webhook.id,
+					isActive: false,
+					name: randomString(),
+					on: ['abuseReport'] as SystemWebhookEventType[],
+					url: randomString(),
+					secret: randomString(),
+				};
+
+				const updatedWebhook = await service.updateSystemWebhook(params, root);
+				expect(updatedWebhook).toMatchObject(params);
+			});
+		});
+
+		describe('deleteSystemWebhook', () => {
+			test('削除成功', async () => {
+				const webhook = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+
+				await service.deleteSystemWebhook(webhook.id, root);
+
+				await expect(systemWebhooksRepository.findOneBy({ id: webhook.id })).resolves.toBeNull();
+			});
+		});
+	});
+
+	describe('アプリを毎回作り直す必要があるグループ', () => {
+		beforeEach(async () => {
+			await beforeAllImpl();
+			await beforeEachImpl();
+		});
+
+		afterEach(async () => {
+			await afterEachImpl();
+			await afterAllImpl();
+		});
+
+		describe('enqueueSystemWebhook', () => {
+			test('キューに追加成功', async () => {
+				const webhook = await createWebhook({
+					isActive: true,
+					on: ['abuseReport'],
+				});
+				await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' });
+
+				expect(queueService.systemWebhookDeliver).toHaveBeenCalled();
+			});
+
+			test('非アクティブなWebhookはキューに追加されない', async () => {
+				const webhook = await createWebhook({
+					isActive: false,
+					on: ['abuseReport'],
+				});
+				await service.enqueueSystemWebhook(webhook.id, 'abuseReport', { foo: 'bar' });
+
+				expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
+			});
+
+			test('未許可のイベント種別が渡された場合はWebhookはキューに追加されない', async () => {
+				const webhook1 = await createWebhook({
+					isActive: true,
+					on: [],
+				});
+				const webhook2 = await createWebhook({
+					isActive: true,
+					on: ['abuseReportResolved'],
+				});
+				await service.enqueueSystemWebhook(webhook1.id, 'abuseReport', { foo: 'bar' });
+				await service.enqueueSystemWebhook(webhook2.id, 'abuseReport', { foo: 'bar' });
+
+				expect(queueService.systemWebhookDeliver).not.toHaveBeenCalled();
+			});
+		});
+
+		describe('fetchActiveSystemWebhooks', () => {
+			describe('systemWebhookCreated', () => {
+				test('ActiveなWebhookが追加された時、キャッシュに追加されている', async () => {
+					const webhook = await service.createSystemWebhook(
+						{
+							isActive: true,
+							name: randomString(),
+							on: ['abuseReport'],
+							url: 'https://example.com',
+							secret: randomString(),
+						},
+						root,
+					);
+
+					// redisでの配信経由で更新されるのでちょっと待つ
+					await sleep(500);
+
+					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+					expect(fetchedWebhooks).toEqual([webhook]);
+				});
+
+				test('NotActiveなWebhookが追加された時、キャッシュに追加されていない', async () => {
+					const webhook = await service.createSystemWebhook(
+						{
+							isActive: false,
+							name: randomString(),
+							on: ['abuseReport'],
+							url: 'https://example.com',
+							secret: randomString(),
+						},
+						root,
+					);
+
+					// redisでの配信経由で更新されるのでちょっと待つ
+					await sleep(500);
+
+					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+					expect(fetchedWebhooks).toEqual([]);
+				});
+			});
+
+			describe('systemWebhookUpdated', () => {
+				test('ActiveなWebhookが編集された時、キャッシュに反映されている', async () => {
+					const id = idService.gen();
+					await createWebhook({ id });
+					// キャッシュ作成
+					const webhook1 = await service.fetchActiveSystemWebhooks();
+					// 読み込まれていることをチェック
+					expect(webhook1.length).toEqual(1);
+					expect(webhook1[0].id).toEqual(id);
+
+					const webhook2 = await service.updateSystemWebhook(
+						{
+							id,
+							isActive: true,
+							name: randomString(),
+							on: ['abuseReport'],
+							url: 'https://example.com',
+							secret: randomString(),
+						},
+						root,
+					);
+
+					// redisでの配信経由で更新されるのでちょっと待つ
+					await sleep(500);
+
+					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+					expect(fetchedWebhooks).toEqual([webhook2]);
+				});
+
+				test('NotActiveなWebhookが編集された時、キャッシュに追加されない', async () => {
+					const id = idService.gen();
+					await createWebhook({ id, isActive: false });
+					// キャッシュ作成
+					const webhook1 = await service.fetchActiveSystemWebhooks();
+					// 読み込まれていないことをチェック
+					expect(webhook1.length).toEqual(0);
+
+					const webhook2 = await service.updateSystemWebhook(
+						{
+							id,
+							isActive: false,
+							name: randomString(),
+							on: ['abuseReport'],
+							url: 'https://example.com',
+							secret: randomString(),
+						},
+						root,
+					);
+
+					// redisでの配信経由で更新されるのでちょっと待つ
+					await sleep(500);
+
+					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+					expect(fetchedWebhooks.length).toEqual(0);
+				});
+
+				test('NotActiveなWebhookがActiveにされた時、キャッシュに追加されている', async () => {
+					const id = idService.gen();
+					const baseWebhook = await createWebhook({ id, isActive: false });
+					// キャッシュ作成
+					const webhook1 = await service.fetchActiveSystemWebhooks();
+					// 読み込まれていないことをチェック
+					expect(webhook1.length).toEqual(0);
+
+					const webhook2 = await service.updateSystemWebhook(
+						{
+							...baseWebhook,
+							isActive: true,
+						},
+						root,
+					);
+
+					// redisでの配信経由で更新されるのでちょっと待つ
+					await sleep(500);
+
+					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+					expect(fetchedWebhooks).toEqual([webhook2]);
+				});
+
+				test('ActiveなWebhookがNotActiveにされた時、キャッシュから削除されている', async () => {
+					const id = idService.gen();
+					const baseWebhook = await createWebhook({ id, isActive: true });
+					// キャッシュ作成
+					const webhook1 = await service.fetchActiveSystemWebhooks();
+					// 読み込まれていることをチェック
+					expect(webhook1.length).toEqual(1);
+					expect(webhook1[0].id).toEqual(id);
+
+					const webhook2 = await service.updateSystemWebhook(
+						{
+							...baseWebhook,
+							isActive: false,
+						},
+						root,
+					);
+
+					// redisでの配信経由で更新されるのでちょっと待つ
+					await sleep(500);
+
+					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+					expect(fetchedWebhooks.length).toEqual(0);
+				});
+			});
+
+			describe('systemWebhookDeleted', () => {
+				test('キャッシュから削除されている', async () => {
+					const id = idService.gen();
+					const baseWebhook = await createWebhook({ id, isActive: true });
+					// キャッシュ作成
+					const webhook1 = await service.fetchActiveSystemWebhooks();
+					// 読み込まれていることをチェック
+					expect(webhook1.length).toEqual(1);
+					expect(webhook1[0].id).toEqual(id);
+
+					const webhook2 = await service.deleteSystemWebhook(
+						id,
+						root,
+					);
+
+					// redisでの配信経由で更新されるのでちょっと待つ
+					await sleep(500);
+
+					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
+					expect(fetchedWebhooks.length).toEqual(0);
+				});
+			});
+		});
+	});
+});
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index 3489255b91..25b003ba5a 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -11,6 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:type="type"
 	:name="name"
 	:value="value"
+	:disabled="disabled"
 	@click="emit('click', $event)"
 	@mousedown="onMousedown"
 >
@@ -55,6 +56,7 @@ const props = defineProps<{
 	asLike?: boolean;
 	name?: string;
 	value?: string;
+	disabled?: boolean;
 }>();
 
 const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkDivider.vue b/packages/frontend/src/components/MkDivider.vue
new file mode 100644
index 0000000000..e4e3af99e4
--- /dev/null
+++ b/packages/frontend/src/components/MkDivider.vue
@@ -0,0 +1,32 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div
+	class="default" :style="[
+		marginTopBottom ? { marginTop: marginTopBottom, marginBottom: marginTopBottom } : {},
+		marginLeftRight ? { marginLeft: marginLeftRight, marginRight: marginLeftRight } : {},
+		borderStyle ? { borderStyle: borderStyle } : {},
+		borderWidth ? { borderWidth: borderWidth } : {},
+		borderColor ? { borderColor: borderColor } : {},
+	]"
+/>
+</template>
+
+<script setup lang="ts">
+defineProps<{
+	marginTopBottom?: string;
+	marginLeftRight?: string;
+	borderStyle?: string;
+	borderWidth?: string;
+	borderColor?: string;
+}>();
+</script>
+
+<style scoped lang="scss">
+.default {
+	border-top: solid 0.5px var(--divider);
+}
+</style>
diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue
index a19b45448b..721ac357f4 100644
--- a/packages/frontend/src/components/MkSwitch.vue
+++ b/packages/frontend/src/components/MkSwitch.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		@keydown.enter="toggle"
 	>
 	<XButton :checked="checked" :disabled="disabled" @toggle="toggle"/>
-	<span :class="$style.body">
+	<span v-if="!noBody" :class="$style.body">
 		<!-- TODO: 無名slotの方は廃止 -->
 		<span :class="$style.label">
 			<span @click="toggle">
@@ -34,16 +34,19 @@ const props = defineProps<{
 	modelValue: boolean | Ref<boolean>;
 	disabled?: boolean;
 	helpText?: string;
+	noBody?: boolean;
 }>();
 
 const emit = defineEmits<{
 	(ev: 'update:modelValue', v: boolean): void;
+	(ev: 'change', v: boolean): void;
 }>();
 
 const checked = toRefs(props).modelValue;
 const toggle = () => {
 	if (props.disabled) return;
 	emit('update:modelValue', !checked.value);
+	emit('change', !checked.value);
 };
 </script>
 
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
new file mode 100644
index 0000000000..1222d3261d
--- /dev/null
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
@@ -0,0 +1,45 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { defineAsyncComponent } from 'vue';
+import * as os from '@/os.js';
+
+export type SystemWebhookEventType = 'abuseReport' | 'abuseReportResolved';
+
+export type MkSystemWebhookEditorProps = {
+	mode: 'create' | 'edit';
+	id?: string;
+	requiredEvents?: SystemWebhookEventType[];
+};
+
+export type MkSystemWebhookResult = {
+	id?: string;
+	isActive: boolean;
+	name: string;
+	on: SystemWebhookEventType[];
+	url: string;
+	secret: string;
+};
+
+export async function showSystemWebhookEditorDialog(props: MkSystemWebhookEditorProps): Promise<MkSystemWebhookResult | null> {
+	const { dispose, result } = await new Promise<{ dispose: () => void, result: MkSystemWebhookResult | null }>(async resolve => {
+		const res = await os.popup(
+			defineAsyncComponent(() => import('@/components/MkSystemWebhookEditor.vue')),
+			props,
+			{
+				submitted: (ev: MkSystemWebhookResult) => {
+					resolve({ dispose: res.dispose, result: ev });
+				},
+				closed: () => {
+					resolve({ dispose: res.dispose, result: null });
+				},
+			},
+		);
+	});
+
+	dispose();
+
+	return result;
+}
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
new file mode 100644
index 0000000000..007d841f00
--- /dev/null
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -0,0 +1,217 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+	:width="450"
+	:height="590"
+	:canClose="true"
+	:withOkButton="false"
+	:okButtonDisabled="false"
+	@click="onCancelClicked"
+	@close="onCancelClicked"
+	@closed="onCancelClicked"
+>
+	<template #header>
+		{{ mode === 'create' ? i18n.ts._webhookSettings.createWebhook : i18n.ts._webhookSettings.modifyWebhook }}
+	</template>
+	<MkSpacer :marginMin="20" :marginMax="28">
+		<MkLoading v-if="loading !== 0"/>
+		<div v-else :class="$style.root" class="_gaps_m">
+			<MkInput v-model="title">
+				<template #label>{{ i18n.ts._webhookSettings.name }}</template>
+			</MkInput>
+			<MkInput v-model="url">
+				<template #label>URL</template>
+			</MkInput>
+			<MkInput v-model="secret">
+				<template #label>{{ i18n.ts._webhookSettings.secret }}</template>
+			</MkInput>
+			<MkFolder :defaultOpen="true">
+				<template #label>{{ i18n.ts._webhookSettings.events }}</template>
+
+				<div class="_gaps_s">
+					<MkSwitch v-model="events.abuseReport" :disabled="disabledEvents.abuseReport">
+						<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReport }}</template>
+					</MkSwitch>
+					<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
+						<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
+					</MkSwitch>
+				</div>
+			</MkFolder>
+
+			<MkSwitch v-model="isActive">
+				<template #label>{{ i18n.ts.enable }}</template>
+			</MkSwitch>
+
+			<div :class="$style.footer" class="_buttonsCenter">
+				<MkButton primary :disabled="disableSubmitButton" @click="onSubmitClicked">
+					<i class="ti ti-check"></i>
+					{{ i18n.ts.ok }}
+				</MkButton>
+				<MkButton @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
+			</div>
+		</div>
+	</MkSpacer>
+</MkModalWindow>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, ref, toRefs } from 'vue';
+import FormSection from '@/components/form/section.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import {
+	MkSystemWebhookEditorProps,
+	MkSystemWebhookResult,
+	SystemWebhookEventType,
+} from '@/components/MkSystemWebhookEditor.impl.js';
+import { i18n } from '@/i18n.js';
+import MkButton from '@/components/MkButton.vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import * as os from '@/os.js';
+
+type EventType = {
+	abuseReport: boolean;
+	abuseReportResolved: boolean;
+}
+
+const emit = defineEmits<{
+	(ev: 'submitted', result: MkSystemWebhookResult): void;
+	(ev: 'closed'): void;
+}>();
+
+const props = defineProps<MkSystemWebhookEditorProps>();
+
+const { mode, id, requiredEvents } = toRefs(props);
+
+const loading = ref<number>(0);
+
+const title = ref<string>('');
+const url = ref<string>('');
+const secret = ref<string>('');
+const events = ref<EventType>({
+	abuseReport: true,
+	abuseReportResolved: true,
+});
+const isActive = ref<boolean>(true);
+
+const disabledEvents = ref<EventType>({
+	abuseReport: false,
+	abuseReportResolved: false,
+});
+
+const disableSubmitButton = computed(() => {
+	if (!title.value) {
+		return true;
+	}
+	if (!url.value) {
+		return true;
+	}
+	if (!secret.value) {
+		return true;
+	}
+
+	return false;
+});
+
+async function onSubmitClicked() {
+	await loadingScope(async () => {
+		const params = {
+			isActive: isActive.value,
+			name: title.value,
+			url: url.value,
+			secret: secret.value,
+			on: Object.keys(events.value).filter(ev => events.value[ev as keyof EventType]) as SystemWebhookEventType[],
+		};
+
+		try {
+			switch (mode.value) {
+				case 'create': {
+					const result = await misskeyApi('admin/system-webhook/create', params);
+					emit('submitted', result);
+					break;
+				}
+				case 'edit': {
+					// eslint-disable-next-line
+					const result = await misskeyApi('admin/system-webhook/update', { id: id.value!, ...params });
+					emit('submitted', result);
+					break;
+				}
+			}
+			// eslint-disable-next-line
+		} catch (ex: any) {
+			const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
+			await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
+			emit('closed');
+		}
+	});
+}
+
+function onCancelClicked() {
+	emit('closed');
+}
+
+async function loadingScope<T>(fn: () => Promise<T>): Promise<T> {
+	loading.value++;
+	try {
+		return await fn();
+	} finally {
+		loading.value--;
+	}
+}
+
+onMounted(async () => {
+	await loadingScope(async () => {
+		switch (mode.value) {
+			case 'edit': {
+				if (!id.value) {
+					throw new Error('id is required');
+				}
+
+				try {
+					const res = await misskeyApi('admin/system-webhook/show', { id: id.value });
+
+					title.value = res.name;
+					url.value = res.url;
+					secret.value = res.secret;
+					isActive.value = res.isActive;
+					for (const ev of Object.keys(events.value)) {
+						events.value[ev] = res.on.includes(ev as SystemWebhookEventType);
+					}
+					// eslint-disable-next-line
+				} catch (ex: any) {
+					const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
+					await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
+					emit('closed');
+				}
+				break;
+			}
+		}
+
+		for (const ev of requiredEvents.value ?? []) {
+			disabledEvents.value[ev] = true;
+		}
+	});
+});
+</script>
+
+<style module lang="scss">
+.root {
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: stretch;
+}
+
+.footer {
+	display: flex;
+	justify-content: center;
+	align-items: flex-end;
+	margin-top: 20px;
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
new file mode 100644
index 0000000000..ffe9c620d6
--- /dev/null
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
@@ -0,0 +1,307 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+	ref="dialog"
+	:width="400"
+	:height="490"
+	:withOkButton="false"
+	:okButtonDisabled="false"
+	@close="onCancelClicked"
+	@closed="emit('closed')"
+>
+	<template #header>
+		{{ mode === 'create' ? i18n.ts._abuseReport._notificationRecipient.createRecipient : i18n.ts._abuseReport._notificationRecipient.modifyRecipient }}
+	</template>
+	<div v-if="loading === 0">
+		<MkSpacer :marginMin="20" :marginMax="28">
+			<div :class="$style.root" class="_gaps_m">
+				<MkInput v-model="title">
+					<template #label>{{ i18n.ts.title }}</template>
+				</MkInput>
+				<MkSelect v-model="method">
+					<template #label>{{ i18n.ts._abuseReport._notificationRecipient.recipientType }}</template>
+					<option value="email">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.mail }}</option>
+					<option value="webhook">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.webhook }}</option>
+					<template #caption>
+						{{ methodCaption }}
+					</template>
+				</MkSelect>
+				<div>
+					<MkSelect v-if="method === 'email'" v-model="userId">
+						<template #label>{{ i18n.ts._abuseReport._notificationRecipient.notifiedUser }}</template>
+						<option v-for="user in moderators" :key="user.id" :value="user.id">
+							{{ user.name ? `${user.name}(${user.username})` : user.username }}
+						</option>
+					</MkSelect>
+					<div v-else-if="method === 'webhook'" :class="$style.systemWebhook">
+						<MkSelect v-model="systemWebhookId" style="flex: 1">
+							<template #label>{{ i18n.ts._abuseReport._notificationRecipient.notifiedWebhook }}</template>
+							<option v-for="webhook in systemWebhooks" :key="webhook.id ?? undefined" :value="webhook.id">
+								{{ webhook.name }}
+							</option>
+						</MkSelect>
+						<MkButton rounded @click="onEditSystemWebhookClicked">
+							<span v-if="systemWebhookId === null" class="ti ti-plus" style="line-height: normal"/>
+							<span v-else class="ti ti-settings" style="line-height: normal"/>
+						</MkButton>
+					</div>
+				</div>
+
+				<MkDivider/>
+
+				<MkSwitch v-model="isActive">
+					<template #label>{{ i18n.ts.enable }}</template>
+				</MkSwitch>
+			</div>
+		</MkSpacer>
+
+		<div :class="$style.footer" class="_buttonsCenter">
+			<MkButton primary :disabled="disableSubmitButton" @click="onSubmitClicked"><i class="ti ti-check"></i> {{ i18n.ts.ok }}</MkButton>
+			<MkButton @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
+		</div>
+	</div>
+	<div v-else>
+		<MkLoading/>
+	</div>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref, toRefs } from 'vue';
+import { entities } from 'misskey-js';
+import MkButton from '@/components/MkButton.vue';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import { i18n } from '@/i18n.js';
+import MkInput from '@/components/MkInput.vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import MkSelect from '@/components/MkSelect.vue';
+import { MkSystemWebhookResult, showSystemWebhookEditorDialog } from '@/components/MkSystemWebhookEditor.impl.js';
+import MkSwitch from '@/components/MkSwitch.vue';
+import MkDivider from '@/components/MkDivider.vue';
+import * as os from '@/os.js';
+
+type NotificationRecipientMethod = 'email' | 'webhook';
+
+const emit = defineEmits<{
+	(ev: 'submitted'): void;
+	(ev: 'closed'): void;
+}>();
+
+const props = defineProps<{
+	mode: 'create' | 'edit';
+	id?: string;
+}>();
+
+const { mode, id } = toRefs(props);
+
+const loading = ref<number>(0);
+
+const title = ref<string>('');
+const method = ref<NotificationRecipientMethod>('email');
+const userId = ref<string | null>(null);
+const systemWebhookId = ref<string | null>(null);
+const isActive = ref<boolean>(true);
+
+const moderators = ref<entities.User[]>([]);
+const systemWebhooks = ref<(entities.SystemWebhook | { id: null, name: string })[]>([]);
+
+const methodCaption = computed(() => {
+	switch (method.value) {
+		case 'email': {
+			return i18n.ts._abuseReport._notificationRecipient._recipientType._captions.mail;
+		}
+		case 'webhook': {
+			return i18n.ts._abuseReport._notificationRecipient._recipientType._captions.webhook;
+		}
+		default: {
+			return '';
+		}
+	}
+});
+
+const disableSubmitButton = computed(() => {
+	if (!title.value) {
+		return true;
+	}
+
+	switch (method.value) {
+		case 'email': {
+			return userId.value === null;
+		}
+		case 'webhook': {
+			return systemWebhookId.value === null;
+		}
+		default: {
+			return true;
+		}
+	}
+});
+
+async function onSubmitClicked() {
+	await loadingScope(async () => {
+		const _userId = (method.value === 'email') ? userId.value : null;
+		const _systemWebhookId = (method.value === 'webhook') ? systemWebhookId.value : null;
+		const params = {
+			isActive: isActive.value,
+			name: title.value,
+			method: method.value,
+			userId: _userId ?? undefined,
+			systemWebhookId: _systemWebhookId ?? undefined,
+		};
+
+		try {
+			switch (mode.value) {
+				case 'create': {
+					await misskeyApi('admin/abuse-report/notification-recipient/create', params);
+					break;
+				}
+				case 'edit': {
+					// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+					await misskeyApi('admin/abuse-report/notification-recipient/update', { id: id.value!, ...params });
+					break;
+				}
+			}
+
+			emit('submitted');
+			// eslint-disable-next-line
+		} catch (ex: any) {
+			const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
+			await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
+			emit('closed');
+		}
+	});
+}
+
+function onCancelClicked() {
+	emit('closed');
+}
+
+async function onEditSystemWebhookClicked() {
+	let result: MkSystemWebhookResult | null;
+	if (systemWebhookId.value === null) {
+		result = await showSystemWebhookEditorDialog({
+			mode: 'create',
+		});
+	} else {
+		result = await showSystemWebhookEditorDialog({
+			mode: 'edit',
+			id: systemWebhookId.value,
+		});
+	}
+	if (!result) {
+		return;
+	}
+
+	await fetchSystemWebhooks();
+	systemWebhookId.value = result.id ?? null;
+}
+
+async function fetchSystemWebhooks() {
+	await loadingScope(async () => {
+		systemWebhooks.value = [
+			{ id: null, name: i18n.ts.createNew },
+			...await misskeyApi('admin/system-webhook/list', { }),
+		];
+	});
+}
+
+async function fetchModerators() {
+	await loadingScope(async () => {
+		const users = Array.of<entities.User>();
+		for (; ;) {
+			const res = await misskeyApi('admin/show-users', {
+				limit: 100,
+				state: 'adminOrModerator',
+				origin: 'local',
+				offset: users.length,
+			});
+
+			if (res.length === 0) {
+				break;
+			}
+
+			users.push(...res);
+		}
+
+		moderators.value = users;
+	});
+}
+
+async function loadingScope<T>(fn: () => Promise<T>): Promise<T> {
+	loading.value++;
+	try {
+		return await fn();
+	} finally {
+		loading.value--;
+	}
+}
+
+onMounted(async () => {
+	await loadingScope(async () => {
+		await fetchModerators();
+		await fetchSystemWebhooks();
+
+		if (mode.value === 'edit') {
+			if (!id.value) {
+				throw new Error('id is required');
+			}
+
+			try {
+				const res = await misskeyApi('admin/abuse-report/notification-recipient/show', { id: id.value });
+
+				title.value = res.name;
+				method.value = res.method;
+				userId.value = res.userId ?? null;
+				systemWebhookId.value = res.systemWebhookId ?? null;
+				isActive.value = res.isActive;
+				// eslint-disable-next-line
+			} catch (ex: any) {
+				const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
+				await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
+				emit('closed');
+			}
+		} else {
+			userId.value = moderators.value[0]?.id ?? null;
+			systemWebhookId.value = systemWebhooks.value[0]?.id ?? null;
+		}
+	});
+});
+
+</script>
+
+<style lang="scss" module>
+.root {
+	display: flex;
+	flex-direction: column;
+	justify-content: flex-start;
+	align-items: stretch;
+}
+
+.footer {
+	display: flex;
+	justify-content: center;
+	align-items: flex-end;
+	margin-top: 20px;
+}
+
+.systemWebhook {
+	display: flex;
+	flex-direction: row;
+	justify-content: stretch;
+	align-items: flex-end;
+	gap: 8px;
+
+	button {
+		width: 2.5em;
+		height: 2.5em;
+		min-width: 2.5em;
+		min-height: 2.5em;
+		box-sizing: border-box;
+		padding: 6px;
+	}
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
new file mode 100644
index 0000000000..0b86808faf
--- /dev/null
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
@@ -0,0 +1,114 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.root" class="_panel _gaps_s">
+	<div :class="$style.rightDivider" style="width: 80px;"><span :class="`ti ${methodIcon}`"/> {{ methodName }}</div>
+	<div :class="$style.rightDivider" style="flex: 0.5">{{ entity.name }}</div>
+	<div :class="$style.rightDivider" style="flex: 1">
+		<div v-if="method === 'email' && user">
+			{{
+				`${i18n.ts._abuseReport._notificationRecipient.notifiedUser}: ` + ((user.name) ? `${user.name}(${user.username})` : user.username)
+			}}
+		</div>
+		<div v-if="method === 'webhook' && systemWebhook">
+			{{ `${i18n.ts._abuseReport._notificationRecipient.notifiedWebhook}: ` + systemWebhook.name }}
+		</div>
+	</div>
+	<div :class="$style.recipientButtons" style="margin-left: auto">
+		<button :class="$style.recipientButton" @click="onEditButtonClicked()">
+			<span class="ti ti-settings"/>
+		</button>
+		<button :class="$style.recipientButton" @click="onDeleteButtonClicked()">
+			<span class="ti ti-trash"/>
+		</button>
+	</div>
+</div>
+</template>
+
+<script setup lang="ts">
+import { entities } from 'misskey-js';
+import { computed, toRefs } from 'vue';
+import { i18n } from '@/i18n.js';
+
+const emit = defineEmits<{
+	(ev: 'edit', id: entities.AbuseReportNotificationRecipient['id']): void;
+	(ev: 'delete', id: entities.AbuseReportNotificationRecipient['id']): void;
+}>();
+
+const props = defineProps<{
+	entity: entities.AbuseReportNotificationRecipient;
+}>();
+
+const { entity } = toRefs(props);
+
+const method = computed(() => entity.value.method);
+const user = computed(() => entity.value.user);
+const systemWebhook = computed(() => entity.value.systemWebhook);
+const methodIcon = computed(() => {
+	switch (entity.value.method) {
+		case 'email':
+			return 'ti-mail';
+		case 'webhook':
+			return 'ti-webhook';
+		default:
+			return 'ti-help';
+	}
+});
+const methodName = computed(() => {
+	switch (entity.value.method) {
+		case 'email':
+			return i18n.ts._abuseReport._notificationRecipient._recipientType.mail;
+		case 'webhook':
+			return i18n.ts._abuseReport._notificationRecipient._recipientType.webhook;
+		default:
+			return '不明';
+	}
+});
+
+function onEditButtonClicked() {
+	emit('edit', entity.value.id);
+}
+
+function onDeleteButtonClicked() {
+	emit('delete', entity.value.id);
+}
+</script>
+
+<style module lang="scss">
+.root {
+	display: flex;
+	flex-direction: row;
+	justify-content: center;
+	align-items: center;
+	padding: 4px 8px;
+}
+
+.rightDivider {
+	border-right: 0.5px solid var(--divider);
+}
+
+.recipientButtons {
+	display: flex;
+	flex-direction: row;
+	justify-content: center;
+	align-items: center;
+	margin-right: -4;
+}
+
+.recipientButton {
+	background-color: transparent;
+	border: none;
+	border-radius: 9999px;
+	box-sizing: border-box;
+	margin-top: -2px;
+	margin-bottom: -2px;
+	padding: 8px;
+
+	&:hover {
+		background-color: var(--buttonBg);
+	}
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
new file mode 100644
index 0000000000..a52f8eb7af
--- /dev/null
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
@@ -0,0 +1,176 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+	<template #header>
+		<XHeader :actions="headerActions" :tabs="headerTabs"/>
+	</template>
+
+	<MkSpacer :contentMax="900">
+		<div :class="$style.root" class="_gaps_m">
+			<div :class="$style.addButton">
+				<MkButton primary @click="onAddButtonClicked">
+					<span class="ti ti-plus"/> {{ i18n.ts._abuseReport._notificationRecipient.createRecipient }}
+				</MkButton>
+			</div>
+			<div :class="$style.subMenus" class="_gaps_s">
+				<MkSelect v-model="filterMethod" style="flex: 1">
+					<template #label>{{ i18n.ts._abuseReport._notificationRecipient.recipientType }}</template>
+					<option :value="null">-</option>
+					<option :value="'email'">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.mail }}</option>
+					<option :value="'webhook'">{{ i18n.ts._abuseReport._notificationRecipient._recipientType.webhook }}</option>
+				</MkSelect>
+				<MkInput v-model="filterText" type="search" style="flex: 1">
+					<template #label>{{ i18n.ts._abuseReport._notificationRecipient.keywords }}</template>
+				</MkInput>
+			</div>
+
+			<MkDivider/>
+
+			<div :class="$style.recipients" class="_gaps_s">
+				<XRecipient
+					v-for="r in filteredRecipients"
+					:key="r.id"
+					:entity="r"
+					@edit="onEditButtonClicked"
+					@delete="onDeleteButtonClicked"
+				/>
+			</div>
+		</div>
+	</MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script setup lang="ts">
+import { entities } from 'misskey-js';
+import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
+import XRecipient from './notification-recipient.item.vue';
+import XHeader from '@/pages/admin/_header_.vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import MkInput from '@/components/MkInput.vue';
+import MkSelect from '@/components/MkSelect.vue';
+import MkButton from '@/components/MkButton.vue';
+import * as os from '@/os.js';
+import MkDivider from '@/components/MkDivider.vue';
+import { i18n } from '@/i18n.js';
+
+const recipients = ref<entities.AbuseReportNotificationRecipient[]>([]);
+
+const filterMethod = ref<string | null>(null);
+const filterText = ref<string>('');
+
+const filteredRecipients = computed(() => {
+	const method = filterMethod.value;
+	const text = filterText.value.trim().length === 0 ? null : filterText.value;
+
+	return recipients.value.filter(it => {
+		if (method ?? text) {
+			if (text) {
+				const keywords = [it.name, it.systemWebhook?.name, it.user?.name, it.user?.username];
+				if (keywords.filter(k => k?.includes(text)).length !== 0) {
+					return true;
+				}
+			}
+
+			if (method) {
+				return it.method.includes(method);
+			}
+
+			return false;
+		}
+
+		return true;
+	});
+});
+const headerActions = computed(() => []);
+const headerTabs = computed(() => []);
+
+async function onAddButtonClicked() {
+	await showEditor('create');
+}
+
+async function onEditButtonClicked(id: string) {
+	await showEditor('edit', id);
+}
+
+async function onDeleteButtonClicked(id: string) {
+	const res = await os.confirm({
+		type: 'warning',
+		title: i18n.ts._abuseReport._notificationRecipient.deleteConfirm,
+	});
+	if (!res.canceled) {
+		await misskeyApi('admin/abuse-report/notification-recipient/delete', { id: id });
+		await fetchRecipients();
+	}
+}
+
+async function showEditor(mode: 'create' | 'edit', id?: string) {
+	const { dispose, needLoad } = await new Promise<{ dispose: () => void, needLoad: boolean }>(async resolve => {
+		const res = await os.popup(
+			defineAsyncComponent(() => import('./notification-recipient.editor.vue')),
+			{
+				mode,
+				id,
+			},
+			{
+				submitted: async () => {
+					resolve({ dispose: res.dispose, needLoad: true });
+				},
+				closed: () => {
+					resolve({ dispose: res.dispose, needLoad: false });
+				},
+			},
+		);
+	});
+
+	dispose();
+
+	if (needLoad) {
+		await fetchRecipients();
+	}
+}
+
+async function fetchRecipients() {
+	const result = await misskeyApi('admin/abuse-report/notification-recipient/list', {
+		method: ['email', 'webhook'],
+	});
+
+	recipients.value = result.sort((a, b) => (a.method + a.id).localeCompare(b.method + b.id));
+}
+
+onMounted(async () => {
+	await fetchRecipients();
+});
+</script>
+
+<style module lang="scss">
+.root {
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: stretch;
+}
+
+.addButton {
+	display: flex;
+	justify-content: flex-end;
+	gap: 8px;
+}
+
+.subMenus {
+	display: flex;
+	flex-direction: row;
+	justify-content: space-between;
+	align-items: flex-end;
+}
+
+.recipients {
+	display: flex;
+	flex-direction: column;
+	justify-content: flex-start;
+	align-items: stretch;
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index d2f4a4b531..9a9fa472a5 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -7,30 +7,33 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkStickyContainer>
 	<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
 	<MkSpacer :contentMax="900">
-		<div>
-			<div class="reports">
-				<div class="">
-					<div class="inputs" style="display: flex;">
-						<MkSelect v-model="state" style="margin: 0; flex: 1;">
-							<template #label>{{ i18n.ts.state }}</template>
-							<option value="all">{{ i18n.ts.all }}</option>
-							<option value="unresolved">{{ i18n.ts.unresolved }}</option>
-							<option value="resolved">{{ i18n.ts.resolved }}</option>
-						</MkSelect>
-						<MkSelect v-model="targetUserOrigin" style="margin: 0; flex: 1;">
-							<template #label>{{ i18n.ts.reporteeOrigin }}</template>
-							<option value="combined">{{ i18n.ts.all }}</option>
-							<option value="local">{{ i18n.ts.local }}</option>
-							<option value="remote">{{ i18n.ts.remote }}</option>
-						</MkSelect>
-						<MkSelect v-model="reporterOrigin" style="margin: 0; flex: 1;">
-							<template #label>{{ i18n.ts.reporterOrigin }}</template>
-							<option value="combined">{{ i18n.ts.all }}</option>
-							<option value="local">{{ i18n.ts.local }}</option>
-							<option value="remote">{{ i18n.ts.remote }}</option>
-						</MkSelect>
-					</div>
-					<!-- TODO
+		<div :class="$style.root" class="_gaps">
+			<div :class="$style.subMenus" class="_gaps">
+				<MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ "通知設定" }}</MkButton>
+			</div>
+
+			<div :class="$style.inputs" class="_gaps">
+				<MkSelect v-model="state" style="margin: 0; flex: 1;">
+					<template #label>{{ i18n.ts.state }}</template>
+					<option value="all">{{ i18n.ts.all }}</option>
+					<option value="unresolved">{{ i18n.ts.unresolved }}</option>
+					<option value="resolved">{{ i18n.ts.resolved }}</option>
+				</MkSelect>
+				<MkSelect v-model="targetUserOrigin" style="margin: 0; flex: 1;">
+					<template #label>{{ i18n.ts.reporteeOrigin }}</template>
+					<option value="combined">{{ i18n.ts.all }}</option>
+					<option value="local">{{ i18n.ts.local }}</option>
+					<option value="remote">{{ i18n.ts.remote }}</option>
+				</MkSelect>
+				<MkSelect v-model="reporterOrigin" style="margin: 0; flex: 1;">
+					<template #label>{{ i18n.ts.reporterOrigin }}</template>
+					<option value="combined">{{ i18n.ts.all }}</option>
+					<option value="local">{{ i18n.ts.local }}</option>
+					<option value="remote">{{ i18n.ts.remote }}</option>
+				</MkSelect>
+			</div>
+
+			<!-- TODO
 			<div class="inputs" style="display: flex; padding-top: 1.2em;">
 				<MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" :spellcheck="false">
 					<span>{{ i18n.ts.username }}</span>
@@ -41,11 +44,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 			-->
 
-					<MkPagination v-slot="{items}" ref="reports" :pagination="pagination" style="margin-top: var(--margin);">
-						<XAbuseReport v-for="report in items" :key="report.id" :report="report" @resolved="resolved"/>
-					</MkPagination>
-				</div>
-			</div>
+			<MkPagination v-slot="{items}" ref="reports" :pagination="pagination" style="margin-top: var(--margin);">
+				<XAbuseReport v-for="report in items" :key="report.id" :report="report" @resolved="resolved"/>
+			</MkPagination>
 		</div>
 	</MkSpacer>
 </MkStickyContainer>
@@ -60,6 +61,7 @@ import MkPagination from '@/components/MkPagination.vue';
 import XAbuseReport from '@/components/MkAbuseReport.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
+import MkButton from '@/components/MkButton.vue';
 
 const reports = shallowRef<InstanceType<typeof MkPagination>>();
 
@@ -80,7 +82,7 @@ const pagination = {
 };
 
 function resolved(reportId) {
-	reports.value.removeItem(reportId);
+	reports.value?.removeItem(reportId);
 }
 
 const headerActions = computed(() => []);
@@ -92,3 +94,26 @@ definePageMetadata(() => ({
 	icon: 'ti ti-exclamation-circle',
 }));
 </script>
+
+<style module lang="scss">
+.root {
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: stretch;
+}
+
+.subMenus {
+	display: flex;
+	flex-direction: row;
+	justify-content: flex-end;
+	align-items: center;
+}
+
+.inputs {
+	display: flex;
+	flex-direction: row;
+	justify-content: space-between;
+	align-items: center;
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 794feae202..292f10da1a 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -214,6 +214,11 @@ const menuDef = computed(() => [{
 		text: i18n.ts.externalServices,
 		to: '/admin/external-services',
 		active: currentPage.value?.route.name === 'external-services',
+	}, {
+		icon: 'ti ti-webhook',
+		text: 'Webhook',
+		to: '/admin/system-webhook',
+		active: currentPage.value?.route.name === 'system-webhook',
 	}, {
 		icon: 'ti ti-adjustments',
 		text: i18n.ts.other,
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index e33c882721..91f1c7c5e6 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -8,9 +8,35 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #label>
 		<b
 			:class="{
-				[$style.logGreen]: ['createRole', 'addCustomEmoji', 'createGlobalAnnouncement', 'createUserAnnouncement', 'createAd', 'createInvitation', 'createAvatarDecoration'].includes(log.type),
-				[$style.logYellow]: ['markSensitiveDriveFile', 'resetPassword'].includes(log.type),
-				[$style.logRed]: ['suspend', 'deleteRole', 'suspendRemoteInstance', 'deleteGlobalAnnouncement', 'deleteUserAnnouncement', 'deleteCustomEmoji', 'deleteNote', 'deleteDriveFile', 'deleteAd', 'deleteAvatarDecoration'].includes(log.type)
+				[$style.logGreen]: [
+					'createRole',
+					'addCustomEmoji',
+					'createGlobalAnnouncement',
+					'createUserAnnouncement',
+					'createAd',
+					'createInvitation',
+					'createAvatarDecoration',
+					'createSystemWebhook',
+					'createAbuseReportNotificationRecipient',
+				].includes(log.type),
+				[$style.logYellow]: [
+					'markSensitiveDriveFile',
+					'resetPassword'
+				].includes(log.type),
+				[$style.logRed]: [
+					'suspend',
+					'deleteRole',
+					'suspendRemoteInstance',
+					'deleteGlobalAnnouncement',
+					'deleteUserAnnouncement',
+					'deleteCustomEmoji',
+					'deleteNote',
+					'deleteDriveFile',
+					'deleteAd',
+					'deleteAvatarDecoration',
+					'deleteSystemWebhook',
+					'deleteAbuseReportNotificationRecipient',
+				].includes(log.type)
 			}"
 		>{{ i18n.ts._moderationLogTypes[log.type] }}</b>
 		<span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
@@ -40,6 +66,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<span v-else-if="log.type === 'createAvatarDecoration'">: {{ log.info.avatarDecoration.name }}</span>
 		<span v-else-if="log.type === 'updateAvatarDecoration'">: {{ log.info.before.name }}</span>
 		<span v-else-if="log.type === 'deleteAvatarDecoration'">: {{ log.info.avatarDecoration.name }}</span>
+		<span v-else-if="log.type === 'createSystemWebhook'">: {{ log.info.webhook.name }}</span>
+		<span v-else-if="log.type === 'updateSystemWebhook'">: {{ log.info.before.name }}</span>
+		<span v-else-if="log.type === 'deleteSystemWebhook'">: {{ log.info.webhook.name }}</span>
+		<span v-else-if="log.type === 'createAbuseReportNotificationRecipient'">: {{ log.info.recipient.name }}</span>
+		<span v-else-if="log.type === 'updateAbuseReportNotificationRecipient'">: {{ log.info.before.name }}</span>
+		<span v-else-if="log.type === 'deleteAbuseReportNotificationRecipient'">: {{ log.info.recipient.name }}</span>
 	</template>
 	<template #icon>
 		<MkAvatar :user="log.user" :class="$style.avatar"/>
@@ -116,6 +148,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
 			</div>
 		</template>
+		<template v-else-if="log.type === 'updateSystemWebhook'">
+			<div :class="$style.diff">
+				<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+			</div>
+		</template>
+		<template v-else-if="log.type === 'updateAbuseReportNotificationRecipient'">
+			<div :class="$style.diff">
+				<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+			</div>
+		</template>
 
 		<details>
 			<summary>raw</summary>
diff --git a/packages/frontend/src/pages/admin/system-webhook.item.vue b/packages/frontend/src/pages/admin/system-webhook.item.vue
new file mode 100644
index 0000000000..0c07122af3
--- /dev/null
+++ b/packages/frontend/src/pages/admin/system-webhook.item.vue
@@ -0,0 +1,117 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.main">
+	<span :class="$style.icon">
+		<i v-if="!entity.isActive" class="ti ti-player-pause"/>
+		<i v-else-if="entity.latestStatus === null" class="ti ti-circle"/>
+		<i
+			v-else-if="[200, 201, 204].includes(entity.latestStatus)"
+			class="ti ti-check"
+			:style="{ color: 'var(--success)' }"
+		/>
+		<i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--error)' }"/>
+	</span>
+	<span :class="$style.text">{{ entity.name || entity.url }}</span>
+	<span :class="$style.suffix">
+		<MkTime v-if="entity.latestSentAt" :time="entity.latestSentAt" style="margin-right: 8px"/>
+		<button :class="$style.suffixButton" @click="onEditClick">
+			<i class="ti ti-settings"></i>
+		</button>
+		<button :class="$style.suffixButton" @click="onDeleteClick">
+			<i class="ti ti-trash"></i>
+		</button>
+	</span>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { entities } from 'misskey-js';
+import { toRefs } from 'vue';
+
+const emit = defineEmits<{
+	(ev: 'edit', value: entities.SystemWebhook): void;
+	(ev: 'delete', value: entities.SystemWebhook): void;
+}>();
+
+const props = defineProps<{
+	entity: entities.SystemWebhook;
+}>();
+
+const { entity } = toRefs(props);
+
+function onEditClick() {
+	emit('edit', entity.value);
+}
+
+function onDeleteClick() {
+	emit('delete', entity.value);
+}
+
+</script>
+
+<style module lang="scss">
+.main {
+	display: flex;
+	align-items: center;
+	width: 100%;
+	box-sizing: border-box;
+	padding: 10px 14px;
+	background: var(--buttonBg);
+	border: none;
+	border-radius: 6px;
+	font-size: 0.9em;
+
+	&:hover {
+		text-decoration: none;
+		background: var(--buttonHoverBg);
+	}
+
+	&.active {
+		color: var(--accent);
+		background: var(--buttonHoverBg);
+	}
+}
+
+.icon {
+	margin-right: 0.75em;
+	flex-shrink: 0;
+	text-align: center;
+	color: var(--fgTransparentWeak);
+}
+
+.text {
+	flex-shrink: 1;
+	white-space: normal;
+	padding-right: 12px;
+	text-align: center;
+}
+
+.suffix {
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+	justify-content: center;
+	gaps: 4px;
+	margin-left: auto;
+	margin-right: -8px;
+	opacity: 0.7;
+	white-space: nowrap;
+}
+
+.suffixButton {
+	background: transparent;
+	border: none;
+	border-radius: 9999px;
+	margin-top: -8px;
+	margin-bottom: -8px;
+	padding: 8px;
+
+	&:hover {
+		background: var(--buttonBg);
+	}
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/system-webhook.vue b/packages/frontend/src/pages/admin/system-webhook.vue
new file mode 100644
index 0000000000..7a40eec944
--- /dev/null
+++ b/packages/frontend/src/pages/admin/system-webhook.vue
@@ -0,0 +1,96 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+	<template #header>
+		<XHeader :actions="headerActions" :tabs="headerTabs"/>
+	</template>
+
+	<MkSpacer :contentMax="900">
+		<div class="_gaps_m">
+			<MkButton :class="$style.linkButton" full @click="onCreateWebhookClicked">
+				{{ i18n.ts._webhookSettings.createWebhook }}
+			</MkButton>
+
+			<FormSection>
+				<div class="_gaps">
+					<XItem v-for="item in webhooks" :key="item.id" :entity="item" @edit="onEditButtonClicked" @delete="onDeleteButtonClicked"/>
+				</div>
+			</FormSection>
+		</div>
+	</MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from 'vue';
+import { entities } from 'misskey-js';
+import XItem from './system-webhook.item.vue';
+import FormSection from '@/components/form/section.vue';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import XHeader from '@/pages/admin/_header_.vue';
+import MkButton from '@/components/MkButton.vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import { showSystemWebhookEditorDialog } from '@/components/MkSystemWebhookEditor.impl.js';
+import * as os from '@/os.js';
+
+const webhooks = ref<entities.SystemWebhook[]>([]);
+
+const headerActions = computed(() => []);
+const headerTabs = computed(() => []);
+
+async function onCreateWebhookClicked() {
+	await showSystemWebhookEditorDialog({
+		mode: 'create',
+	});
+
+	await fetchWebhooks();
+}
+
+async function onEditButtonClicked(webhook: entities.SystemWebhook) {
+	await showSystemWebhookEditorDialog({
+		mode: 'edit',
+		id: webhook.id,
+	});
+
+	await fetchWebhooks();
+}
+
+async function onDeleteButtonClicked(webhook: entities.SystemWebhook) {
+	const result = await os.confirm({
+		type: 'warning',
+		title: i18n.ts._webhookSettings.deleteConfirm,
+	});
+	if (!result.canceled) {
+		await misskeyApi('admin/system-webhook/delete', {
+			id: webhook.id,
+		});
+		await fetchWebhooks();
+	}
+}
+
+async function fetchWebhooks() {
+	const result = await misskeyApi('admin/system-webhook/list', {});
+	webhooks.value = result.sort((a, b) => a.id.localeCompare(b.id));
+}
+
+onMounted(async () => {
+	await fetchWebhooks();
+});
+
+definePageMetadata(() => ({
+	title: 'SystemWebhook',
+	icon: 'ti ti-webhook',
+}));
+</script>
+
+<style module lang="scss">
+.linkButton {
+	text-align: left;
+	padding: 10px 18px;
+}
+</style>
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index c12ae0fa57..8a443f627b 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -471,6 +471,14 @@ const routes: RouteDef[] = [{
 		path: '/invites',
 		name: 'invites',
 		component: page(() => import('@/pages/admin/invites.vue')),
+	}, {
+		path: '/abuse-report-notification-recipient',
+		name: 'abuse-report-notification-recipient',
+		component: page(() => import('@/pages/admin/abuse-report/notification-recipient.vue')),
+	}, {
+		path: '/system-webhook',
+		name: 'system-webhook',
+		component: page(() => import('@/pages/admin/system-webhook.vue')),
 	}, {
 		path: '/',
 		component: page(() => import('@/pages/_empty_.vue')),
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 6ff711cabb..bea89f2a7c 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -6,6 +6,11 @@
 
 import { EventEmitter } from 'eventemitter3';
 
+// Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+type AbuseReportNotificationRecipient = components['schemas']['AbuseReportNotificationRecipient'];
+
 // @public (undocumented)
 export type Acct = {
     username: string;
@@ -21,13 +26,38 @@ declare namespace acct {
 }
 export { acct }
 
-// Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts
-//
 // @public (undocumented)
 type Ad = components['schemas']['Ad'];
 
 // Warning: (ae-forgotten-export) The symbol "operations" needs to be exported by the entry point index.d.ts
 //
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientCreateRequest = operations['admin___abuse-report___notification-recipient___create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientCreateResponse = operations['admin___abuse-report___notification-recipient___create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientDeleteRequest = operations['admin___abuse-report___notification-recipient___delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientListRequest = operations['admin___abuse-report___notification-recipient___list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientListResponse = operations['admin___abuse-report___notification-recipient___list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientShowRequest = operations['admin___abuse-report___notification-recipient___show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientShowResponse = operations['admin___abuse-report___notification-recipient___show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientUpdateRequest = operations['admin___abuse-report___notification-recipient___update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAbuseReportNotificationRecipientUpdateResponse = operations['admin___abuse-report___notification-recipient___update']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminAbuseUserReportsRequest = operations['admin___abuse-user-reports']['requestBody']['content']['application/json'];
 
@@ -307,6 +337,33 @@ type AdminShowUsersResponse = operations['admin___show-users']['responses']['200
 // @public (undocumented)
 type AdminSuspendUserRequest = operations['admin___suspend-user']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminSystemWebhookCreateRequest = operations['admin___system-webhook___create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookCreateResponse = operations['admin___system-webhook___create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookDeleteRequest = operations['admin___system-webhook___delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookListRequest = operations['admin___system-webhook___list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookListResponse = operations['admin___system-webhook___list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookShowRequest = operations['admin___system-webhook___show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookShowResponse = operations['admin___system-webhook___show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookUpdateRequest = operations['admin___system-webhook___update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSystemWebhookUpdateResponse = operations['admin___system-webhook___update']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminUnsetUserAvatarRequest = operations['admin___unset-user-avatar']['requestBody']['content']['application/json'];
 
@@ -1133,6 +1190,15 @@ declare namespace entities {
         AdminMetaResponse,
         AdminAbuseUserReportsRequest,
         AdminAbuseUserReportsResponse,
+        AdminAbuseReportNotificationRecipientListRequest,
+        AdminAbuseReportNotificationRecipientListResponse,
+        AdminAbuseReportNotificationRecipientShowRequest,
+        AdminAbuseReportNotificationRecipientShowResponse,
+        AdminAbuseReportNotificationRecipientCreateRequest,
+        AdminAbuseReportNotificationRecipientCreateResponse,
+        AdminAbuseReportNotificationRecipientUpdateRequest,
+        AdminAbuseReportNotificationRecipientUpdateResponse,
+        AdminAbuseReportNotificationRecipientDeleteRequest,
         AdminAccountsCreateRequest,
         AdminAccountsCreateResponse,
         AdminAccountsDeleteRequest,
@@ -1228,6 +1294,15 @@ declare namespace entities {
         AdminRolesUpdateDefaultPoliciesRequest,
         AdminRolesUsersRequest,
         AdminRolesUsersResponse,
+        AdminSystemWebhookCreateRequest,
+        AdminSystemWebhookCreateResponse,
+        AdminSystemWebhookDeleteRequest,
+        AdminSystemWebhookListRequest,
+        AdminSystemWebhookListResponse,
+        AdminSystemWebhookShowRequest,
+        AdminSystemWebhookShowResponse,
+        AdminSystemWebhookUpdateRequest,
+        AdminSystemWebhookUpdateResponse,
         AnnouncementsRequest,
         AnnouncementsResponse,
         AnnouncementsShowRequest,
@@ -1733,7 +1808,9 @@ declare namespace entities {
         ReversiGameDetailed,
         MetaLite,
         MetaDetailedOnly,
-        MetaDetailed
+        MetaDetailed,
+        SystemWebhook,
+        AbuseReportNotificationRecipient
     }
 }
 export { entities }
@@ -2380,8 +2457,23 @@ type ModerationLog = {
     type: 'unsetUserAvatar';
     info: ModerationLogPayloads['unsetUserAvatar'];
 } | {
-    type: 'unsetUserBanner';
-    info: ModerationLogPayloads['unsetUserBanner'];
+    type: 'createSystemWebhook';
+    info: ModerationLogPayloads['createSystemWebhook'];
+} | {
+    type: 'updateSystemWebhook';
+    info: ModerationLogPayloads['updateSystemWebhook'];
+} | {
+    type: 'deleteSystemWebhook';
+    info: ModerationLogPayloads['deleteSystemWebhook'];
+} | {
+    type: 'createAbuseReportNotificationRecipient';
+    info: ModerationLogPayloads['createAbuseReportNotificationRecipient'];
+} | {
+    type: 'updateAbuseReportNotificationRecipient';
+    info: ModerationLogPayloads['updateAbuseReportNotificationRecipient'];
+} | {
+    type: 'deleteAbuseReportNotificationRecipient';
+    info: ModerationLogPayloads['deleteAbuseReportNotificationRecipient'];
 });
 
 // @public (undocumented)
@@ -2921,6 +3013,9 @@ type SwUpdateRegistrationRequest = operations['sw___update-registration']['reque
 // @public (undocumented)
 type SwUpdateRegistrationResponse = operations['sw___update-registration']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type SystemWebhook = components['schemas']['SystemWebhook'];
+
 // @public (undocumented)
 type TestRequest = operations['test']['requestBody']['content']['application/json'];
 
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 181f7274b7..e799d4a0c5 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -25,6 +25,66 @@ declare module '../api.js' {
       credential?: string | null,
     ): Promise<SwitchCaseResponseType<E, P>>;
 
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-report:notification-recipient*
+     */
+    request<E extends 'admin/abuse-report/notification-recipient/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-report:notification-recipient*
+     */
+    request<E extends 'admin/abuse-report/notification-recipient/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+     */
+    request<E extends 'admin/abuse-report/notification-recipient/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+     */
+    request<E extends 'admin/abuse-report/notification-recipient/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+     */
+    request<E extends 'admin/abuse-report/notification-recipient/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
     /**
      * No description provided.
      * 
@@ -840,6 +900,66 @@ declare module '../api.js' {
       credential?: string | null,
     ): Promise<SwitchCaseResponseType<E, P>>;
 
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    request<E extends 'admin/system-webhook/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    request<E extends 'admin/system-webhook/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    request<E extends 'admin/system-webhook/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    request<E extends 'admin/system-webhook/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    request<E extends 'admin/system-webhook/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
     /**
      * No description provided.
      * 
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index ab3baf1670..20c8509d4c 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -4,6 +4,15 @@ import type {
 	AdminMetaResponse,
 	AdminAbuseUserReportsRequest,
 	AdminAbuseUserReportsResponse,
+	AdminAbuseReportNotificationRecipientListRequest,
+	AdminAbuseReportNotificationRecipientListResponse,
+	AdminAbuseReportNotificationRecipientShowRequest,
+	AdminAbuseReportNotificationRecipientShowResponse,
+	AdminAbuseReportNotificationRecipientCreateRequest,
+	AdminAbuseReportNotificationRecipientCreateResponse,
+	AdminAbuseReportNotificationRecipientUpdateRequest,
+	AdminAbuseReportNotificationRecipientUpdateResponse,
+	AdminAbuseReportNotificationRecipientDeleteRequest,
 	AdminAccountsCreateRequest,
 	AdminAccountsCreateResponse,
 	AdminAccountsDeleteRequest,
@@ -99,6 +108,15 @@ import type {
 	AdminRolesUpdateDefaultPoliciesRequest,
 	AdminRolesUsersRequest,
 	AdminRolesUsersResponse,
+	AdminSystemWebhookCreateRequest,
+	AdminSystemWebhookCreateResponse,
+	AdminSystemWebhookDeleteRequest,
+	AdminSystemWebhookListRequest,
+	AdminSystemWebhookListResponse,
+	AdminSystemWebhookShowRequest,
+	AdminSystemWebhookShowResponse,
+	AdminSystemWebhookUpdateRequest,
+	AdminSystemWebhookUpdateResponse,
 	AnnouncementsRequest,
 	AnnouncementsResponse,
 	AnnouncementsShowRequest,
@@ -558,6 +576,11 @@ import type {
 export type Endpoints = {
 	'admin/meta': { req: EmptyRequest; res: AdminMetaResponse };
 	'admin/abuse-user-reports': { req: AdminAbuseUserReportsRequest; res: AdminAbuseUserReportsResponse };
+	'admin/abuse-report/notification-recipient/list': { req: AdminAbuseReportNotificationRecipientListRequest; res: AdminAbuseReportNotificationRecipientListResponse };
+	'admin/abuse-report/notification-recipient/show': { req: AdminAbuseReportNotificationRecipientShowRequest; res: AdminAbuseReportNotificationRecipientShowResponse };
+	'admin/abuse-report/notification-recipient/create': { req: AdminAbuseReportNotificationRecipientCreateRequest; res: AdminAbuseReportNotificationRecipientCreateResponse };
+	'admin/abuse-report/notification-recipient/update': { req: AdminAbuseReportNotificationRecipientUpdateRequest; res: AdminAbuseReportNotificationRecipientUpdateResponse };
+	'admin/abuse-report/notification-recipient/delete': { req: AdminAbuseReportNotificationRecipientDeleteRequest; res: EmptyResponse };
 	'admin/accounts/create': { req: AdminAccountsCreateRequest; res: AdminAccountsCreateResponse };
 	'admin/accounts/delete': { req: AdminAccountsDeleteRequest; res: EmptyResponse };
 	'admin/accounts/find-by-email': { req: AdminAccountsFindByEmailRequest; res: AdminAccountsFindByEmailResponse };
@@ -632,6 +655,11 @@ export type Endpoints = {
 	'admin/roles/unassign': { req: AdminRolesUnassignRequest; res: EmptyResponse };
 	'admin/roles/update-default-policies': { req: AdminRolesUpdateDefaultPoliciesRequest; res: EmptyResponse };
 	'admin/roles/users': { req: AdminRolesUsersRequest; res: AdminRolesUsersResponse };
+	'admin/system-webhook/create': { req: AdminSystemWebhookCreateRequest; res: AdminSystemWebhookCreateResponse };
+	'admin/system-webhook/delete': { req: AdminSystemWebhookDeleteRequest; res: EmptyResponse };
+	'admin/system-webhook/list': { req: AdminSystemWebhookListRequest; res: AdminSystemWebhookListResponse };
+	'admin/system-webhook/show': { req: AdminSystemWebhookShowRequest; res: AdminSystemWebhookShowResponse };
+	'admin/system-webhook/update': { req: AdminSystemWebhookUpdateRequest; res: AdminSystemWebhookUpdateResponse };
 	'announcements': { req: AnnouncementsRequest; res: AnnouncementsResponse };
 	'announcements/show': { req: AnnouncementsShowRequest; res: AnnouncementsShowResponse };
 	'antennas/create': { req: AntennasCreateRequest; res: AntennasCreateResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 02ca932d8a..357b5e9eaf 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -7,6 +7,15 @@ export type EmptyResponse = Record<string, unknown> | undefined;
 export type AdminMetaResponse = operations['admin___meta']['responses']['200']['content']['application/json'];
 export type AdminAbuseUserReportsRequest = operations['admin___abuse-user-reports']['requestBody']['content']['application/json'];
 export type AdminAbuseUserReportsResponse = operations['admin___abuse-user-reports']['responses']['200']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientListRequest = operations['admin___abuse-report___notification-recipient___list']['requestBody']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientListResponse = operations['admin___abuse-report___notification-recipient___list']['responses']['200']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientShowRequest = operations['admin___abuse-report___notification-recipient___show']['requestBody']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientShowResponse = operations['admin___abuse-report___notification-recipient___show']['responses']['200']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientCreateRequest = operations['admin___abuse-report___notification-recipient___create']['requestBody']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientCreateResponse = operations['admin___abuse-report___notification-recipient___create']['responses']['200']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientUpdateRequest = operations['admin___abuse-report___notification-recipient___update']['requestBody']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientUpdateResponse = operations['admin___abuse-report___notification-recipient___update']['responses']['200']['content']['application/json'];
+export type AdminAbuseReportNotificationRecipientDeleteRequest = operations['admin___abuse-report___notification-recipient___delete']['requestBody']['content']['application/json'];
 export type AdminAccountsCreateRequest = operations['admin___accounts___create']['requestBody']['content']['application/json'];
 export type AdminAccountsCreateResponse = operations['admin___accounts___create']['responses']['200']['content']['application/json'];
 export type AdminAccountsDeleteRequest = operations['admin___accounts___delete']['requestBody']['content']['application/json'];
@@ -102,6 +111,15 @@ export type AdminRolesUnassignRequest = operations['admin___roles___unassign']['
 export type AdminRolesUpdateDefaultPoliciesRequest = operations['admin___roles___update-default-policies']['requestBody']['content']['application/json'];
 export type AdminRolesUsersRequest = operations['admin___roles___users']['requestBody']['content']['application/json'];
 export type AdminRolesUsersResponse = operations['admin___roles___users']['responses']['200']['content']['application/json'];
+export type AdminSystemWebhookCreateRequest = operations['admin___system-webhook___create']['requestBody']['content']['application/json'];
+export type AdminSystemWebhookCreateResponse = operations['admin___system-webhook___create']['responses']['200']['content']['application/json'];
+export type AdminSystemWebhookDeleteRequest = operations['admin___system-webhook___delete']['requestBody']['content']['application/json'];
+export type AdminSystemWebhookListRequest = operations['admin___system-webhook___list']['requestBody']['content']['application/json'];
+export type AdminSystemWebhookListResponse = operations['admin___system-webhook___list']['responses']['200']['content']['application/json'];
+export type AdminSystemWebhookShowRequest = operations['admin___system-webhook___show']['requestBody']['content']['application/json'];
+export type AdminSystemWebhookShowResponse = operations['admin___system-webhook___show']['responses']['200']['content']['application/json'];
+export type AdminSystemWebhookUpdateRequest = operations['admin___system-webhook___update']['requestBody']['content']['application/json'];
+export type AdminSystemWebhookUpdateResponse = operations['admin___system-webhook___update']['responses']['200']['content']['application/json'];
 export type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
 export type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
 export type AnnouncementsShowRequest = operations['announcements___show']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index a6e5fbe689..04574849d4 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -51,3 +51,5 @@ export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
 export type MetaLite = components['schemas']['MetaLite'];
 export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
 export type MetaDetailed = components['schemas']['MetaDetailed'];
+export type SystemWebhook = components['schemas']['SystemWebhook'];
+export type AbuseReportNotificationRecipient = components['schemas']['AbuseReportNotificationRecipient'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 2c80676f3e..bdcc1dfd77 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -30,6 +30,56 @@ export type paths = {
      */
     post: operations['admin___abuse-user-reports'];
   };
+  '/admin/abuse-report/notification-recipient/list': {
+    /**
+     * admin/abuse-report/notification-recipient/list
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-report:notification-recipient*
+     */
+    post: operations['admin___abuse-report___notification-recipient___list'];
+  };
+  '/admin/abuse-report/notification-recipient/show': {
+    /**
+     * admin/abuse-report/notification-recipient/show
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-report:notification-recipient*
+     */
+    post: operations['admin___abuse-report___notification-recipient___show'];
+  };
+  '/admin/abuse-report/notification-recipient/create': {
+    /**
+     * admin/abuse-report/notification-recipient/create
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+     */
+    post: operations['admin___abuse-report___notification-recipient___create'];
+  };
+  '/admin/abuse-report/notification-recipient/update': {
+    /**
+     * admin/abuse-report/notification-recipient/update
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+     */
+    post: operations['admin___abuse-report___notification-recipient___update'];
+  };
+  '/admin/abuse-report/notification-recipient/delete': {
+    /**
+     * admin/abuse-report/notification-recipient/delete
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+     */
+    post: operations['admin___abuse-report___notification-recipient___delete'];
+  };
   '/admin/accounts/create': {
     /**
      * admin/accounts/create
@@ -697,6 +747,56 @@ export type paths = {
      */
     post: operations['admin___roles___users'];
   };
+  '/admin/system-webhook/create': {
+    /**
+     * admin/system-webhook/create
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    post: operations['admin___system-webhook___create'];
+  };
+  '/admin/system-webhook/delete': {
+    /**
+     * admin/system-webhook/delete
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    post: operations['admin___system-webhook___delete'];
+  };
+  '/admin/system-webhook/list': {
+    /**
+     * admin/system-webhook/list
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    post: operations['admin___system-webhook___list'];
+  };
+  '/admin/system-webhook/show': {
+    /**
+     * admin/system-webhook/show
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    post: operations['admin___system-webhook___show'];
+  };
+  '/admin/system-webhook/update': {
+    /**
+     * admin/system-webhook/update
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+     */
+    post: operations['admin___system-webhook___update'];
+  };
   '/announcements': {
     /**
      * announcements
@@ -4859,6 +4959,32 @@ export type components = {
       cacheRemoteSensitiveFiles: boolean;
     };
     MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly'];
+    SystemWebhook: {
+      id: string;
+      isActive: boolean;
+      /** Format: date-time */
+      updatedAt: string;
+      /** Format: date-time */
+      latestSentAt: string | null;
+      latestStatus: number | null;
+      name: string;
+      on: ('abuseReport' | 'abuseReportResolved')[];
+      url: string;
+      secret: string;
+    };
+    AbuseReportNotificationRecipient: {
+      id: string;
+      isActive: boolean;
+      /** Format: date-time */
+      updatedAt: string;
+      name: string;
+      /** @enum {string} */
+      method: 'email' | 'webhook';
+      userId?: string;
+      user?: components['schemas']['UserLite'];
+      systemWebhookId?: string;
+      systemWebhook?: components['schemas']['SystemWebhook'];
+    };
   };
   responses: never;
   parameters: never;
@@ -5125,6 +5251,292 @@ export type operations = {
       };
     };
   };
+  /**
+   * admin/abuse-report/notification-recipient/list
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-report:notification-recipient*
+   */
+  'admin___abuse-report___notification-recipient___list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          method?: ('email' | 'webhook')[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['AbuseReportNotificationRecipient'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/abuse-report/notification-recipient/show
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-report:notification-recipient*
+   */
+  'admin___abuse-report___notification-recipient___show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['AbuseReportNotificationRecipient'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/abuse-report/notification-recipient/create
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+   */
+  'admin___abuse-report___notification-recipient___create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          isActive: boolean;
+          name: string;
+          /** @enum {string} */
+          method: 'email' | 'webhook';
+          /** Format: misskey:id */
+          userId?: string;
+          /** Format: misskey:id */
+          systemWebhookId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['AbuseReportNotificationRecipient'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/abuse-report/notification-recipient/update
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+   */
+  'admin___abuse-report___notification-recipient___update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+          isActive: boolean;
+          name: string;
+          /** @enum {string} */
+          method: 'email' | 'webhook';
+          /** Format: misskey:id */
+          userId?: string;
+          /** Format: misskey:id */
+          systemWebhookId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['AbuseReportNotificationRecipient'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/abuse-report/notification-recipient/delete
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:abuse-report:notification-recipient*
+   */
+  'admin___abuse-report___notification-recipient___delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * admin/accounts/create
    * @description No description provided.
@@ -9615,6 +10027,287 @@ export type operations = {
       };
     };
   };
+  /**
+   * admin/system-webhook/create
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+   */
+  'admin___system-webhook___create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          isActive: boolean;
+          name: string;
+          on: ('abuseReport' | 'abuseReportResolved')[];
+          url: string;
+          secret: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['SystemWebhook'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/system-webhook/delete
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+   */
+  'admin___system-webhook___delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/system-webhook/list
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+   */
+  'admin___system-webhook___list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          isActive?: boolean;
+          on?: ('abuseReport' | 'abuseReportResolved')[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['SystemWebhook'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/system-webhook/show
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+   */
+  'admin___system-webhook___show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['SystemWebhook'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/system-webhook/update
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes* / **Permission**: *write:admin:system-webhook*
+   */
+  'admin___system-webhook___update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+          isActive: boolean;
+          name: string;
+          on: ('abuseReport' | 'abuseReportResolved')[];
+          url: string;
+          secret: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['SystemWebhook'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * announcements
    * @description No description provided.
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index fd6ef4d68d..03b9069290 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -325,4 +325,30 @@ export type ModerationLogPayloads = {
 		userHost: string | null;
 		fileId: string;
 	};
+	createSystemWebhook: {
+		systemWebhookId: string;
+		webhook: any;
+	};
+	updateSystemWebhook: {
+		systemWebhookId: string;
+		before: any;
+		after: any;
+	};
+	deleteSystemWebhook: {
+		systemWebhookId: string;
+		webhook: any;
+	};
+	createAbuseReportNotificationRecipient: {
+		recipientId: string;
+		recipient: any;
+	};
+	updateAbuseReportNotificationRecipient: {
+		recipientId: string;
+		before: any;
+		after: any;
+	};
+	deleteAbuseReportNotificationRecipient: {
+		recipientId: string;
+		recipient: any;
+	};
 };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 35503d6d6f..7a84cb6a1a 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -132,8 +132,23 @@ export type ModerationLog = {
 	type: 'unsetUserAvatar';
 	info: ModerationLogPayloads['unsetUserAvatar'];
 } | {
-	type: 'unsetUserBanner';
-	info: ModerationLogPayloads['unsetUserBanner'];
+	type: 'createSystemWebhook';
+	info: ModerationLogPayloads['createSystemWebhook'];
+} | {
+	type: 'updateSystemWebhook';
+	info: ModerationLogPayloads['updateSystemWebhook'];
+} | {
+	type: 'deleteSystemWebhook';
+	info: ModerationLogPayloads['deleteSystemWebhook'];
+} | {
+	type: 'createAbuseReportNotificationRecipient';
+	info: ModerationLogPayloads['createAbuseReportNotificationRecipient'];
+} | {
+	type: 'updateAbuseReportNotificationRecipient';
+	info: ModerationLogPayloads['updateAbuseReportNotificationRecipient'];
+} | {
+	type: 'deleteAbuseReportNotificationRecipient';
+	info: ModerationLogPayloads['deleteAbuseReportNotificationRecipient'];
 });
 
 export type ServerStats = {

From 9849aab40283cbde2184e74d4795aec8ef8ccba3 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 8 Jun 2024 18:00:54 +0900
Subject: [PATCH 014/206] test(#10336): add `components/MkC.*` stories (#13830)

* test(storybook): add `components/MkC.*` stories

* test(storybook): add some tests

* test: add sleep

* test: comment-out flaky test

* test(storybook): add test for `MkChannelFollowButton`

* chore(storybook): tweak sleep duration in `MkChannelFollowButton` story test

* fix(chromatic): add delay to `MkChannelList`

* chore: replace `mswDecorator` with `mswLoader`

* fix(storybook): tweak some parameters

* chore: serve static files

* fix(chromatic): add delay to `MkCwButton`

* chore: delete logging for debug

* fix: add right click in `MkContextMenu` play

* refactor: remove unused imports
---
 packages/frontend/.storybook/fakes.ts         |  60 +++++++++
 packages/frontend/.storybook/generate.tsx     |   2 +-
 packages/frontend/.storybook/main.ts          |   1 +
 packages/frontend/.storybook/preview.ts       |   4 +-
 packages/frontend/package.json                |   2 +
 .../MkChannelFollowButton.stories.impl.ts     |  77 ++++++++++++
 .../src/components/MkChannelFollowButton.vue  |   5 +-
 .../components/MkChannelList.stories.impl.ts  |  65 ++++++++++
 .../MkChannelPreview.stories.impl.ts          |  43 +++++++
 .../src/components/MkChart.stories.impl.ts    | 117 ++++++++++++++++++
 packages/frontend/src/components/MkChart.vue  |  90 ++++++++------
 .../components/MkChartLegend.stories.impl.ts  |   7 ++
 .../components/MkChartTooltip.stories.impl.ts |   7 ++
 .../components/MkClickerGame.stories.impl.ts  |  79 ++++++++++++
 .../frontend/src/components/MkClickerGame.vue |   2 +-
 .../components/MkClipPreview.stories.impl.ts  |  43 +++++++
 .../components/MkCode.core.stories.impl.ts    |   7 ++
 .../src/components/MkCode.stories.impl.ts     |  44 +++++++
 .../components/MkCodeEditor.stories.impl.ts   |  62 ++++++++++
 .../components/MkCodeInline.stories.impl.ts   |  37 ++++++
 .../components/MkColorInput.stories.impl.ts   |  50 ++++++++
 .../components/MkContainer.stories.impl.ts    |   7 ++
 .../components/MkContextMenu.stories.impl.ts  |  58 +++++++++
 .../MkCropperDialog.stories.impl.ts           |  75 +++++++++++
 ...kCustomEmojiDetailedDialog.stories.impl.ts |  38 ++++++
 .../src/components/MkCwButton.stories.impl.ts |  89 +++++++++++++
 packages/frontend/src/scripts/test-utils.ts   |  10 ++
 pnpm-lock.yaml                                |  88 +++++++------
 28 files changed, 1083 insertions(+), 86 deletions(-)
 create mode 100644 packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkChannelList.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkChannelPreview.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkChart.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkChartLegend.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkChartTooltip.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkClickerGame.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkClipPreview.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkCode.core.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkCode.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkCodeEditor.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkCodeInline.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkColorInput.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkContainer.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkContextMenu.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkCropperDialog.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkCwButton.stories.impl.ts

diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index 3a24ccb248..fdb155261b 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -22,6 +22,66 @@ export function abuseUserReport() {
 	};
 }
 
+export function channel(id = 'somechannelid', name = 'Some Channel', bannerUrl: string | null = 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true'): entities.Channel {
+	return {
+		id,
+		createdAt: '2016-12-28T22:49:51.000Z',
+		lastNotedAt: '2016-12-28T22:49:51.000Z',
+		name,
+		description: null,
+		userId: null,
+		bannerUrl,
+		pinnedNoteIds: [],
+		color: '#000',
+		isArchived: false,
+		usersCount: 1,
+		notesCount: 1,
+		isSensitive: false,
+		allowRenoteToExternal: false,
+	};
+}
+
+export function clip(id = 'someclipid', name = 'Some Clip'): entities.Clip {
+	return {
+		id,
+		createdAt: '2016-12-28T22:49:51.000Z',
+		lastClippedAt: null,
+		userId: 'someuserid',
+		user: {
+			id: 'someuserid',
+			name: 'Misskey User',
+			username: 'miskist',
+			host: 'misskey-hub.net',
+			avatarUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
+			avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay',
+			avatarDecorations: [],
+			emojis: {},
+			badgeRoles: [],
+			onlineStatus: 'unknown',
+		},
+		notesCount: undefined,
+		name,
+		description: 'Some clip description',
+		isPublic: false,
+		favoritedCount: 0,
+	};
+}
+
+export function emojiDetailed(id = 'someemojiid', name = 'some_emoji'): entities.EmojiDetailed {
+	return {
+		id,
+		aliases: ['alias1', 'alias2'],
+		name,
+		category: 'emojiCategory',
+		host: null,
+		url: '/client-assets/about-icon.png',
+		license: null,
+		isSensitive: false,
+		localOnly: false,
+		roleIdsThatCanBeUsedThisEmojiAsReaction: ['roleId1', 'roleId2'],
+	};
+}
+
 export function galleryPost(isSensitive = false) {
 	return {
 		id: 'somepostid',
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index d74c83a500..d21eea9d17 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -397,7 +397,7 @@ function toStories(component: string): Promise<string> {
 	const globs = await Promise.all([
 		glob('src/components/global/Mk*.vue'),
 		glob('src/components/global/RouterView.vue'),
-		glob('src/components/Mk{A,B}*.vue'),
+		glob('src/components/Mk[A-C]*.vue'),
 		glob('src/components/MkDigitalClock.vue'),
 		glob('src/components/MkGalleryPostPreview.vue'),
 		glob('src/components/MkSignupServerRules.vue'),
diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts
index d3822942cd..9f318cf449 100644
--- a/packages/frontend/.storybook/main.ts
+++ b/packages/frontend/.storybook/main.ts
@@ -15,6 +15,7 @@ const _dirname = fileURLToPath(new URL('.', import.meta.url));
 
 const config = {
 	stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
+	staticDirs: [{ from: '../assets', to: '/client-assets' }],
 	addons: [
 		getAbsolutePath('@storybook/addon-essentials'),
 		getAbsolutePath('@storybook/addon-interactions'),
diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts
index 982a2979ac..73ee007fb8 100644
--- a/packages/frontend/.storybook/preview.ts
+++ b/packages/frontend/.storybook/preview.ts
@@ -7,7 +7,7 @@ import { FORCE_REMOUNT } from '@storybook/core-events';
 import { addons } from '@storybook/preview-api';
 import { type Preview, setup } from '@storybook/vue3';
 import isChromatic from 'chromatic/isChromatic';
-import { initialize, mswDecorator } from 'msw-storybook-addon';
+import { initialize, mswLoader } from 'msw-storybook-addon';
 import { userDetailed } from './fakes.js';
 import locale from './locale.js';
 import { commonHandlers, onUnhandledRequest } from './mocks.js';
@@ -122,7 +122,6 @@ const preview = {
 			}
 			return story;
 		},
-		mswDecorator,
 		(Story, context) => {
 			return {
 				setup() {
@@ -137,6 +136,7 @@ const preview = {
 			};
 		},
 	],
+	loaders: [mswLoader],
 	parameters: {
 		controls: {
 			exclude: /^__/,
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 56b824c0c5..66940a1601 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -104,6 +104,7 @@
 		"@types/node": "20.12.7",
 		"@types/punycode": "2.1.4",
 		"@types/sanitize-html": "2.11.0",
+		"@types/seedrandom": "3.0.8",
 		"@types/throttle-debounce": "5.0.2",
 		"@types/tinycolor2": "1.4.6",
 		"@types/uuid": "9.0.8",
@@ -128,6 +129,7 @@
 		"prettier": "3.2.5",
 		"react": "18.3.1",
 		"react-dom": "18.3.1",
+		"seedrandom": "3.0.5",
 		"start-server-and-test": "2.0.3",
 		"storybook": "8.0.9",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
diff --git a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
new file mode 100644
index 0000000000..b99620da22
--- /dev/null
+++ b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
@@ -0,0 +1,77 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { expect, userEvent, within } from '@storybook/test';
+import { channel } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkChannelFollowButton from './MkChannelFollowButton.vue';
+import { semaphore } from '@/scripts/test-utils.js';
+import { i18n } from '@/i18n.js';
+
+function sleep(ms: number) {
+	return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+const s = semaphore();
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkChannelFollowButton,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkChannelFollowButton v-bind="props" />',
+		};
+	},
+	args: {
+		channel: channel(),
+		full: true,
+	},
+	async play({ canvasElement }) {
+		await s.acquire();
+		await sleep(1000);
+		const canvas = within(canvasElement);
+		const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+		await expect(buttonElement).toHaveTextContent(i18n.ts.follow);
+		await userEvent.click(buttonElement);
+		await sleep(1000);
+		await expect(buttonElement).toHaveTextContent(i18n.ts.unfollow);
+		await sleep(100);
+		await userEvent.click(buttonElement);
+		s.release();
+	},
+	parameters: {
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/channels/follow', async ({ request }) => {
+					action('POST /api/channels/follow')(await request.json());
+					return HttpResponse.json({});
+				}),
+				http.post('/api/channels/unfollow', async ({ request }) => {
+					action('POST /api/channels/unfollow')(await request.json());
+					return HttpResponse.json({});
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkChannelFollowButton>;
diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue
index 6b1b380e41..841d37a568 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.vue
+++ b/packages/frontend/src/components/MkChannelFollowButton.vue
@@ -26,17 +26,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
-	channel: Record<string, any>;
+	channel: Misskey.entities.Channel;
 	full?: boolean;
 }>(), {
 	full: false,
 });
 
-const isFollowing = ref<boolean>(props.channel.isFollowing);
+const isFollowing = ref(props.channel.isFollowing);
 const wait = ref(false);
 
 async function onClick() {
diff --git a/packages/frontend/src/components/MkChannelList.stories.impl.ts b/packages/frontend/src/components/MkChannelList.stories.impl.ts
new file mode 100644
index 0000000000..f69b20c049
--- /dev/null
+++ b/packages/frontend/src/components/MkChannelList.stories.impl.ts
@@ -0,0 +1,65 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { channel } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkChannelList from './MkChannelList.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkChannelList,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkChannelList v-bind="props" />',
+		};
+	},
+	args: {
+		pagination: {
+			endpoint: 'channels/search',
+			limit: 10,
+		},
+	},
+	parameters: {
+		chromatic: {
+			// NOTE: ロードが終わるまで待つ
+			delay: 3000,
+		},
+		layout: 'fullscreen',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/channels/search', async ({ request, params }) => {
+					action('POST /api/channels/search')(await request.json());
+					return HttpResponse.json(params.untilId === 'lastchannel' ? [] : [
+						channel(),
+						channel('lastchannel', 'Last Channel', null),
+					]);
+				}),
+			],
+		},
+	},
+	decorators: [
+		() => ({
+			template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 700px; width: 100%; margin: 3rem"><story/></div></div>',
+		}),
+	],
+} satisfies StoryObj<typeof MkChannelList>;
diff --git a/packages/frontend/src/components/MkChannelPreview.stories.impl.ts b/packages/frontend/src/components/MkChannelPreview.stories.impl.ts
new file mode 100644
index 0000000000..de0193c78f
--- /dev/null
+++ b/packages/frontend/src/components/MkChannelPreview.stories.impl.ts
@@ -0,0 +1,43 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { channel } from '../../.storybook/fakes.js';
+import MkChannelPreview from './MkChannelPreview.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkChannelPreview,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkChannelPreview v-bind="props" />',
+		};
+	},
+	args: {
+		channel: channel(),
+	},
+	parameters: {
+		layout: 'fullscreen',
+	},
+	decorators: [
+		() => ({
+			template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 700px; width: 100%; margin: 3rem"><story/></div></div>',
+		}),
+	],
+} satisfies StoryObj<typeof MkChannelPreview>;
diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts
new file mode 100644
index 0000000000..6b0cc3b858
--- /dev/null
+++ b/packages/frontend/src/components/MkChart.stories.impl.ts
@@ -0,0 +1,117 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { DefaultBodyType, HttpResponse, HttpResponseResolver, JsonBodyType, PathParams, http } from 'msw';
+import seedrandom from 'seedrandom';
+import { action } from '@storybook/addon-actions';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkChart from './MkChart.vue';
+
+function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] {
+	const rng = seedrandom(seed);
+	const max = Math.floor(option?.mul ?? 250 * rng());
+	let accumulation = 0;
+	const array: number[] = [];
+	for (let i = 0; i < limit; i++) {
+		const num = Math.floor((max + 1) * rng());
+		if (option?.accumulate) {
+			accumulation += num;
+			array.unshift(accumulation);
+		} else {
+			array.push(num);
+		}
+	}
+	return array;
+}
+
+function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
+	return ({ request }) => {
+		action(`GET ${request.url}`)();
+		const limitParam = new URL(request.url).searchParams.get('limit');
+		const limit = limitParam ? parseInt(limitParam) : 30;
+		const res = {};
+		for (const field of fields) {
+			const layers = field.split('.');
+			let current = res;
+			while (layers.length > 1) {
+				const currentKey = layers.shift()!;
+				if (current[currentKey] == null) current[currentKey] = {};
+				current = current[currentKey];
+			}
+			current[layers[0]] = getChartArray(field, limit, {
+				accumulate: option?.accumulate,
+				mul: option?.mulMap != null && field in option.mulMap ? option.mulMap[field] : undefined,
+			});
+		}
+		return HttpResponse.json(res);
+	};
+}
+
+const Base = {
+	render(args) {
+		return {
+			components: {
+				MkChart,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkChart v-bind="props" />',
+		};
+	},
+	args: {
+		src: 'federation',
+		span: 'hour',
+	},
+	parameters: {
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.get('/api/charts/federation', getChartResolver(
+					['deliveredInstances', 'inboxInstances', 'stalled', 'sub', 'pub', 'pubsub', 'subActive', 'pubActive'],
+				)),
+				http.get('/api/charts/notes', getChartResolver(
+					['local.total', 'remote.total'],
+					{ accumulate: true },
+				)),
+				http.get('/api/charts/drive', getChartResolver(
+					['local.incSize', 'local.decSize', 'remote.incSize', 'remote.decSize'],
+					{ mulMap: { 'local.incSize': 1e7, 'local.decSize': 5e6, 'remote.incSize': 1e6, 'remote.decSize': 5e5 } },
+				)),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkChart>;
+export const FederationChart = {
+	...Base,
+	args: {
+		src: 'federation',
+	},
+} satisfies StoryObj<typeof MkChart>;
+export const NotesTotalChart = {
+	...Base,
+	args: {
+		src: 'notes-total',
+	},
+} satisfies StoryObj<typeof MkChart>;
+export const DriveChart = {
+	...Base,
+	args: {
+		src: 'drive',
+	},
+} satisfies StoryObj<typeof MkChart>;
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 04b6d2f29c..a823a0ec4b 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -19,8 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only
   id-denylist violation when setting it. This is causing about 60+ lint issues.
   As this is part of Chart.js's API it makes sense to disable the check here.
 */
-import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
+import { onMounted, ref, shallowRef, watch } from 'vue';
 import { Chart } from 'chart.js';
+import * as Misskey from 'misskey-js';
 import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
@@ -34,44 +35,55 @@ import MkChartLegend from '@/components/MkChartLegend.vue';
 
 initChart();
 
-const props = defineProps({
-	src: {
-		type: String,
-		required: true,
-	},
-	args: {
-		type: Object,
-		required: false,
-	},
-	limit: {
-		type: Number,
-		required: false,
-		default: 90,
-	},
-	span: {
-		type: String as PropType<'hour' | 'day'>,
-		required: true,
-	},
-	detailed: {
-		type: Boolean,
-		required: false,
-		default: false,
-	},
-	stacked: {
-		type: Boolean,
-		required: false,
-		default: false,
-	},
-	bar: {
-		type: Boolean,
-		required: false,
-		default: false,
-	},
-	aspectRatio: {
-		type: Number,
-		required: false,
-		default: null,
-	},
+type ChartSrc =
+	| 'federation'
+	| 'ap-request'
+	| 'users'
+	| 'users-total'
+	| 'active-users'
+	| 'notes'
+	| 'local-notes'
+	| 'remote-notes'
+	| 'notes-total'
+	| 'drive'
+	| 'drive-files'
+	| 'instance-requests'
+	| 'instance-users'
+	| 'instance-users-total'
+	| 'instance-notes'
+	| 'instance-notes-total'
+	| 'instance-ff'
+	| 'instance-ff-total'
+	| 'instance-drive-usage'
+	| 'instance-drive-usage-total'
+	| 'instance-drive-files'
+	| 'instance-drive-files-total'
+	| 'per-user-notes'
+	| 'per-user-pv'
+	| 'per-user-following'
+	| 'per-user-followers'
+	| 'per-user-drive'
+
+const props = withDefaults(defineProps<{
+	src: ChartSrc;
+	args?: {
+		host?: string;
+		user?: Misskey.entities.UserLite;
+		withoutAll?: boolean;
+	};
+	limit?: number;
+	span: 'hour' | 'day';
+	detailed?: boolean;
+	stacked?: boolean;
+	bar?: boolean;
+	aspectRatio?: number | null;
+}>(), {
+	args: undefined,
+	limit: 90,
+	detailed: false,
+	stacked: false,
+	bar: false,
+	aspectRatio: null,
 });
 
 const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
diff --git a/packages/frontend/src/components/MkChartLegend.stories.impl.ts b/packages/frontend/src/components/MkChartLegend.stories.impl.ts
new file mode 100644
index 0000000000..06146e20e4
--- /dev/null
+++ b/packages/frontend/src/components/MkChartLegend.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkChartLegend from './MkChartLegend.vue';
+void MkChartLegend;
diff --git a/packages/frontend/src/components/MkChartTooltip.stories.impl.ts b/packages/frontend/src/components/MkChartTooltip.stories.impl.ts
new file mode 100644
index 0000000000..289a9e9f27
--- /dev/null
+++ b/packages/frontend/src/components/MkChartTooltip.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkChartTooltip from './MkChartTooltip.vue';
+void MkChartTooltip;
diff --git a/packages/frontend/src/components/MkClickerGame.stories.impl.ts b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
new file mode 100644
index 0000000000..8378010f8b
--- /dev/null
+++ b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
@@ -0,0 +1,79 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { expect, within } from '@storybook/test';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkClickerGame from './MkClickerGame.vue';
+
+function sleep(ms: number) {
+	return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkClickerGame,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkClickerGame v-bind="props" />',
+		};
+	},
+	async play({ canvasElement }) {
+		await sleep(1000);
+		const canvas = within(canvasElement);
+		const count = canvas.getByTestId('count');
+		// NOTE: flaky なので N/A も通しておく
+		await expect(count).toHaveTextContent(/^(0|N\/A)$/);
+		// FIXME: flaky
+		// const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+		// await userEvent.click(buttonElement);
+		// await expect(count).toHaveTextContent('1');
+	},
+	parameters: {
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/i/registry/get', async ({ request }) => {
+					action('POST /api/i/registry/get')(await request.json());
+					return HttpResponse.json({
+						error: {
+							message: 'No such key.',
+							code: 'NO_SUCH_KEY',
+							id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a',
+						},
+					}, {
+						status: 400,
+					});
+				}),
+				http.post('/api/i/registry/set', async ({ request }) => {
+					action('POST /api/i/registry/set')(await request.json());
+					return HttpResponse.json(undefined, { status: 204 });
+				}),
+				http.post('/api/i/claim-achievement', async ({ request }) => {
+					action('POST /api/i/claim-achievement')(await request.json());
+					return HttpResponse.json(undefined, { status: 204 });
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkClickerGame>;
diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue
index 23046bf345..b592609e18 100644
--- a/packages/frontend/src/components/MkClickerGame.vue
+++ b/packages/frontend/src/components/MkClickerGame.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div>
 	<div v-if="game.ready" :class="$style.game">
 		<div :class="$style.cps" class="">{{ number(cps) }}cps</div>
-		<div :class="$style.count" class=""><i class="ti ti-cookie" style="font-size: 70%;"></i> {{ number(cookies) }}</div>
+		<div :class="$style.count" class="" data-testid="count"><i class="ti ti-cookie" style="font-size: 70%;"></i> {{ number(cookies) }}</div>
 		<button v-click-anime class="_button" @click="onClick">
 			<img src="/client-assets/cookie.png" :class="$style.img">
 		</button>
diff --git a/packages/frontend/src/components/MkClipPreview.stories.impl.ts b/packages/frontend/src/components/MkClipPreview.stories.impl.ts
new file mode 100644
index 0000000000..1011254e7a
--- /dev/null
+++ b/packages/frontend/src/components/MkClipPreview.stories.impl.ts
@@ -0,0 +1,43 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { clip } from '../../.storybook/fakes.js';
+import MkClipPreview from './MkClipPreview.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkClipPreview,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkClipPreview v-bind="props" />',
+		};
+	},
+	args: {
+		clip: clip(),
+	},
+	parameters: {
+		layout: 'fullscreen',
+	},
+	decorators: [
+		() => ({
+			template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 700px; width: 100%; margin: 3rem"><story/></div></div>',
+		}),
+	],
+} satisfies StoryObj<typeof MkClipPreview>;
diff --git a/packages/frontend/src/components/MkCode.core.stories.impl.ts b/packages/frontend/src/components/MkCode.core.stories.impl.ts
new file mode 100644
index 0000000000..91990fffd5
--- /dev/null
+++ b/packages/frontend/src/components/MkCode.core.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkCode_core from './MkCode.core.vue';
+void MkCode_core;
diff --git a/packages/frontend/src/components/MkCode.stories.impl.ts b/packages/frontend/src/components/MkCode.stories.impl.ts
new file mode 100644
index 0000000000..b7e53e8e35
--- /dev/null
+++ b/packages/frontend/src/components/MkCode.stories.impl.ts
@@ -0,0 +1,44 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import MkCode from './MkCode.vue';
+const code = `for (let i, 100) {
+	<: if (i % 15 == 0) "FizzBuzz"
+		elif (i % 3 == 0) "Fizz"
+		elif (i % 5 == 0) "Buzz"
+		else i
+}`;
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkCode,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkCode v-bind="props" />',
+		};
+	},
+	args: {
+		code,
+		lang: 'is',
+	},
+	parameters: {
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkCode>;
diff --git a/packages/frontend/src/components/MkCodeEditor.stories.impl.ts b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts
new file mode 100644
index 0000000000..5c410c4886
--- /dev/null
+++ b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts
@@ -0,0 +1,62 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { action } from '@storybook/addon-actions';
+import MkCodeEditor from './MkCodeEditor.vue';
+const code = `for (let i, 100) {
+	<: if (i % 15 == 0) "FizzBuzz"
+		elif (i % 3 == 0) "Fizz"
+		elif (i % 5 == 0) "Buzz"
+		else i
+}`;
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkCodeEditor,
+			},
+			data() {
+				return {
+					code,
+				};
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						'change': action('change'),
+						'keydown': action('keydown'),
+						'enter': action('enter'),
+						'update:modelValue': action('update:modelValue'),
+					};
+				},
+			},
+			template: '<MkCodeEditor v-model="code" v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+		lang: 'aiscript',
+	},
+	parameters: {
+		layout: 'fullscreen',
+	},
+	decorators: [
+		() => ({
+			template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 800px; width: 100%; margin: 3rem"><Suspense><story/></Suspense></div></div>',
+		}),
+	],
+} satisfies StoryObj<typeof MkCodeEditor>;
diff --git a/packages/frontend/src/components/MkCodeInline.stories.impl.ts b/packages/frontend/src/components/MkCodeInline.stories.impl.ts
new file mode 100644
index 0000000000..51d4d106ff
--- /dev/null
+++ b/packages/frontend/src/components/MkCodeInline.stories.impl.ts
@@ -0,0 +1,37 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import MkCodeInline from './MkCodeInline.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkCodeInline,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkCodeInline v-bind="props"/>',
+		};
+	},
+	args: {
+		code: '<: "Hello, world!"',
+	},
+	parameters: {
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkCodeInline>;
diff --git a/packages/frontend/src/components/MkColorInput.stories.impl.ts b/packages/frontend/src/components/MkColorInput.stories.impl.ts
new file mode 100644
index 0000000000..61383e2cae
--- /dev/null
+++ b/packages/frontend/src/components/MkColorInput.stories.impl.ts
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { action } from '@storybook/addon-actions';
+import MkColorInput from './MkColorInput.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkColorInput,
+			},
+			data() {
+				return {
+					color: '#cccccc',
+				};
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						'update:modelValue': action('update:modelValue'),
+					};
+				},
+			},
+			template: '<MkColorInput v-model="color" v-bind="props" v-on="events" />',
+		};
+	},
+	parameters: {
+		layout: 'fullscreen',
+	},
+	decorators: [
+		() => ({
+			template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 800px; width: 100%; margin: 3rem"><story/></div></div>',
+		}),
+	],
+} satisfies StoryObj<typeof MkColorInput>;
diff --git a/packages/frontend/src/components/MkContainer.stories.impl.ts b/packages/frontend/src/components/MkContainer.stories.impl.ts
new file mode 100644
index 0000000000..72a7659521
--- /dev/null
+++ b/packages/frontend/src/components/MkContainer.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkContainer from './MkContainer.vue';
+void MkContainer;
diff --git a/packages/frontend/src/components/MkContextMenu.stories.impl.ts b/packages/frontend/src/components/MkContextMenu.stories.impl.ts
new file mode 100644
index 0000000000..1ff0f51bd4
--- /dev/null
+++ b/packages/frontend/src/components/MkContextMenu.stories.impl.ts
@@ -0,0 +1,58 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { userEvent, within } from '@storybook/test';
+import MkContextMenu from './MkContextMenu.vue';
+import * as os from '@/os.js';
+export const Empty = {
+	render(args) {
+		return {
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			methods: {
+				onContextmenu(ev: MouseEvent) {
+					os.contextMenu(args.items, ev);
+				},
+			},
+			template: '<div @contextmenu.stop="onContextmenu">Right Click Here</div>',
+		};
+	},
+	args: {
+		items: [],
+	},
+	async play({ canvasElement }) {
+		const canvas = within(canvasElement);
+		const target = canvas.getByText('Right Click Here');
+		await userEvent.pointer({ keys: '[MouseRight>]', target });
+	},
+	parameters: {
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkContextMenu>;
+export const SomeTabs = {
+	...Empty,
+	args: {
+		items: [
+			{
+				text: 'Home',
+				icon: 'ti ti-home',
+				action() {},
+			},
+		],
+	},
+} satisfies StoryObj<typeof MkContextMenu>;
diff --git a/packages/frontend/src/components/MkCropperDialog.stories.impl.ts b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts
new file mode 100644
index 0000000000..ce13093975
--- /dev/null
+++ b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts
@@ -0,0 +1,75 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { file } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkCropperDialog from './MkCropperDialog.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkCropperDialog,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						'ok': action('ok'),
+						'cancel': action('cancel'),
+						'closed': action('closed'),
+					};
+				},
+			},
+			template: '<MkCropperDialog v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+		file: file(),
+		aspectRatio: NaN,
+	},
+	parameters: {
+		chromatic: {
+			// NOTE: ロードが終わるまで待つ
+			delay: 3000,
+		},
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.get('/proxy/image.webp', async ({ request }) => {
+					const url = new URL(request.url).searchParams.get('url');
+					if (url === 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true') {
+						const image = await (await fetch('client-assets/fedi.jpg')).blob();
+						return new HttpResponse(image, {
+							headers: {
+								'Content-Type': 'image/jpeg',
+							},
+						});
+					} else {
+						return new HttpResponse(null, { status: 404 });
+					}
+				}),
+				http.post('/api/drive/files/create', async ({ request }) => {
+					action('POST /api/drive/files/create')(await request.formData());
+					return HttpResponse.json(file());
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkCropperDialog>;
diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts
new file mode 100644
index 0000000000..8a05e06311
--- /dev/null
+++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts
@@ -0,0 +1,38 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { emojiDetailed } from '../../.storybook/fakes.js';
+import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkCustomEmojiDetailedDialog,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkCustomEmojiDetailedDialog v-bind="props" />',
+		};
+	},
+	args: {
+		emoji: emojiDetailed(),
+	},
+	parameters: {
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkCustomEmojiDetailedDialog>;
diff --git a/packages/frontend/src/components/MkCwButton.stories.impl.ts b/packages/frontend/src/components/MkCwButton.stories.impl.ts
new file mode 100644
index 0000000000..05c6001552
--- /dev/null
+++ b/packages/frontend/src/components/MkCwButton.stories.impl.ts
@@ -0,0 +1,89 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable import/no-default-export */
+import { StoryObj } from '@storybook/vue3';
+import { action } from '@storybook/addon-actions';
+import { expect, userEvent, within } from '@storybook/test';
+import { file } from '../../.storybook/fakes.js';
+import MkCwButton from './MkCwButton.vue';
+import { i18n } from '@/i18n.js';
+import { semaphore } from '@/scripts/test-utils.js';
+
+function sleep(ms: number) {
+	return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+const s = semaphore();
+
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkCwButton,
+			},
+			data() {
+				return {
+					showContent: false,
+				};
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						'update:modelValue': action('update:modelValue'),
+					};
+				},
+			},
+			template: '<MkCwButton v-model="showContent" v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+		text: 'Some CW content',
+	},
+	async play({ canvasElement }) {
+		await s.acquire();
+		await sleep(1000);
+		const canvas = within(canvasElement);
+		const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+		await expect(buttonElement).toHaveTextContent(i18n.ts._cw.show);
+		await expect(buttonElement).toHaveTextContent(i18n.tsx._cw.chars({ count: 15 }));
+		await userEvent.click(buttonElement);
+		await expect(buttonElement).toHaveTextContent(i18n.ts._cw.hide);
+		await userEvent.click(buttonElement);
+		s.release();
+	},
+	parameters: {
+		chromatic: {
+			// NOTE: テストが終わるまで待つ
+			delay: 5000,
+		},
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkCwButton>;
+export const IncludesTextAndDriveFile = {
+	...Default,
+	args: {
+		text: 'Some CW content',
+		files: [file()],
+	},
+	async play({ canvasElement }) {
+		const canvas = within(canvasElement);
+		const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+		await expect(buttonElement).toHaveTextContent(i18n.tsx._cw.chars({ count: 15 }));
+		await expect(buttonElement).toHaveTextContent(' / ');
+		await expect(buttonElement).toHaveTextContent(i18n.tsx._cw.files({ count: 1 }));
+	},
+} satisfies StoryObj<typeof MkCwButton>;
diff --git a/packages/frontend/src/scripts/test-utils.ts b/packages/frontend/src/scripts/test-utils.ts
index 52bb2d94e0..a32315f4df 100644
--- a/packages/frontend/src/scripts/test-utils.ts
+++ b/packages/frontend/src/scripts/test-utils.ts
@@ -7,3 +7,13 @@ export async function tick(): Promise<void> {
 	// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 	await new Promise((globalThis.requestIdleCallback ?? setTimeout) as never);
 }
+
+/**
+ * @see https://github.com/misskey-dev/misskey/issues/11267
+ */
+export function semaphore(counter = 0, waiting: (() => void)[] = []) {
+	return {
+		acquire: () => ++counter > 1 && new Promise<void>(resolve => waiting.push(resolve)),
+		release: () => --counter && waiting.pop()?.(),
+	};
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6bf1cf158c..159b97656a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -947,6 +947,9 @@ importers:
       '@types/sanitize-html':
         specifier: 2.11.0
         version: 2.11.0
+      '@types/seedrandom':
+        specifier: 3.0.8
+        version: 3.0.8
       '@types/throttle-debounce':
         specifier: 5.0.2
         version: 5.0.2
@@ -1019,6 +1022,9 @@ importers:
       react-dom:
         specifier: 18.3.1
         version: 18.3.1(react@18.3.1)
+      seedrandom:
+        specifier: 3.0.5
+        version: 3.0.5
       start-server-and-test:
         specifier: 2.0.3
         version: 2.0.3
@@ -11917,7 +11923,7 @@ snapshots:
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -11937,7 +11943,7 @@ snapshots:
       '@babel/traverse': 7.24.0
       '@babel/types': 7.24.0
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -12007,7 +12013,7 @@ snapshots:
       '@babel/core': 7.24.0
       '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-plugin-utils': 7.22.5
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -12778,7 +12784,7 @@ snapshots:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.9
       '@babel/types': 7.23.5
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -12793,7 +12799,7 @@ snapshots:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.24.0
       '@babel/types': 7.24.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -13185,7 +13191,7 @@ snapshots:
   '@eslint/eslintrc@2.1.4':
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       espree: 9.6.1
       globals: 13.19.0
       ignore: 5.2.4
@@ -13336,7 +13342,7 @@ snapshots:
   '@humanwhocodes/config-array@0.11.13':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -13344,7 +13350,7 @@ snapshots:
   '@humanwhocodes/config-array@0.11.14':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -16198,7 +16204,7 @@ snapshots:
       '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -16218,7 +16224,7 @@ snapshots:
       '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -16238,7 +16244,7 @@ snapshots:
       '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.3.1
@@ -16256,7 +16262,7 @@ snapshots:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
     optionalDependencies:
       typescript: 5.3.3
@@ -16269,7 +16275,7 @@ snapshots:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.57.0
     optionalDependencies:
       typescript: 5.3.3
@@ -16282,7 +16288,7 @@ snapshots:
       '@typescript-eslint/types': 7.7.1
       '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
       '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.57.0
     optionalDependencies:
       typescript: 5.4.5
@@ -16308,7 +16314,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
@@ -16320,7 +16326,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.57.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
@@ -16332,7 +16338,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
       '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.57.0
       ts-api-utils: 1.3.0(typescript@5.4.5)
     optionalDependencies:
@@ -16350,7 +16356,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -16364,7 +16370,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.3
@@ -16379,7 +16385,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 7.7.1
       '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.4
@@ -16714,13 +16720,13 @@ snapshots:
 
   agent-base@6.0.2:
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
   agent-base@7.1.0:
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -16950,7 +16956,7 @@ snapshots:
     dependencies:
       '@fastify/error': 3.4.0
       archy: 1.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       fastq: 1.17.1
     transitivePeerDependencies:
       - supports-color
@@ -18058,7 +18064,7 @@ snapshots:
   detect-port@1.5.1:
     dependencies:
       address: 1.2.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -18274,7 +18280,7 @@ snapshots:
 
   esbuild-register@3.5.0(esbuild@0.20.2):
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       esbuild: 0.20.2
     transitivePeerDependencies:
       - supports-color
@@ -18544,7 +18550,7 @@ snapshots:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -18587,7 +18593,7 @@ snapshots:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -19049,7 +19055,7 @@ snapshots:
 
   follow-redirects@1.15.2(debug@4.3.4):
     optionalDependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
 
   for-each@0.3.3:
     dependencies:
@@ -19504,7 +19510,7 @@ snapshots:
   http-proxy-agent@7.0.0:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -19543,14 +19549,14 @@ snapshots:
   https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
   https-proxy-agent@7.0.2:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -19646,7 +19652,7 @@ snapshots:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       denque: 2.1.0
       lodash.defaults: 4.2.0
       lodash.isarguments: 3.1.0
@@ -19899,7 +19905,7 @@ snapshots:
 
   istanbul-lib-source-maps@4.0.1:
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       istanbul-lib-coverage: 3.2.2
       source-map: 0.6.1
     transitivePeerDependencies:
@@ -20963,7 +20969,7 @@ snapshots:
   micromark@4.0.0:
     dependencies:
       '@types/debug': 4.1.12
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       decode-named-character-reference: 1.0.2
       devlop: 1.1.0
       micromark-core-commonmark: 2.0.0
@@ -22814,7 +22820,7 @@ snapshots:
     dependencies:
       '@hapi/hoek': 10.0.1
       '@hapi/wreck': 18.0.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       joi: 17.11.0
     transitivePeerDependencies:
       - supports-color
@@ -22912,7 +22918,7 @@ snapshots:
   socks-proxy-agent@8.0.2:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       socks: 2.7.1
     transitivePeerDependencies:
       - supports-color
@@ -23009,7 +23015,7 @@ snapshots:
       arg: 5.0.2
       bluebird: 3.7.2
       check-more-types: 2.24.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       execa: 5.1.1
       lazy-ass: 1.6.0
       ps-tree: 1.2.0
@@ -23522,7 +23528,7 @@ snapshots:
       chalk: 4.1.2
       cli-highlight: 2.1.11
       dayjs: 1.11.10
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       dotenv: 16.0.3
       glob: 10.3.10
       mkdirp: 2.1.6
@@ -23732,7 +23738,7 @@ snapshots:
   vite-node@0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3):
     dependencies:
       cac: 6.7.14
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       mlly: 1.5.0
       pathe: 1.1.2
       picocolors: 1.0.0
@@ -23781,7 +23787,7 @@ snapshots:
       acorn-walk: 8.3.2
       cac: 6.7.14
       chai: 4.3.10
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       local-pkg: 0.4.3
       magic-string: 0.30.7
       pathe: 1.1.2
@@ -23864,7 +23870,7 @@ snapshots:
 
   vue-eslint-parser@9.4.2(eslint@8.57.0):
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.57.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3

From ff3a38a7f59baa742b7072dbf98d9ecf43baa00b Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Wed, 12 Jun 2024 10:52:22 +0900
Subject: [PATCH 015/206] =?UTF-8?q?fix(frontend):=20=E8=BF=BD=E5=8A=A0?=
 =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=81=AE=E3=83=A9=E3=83=99=E3=83=AB=E3=82=92?=
 =?UTF-8?q?=E6=8A=95=E7=A8=BF=E8=80=85=E3=81=AE=E3=82=B5=E3=83=BC=E3=83=90?=
 =?UTF-8?q?=E3=83=BC=E3=81=AE=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=A7=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E3=81=99=E3=82=8B=20(#13968)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 追加情報のラベルを投稿者のサーバーの絵文字で表示する

* docs: update changelog
---
 CHANGELOG.md                              | 2 +-
 packages/frontend/src/pages/user/home.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b70636d82..7ca91c66e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 
 ### Client
--
+- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 4e3e383e33..d0be973552 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<div v-if="user.fields.length > 0" class="fields">
 						<dl v-for="(field, i) in user.fields" :key="i" class="field">
 							<dt class="name">
-								<Mfm :text="field.name" :plain="true" :colored="false"/>
+								<Mfm :text="field.name" :author="user" :plain="true" :colored="false"/>
 							</dt>
 							<dd class="value">
 								<Mfm :text="field.value" :author="user" :colored="false"/>

From 92367cf70065dca5e33ece2266f0318c3b682d1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=82=84=E3=81=8D?=
 <154856000+oyakimochocho@users.noreply.github.com>
Date: Wed, 12 Jun 2024 11:08:42 +0900
Subject: [PATCH 016/206] =?UTF-8?q?node=5Fmodules=E3=82=92volume=E5=8C=96?=
 =?UTF-8?q?=E3=81=97=E3=81=A6=E9=AB=98=E9=80=9F=E5=8C=96=EF=BC=8B=E3=83=91?=
 =?UTF-8?q?=E3=83=BC=E3=83=9F=E3=83=83=E3=82=B7=E3=83=A7=E3=83=B3=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=E5=9B=9E=E9=81=BF=E3=80=81git=20submodule=20update?=
 =?UTF-8?q?=E6=99=82=E3=81=AB=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB=E3=81=AB?=
 =?UTF-8?q?submodule=E3=81=8C=E3=81=82=E3=81=A3=E3=81=A6=E3=82=82=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=E3=81=8C=E8=B5=B7=E3=81=93=E3=82=89=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#13956)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .devcontainer/devcontainer.json  | 2 +-
 .devcontainer/docker-compose.yml | 2 ++
 .devcontainer/init.sh            | 3 ++-
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 31b6212cb5..344edbd65d 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -10,7 +10,7 @@
 		"ghcr.io/devcontainers-contrib/features/corepack:1": {}
 	},
 	"forwardPorts": [3000],
-	"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh",
+	"postCreateCommand": "/bin/bash .devcontainer/init.sh",
 	"customizations": {
 		"vscode": {
 			"extensions": [
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index 2809cd2ca4..a52d086fb6 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -8,6 +8,7 @@ services:
 
     volumes:
       - ../:/workspace:cached
+      - node_modules:/workspace/node_modules
 
     command: sleep infinity
 
@@ -46,6 +47,7 @@ services:
 volumes:
   postgres-data:
   redis-data:
+  node_modules:
 
 networks:
   internal_network:
diff --git a/.devcontainer/init.sh b/.devcontainer/init.sh
index 729e1a9d2d..55fb1e6fa6 100755
--- a/.devcontainer/init.sh
+++ b/.devcontainer/init.sh
@@ -2,7 +2,8 @@
 
 set -xe
 
-sudo chown -R node /workspace
+sudo chown node node_modules
+git config --global --add safe.directory /workspace
 git submodule update --init
 corepack install
 corepack enable

From 1616cb533ee7099bdf8ea982ea0557b1081e9c6c Mon Sep 17 00:00:00 2001
From: sirsegv <56238661+squidink7@users.noreply.github.com>
Date: Thu, 13 Jun 2024 10:48:01 +0930
Subject: [PATCH 017/206] Fix json module imports for node 22 (#13875)

---
 packages/frontend/vite.config.ts | 2 +-
 packages/sw/build.js             | 2 +-
 scripts/build-assets.mjs         | 2 +-
 scripts/tarball.mjs              | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index 82eb2af464..6decbc0ef7 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -5,7 +5,7 @@ import { type UserConfig, defineConfig } from 'vite';
 
 import locales from '../../locales/index.js';
 import meta from '../../package.json';
-import packageInfo from './package.json' assert { type: 'json' };
+import packageInfo from './package.json' with { type: 'json' };
 import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
 import pluginJson5 from './vite.json5.js';
 
diff --git a/packages/sw/build.js b/packages/sw/build.js
index eb9a944f47..9522d061e0 100644
--- a/packages/sw/build.js
+++ b/packages/sw/build.js
@@ -8,7 +8,7 @@
 import { fileURLToPath } from 'node:url';
 import * as esbuild from 'esbuild';
 import locales from '../../locales/index.js';
-import meta from '../../package.json' assert { type: "json" };
+import meta from '../../package.json' with { type: "json" };
 const watch = process.argv[2]?.includes('watch');
 
 const __dirname = fileURLToPath(new URL('.', import.meta.url))
diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs
index b5aa5eb4ab..2b275e12d6 100644
--- a/scripts/build-assets.mjs
+++ b/scripts/build-assets.mjs
@@ -13,7 +13,7 @@ import * as terser from 'terser';
 
 import { build as buildLocales } from '../locales/index.js';
 import generateDTS from '../locales/generateDTS.js';
-import meta from '../package.json' assert { type: "json" };
+import meta from '../package.json' with { type: "json" };
 import buildTarball from './tarball.mjs';
 
 const configDir = fileURLToPath(new URL('../.config', import.meta.url));
diff --git a/scripts/tarball.mjs b/scripts/tarball.mjs
index b1862ad289..e9d8900aca 100644
--- a/scripts/tarball.mjs
+++ b/scripts/tarball.mjs
@@ -10,7 +10,7 @@ import { fileURLToPath } from 'node:url';
 import glob from 'fast-glob';
 import walk from 'ignore-walk';
 import Pack from 'tar/lib/pack.js';
-import meta from '../package.json' assert { type: "json" };
+import meta from '../package.json' with { type: "json" };
 
 const cwd = fileURLToPath(new URL('..', import.meta.url));
 const ignore = [

From c73d739bd677702d8db0e7dd311081546ad45d65 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 13 Jun 2024 10:40:20 +0900
Subject: [PATCH 018/206] node 22 support

---
 packages/backend/package.json |   4 +-
 pnpm-lock.yaml                | 120 +++++++++++++++++-----------------
 2 files changed, 62 insertions(+), 62 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index e034f75dc5..772dc8f8b5 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -4,7 +4,7 @@
 	"private": true,
 	"type": "module",
 	"engines": {
-		"node": "^20.10.0"
+		"node": "^20.10.0 || ^22.0.0"
 	},
 	"scripts": {
 		"start": "node ./built/boot/entry.js",
@@ -159,7 +159,7 @@
 		"qrcode": "1.5.3",
 		"random-seed": "0.3.0",
 		"ratelimiter": "3.4.1",
-		"re2": "1.20.10",
+		"re2": "1.21.2",
 		"redis-lock": "0.1.4",
 		"reflect-metadata": "0.2.2",
 		"rename": "1.0.4",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 159b97656a..5400828781 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -360,8 +360,8 @@ importers:
         specifier: 3.4.1
         version: 3.4.1
       re2:
-        specifier: 1.20.10
-        version: 1.20.10
+        specifier: 1.21.2
+        version: 1.21.2
       redis-lock:
         specifier: 0.1.4
         version: 0.1.4
@@ -8584,8 +8584,8 @@ packages:
   mz@2.7.0:
     resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
 
-  nan@2.18.0:
-    resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==}
+  nan@2.20.0:
+    resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==}
 
   nanoid@3.3.7:
     resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
@@ -8697,8 +8697,8 @@ packages:
     resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
     hasBin: true
 
-  node-gyp@10.0.1:
-    resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==}
+  node-gyp@10.1.0:
+    resolution: {integrity: sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA==}
     engines: {node: ^16.14.0 || >=18.0.0}
     hasBin: true
 
@@ -9650,8 +9650,8 @@ packages:
     resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==}
     engines: {node: '>=12'}
 
-  re2@1.20.10:
-    resolution: {integrity: sha512-/5JjSPXobSDaKFL6rD5Gb4qD4CVBITQb7NAxfQ/NA7o0HER3SJAPV3lPO2kvzw0/PN1pVJNVATEUk4y9j7oIIA==}
+  re2@1.21.2:
+    resolution: {integrity: sha512-f8jqI0vCbwDhzY66Fgx1V2RoNDdmAupKkqRqR/AEF+2/MZNRbtEOjax6oHSht95MU40vx6+2ITsJr/9esukckg==}
 
   react-colorful@5.6.1:
     resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
@@ -11130,8 +11130,8 @@ packages:
   vue-component-type-helpers@2.0.16:
     resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
 
-  vue-component-type-helpers@2.0.19:
-    resolution: {integrity: sha512-cN3f1aTxxKo4lzNeQAkVopswuImUrb5Iurll9Gaw5cqpnbTAxtEMM1mgi6ou4X79OCyqYv1U1mzBHJkzmiK82w==}
+  vue-component-type-helpers@2.0.21:
+    resolution: {integrity: sha512-3NaicyZ7N4B6cft4bfb7dOnPbE9CjLcx+6wZWAg5zwszfO4qXRh+U52dN5r5ZZfc6iMaxKCEcoH9CmxxoFZHLg==}
 
   vue-demi@0.14.7:
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
@@ -11923,7 +11923,7 @@ snapshots:
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -11943,7 +11943,7 @@ snapshots:
       '@babel/traverse': 7.24.0
       '@babel/types': 7.24.0
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -12013,7 +12013,7 @@ snapshots:
       '@babel/core': 7.24.0
       '@babel/helper-compilation-targets': 7.23.6
       '@babel/helper-plugin-utils': 7.22.5
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -12784,7 +12784,7 @@ snapshots:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.9
       '@babel/types': 7.23.5
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -12799,7 +12799,7 @@ snapshots:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.24.0
       '@babel/types': 7.24.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -13191,7 +13191,7 @@ snapshots:
   '@eslint/eslintrc@2.1.4':
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       espree: 9.6.1
       globals: 13.19.0
       ignore: 5.2.4
@@ -13342,7 +13342,7 @@ snapshots:
   '@humanwhocodes/config-array@0.11.13':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -13350,7 +13350,7 @@ snapshots:
   '@humanwhocodes/config-array@0.11.14':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.2
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -13955,7 +13955,7 @@ snapshots:
       agent-base: 7.1.0
       http-proxy-agent: 7.0.0
       https-proxy-agent: 7.0.2
-      lru-cache: 10.0.2
+      lru-cache: 10.2.2
       socks-proxy-agent: 8.0.2
     transitivePeerDependencies:
       - supports-color
@@ -15489,7 +15489,7 @@ snapshots:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.26(typescript@5.4.5)
-      vue-component-type-helpers: 2.0.19
+      vue-component-type-helpers: 2.0.21
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -16204,7 +16204,7 @@ snapshots:
       '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -16224,7 +16224,7 @@ snapshots:
       '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -16244,7 +16244,7 @@ snapshots:
       '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.3.1
@@ -16262,7 +16262,7 @@ snapshots:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
     optionalDependencies:
       typescript: 5.3.3
@@ -16275,7 +16275,7 @@ snapshots:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
     optionalDependencies:
       typescript: 5.3.3
@@ -16288,7 +16288,7 @@ snapshots:
       '@typescript-eslint/types': 7.7.1
       '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
       '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
     optionalDependencies:
       typescript: 5.4.5
@@ -16314,7 +16314,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
@@ -16326,7 +16326,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
@@ -16338,7 +16338,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
       '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       ts-api-utils: 1.3.0(typescript@5.4.5)
     optionalDependencies:
@@ -16356,7 +16356,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -16370,7 +16370,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.3
@@ -16385,7 +16385,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 7.7.1
       '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.4
@@ -16720,13 +16720,13 @@ snapshots:
 
   agent-base@6.0.2:
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
   agent-base@7.1.0:
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -16956,7 +16956,7 @@ snapshots:
     dependencies:
       '@fastify/error': 3.4.0
       archy: 1.0.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       fastq: 1.17.1
     transitivePeerDependencies:
       - supports-color
@@ -17264,7 +17264,7 @@ snapshots:
       '@npmcli/fs': 3.1.0
       fs-minipass: 3.0.2
       glob: 10.3.12
-      lru-cache: 10.0.2
+      lru-cache: 10.2.2
       minipass: 7.0.4
       minipass-collect: 1.0.2
       minipass-flush: 1.0.5
@@ -18064,7 +18064,7 @@ snapshots:
   detect-port@1.5.1:
     dependencies:
       address: 1.2.2
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -18280,7 +18280,7 @@ snapshots:
 
   esbuild-register@3.5.0(esbuild@0.20.2):
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       esbuild: 0.20.2
     transitivePeerDependencies:
       - supports-color
@@ -18550,7 +18550,7 @@ snapshots:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -18593,7 +18593,7 @@ snapshots:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -19055,7 +19055,7 @@ snapshots:
 
   follow-redirects@1.15.2(debug@4.3.4):
     optionalDependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
 
   for-each@0.3.3:
     dependencies:
@@ -19510,7 +19510,7 @@ snapshots:
   http-proxy-agent@7.0.0:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -19549,14 +19549,14 @@ snapshots:
   https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
   https-proxy-agent@7.0.2:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -19652,7 +19652,7 @@ snapshots:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       denque: 2.1.0
       lodash.defaults: 4.2.0
       lodash.isarguments: 3.1.0
@@ -19905,7 +19905,7 @@ snapshots:
 
   istanbul-lib-source-maps@4.0.1:
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       istanbul-lib-coverage: 3.2.2
       source-map: 0.6.1
     transitivePeerDependencies:
@@ -20969,7 +20969,7 @@ snapshots:
   micromark@4.0.0:
     dependencies:
       '@types/debug': 4.1.12
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       decode-named-character-reference: 1.0.2
       devlop: 1.1.0
       micromark-core-commonmark: 2.0.0
@@ -21199,7 +21199,7 @@ snapshots:
       object-assign: 4.1.1
       thenify-all: 1.6.0
 
-  nan@2.18.0: {}
+  nan@2.20.0: {}
 
   nanoid@3.3.7: {}
 
@@ -21301,7 +21301,7 @@ snapshots:
   node-gyp-build@4.6.0:
     optional: true
 
-  node-gyp@10.0.1:
+  node-gyp@10.1.0:
     dependencies:
       env-paths: 2.2.1
       exponential-backoff: 3.1.1
@@ -22271,11 +22271,11 @@ snapshots:
     dependencies:
       setimmediate: 1.0.5
 
-  re2@1.20.10:
+  re2@1.21.2:
     dependencies:
       install-artifact-from-github: 1.3.5
-      nan: 2.18.0
-      node-gyp: 10.0.1
+      nan: 2.20.0
+      node-gyp: 10.1.0
     transitivePeerDependencies:
       - supports-color
 
@@ -22820,7 +22820,7 @@ snapshots:
     dependencies:
       '@hapi/hoek': 10.0.1
       '@hapi/wreck': 18.0.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       joi: 17.11.0
     transitivePeerDependencies:
       - supports-color
@@ -22918,7 +22918,7 @@ snapshots:
   socks-proxy-agent@8.0.2:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       socks: 2.7.1
     transitivePeerDependencies:
       - supports-color
@@ -23015,7 +23015,7 @@ snapshots:
       arg: 5.0.2
       bluebird: 3.7.2
       check-more-types: 2.24.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       execa: 5.1.1
       lazy-ass: 1.6.0
       ps-tree: 1.2.0
@@ -23528,7 +23528,7 @@ snapshots:
       chalk: 4.1.2
       cli-highlight: 2.1.11
       dayjs: 1.11.10
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       dotenv: 16.0.3
       glob: 10.3.10
       mkdirp: 2.1.6
@@ -23738,7 +23738,7 @@ snapshots:
   vite-node@0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3):
     dependencies:
       cac: 6.7.14
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       mlly: 1.5.0
       pathe: 1.1.2
       picocolors: 1.0.0
@@ -23787,7 +23787,7 @@ snapshots:
       acorn-walk: 8.3.2
       cac: 6.7.14
       chai: 4.3.10
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       local-pkg: 0.4.3
       magic-string: 0.30.7
       pathe: 1.1.2
@@ -23847,7 +23847,7 @@ snapshots:
 
   vue-component-type-helpers@2.0.16: {}
 
-  vue-component-type-helpers@2.0.19: {}
+  vue-component-type-helpers@2.0.21: {}
 
   vue-demi@0.14.7(vue@3.4.26(typescript@5.4.5)):
     dependencies:
@@ -23870,7 +23870,7 @@ snapshots:
 
   vue-eslint-parser@9.4.2(eslint@8.57.0):
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3

From dc3629e732e5aefd792452f0b43a7bb7fdaf103e Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Thu, 13 Jun 2024 10:56:26 +0900
Subject: [PATCH 019/206] feat(backend): report `Retry-After` if client hit
 rate limit (#13949)

* feat(backend): report `Retry-After` if client hit rate limit

* refactor(backend): fix lint error
---
 .../backend/src/server/api/ApiCallService.ts  | 27 ++++++++++----
 .../src/server/api/RateLimiterService.ts      | 36 ++++++++++---------
 2 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index 166f9c8675..47f64f6609 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -73,6 +73,16 @@ export class ApiCallService implements OnApplicationShutdown {
 				reply.header('WWW-Authenticate', `Bearer realm="Misskey", error="insufficient_scope", error_description="${err.message}"`);
 			}
 			statusCode = statusCode ?? 403;
+		} else if (err.code === 'RATE_LIMIT_EXCEEDED') {
+			const info: unknown = err.info;
+			const unixEpochInSeconds = Date.now();
+			if (typeof(info) === 'object' && info && 'resetMs' in info && typeof(info.resetMs) === 'number') {
+				const cooldownInSeconds = Math.ceil((info.resetMs - unixEpochInSeconds) / 1000);
+				// もしかするとマイナスになる可能性がなくはないのでマイナスだったら0にしておく
+				reply.header('Retry-After', Math.max(cooldownInSeconds, 0).toString(10));
+			} else {
+				this.logger.warn(`rate limit information has unexpected type ${typeof(err.info?.reset)}`);
+			}
 		} else if (!statusCode) {
 			statusCode = 500;
 		}
@@ -308,12 +318,17 @@ export class ApiCallService implements OnApplicationShutdown {
 			if (factor > 0) {
 				// Rate limit
 				await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable<string> }, limitActor, factor).catch(err => {
-					throw new ApiError({
-						message: 'Rate limit exceeded. Please try again later.',
-						code: 'RATE_LIMIT_EXCEEDED',
-						id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
-						httpStatusCode: 429,
-					});
+					if ('info' in err) {
+						// errはLimiter.LimiterInfoであることが期待される
+						throw new ApiError({
+							message: 'Rate limit exceeded. Please try again later.',
+							code: 'RATE_LIMIT_EXCEEDED',
+							id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
+							httpStatusCode: 429,
+						}, err.info);
+					} else {
+						throw new TypeError('information must be a rate-limiter information.');
+					}
 				});
 			}
 		}
diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts
index 0439cdfe5e..cae106c273 100644
--- a/packages/backend/src/server/api/RateLimiterService.ts
+++ b/packages/backend/src/server/api/RateLimiterService.ts
@@ -32,11 +32,13 @@ export class RateLimiterService {
 
 	@bindThis
 	public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string, factor = 1) {
-		return new Promise<void>((ok, reject) => {
-			if (this.disabled) ok();
+		{
+			if (this.disabled) {
+				return Promise.resolve();
+			}
 
 			// Short-term limit
-			const min = (): void => {
+			const min = new Promise<void>((ok, reject) => {
 				const minIntervalLimiter = new Limiter({
 					id: `${actor}:${limitation.key}:min`,
 					duration: limitation.minInterval! * factor,
@@ -46,25 +48,25 @@ export class RateLimiterService {
 
 				minIntervalLimiter.get((err, info) => {
 					if (err) {
-						return reject('ERR');
+						return reject({ code: 'ERR', info });
 					}
 
 					this.logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`);
 
 					if (info.remaining === 0) {
-						reject('BRIEF_REQUEST_INTERVAL');
+						return reject({ code: 'BRIEF_REQUEST_INTERVAL', info });
 					} else {
 						if (hasLongTermLimit) {
-							max();
+							return max;
 						} else {
-							ok();
+							return ok();
 						}
 					}
 				});
-			};
+			});
 
 			// Long term limit
-			const max = (): void => {
+			const max = new Promise<void>((ok, reject) => {
 				const limiter = new Limiter({
 					id: `${actor}:${limitation.key}`,
 					duration: limitation.duration! * factor,
@@ -74,18 +76,18 @@ export class RateLimiterService {
 
 				limiter.get((err, info) => {
 					if (err) {
-						return reject('ERR');
+						return reject({ code: 'ERR', info });
 					}
 
 					this.logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`);
 
 					if (info.remaining === 0) {
-						reject('RATE_LIMIT_EXCEEDED');
+						return reject({ code: 'RATE_LIMIT_EXCEEDED', info });
 					} else {
-						ok();
+						return ok();
 					}
 				});
-			};
+			});
 
 			const hasShortTermLimit = typeof limitation.minInterval === 'number';
 
@@ -94,12 +96,12 @@ export class RateLimiterService {
 				typeof limitation.max === 'number';
 
 			if (hasShortTermLimit) {
-				min();
+				return min;
 			} else if (hasLongTermLimit) {
-				max();
+				return max;
 			} else {
-				ok();
+				return Promise.resolve();
 			}
-		});
+		}
 	}
 }

From c51347d78bc6dd30b6b4db2af64f0ea4bc83091e Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Thu, 13 Jun 2024 11:09:03 +0900
Subject: [PATCH 020/206] docs: update changelog (follow-up of #13949) (#13971)

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7ca91c66e2..2d482d50d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@
 
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
-
+- Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
 
 ## 2024.5.0
 

From 220e112c833ec9b382658caee99eabdb6ec330ef Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 15 Jun 2024 08:42:13 +0900
Subject: [PATCH 021/206] fix rate limit check never ends (#13994)

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

diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts
index cae106c273..52d73baa0a 100644
--- a/packages/backend/src/server/api/RateLimiterService.ts
+++ b/packages/backend/src/server/api/RateLimiterService.ts
@@ -57,7 +57,7 @@ export class RateLimiterService {
 						return reject({ code: 'BRIEF_REQUEST_INTERVAL', info });
 					} else {
 						if (hasLongTermLimit) {
-							return max;
+							return max.then(ok, reject);
 						} else {
 							return ok();
 						}

From 9bddb81efca3b2c5821383709cf30cd63ad21371 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sat, 15 Jun 2024 08:43:11 +0900
Subject: [PATCH 022/206] =?UTF-8?q?chore:=20issue=E3=82=92=E8=B5=B7?=
 =?UTF-8?q?=E7=A5=A8=E3=81=99=E3=82=8B=E5=89=8D=E3=81=ABGitHub=20Discussio?=
 =?UTF-8?q?ns=E3=81=AB=E3=82=82=E8=AA=98=E5=B0=8E=E3=81=99=E3=82=8B=20(#13?=
 =?UTF-8?q?991)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .github/ISSUE_TEMPLATE/config.yml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index e8b65dc3b9..5acad83336 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -2,3 +2,7 @@ contact_links:
   - name: 💬 Misskey official Discord
     url: https://discord.gg/Wp8gVStHW3
     about: Chat freely about Misskey
+  # 仮
+  - name: 💬 Start discussion
+    url: https://github.com/misskey-dev/misskey/discussions
+    about: The official forum to join conversation and ask question

From 1a82a41f9274b3a56edeccf755bd7fc91a3fdf4b Mon Sep 17 00:00:00 2001
From: Acid Chicken <root@acid-chicken.com>
Date: Sat, 15 Jun 2024 10:28:57 +0900
Subject: [PATCH 023/206] refactor(backend): get column names from metadata
 (#13943)

* ci: enable

* chore: stop when generated column found

* chore: get column names from metadata

* ci: disable
---
 packages/backend/src/models/_.ts | 30 +++++-------------------------
 1 file changed, 5 insertions(+), 25 deletions(-)

diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts
index d366ce48d0..c72bdaa727 100644
--- a/packages/backend/src/models/_.ts
+++ b/packages/backend/src/models/_.ts
@@ -82,34 +82,14 @@ import { MiReversiGame } from '@/models/ReversiGame.js';
 import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
 
 export interface MiRepository<T extends ObjectLiteral> {
-	createTableColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
-	createTableColumnNamesWithPrimaryKey(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
+	createTableColumnNames(this: Repository<T> & MiRepository<T>): string[];
 	insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
 	selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
 }
 
 export const miRepository = {
-	createTableColumnNames(queryBuilder) {
-		// @ts-expect-error -- protected
-		const insertedColumns = queryBuilder.getInsertedColumns();
-		if (insertedColumns.length) {
-			return insertedColumns.map(column => column.databaseName);
-		}
-		if (!queryBuilder.expressionMap.mainAlias?.hasMetadata && !queryBuilder.expressionMap.insertColumns.length) {
-			// @ts-expect-error -- protected
-			const valueSets = queryBuilder.getValueSets();
-			if (valueSets.length === 1) {
-				return Object.keys(valueSets[0]);
-			}
-		}
-		return queryBuilder.expressionMap.insertColumns;
-	},
-	createTableColumnNamesWithPrimaryKey(queryBuilder) {
-		const columnNames = this.createTableColumnNames(queryBuilder);
-		if (!columnNames.includes('id')) {
-			columnNames.unshift('id');
-		}
-		return columnNames;
+	createTableColumnNames() {
+		return this.metadata.columns.filter(column => column.isSelect && !column.isVirtual).map(column => column.databaseName);
 	},
 	async insertOne(entity, findOptions?) {
 		const queryBuilder = this.createQueryBuilder().insert().values(entity);
@@ -117,7 +97,7 @@ export const miRepository = {
 		const mainAlias = queryBuilder.expressionMap.mainAlias!;
 		const name = mainAlias.name;
 		mainAlias.name = 't';
-		const columnNames = this.createTableColumnNamesWithPrimaryKey(queryBuilder);
+		const columnNames = this.createTableColumnNames();
 		queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
 		const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames });
 		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -138,7 +118,7 @@ export const miRepository = {
 			selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName);
 			return builder.select(selection, selectionAliasName);
 		};
-		for (const columnName of this.createTableColumnNamesWithPrimaryKey(queryBuilder)) {
+		for (const columnName of this.createTableColumnNames()) {
 			selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`);
 		}
 	},

From d4e2be68eeae69b758f1c9154ee7c1c68ae16b43 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 15 Jun 2024 10:32:51 +0900
Subject: [PATCH 024/206] fix(frontend): chart in `MkInstanceCardMini` is no
 longer displayed (#13932)

* fix(frontend): chart in `MkInstanceCardMini` is no longer displayed

* Update CHANGELOG.md

* test: add `MkInstanceCardMini` story

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 packages/frontend/.storybook/fakes.ts         | 29 +++++++++
 packages/frontend/.storybook/generate.tsx     |  1 +
 .../src/components/MkChart.stories.impl.ts    |  2 +-
 .../MkInstanceCardMini.stories.impl.ts        | 64 +++++++++++++++++++
 .../src/components/MkInstanceCardMini.vue     |  4 +-
 6 files changed, 98 insertions(+), 3 deletions(-)
 create mode 100644 packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d482d50d0..097a69b687 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 
 ### Client
+- `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 
 ### Server
diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index fdb155261b..9d789a34ff 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -125,6 +125,35 @@ export function file(isSensitive = false) {
 	};
 }
 
+export function federationInstance(): entities.FederationInstance {
+	return {
+		id: 'someinstanceid',
+		firstRetrievedAt: '2021-01-01T00:00:00.000Z',
+		host: 'misskey-hub.net',
+		usersCount: 10,
+		notesCount: 20,
+		followingCount: 5,
+		followersCount: 15,
+		isNotResponding: false,
+		isSuspended: false,
+		suspensionState: 'none',
+		isBlocked: false,
+		softwareName: 'misskey',
+		softwareVersion: '2024.5.0',
+		openRegistrations: false,
+		name: 'Misskey Hub',
+		description: '',
+		maintainerName: '',
+		maintainerEmail: '',
+		isSilenced: false,
+		iconUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
+		faviconUrl: '',
+		themeColor: '',
+		infoUpdatedAt: '',
+		latestRequestReceivedAt: '',
+	};
+}
+
 export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed {
 	return {
 		id,
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index d21eea9d17..7b6c86447e 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -403,6 +403,7 @@ function toStories(component: string): Promise<string> {
 		glob('src/components/MkSignupServerRules.vue'),
 		glob('src/components/MkUserSetupDialog.vue'),
 		glob('src/components/MkUserSetupDialog.*.vue'),
+		glob('src/components/MkInstanceCardMini.vue'),
 		glob('src/components/MkInviteCode.vue'),
 		glob('src/pages/user/home.vue'),
 	]);
diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts
index 6b0cc3b858..d1d17f6dc0 100644
--- a/packages/frontend/src/components/MkChart.stories.impl.ts
+++ b/packages/frontend/src/components/MkChart.stories.impl.ts
@@ -29,7 +29,7 @@ function getChartArray(seed: string, limit: number, option?: { accumulate?: bool
 	return array;
 }
 
-function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
+export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
 	return ({ request }) => {
 		action(`GET ${request.url}`)();
 		const limitParam = new URL(request.url).searchParams.get('limit');
diff --git a/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts
new file mode 100644
index 0000000000..a069a0eb8e
--- /dev/null
+++ b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts
@@ -0,0 +1,64 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { federationInstance } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkInstanceCardMini from './MkInstanceCardMini.vue';
+import { getChartResolver } from './MkChart.stories.impl.js';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkInstanceCardMini,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkInstanceCardMini v-bind="props" />',
+		};
+	},
+	args: {
+		instance: federationInstance(),
+	},
+	parameters: {
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.get('/undefined/preview.webp', async ({ request }) => {
+					const urlStr = new URL(request.url).searchParams.get('url');
+					if (urlStr == null) {
+						return new HttpResponse(null, { status: 404 });
+					}
+					const url = new URL(urlStr);
+
+					if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) {
+						const image = await (await fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob();
+						return new HttpResponse(image, {
+							headers: {
+								'Content-Type': 'image/jpeg',
+							},
+						});
+					} else {
+						return new HttpResponse(null, { status: 404 });
+					}
+				}),
+				http.get('/api/charts/instance', getChartResolver(['requests.received'])),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkInstanceCardMini>;
diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue
index e26aef0f69..17c974dd04 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.vue
+++ b/packages/frontend/src/components/MkInstanceCardMini.vue
@@ -29,8 +29,8 @@ const chartValues = ref<number[] | null>(null);
 
 misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
 	// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
-	res['requests.received'].splice(0, 1);
-	chartValues.value = res['requests.received'];
+	res.requests.received.splice(0, 1);
+	chartValues.value = res.requests.received;
 });
 
 function getInstanceIcon(instance): string {

From 96fcb9f54c9576c8daa78064cbba630895ec8877 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 15 Jun 2024 14:10:37 +0900
Subject: [PATCH 025/206] ci: upgrade dockle (#14002)

---
 .github/workflows/dockle.yml | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml
index eee7a78fed..968971dd8d 100644
--- a/.github/workflows/dockle.yml
+++ b/.github/workflows/dockle.yml
@@ -13,10 +13,12 @@ jobs:
     runs-on: ubuntu-latest
     env:
       DOCKER_CONTENT_TRUST: 1
+      DOCKLE_VERSION: 0.4.14
     steps:
       - uses: actions/checkout@v4.1.1
-      - run: |
-          curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb"
+      - name: Download and install dockle v${{ env.DOCKLE_VERSION }}
+        run: |
+          curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
           sudo dpkg -i dockle.deb
       - run: |
           cp .config/docker_example.env .config/docker.env

From 34458d767bd4430c8ed5f080409390aba6f62f94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 15 Jun 2024 15:46:36 +0900
Subject: [PATCH 026/206] fix changelog

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 097a69b687..76fed76b56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 
 ### Client
-- `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
+- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 
 ### Server

From 379ce0145b9d0b74334b35fa57f2ef87366388f2 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 15 Jun 2024 16:35:41 +0900
Subject: [PATCH 027/206] fix(frontend): fix time on `MkChart`'s story (#13958)

---
 .../frontend/src/components/MkChart.stories.impl.ts   |  4 ++++
 packages/frontend/src/components/MkChart.vue          | 11 ++++++++++-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts
index d1d17f6dc0..3bae703245 100644
--- a/packages/frontend/src/components/MkChart.stories.impl.ts
+++ b/packages/frontend/src/components/MkChart.stories.impl.ts
@@ -76,6 +76,7 @@ const Base = {
 	args: {
 		src: 'federation',
 		span: 'hour',
+		nowForChromatic: 1716263640000,
 	},
 	parameters: {
 		layout: 'centered',
@@ -100,18 +101,21 @@ const Base = {
 export const FederationChart = {
 	...Base,
 	args: {
+		...Base.args,
 		src: 'federation',
 	},
 } satisfies StoryObj<typeof MkChart>;
 export const NotesTotalChart = {
 	...Base,
 	args: {
+		...Base.args,
 		src: 'notes-total',
 	},
 } satisfies StoryObj<typeof MkChart>;
 export const DriveChart = {
 	...Base,
 	args: {
+		...Base.args,
 		src: 'drive',
 	},
 } satisfies StoryObj<typeof MkChart>;
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index a823a0ec4b..3816bca348 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -77,6 +77,7 @@ const props = withDefaults(defineProps<{
 	stacked?: boolean;
 	bar?: boolean;
 	aspectRatio?: number | null;
+	nowForChromatic?: number;
 }>(), {
 	args: undefined,
 	limit: 90,
@@ -84,6 +85,13 @@ const props = withDefaults(defineProps<{
 	stacked: false,
 	bar: false,
 	aspectRatio: null,
+
+	/**
+	 * @desc Overwrites current date to fix background lines of chart.
+	 * @ignore Only used for Chromatic. Don't use this for production.
+	 * @see https://github.com/misskey-dev/misskey/pull/13830#issuecomment-2155886151
+	 */
+	nowForChromatic: undefined,
 });
 
 const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
@@ -106,7 +114,8 @@ const getColor = (i) => {
 	return colorSets[i % colorSets.length];
 };
 
-const now = new Date();
+// eslint-disable-next-line vue/no-setup-props-destructure
+const now = props.nowForChromatic != null ? new Date(props.nowForChromatic) : new Date();
 let chartInstance: Chart | null = null;
 let chartData: {
 	series: {

From d0ee0203e12d41515b05d7d99ae09f70eeae5874 Mon Sep 17 00:00:00 2001
From: Ryu jongheon <lapy@lapy.link>
Date: Tue, 18 Jun 2024 12:18:04 +0900
Subject: [PATCH 028/206] Fix(backend): Limit antenna/webhook/list to exact
 amount (#14036)

... not +1
* Update antennas/clips e2e test
---
 packages/backend/src/core/ClipService.ts                   | 4 ++--
 packages/backend/src/core/UserListService.ts               | 2 +-
 .../backend/src/server/api/endpoints/antennas/create.ts    | 2 +-
 .../backend/src/server/api/endpoints/i/import-antennas.ts  | 2 +-
 .../backend/src/server/api/endpoints/i/webhooks/create.ts  | 2 +-
 .../server/api/endpoints/users/lists/create-from-public.ts | 2 +-
 .../backend/src/server/api/endpoints/users/lists/create.ts | 2 +-
 packages/backend/test/e2e/antennas.ts                      | 3 +--
 packages/backend/test/e2e/clips.ts                         | 7 +++----
 9 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/packages/backend/src/core/ClipService.ts b/packages/backend/src/core/ClipService.ts
index 9fd1ebad87..929a9db064 100644
--- a/packages/backend/src/core/ClipService.ts
+++ b/packages/backend/src/core/ClipService.ts
@@ -41,7 +41,7 @@ export class ClipService {
 		const currentCount = await this.clipsRepository.countBy({
 			userId: me.id,
 		});
-		if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) {
+		if (currentCount >= (await this.roleService.getUserPolicies(me.id)).clipLimit) {
 			throw new ClipService.TooManyClipsError();
 		}
 
@@ -102,7 +102,7 @@ export class ClipService {
 		const currentCount = await this.clipNotesRepository.countBy({
 			clipId: clip.id,
 		});
-		if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) {
+		if (currentCount >= (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) {
 			throw new ClipService.TooManyClipNotesError();
 		}
 
diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts
index bbdcfed738..6333356fe9 100644
--- a/packages/backend/src/core/UserListService.ts
+++ b/packages/backend/src/core/UserListService.ts
@@ -95,7 +95,7 @@ export class UserListService implements OnApplicationShutdown, OnModuleInit {
 		const currentCount = await this.userListMembershipsRepository.countBy({
 			userListId: list.id,
 		});
-		if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
+		if (currentCount >= (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) {
 			throw new UserListService.TooManyUsersError();
 		}
 
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index ec08198514..577b9e1b1f 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -93,7 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const currentAntennasCount = await this.antennasRepository.countBy({
 				userId: me.id,
 			});
-			if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
+			if (currentAntennasCount >= (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
 				throw new ApiError(meta.errors.tooManyAntennas);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/import-antennas.ts b/packages/backend/src/server/api/endpoints/i/import-antennas.ts
index b4661a93e2..bc46163e3d 100644
--- a/packages/backend/src/server/api/endpoints/i/import-antennas.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-antennas.ts
@@ -78,7 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			if (file.size === 0) throw new ApiError(meta.errors.emptyFile);
 			const antennas: (_Antenna & { userListAccts: string[] | null })[] = JSON.parse(await this.downloadService.downloadTextFile(file.url));
 			const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id });
-			if (currentAntennasCount + antennas.length > (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
+			if (currentAntennasCount + antennas.length >= (await this.roleService.getUserPolicies(me.id)).antennaLimit) {
 				throw new ApiError(meta.errors.tooManyAntennas);
 			}
 			this.queueService.createImportAntennasJob(me, antennas);
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
index c692380288..9eb7f5b3a0 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
@@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const currentWebhooksCount = await this.webhooksRepository.countBy({
 				userId: me.id,
 			});
-			if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) {
+			if (currentWebhooksCount >= (await this.roleService.getUserPolicies(me.id)).webhookLimit) {
 				throw new ApiError(meta.errors.tooManyWebhooks);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
index 8504da0209..7e44d501ab 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
@@ -100,7 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const currentCount = await this.userListsRepository.countBy({
 				userId: me.id,
 			});
-			if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) {
+			if (currentCount >= (await this.roleService.getUserPolicies(me.id)).userListLimit) {
 				throw new ApiError(meta.errors.tooManyUserLists);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts
index 9378bde5cb..7daf05ba4e 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts
@@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const currentCount = await this.userListsRepository.countBy({
 				userId: me.id,
 			});
-			if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) {
+			if (currentCount >= (await this.roleService.getUserPolicies(me.id)).userListLimit) {
 				throw new ApiError(meta.errors.tooManyUserLists);
 			}
 
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index 101238b601..6ac14cd8dc 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -163,8 +163,7 @@ describe('アンテナ', () => {
 	});
 
 	test('が上限いっぱいまで作成できること', async () => {
-		// antennaLimit + 1まで作れるのがキモ
-		const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit + 1)].map(() => successfulApiCall({
+		const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit)].map(() => successfulApiCall({
 			endpoint: 'antennas/create',
 			parameters: { ...defaultParam },
 			user: alice,
diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts
index ba6f9d6a65..a229ec06f9 100644
--- a/packages/backend/test/e2e/clips.ts
+++ b/packages/backend/test/e2e/clips.ts
@@ -153,8 +153,7 @@ describe('クリップ', () => {
 	});
 
 	test('の作成はポリシーで定められた数以上はできない。', async () => {
-		// ポリシー + 1まで作れるという所がミソ
-		const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
+		const clipLimit = DEFAULT_POLICIES.clipLimit;
 		for (let i = 0; i < clipLimit; i++) {
 			await create();
 		}
@@ -327,7 +326,7 @@ describe('クリップ', () => {
 	});
 
 	test('の一覧(clips/list)が取得できる(上限いっぱい)', async () => {
-		const clipLimit = DEFAULT_POLICIES.clipLimit + 1;
+		const clipLimit = DEFAULT_POLICIES.clipLimit;
 		const clips = await createMany({}, clipLimit);
 		const res = await list({
 			parameters: { limit: 1 }, // FIXME: 無視されて11全部返ってくる
@@ -705,7 +704,7 @@ describe('クリップ', () => {
 
 		// TODO: 17000msくらいかかる...
 		test('をポリシーで定められた上限いっぱい(200)を超えて追加はできない。', async () => {
-			const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit + 1;
+			const noteLimit = DEFAULT_POLICIES.noteEachClipsLimit;
 			const noteList = await Promise.all([...Array(noteLimit)].map((_, i) => post(alice, {
 				text: `test ${i}`,
 			}) as unknown)) as Misskey.entities.Note[];

From a88579ca98a70115d6a61c74f26c36215f1f3daa Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Tue, 18 Jun 2024 12:44:30 +0900
Subject: [PATCH 029/206] docs: add changelog entry (follow-up of #14036)
 (#14037)

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76fed76b56..3341d4afff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
+- Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
 
 ## 2024.5.0
 

From f37d684fab115591f2565c9ca11964f802d1c36c Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Tue, 18 Jun 2024 19:46:20 +0900
Subject: [PATCH 030/206] Add missing styles (#14031)

---
 packages/frontend/src/pages/announcement.vue | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue
index 85ae9062d4..802a6bf399 100644
--- a/packages/frontend/src/pages/announcement.vue
+++ b/packages/frontend/src/pages/announcement.vue
@@ -109,6 +109,15 @@ definePageMetadata(() => ({
 </script>
 
 <style lang="scss" module>
+.fadeEnterActive,
+.fadeLeaveActive {
+	transition: opacity 0.125s ease;
+}
+.fadeEnterFrom,
+.fadeLeaveTo {
+	opacity: 0;
+}
+
 .announcement {
 	padding: 16px;
 }

From 77ae69355c706a5024fae3d645e2f8ef5f436fbf Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Wed, 19 Jun 2024 12:19:38 +0900
Subject: [PATCH 031/206] Enable to iterate over DOM collections (#14040)

---
 packages/frontend/tsconfig.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 819629a9cf..187a2473ba 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -37,7 +37,8 @@
 		],
 		"lib": [
 			"esnext",
-			"dom"
+			"dom",
+			"dom.iterable"
 		],
 		"jsx": "preserve"
 	},

From b683d79f8b3efffa4498380a47f136d79e0ddbe7 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Thu, 20 Jun 2024 16:24:10 +0900
Subject: [PATCH 032/206] Fix type checking (#14047)

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

diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index 7f20e0b1a2..f99b550a83 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -120,7 +120,7 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
 				res.json().then(done2, fail2);
 			}))
 			.then(async res => {
-				if (res.error) {
+				if ('error' in res) {
 					if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') {
 						// SUSPENDED
 						if (forceShowDialog || $i && (token === $i.token || id === $i.id)) {

From 1df90cef4c5ca8fc71b8dd491e0b90ad47263a35 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 21 Jun 2024 13:03:00 +0900
Subject: [PATCH 033/206] update typescript

---
 package.json                     |   2 +-
 packages/backend/package.json    |   2 +-
 packages/frontend/package.json   |   2 +-
 packages/misskey-js/package.json |   2 +-
 packages/sw/package.json         |   2 +-
 pnpm-lock.yaml                   | 264 +++++++++++++++----------------
 6 files changed, 137 insertions(+), 137 deletions(-)

diff --git a/package.json b/package.json
index b1786e16f1..5adce65415 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
 		"postcss": "8.4.38",
 		"tar": "6.2.1",
 		"terser": "5.30.3",
-		"typescript": "5.4.5",
+		"typescript": "5.5.2",
 		"esbuild": "0.20.2",
 		"glob": "10.3.12"
 	},
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 772dc8f8b5..15134b1ca8 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -177,7 +177,7 @@
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
 		"typeorm": "0.3.20",
-		"typescript": "5.4.5",
+		"typescript": "5.5.2",
 		"ulid": "2.3.0",
 		"vary": "1.1.2",
 		"web-push": "3.6.7",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 66940a1601..a63d97658b 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -68,7 +68,7 @@
 		"tinycolor2": "1.6.0",
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
-		"typescript": "5.4.5",
+		"typescript": "5.5.2",
 		"uuid": "9.0.1",
 		"v-code-diff": "1.11.0",
 		"vite": "5.2.11",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 4ff1a57309..b99d0dd260 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -51,7 +51,7 @@
 		"nodemon": "3.1.0",
 		"execa": "8.0.1",
 		"tsd": "0.30.7",
-		"typescript": "5.4.5",
+		"typescript": "5.5.2",
 		"esbuild": "0.19.11",
 		"glob": "10.3.12"
 	},
diff --git a/packages/sw/package.json b/packages/sw/package.json
index cb59a70238..2deda47369 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -20,7 +20,7 @@
 		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
 		"nodemon": "3.1.0",
-		"typescript": "5.4.5"
+		"typescript": "5.5.2"
 	},
 	"type": "module"
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5400828781..1281f7eefe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -43,8 +43,8 @@ importers:
         specifier: 5.30.3
         version: 5.30.3
       typescript:
-        specifier: 5.4.5
-        version: 5.4.5
+        specifier: 5.5.2
+        version: 5.5.2
     optionalDependencies:
       '@tensorflow/tfjs-core':
         specifier: 4.4.0
@@ -55,10 +55,10 @@ importers:
         version: 20.12.7
       '@typescript-eslint/eslint-plugin':
         specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/parser':
         specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
@@ -414,8 +414,8 @@ importers:
         specifier: 0.3.20
         version: 0.3.20(ioredis@5.4.1)(pg@8.11.5)
       typescript:
-        specifier: 5.4.5
-        version: 5.4.5
+        specifier: 5.5.2
+        version: 5.5.2
       ulid:
         specifier: 2.3.0
         version: 2.3.0
@@ -525,7 +525,7 @@ importers:
         version: 29.7.0
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
       '@nestjs/platform-express':
         specifier: 10.3.8
         version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)
@@ -651,10 +651,10 @@ importers:
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
         specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/parser':
         specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       aws-sdk-client-mock:
         specifier: 3.0.1
         version: 3.0.1
@@ -666,7 +666,7 @@ importers:
         version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -723,7 +723,7 @@ importers:
         version: 15.1.1
       '@vitejs/plugin-vue':
         specifier: 5.0.4
-        version: 5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))
+        version: 5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))
       '@vue/compiler-sfc':
         specifier: 3.4.26
         version: 3.4.26
@@ -845,27 +845,27 @@ importers:
         specifier: 4.2.0
         version: 4.2.0
       typescript:
-        specifier: 5.4.5
-        version: 5.4.5
+        specifier: 5.5.2
+        version: 5.5.2
       uuid:
         specifier: 9.0.1
         version: 9.0.1
       v-code-diff:
         specifier: 1.11.0
-        version: 1.11.0(vue@3.4.26(typescript@5.4.5))
+        version: 1.11.0(vue@3.4.26(typescript@5.5.2))
       vite:
         specifier: 5.2.11
         version: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
       vue:
         specifier: 3.4.26
-        version: 3.4.26(typescript@5.4.5)
+        version: 3.4.26(typescript@5.5.2)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.4.26(typescript@5.4.5))
+        version: 4.1.0(vue@3.4.26(typescript@5.5.2))
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
       '@misskey-dev/summaly':
         specifier: 5.1.0
         version: 5.1.0
@@ -904,10 +904,10 @@ importers:
         version: 8.0.9
       '@storybook/react':
         specifier: 8.0.9
-        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
+        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2)
       '@storybook/react-vite':
         specifier: 8.0.9
-        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
+        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
       '@storybook/test':
         specifier: 8.0.9
         version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
@@ -919,13 +919,13 @@ importers:
         version: 8.0.9
       '@storybook/vue3':
         specifier: 8.0.9
-        version: 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.4.5))
+        version: 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.5.2))
       '@storybook/vue3-vite':
         specifier: 8.0.9
-        version: 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))
+        version: 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))
       '@testing-library/vue':
         specifier: 8.0.3
-        version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
+        version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -964,10 +964,10 @@ importers:
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
         specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/parser':
         specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
@@ -988,7 +988,7 @@ importers:
         version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
       eslint-plugin-vue:
         specifier: 9.25.0
         version: 9.25.0(eslint@8.57.0)
@@ -1006,10 +1006,10 @@ importers:
         version: 4.0.5
       msw:
         specifier: 2.2.14
-        version: 2.2.14(typescript@5.4.5)
+        version: 2.2.14(typescript@5.5.2)
       msw-storybook-addon:
         specifier: 2.0.1
-        version: 2.0.1(msw@2.2.14(typescript@5.4.5))
+        version: 2.0.1(msw@2.2.14(typescript@5.5.2))
       nodemon:
         specifier: 3.1.0
         version: 3.1.0
@@ -1051,7 +1051,7 @@ importers:
         version: 9.4.2(eslint@8.57.0)
       vue-tsc:
         specifier: 2.0.16
-        version: 2.0.16(typescript@5.4.5)
+        version: 2.0.16(typescript@5.5.2)
 
   packages/misskey-bubble-game:
     dependencies:
@@ -1116,7 +1116,7 @@ importers:
         version: 7.43.1(@types/node@20.12.7)
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
       '@swc/jest':
         specifier: 0.2.36
         version: 0.2.36(@swc/core@1.4.17)
@@ -1128,10 +1128,10 @@ importers:
         version: 20.12.7
       '@typescript-eslint/eslint-plugin':
         specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/parser':
         specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
@@ -1166,8 +1166,8 @@ importers:
         specifier: 0.30.7
         version: 0.30.7
       typescript:
-        specifier: 5.4.5
-        version: 5.4.5
+        specifier: 5.5.2
+        version: 5.5.2
 
   packages/misskey-js/generator:
     devDependencies:
@@ -1256,10 +1256,10 @@ importers:
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)
+        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
       '@typescript-eslint/parser':
         specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: '@types/serviceworker@0.0.67'
@@ -1268,13 +1268,13 @@ importers:
         version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
       nodemon:
         specifier: 3.1.0
         version: 3.1.0
       typescript:
-        specifier: 5.4.5
-        version: 5.4.5
+        specifier: 5.5.2
+        version: 5.5.2
 
 packages:
 
@@ -10836,8 +10836,8 @@ packages:
     engines: {node: '>=14.17'}
     hasBin: true
 
-  typescript@5.4.5:
-    resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
+  typescript@5.5.2:
+    resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -13662,15 +13662,15 @@ snapshots:
       '@types/yargs': 17.0.19
       chalk: 4.1.2
 
-  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
+  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       glob: 7.2.3
       glob-promise: 4.2.2(glob@7.2.3)
       magic-string: 0.27.0
-      react-docgen-typescript: 2.2.2(typescript@5.4.5)
+      react-docgen-typescript: 2.2.2(typescript@5.5.2)
       vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
 
   '@jridgewell/gen-mapping@0.3.2':
     dependencies:
@@ -13785,12 +13785,12 @@ snapshots:
       eslint: 8.57.0
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)
 
-  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0)':
+  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)':
     dependencies:
-      '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       eslint: 8.57.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
 
   '@misskey-dev/sharp-read-bmp@1.2.0':
     dependencies:
@@ -15020,7 +15020,7 @@ snapshots:
       - encoding
       - supports-color
 
-  '@storybook/builder-vite@8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/builder-vite@8.0.9(encoding@0.1.13)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
     dependencies:
       '@storybook/channels': 8.0.9
       '@storybook/client-logger': 8.0.9
@@ -15041,7 +15041,7 @@ snapshots:
       ts-dedent: 2.2.0
       vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -15339,13 +15339,13 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  '@storybook/react-vite@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/react-vite@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
       '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
-      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
+      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
       '@storybook/node-logger': 8.0.9
-      '@storybook/react': 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
+      '@storybook/react': 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2)
       find-up: 5.0.0
       magic-string: 0.30.7
       react: 18.3.1
@@ -15362,7 +15362,7 @@ snapshots:
       - typescript
       - vite-plugin-glimmerx
 
-  '@storybook/react@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)':
+  '@storybook/react@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2)':
     dependencies:
       '@storybook/client-logger': 8.0.9
       '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
@@ -15388,7 +15388,7 @@ snapshots:
       type-fest: 2.19.0
       util-deprecate: 1.0.2
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -15456,17 +15456,17 @@ snapshots:
       '@types/express': 4.17.17
       file-system-cache: 2.3.0
 
-  '@storybook/vue3-vite@8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))':
+  '@storybook/vue3-vite@8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))':
     dependencies:
-      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
+      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
       '@storybook/core-server': 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
-      '@storybook/vue3': 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.4.5))
+      '@storybook/vue3': 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.5.2))
       find-package-json: 1.2.0
       magic-string: 0.30.7
-      typescript: 5.4.5
+      typescript: 5.5.2
       vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
-      vue-component-meta: 2.0.16(typescript@5.4.5)
-      vue-docgen-api: 4.75.1(vue@3.4.26(typescript@5.4.5))
+      vue-component-meta: 2.0.16(typescript@5.5.2)
+      vue-docgen-api: 4.75.1(vue@3.4.26(typescript@5.5.2))
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - bufferutil
@@ -15478,7 +15478,7 @@ snapshots:
       - vite-plugin-glimmerx
       - vue
 
-  '@storybook/vue3@8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.4.5))':
+  '@storybook/vue3@8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.5.2))':
     dependencies:
       '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
       '@storybook/global': 5.0.0
@@ -15488,7 +15488,7 @@ snapshots:
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
       vue-component-type-helpers: 2.0.21
     transitivePeerDependencies:
       - encoding
@@ -15750,12 +15750,12 @@ snapshots:
     dependencies:
       '@testing-library/dom': 9.3.4
 
-  '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
+  '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))':
     dependencies:
       '@babel/runtime': 7.23.4
       '@testing-library/dom': 9.3.3
-      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))
-      vue: 3.4.26(typescript@5.4.5)
+      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))
+      vue: 3.4.26(typescript@5.5.2)
     optionalDependencies:
       '@vue/compiler-sfc': 3.4.26
     transitivePeerDependencies:
@@ -16236,13 +16236,13 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)':
     dependencies:
       '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/scope-manager': 7.7.1
-      '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
-      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
+      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       '@typescript-eslint/visitor-keys': 7.7.1
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
@@ -16250,9 +16250,9 @@ snapshots:
       ignore: 5.3.1
       natural-compare: 1.4.0
       semver: 7.6.0
-      ts-api-utils: 1.3.0(typescript@5.4.5)
+      ts-api-utils: 1.3.0(typescript@5.5.2)
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
     transitivePeerDependencies:
       - supports-color
 
@@ -16282,16 +16282,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2)':
     dependencies:
       '@typescript-eslint/scope-manager': 7.7.1
       '@typescript-eslint/types': 7.7.1
-      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
+      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.2)
       '@typescript-eslint/visitor-keys': 7.7.1
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
     transitivePeerDependencies:
       - supports-color
 
@@ -16334,15 +16334,15 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/type-utils@7.7.1(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/type-utils@7.7.1(eslint@8.57.0)(typescript@5.5.2)':
     dependencies:
-      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
-      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.2)
+      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.57.0
-      ts-api-utils: 1.3.0(typescript@5.4.5)
+      ts-api-utils: 1.3.0(typescript@5.5.2)
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
     transitivePeerDependencies:
       - supports-color
 
@@ -16381,7 +16381,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/typescript-estree@7.7.1(typescript@5.4.5)':
+  '@typescript-eslint/typescript-estree@7.7.1(typescript@5.5.2)':
     dependencies:
       '@typescript-eslint/types': 7.7.1
       '@typescript-eslint/visitor-keys': 7.7.1
@@ -16390,9 +16390,9 @@ snapshots:
       is-glob: 4.0.3
       minimatch: 9.0.4
       semver: 7.6.0
-      ts-api-utils: 1.3.0(typescript@5.4.5)
+      ts-api-utils: 1.3.0(typescript@5.5.2)
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
     transitivePeerDependencies:
       - supports-color
 
@@ -16424,14 +16424,14 @@ snapshots:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@7.7.1(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/utils@7.7.1(eslint@8.57.0)(typescript@5.5.2)':
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
       '@types/json-schema': 7.0.15
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 7.7.1
       '@typescript-eslint/types': 7.7.1
-      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
+      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.2)
       eslint: 8.57.0
       semver: 7.6.0
     transitivePeerDependencies:
@@ -16455,10 +16455,10 @@ snapshots:
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5))':
+  '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))':
     dependencies:
       vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
 
   '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
     dependencies:
@@ -16604,7 +16604,7 @@ snapshots:
 
   '@vue/devtools-api@6.6.1': {}
 
-  '@vue/language-core@2.0.16(typescript@5.4.5)':
+  '@vue/language-core@2.0.16(typescript@5.5.2)':
     dependencies:
       '@volar/language-core': 2.2.0
       '@vue/compiler-dom': 3.4.25
@@ -16614,7 +16614,7 @@ snapshots:
       path-browserify: 1.0.1
       vue-template-compiler: 2.7.14
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
 
   '@vue/reactivity@3.4.26':
     dependencies:
@@ -16631,11 +16631,11 @@ snapshots:
       '@vue/shared': 3.4.26
       csstype: 3.1.3
 
-  '@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5))':
+  '@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2))':
     dependencies:
       '@vue/compiler-ssr': 3.4.26
       '@vue/shared': 3.4.26
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
 
   '@vue/shared@3.4.21': {}
 
@@ -16643,13 +16643,13 @@ snapshots:
 
   '@vue/shared@3.4.26': {}
 
-  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))':
+  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))':
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
       vue-component-type-helpers: 1.8.4
     optionalDependencies:
-      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.4.5))
+      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.5.2))
 
   '@webgpu/types@0.1.30': {}
 
@@ -18423,11 +18423,11 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
+  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
     dependencies:
       debug: 3.2.7(supports-color@8.1.1)
     optionalDependencies:
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
@@ -18487,7 +18487,7 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0):
     dependencies:
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
@@ -18497,7 +18497,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -18508,7 +18508,7 @@ snapshots:
       semver: 6.3.1
       tsconfig-paths: 3.15.0
     optionalDependencies:
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
@@ -21148,12 +21148,12 @@ snapshots:
     optionalDependencies:
       msgpackr-extract: 3.0.2
 
-  msw-storybook-addon@2.0.1(msw@2.2.14(typescript@5.4.5)):
+  msw-storybook-addon@2.0.1(msw@2.2.14(typescript@5.5.2)):
     dependencies:
       is-node-process: 1.2.0
-      msw: 2.2.14(typescript@5.4.5)
+      msw: 2.2.14(typescript@5.5.2)
 
-  msw@2.2.14(typescript@5.4.5):
+  msw@2.2.14(typescript@5.5.2):
     dependencies:
       '@bundled-es-modules/cookie': 2.0.0
       '@bundled-es-modules/statuses': 1.0.1
@@ -21173,7 +21173,7 @@ snapshots:
       type-fest: 4.9.0
       yargs: 17.7.2
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
 
   muggle-string@0.4.1: {}
 
@@ -22284,9 +22284,9 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  react-docgen-typescript@2.2.2(typescript@5.4.5):
+  react-docgen-typescript@2.2.2(typescript@5.5.2):
     dependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
 
   react-docgen@7.0.1:
     dependencies:
@@ -23405,9 +23405,9 @@ snapshots:
     dependencies:
       typescript: 5.3.3
 
-  ts-api-utils@1.3.0(typescript@5.4.5):
+  ts-api-utils@1.3.0(typescript@5.5.2):
     dependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
 
   ts-case-convert@2.0.2: {}
 
@@ -23547,7 +23547,7 @@ snapshots:
 
   typescript@5.4.2: {}
 
-  typescript@5.4.5: {}
+  typescript@5.5.2: {}
 
   ufo@1.3.2: {}
 
@@ -23694,14 +23694,14 @@ snapshots:
 
   uuid@9.0.1: {}
 
-  v-code-diff@1.11.0(vue@3.4.26(typescript@5.4.5)):
+  v-code-diff@1.11.0(vue@3.4.26(typescript@5.5.2)):
     dependencies:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.9.0
-      vue: 3.4.26(typescript@5.4.5)
-      vue-demi: 0.14.7(vue@3.4.26(typescript@5.4.5))
-      vue-i18n: 9.13.1(vue@3.4.26(typescript@5.4.5))
+      vue: 3.4.26(typescript@5.5.2)
+      vue-demi: 0.14.7(vue@3.4.26(typescript@5.5.2))
+      vue-i18n: 9.13.1(vue@3.4.26(typescript@5.5.2))
 
   v8-to-istanbul@9.2.0:
     dependencies:
@@ -23834,14 +23834,14 @@ snapshots:
     dependencies:
       vscode-languageserver-protocol: 3.17.5
 
-  vue-component-meta@2.0.16(typescript@5.4.5):
+  vue-component-meta@2.0.16(typescript@5.5.2):
     dependencies:
       '@volar/typescript': 2.2.0
-      '@vue/language-core': 2.0.16(typescript@5.4.5)
+      '@vue/language-core': 2.0.16(typescript@5.5.2)
       path-browserify: 1.0.1
       vue-component-type-helpers: 2.0.16
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
 
   vue-component-type-helpers@1.8.4: {}
 
@@ -23849,11 +23849,11 @@ snapshots:
 
   vue-component-type-helpers@2.0.21: {}
 
-  vue-demi@0.14.7(vue@3.4.26(typescript@5.4.5)):
+  vue-demi@0.14.7(vue@3.4.26(typescript@5.5.2)):
     dependencies:
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
 
-  vue-docgen-api@4.75.1(vue@3.4.26(typescript@5.4.5)):
+  vue-docgen-api@4.75.1(vue@3.4.26(typescript@5.5.2)):
     dependencies:
       '@babel/parser': 7.24.0
       '@babel/types': 7.24.0
@@ -23865,8 +23865,8 @@ snapshots:
       pug: 3.0.2
       recast: 0.23.4
       ts-map: 1.0.3
-      vue: 3.4.26(typescript@5.4.5)
-      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.26(typescript@5.4.5))
+      vue: 3.4.26(typescript@5.5.2)
+      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.26(typescript@5.5.2))
 
   vue-eslint-parser@9.4.2(eslint@8.57.0):
     dependencies:
@@ -23881,43 +23881,43 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  vue-i18n@9.13.1(vue@3.4.26(typescript@5.4.5)):
+  vue-i18n@9.13.1(vue@3.4.26(typescript@5.5.2)):
     dependencies:
       '@intlify/core-base': 9.13.1
       '@intlify/shared': 9.13.1
       '@vue/devtools-api': 6.6.1
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
 
-  vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.26(typescript@5.4.5)):
+  vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.26(typescript@5.5.2)):
     dependencies:
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
 
   vue-template-compiler@2.7.14:
     dependencies:
       de-indent: 1.0.2
       he: 1.2.0
 
-  vue-tsc@2.0.16(typescript@5.4.5):
+  vue-tsc@2.0.16(typescript@5.5.2):
     dependencies:
       '@volar/typescript': 2.2.0
-      '@vue/language-core': 2.0.16(typescript@5.4.5)
+      '@vue/language-core': 2.0.16(typescript@5.5.2)
       semver: 7.6.0
-      typescript: 5.4.5
+      typescript: 5.5.2
 
-  vue@3.4.26(typescript@5.4.5):
+  vue@3.4.26(typescript@5.5.2):
     dependencies:
       '@vue/compiler-dom': 3.4.26
       '@vue/compiler-sfc': 3.4.26
       '@vue/runtime-dom': 3.4.26
-      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.4.5))
+      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.5.2))
       '@vue/shared': 3.4.26
     optionalDependencies:
-      typescript: 5.4.5
+      typescript: 5.5.2
 
-  vuedraggable@4.1.0(vue@3.4.26(typescript@5.4.5)):
+  vuedraggable@4.1.0(vue@3.4.26(typescript@5.5.2)):
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.4.26(typescript@5.4.5)
+      vue: 3.4.26(typescript@5.5.2)
 
   w3c-xmlserializer@5.0.0:
     dependencies:

From bf33382082c41199b631c06ac20ea369b0747470 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 21 Jun 2024 13:03:51 +0900
Subject: [PATCH 034/206] refactor(backend): remove unnecessary isNotNull sugar

---
 .../src/core/AbuseReportNotificationService.ts      |  7 +++----
 packages/backend/src/core/NoteCreateService.ts      |  3 +--
 .../src/core/activitypub/ApAudienceService.ts       |  3 +--
 .../backend/src/core/activitypub/ApInboxService.ts  |  3 +--
 .../src/core/activitypub/ApRendererService.ts       |  5 ++---
 .../src/core/activitypub/models/ApMentionService.ts |  3 +--
 .../src/core/activitypub/models/ApNoteService.ts    |  3 +--
 .../src/core/activitypub/models/ApPersonService.ts  |  3 +--
 .../core/activitypub/models/ApQuestionService.ts    |  3 +--
 packages/backend/src/core/activitypub/models/tag.ts |  3 +--
 ...AbuseReportNotificationRecipientEntityService.ts |  5 ++---
 .../core/entities/AbuseUserReportEntityService.ts   |  3 +--
 .../src/core/entities/DriveFileEntityService.ts     |  7 +++----
 .../src/core/entities/InviteCodeEntityService.ts    |  5 ++---
 .../backend/src/core/entities/NoteEntityService.ts  |  9 ++++-----
 .../src/core/entities/NotificationEntityService.ts  | 13 ++++++-------
 .../backend/src/core/entities/PageEntityService.ts  |  3 +--
 .../backend/src/core/entities/UserEntityService.ts  |  3 +--
 packages/backend/src/misc/is-not-null.ts            |  8 --------
 .../server/api/endpoints/gallery/posts/create.ts    |  3 +--
 .../server/api/endpoints/gallery/posts/update.ts    |  3 +--
 .../src/server/api/endpoints/pinned-users.ts        |  3 +--
 22 files changed, 36 insertions(+), 65 deletions(-)
 delete mode 100644 packages/backend/src/misc/is-not-null.ts

diff --git a/packages/backend/src/core/AbuseReportNotificationService.ts b/packages/backend/src/core/AbuseReportNotificationService.ts
index df752afcd8..42e5931212 100644
--- a/packages/backend/src/core/AbuseReportNotificationService.ts
+++ b/packages/backend/src/core/AbuseReportNotificationService.ts
@@ -10,7 +10,6 @@ import sanitizeHtml from 'sanitize-html';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import { GlobalEvents, GlobalEventService } from '@/core/GlobalEventService.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import type {
 	AbuseReportNotificationRecipientRepository,
 	MiAbuseReportNotificationRecipient,
@@ -91,7 +90,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
 		const recipientEMailAddresses = await this.fetchEMailRecipients().then(it => it
 			.filter(it => it.isActive && it.userProfile?.emailVerified)
 			.map(it => it.userProfile?.email)
-			.filter(isNotNull),
+			.filter(x => x != null),
 		);
 
 		// 送信先の鮮度を保つため、毎回取得する
@@ -138,7 +137,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
 			.then(it => it
 				.filter(it => it.isActive && it.systemWebhookId && it.method === 'webhook')
 				.map(it => it.systemWebhookId)
-				.filter(isNotNull));
+				.filter(x => x != null));
 		for (const webhookId of recipientWebhookIds) {
 			await Promise.all(
 				abuseReports.map(it => {
@@ -340,7 +339,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
 	@bindThis
 	private async removeUnauthorizedRecipientUsers(recipients: MiAbuseReportNotificationRecipient[]): Promise<MiAbuseReportNotificationRecipient[]> {
 		const userRecipients = recipients.filter(it => it.userId !== null);
-		const recipientUserIds = new Set(userRecipients.map(it => it.userId).filter(isNotNull));
+		const recipientUserIds = new Set(userRecipients.map(it => it.userId).filter(x => x != null));
 		if (recipientUserIds.size <= 0) {
 			// ユーザが通知先として設定されていない場合、この関数での処理を行うべきレコードが無い
 			return recipients;
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 0c9de117d2..a2c3aaa701 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -59,7 +59,6 @@ import { UtilityService } from '@/core/UtilityService.js';
 import { UserBlockingService } from '@/core/UserBlockingService.js';
 import { isReply } from '@/misc/is-reply.js';
 import { trackPromise } from '@/misc/promise-tracker.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { IdentifiableError } from '@/misc/identifiable-error.js';
 
 type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@@ -839,7 +838,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		const mentions = extractMentions(tokens);
 		let mentionedUsers = (await Promise.all(mentions.map(m =>
 			this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null),
-		))).filter(isNotNull);
+		))).filter(x => x != null);
 
 		// Drop duplicate users
 		mentionedUsers = mentionedUsers.filter((u, i, self) =>
diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts
index 0fccc7b950..5a5a76f7d6 100644
--- a/packages/backend/src/core/activitypub/ApAudienceService.ts
+++ b/packages/backend/src/core/activitypub/ApAudienceService.ts
@@ -8,7 +8,6 @@ import promiseLimit from 'promise-limit';
 import type { MiRemoteUser, MiUser } from '@/models/User.js';
 import { concat, unique } from '@/misc/prelude/array.js';
 import { bindThis } from '@/decorators.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { getApIds } from './type.js';
 import { ApPersonService } from './models/ApPersonService.js';
 import type { ApObject } from './type.js';
@@ -41,7 +40,7 @@ export class ApAudienceService {
 		const limit = promiseLimit<MiUser | null>(2);
 		const mentionedUsers = (await Promise.all(
 			others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))),
-		)).filter(isNotNull);
+		)).filter(x => x != null);
 
 		if (toGroups.public.length > 0) {
 			return {
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index de3178b482..e2164fec1d 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -27,7 +27,6 @@ import { QueueService } from '@/core/QueueService.js';
 import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import type { MiRemoteUser } from '@/models/User.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { AbuseReportService } from '@/core/AbuseReportService.js';
 import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
@@ -538,7 +537,7 @@ export class ApInboxService {
 		const userIds = uris
 			.filter(uri => uri.startsWith(this.config.url + '/users/'))
 			.map(uri => uri.split('/').at(-1))
-			.filter(isNotNull);
+			.filter(x => x != null);
 		const users = await this.usersRepository.findBy({
 			id: In(userIds),
 		});
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 4fc724b548..98e944f347 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -26,7 +26,6 @@ import type { MiUserKeypair } from '@/models/UserKeypair.js';
 import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import { CustomEmojiService } from '@/core/CustomEmojiService.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { IdService } from '@/core/IdService.js';
 import { JsonLdService } from './JsonLdService.js';
 import { ApMfmService } from './ApMfmService.js';
@@ -317,7 +316,7 @@ export class ApRendererService {
 		const getPromisedFiles = async (ids: string[]): Promise<MiDriveFile[]> => {
 			if (ids.length === 0) return [];
 			const items = await this.driveFilesRepository.findBy({ id: In(ids) });
-			return ids.map(id => items.find(item => item.id === id)).filter(isNotNull);
+			return ids.map(id => items.find(item => item.id === id)).filter(x => x != null);
 		};
 
 		let inReplyTo;
@@ -686,7 +685,7 @@ export class ApRendererService {
 		if (names.length === 0) return [];
 
 		const allEmojis = await this.customEmojiService.localEmojisCache.fetch();
-		const emojis = names.map(name => allEmojis.get(name)).filter(isNotNull);
+		const emojis = names.map(name => allEmojis.get(name)).filter(x => x != null);
 
 		return emojis;
 	}
diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts
index 0ced7e88af..2cd151fa04 100644
--- a/packages/backend/src/core/activitypub/models/ApMentionService.ts
+++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts
@@ -8,7 +8,6 @@ import promiseLimit from 'promise-limit';
 import type { MiUser } from '@/models/_.js';
 import { toArray, unique } from '@/misc/prelude/array.js';
 import { bindThis } from '@/decorators.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { isMention } from '../type.js';
 import { Resolver } from '../ApResolverService.js';
 import { ApPersonService } from './ApPersonService.js';
@@ -28,7 +27,7 @@ export class ApMentionService {
 		const limit = promiseLimit<MiUser | null>(2);
 		const mentionedUsers = (await Promise.all(
 			hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))),
-		)).filter(isNotNull);
+		)).filter(x => x != null);
 
 		return mentionedUsers;
 	}
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index c6e6b3a1e8..fc7aa1e0b9 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -24,7 +24,6 @@ import { UtilityService } from '@/core/UtilityService.js';
 import { bindThis } from '@/decorators.js';
 import { checkHttps } from '@/misc/check-https.js';
 import { IdentifiableError } from '@/misc/identifiable-error.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
 import { ApLoggerService } from '../ApLoggerService.js';
 import { ApMfmService } from '../ApMfmService.js';
@@ -253,7 +252,7 @@ export class ApNoteService {
 				}
 			};
 
-			const uris = unique([note._misskey_quote, note.quoteUrl].filter(isNotNull));
+			const uris = unique([note._misskey_quote, note.quoteUrl].filter(x => x != null));
 			const results = await Promise.all(uris.map(tryResolveNote));
 
 			quote = results.filter((x): x is { status: 'ok', res: MiNote } => x.status === 'ok').map(x => x.res).at(0);
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index 744b1ea683..398c8695d2 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -38,7 +38,6 @@ import { MetaService } from '@/core/MetaService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
 import type { AccountMoveService } from '@/core/AccountMoveService.js';
 import { checkHttps } from '@/misc/check-https.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js';
 import { extractApHashtags } from './tag.js';
 import type { OnModuleInit } from '@nestjs/common';
@@ -637,7 +636,7 @@ export class ApPersonService implements OnModuleInit {
 
 			// とりあえずidを別の時間で生成して順番を維持
 			let td = 0;
-			for (const note of featuredNotes.filter(isNotNull)) {
+			for (const note of featuredNotes.filter(x => x != null)) {
 				td -= 1000;
 				transactionalEntityManager.insert(MiUserNotePining, {
 					id: this.idService.gen(Date.now() + td),
diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
index d1936cfe1d..4fae1e897b 100644
--- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts
+++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
@@ -10,7 +10,6 @@ import type { Config } from '@/config.js';
 import type { IPoll } from '@/models/Poll.js';
 import type Logger from '@/logger.js';
 import { bindThis } from '@/decorators.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { isQuestion } from '../type.js';
 import { ApLoggerService } from '../ApLoggerService.js';
 import { ApResolverService } from '../ApResolverService.js';
@@ -52,7 +51,7 @@ export class ApQuestionService {
 
 		const choices = question[multiple ? 'anyOf' : 'oneOf']
 			?.map((x) => x.name)
-			.filter(isNotNull)
+			.filter(x => x != null)
 			?? [];
 
 		const votes = question[multiple ? 'anyOf' : 'oneOf']?.map((x) => x.replies?.totalItems ?? x._misskey_votes ?? 0);
diff --git a/packages/backend/src/core/activitypub/models/tag.ts b/packages/backend/src/core/activitypub/models/tag.ts
index e7ceec3262..f75cc45f7e 100644
--- a/packages/backend/src/core/activitypub/models/tag.ts
+++ b/packages/backend/src/core/activitypub/models/tag.ts
@@ -4,7 +4,6 @@
  */
 
 import { toArray } from '@/misc/prelude/array.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { isHashtag } from '../type.js';
 import type { IObject, IApHashtag } from '../type.js';
 
@@ -16,7 +15,7 @@ export function extractApHashtags(tags: IObject | IObject[] | null | undefined):
 	return hashtags.map(tag => {
 		const m = tag.name.match(/^#(.+)/);
 		return m ? m[1] : null;
-	}).filter(isNotNull);
+	}).filter(x => x != null);
 }
 
 export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] {
diff --git a/packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts b/packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts
index 6819afafd9..1e23c194c5 100644
--- a/packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts
+++ b/packages/backend/src/core/entities/AbuseReportNotificationRecipientEntityService.ts
@@ -11,7 +11,6 @@ import { bindThis } from '@/decorators.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { Packed } from '@/misc/json-schema.js';
 import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 
 @Injectable()
 export class AbuseReportNotificationRecipientEntityService {
@@ -66,13 +65,13 @@ export class AbuseReportNotificationRecipientEntityService {
 			);
 		}
 
-		const userIds = objs.map(it => it.userId).filter(isNotNull);
+		const userIds = objs.map(it => it.userId).filter(x => x != null);
 		const users: Map<string, Packed<'UserLite'>> = (userIds.length > 0)
 			? await this.userEntityService.packMany(userIds)
 				.then(it => new Map(it.map(it => [it.id, it])))
 			: new Map();
 
-		const systemWebhookIds = objs.map(it => it.systemWebhookId).filter(isNotNull);
+		const systemWebhookIds = objs.map(it => it.systemWebhookId).filter(x => x != null);
 		const systemWebhooks: Map<string, Packed<'SystemWebhook'>> = (systemWebhookIds.length > 0)
 			? await this.systemWebhookEntityService.packMany(systemWebhookIds)
 				.then(it => new Map(it.map(it => [it.id, it])))
diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
index b0e1d1ab36..a13c244c19 100644
--- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
+++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
@@ -10,7 +10,6 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
 import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { UserEntityService } from './UserEntityService.js';
 
@@ -63,7 +62,7 @@ export class AbuseUserReportEntityService {
 	) {
 		const _reporters = reports.map(({ reporter, reporterId }) => reporter ?? reporterId);
 		const _targetUsers = reports.map(({ targetUser, targetUserId }) => targetUser ?? targetUserId);
-		const _assignees = reports.map(({ assignee, assigneeId }) => assignee ?? assigneeId).filter(isNotNull);
+		const _assignees = reports.map(({ assignee, assigneeId }) => assignee ?? assigneeId).filter(x => x != null);
 		const _userMap = await this.userEntityService.packMany(
 			[..._reporters, ..._targetUsers, ..._assignees],
 			null,
diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts
index 02ff2e7754..c485555f90 100644
--- a/packages/backend/src/core/entities/DriveFileEntityService.ts
+++ b/packages/backend/src/core/entities/DriveFileEntityService.ts
@@ -16,7 +16,6 @@ import { appendQuery, query } from '@/misc/prelude/url.js';
 import { deepClone } from '@/misc/clone.js';
 import { bindThis } from '@/decorators.js';
 import { isMimeImage } from '@/misc/is-mime-image.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { IdService } from '@/core/IdService.js';
 import { UtilityService } from '../UtilityService.js';
 import { VideoProcessingService } from '../VideoProcessingService.js';
@@ -261,11 +260,11 @@ export class DriveFileEntityService {
 		files: MiDriveFile[],
 		options?: PackOptions,
 	): Promise<Packed<'DriveFile'>[]> {
-		const _user = files.map(({ user, userId }) => user ?? userId).filter(isNotNull);
+		const _user = files.map(({ user, userId }) => user ?? userId).filter(x => x != null);
 		const _userMap = await this.userEntityService.packMany(_user)
 			.then(users => new Map(users.map(user => [user.id, user])));
 		const items = await Promise.all(files.map(f => this.packNullable(f, options, f.userId ? { packedUser: _userMap.get(f.userId) } : {})));
-		return items.filter(isNotNull);
+		return items.filter(x => x != null);
 	}
 
 	@bindThis
@@ -290,6 +289,6 @@ export class DriveFileEntityService {
 	): Promise<Packed<'DriveFile'>[]> {
 		if (fileIds.length === 0) return [];
 		const filesMap = await this.packManyByIdsMap(fileIds, options);
-		return fileIds.map(id => filesMap.get(id)).filter(isNotNull);
+		return fileIds.map(id => filesMap.get(id)).filter(x => x != null);
 	}
 }
diff --git a/packages/backend/src/core/entities/InviteCodeEntityService.ts b/packages/backend/src/core/entities/InviteCodeEntityService.ts
index 26f57e1299..5d3e823a2a 100644
--- a/packages/backend/src/core/entities/InviteCodeEntityService.ts
+++ b/packages/backend/src/core/entities/InviteCodeEntityService.ts
@@ -12,7 +12,6 @@ import type { MiUser } from '@/models/User.js';
 import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { UserEntityService } from './UserEntityService.js';
 
 @Injectable()
@@ -59,8 +58,8 @@ export class InviteCodeEntityService {
 		tickets: MiRegistrationTicket[],
 		me: { id: MiUser['id'] },
 	) {
-		const _createdBys = tickets.map(({ createdBy, createdById }) => createdBy ?? createdById).filter(isNotNull);
-		const _usedBys = tickets.map(({ usedBy, usedById }) => usedBy ?? usedById).filter(isNotNull);
+		const _createdBys = tickets.map(({ createdBy, createdById }) => createdBy ?? createdById).filter(x => x != null);
+		const _usedBys = tickets.map(({ usedBy, usedById }) => usedBy ?? usedById).filter(x => x != null);
 		const _userMap = await this.userEntityService.packMany([..._createdBys, ..._usedBys], me)
 			.then(users => new Map(users.map(u => [u.id, u])));
 		return Promise.all(
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 2ce72c50b8..2cd092231c 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -14,7 +14,6 @@ import type { MiNote } from '@/models/Note.js';
 import type { MiNoteReaction } from '@/models/NoteReaction.js';
 import type { UsersRepository, NotesRepository, FollowingsRepository, PollsRepository, PollVotesRepository, NoteReactionsRepository, ChannelsRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { DebounceLoader } from '@/misc/loader.js';
 import { IdService } from '@/core/IdService.js';
 import type { OnModuleInit } from '@nestjs/common';
@@ -276,7 +275,7 @@ export class NoteEntityService implements OnModuleInit {
 				packedFiles.set(k, v);
 			}
 		}
-		return fileIds.map(id => packedFiles.get(id)).filter(isNotNull);
+		return fileIds.map(id => packedFiles.get(id)).filter(x => x != null);
 	}
 
 	@bindThis
@@ -449,12 +448,12 @@ export class NoteEntityService implements OnModuleInit {
 
 		await this.customEmojiService.prefetchEmojis(this.aggregateNoteEmojis(notes));
 		// TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく
-		const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
+		const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(x => x != null);
 		const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
 		const users = [
 			...notes.map(({ user, userId }) => user ?? userId),
-			...notes.map(({ replyUserId }) => replyUserId).filter(isNotNull),
-			...notes.map(({ renoteUserId }) => renoteUserId).filter(isNotNull),
+			...notes.map(({ replyUserId }) => replyUserId).filter(x => x != null),
+			...notes.map(({ renoteUserId }) => renoteUserId).filter(x => x != null),
 		];
 		const packedUsers = await this.userEntityService.packMany(users, me)
 			.then(users => new Map(users.map(u => [u.id, u])));
diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts
index 94d56c883b..f393513510 100644
--- a/packages/backend/src/core/entities/NotificationEntityService.ts
+++ b/packages/backend/src/core/entities/NotificationEntityService.ts
@@ -13,7 +13,6 @@ import type { MiGroupedNotification, MiNotification } from '@/models/Notificatio
 import type { MiNote } from '@/models/Note.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { bindThis } from '@/decorators.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { FilterUnionByProperty, groupedNotificationTypes } from '@/types.js';
 import { CacheService } from '@/core/CacheService.js';
 import { RoleEntityService } from './RoleEntityService.js';
@@ -103,7 +102,7 @@ export class NotificationEntityService implements OnModuleInit {
 					user,
 					reaction: reaction.reaction,
 				};
-			}))).filter(r => isNotNull(r.user));
+			}))).filter(r => r.user != null);
 			// if all users have been deleted, don't show this notification
 			if (reactions.length === 0) {
 				return null;
@@ -124,7 +123,7 @@ export class NotificationEntityService implements OnModuleInit {
 				}
 
 				return this.userEntityService.pack(userId, { id: meId });
-			}))).filter(isNotNull);
+			}))).filter(x => x != null);
 			// if all users have been deleted, don't show this notification
 			if (users.length === 0) {
 				return null;
@@ -181,7 +180,7 @@ export class NotificationEntityService implements OnModuleInit {
 
 		validNotifications = await this.#filterValidNotifier(validNotifications, meId);
 
-		const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(isNotNull);
+		const noteIds = validNotifications.map(x => 'noteId' in x ? x.noteId : null).filter(x => x != null);
 		const notes = noteIds.length > 0 ? await this.notesRepository.find({
 			where: { id: In(noteIds) },
 			relations: ['user', 'reply', 'reply.user', 'renote', 'renote.user'],
@@ -223,7 +222,7 @@ export class NotificationEntityService implements OnModuleInit {
 			);
 		});
 
-		return (await Promise.all(packPromises)).filter(isNotNull);
+		return (await Promise.all(packPromises)).filter(x => x != null);
 	}
 
 	@bindThis
@@ -305,7 +304,7 @@ export class NotificationEntityService implements OnModuleInit {
 			this.cacheService.userProfileCache.fetch(meId).then(p => new Set(p.mutedInstances)),
 		]);
 
-		const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(isNotNull);
+		const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(x => x != null);
 		const notifiers = notifierIds.length > 0 ? await this.usersRepository.find({
 			where: { id: In(notifierIds) },
 		}) : [];
@@ -313,7 +312,7 @@ export class NotificationEntityService implements OnModuleInit {
 		const filteredNotifications = ((await Promise.all(notifications.map(async (notification) => {
 			const isValid = this.#validateNotifier(notification, userIdsWhoMeMuting, userMutedInstances, notifiers);
 			return isValid ? notification : null;
-		}))) as [T | null] ).filter(isNotNull);
+		}))) as [T | null] ).filter(x => x != null);
 
 		return filteredNotifications;
 	}
diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts
index 142d9e81db..46bf51bb6d 100644
--- a/packages/backend/src/core/entities/PageEntityService.ts
+++ b/packages/backend/src/core/entities/PageEntityService.ts
@@ -14,7 +14,6 @@ import type { MiPage } from '@/models/Page.js';
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import { UserEntityService } from './UserEntityService.js';
 import { DriveFileEntityService } from './DriveFileEntityService.js';
 
@@ -106,7 +105,7 @@ export class PageEntityService {
 			script: page.script,
 			eyeCatchingImageId: page.eyeCatchingImageId,
 			eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null,
-			attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter(isNotNull)),
+			attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter(x => x != null)),
 			likedCount: page.likedCount,
 			isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined,
 		});
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index b80a1ec206..da96878713 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -47,7 +47,6 @@ import { IdService } from '@/core/IdService.js';
 import type { AnnouncementService } from '@/core/AnnouncementService.js';
 import type { CustomEmojiService } from '@/core/CustomEmojiService.js';
 import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 import type { OnModuleInit } from '@nestjs/common';
 import type { NoteEntityService } from './NoteEntityService.js';
 import type { DriveFileEntityService } from './DriveFileEntityService.js';
@@ -514,7 +513,7 @@ export class UserEntityService implements OnModuleInit {
 				movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
 				alsoKnownAs: user.alsoKnownAs
 					? Promise.all(user.alsoKnownAs.map(uri => this.apPersonService.fetchPerson(uri).then(user => user?.id).catch(() => null)))
-						.then(xs => xs.length === 0 ? null : xs.filter(isNotNull))
+						.then(xs => xs.length === 0 ? null : xs.filter(x => x != null))
 					: null,
 				createdAt: this.idService.parse(user.id).date.toISOString(),
 				updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
diff --git a/packages/backend/src/misc/is-not-null.ts b/packages/backend/src/misc/is-not-null.ts
deleted file mode 100644
index 8d9dc8bb39..0000000000
--- a/packages/backend/src/misc/is-not-null.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export function isNotNull<T extends NonNullable<unknown>>(input: T | undefined | null): input is T {
-	return input != null;
-}
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
index 46f8998810..504a9c789e 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts
@@ -12,7 +12,6 @@ import type { MiDriveFile } from '@/models/DriveFile.js';
 import { IdService } from '@/core/IdService.js';
 import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
 import { DI } from '@/di-symbols.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 
 export const meta = {
 	tags: ['gallery'],
@@ -70,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					id: fileId,
 					userId: me.id,
 				}),
-			))).filter(isNotNull);
+			))).filter(x => x != null);
 
 			if (files.length === 0) {
 				throw new Error();
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
index 8bd83ff5ba..2f977784ec 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
@@ -10,7 +10,6 @@ import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/_.js
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
 import { DI } from '@/di-symbols.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 
 export const meta = {
 	tags: ['gallery'],
@@ -68,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					id: fileId,
 					userId: me.id,
 				}),
-			))).filter(isNotNull);
+			))).filter(x => x != null);
 
 			if (files.length === 0) {
 				throw new Error();
diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts
index 784766bcb5..15832ef7f8 100644
--- a/packages/backend/src/server/api/endpoints/pinned-users.ts
+++ b/packages/backend/src/server/api/endpoints/pinned-users.ts
@@ -12,7 +12,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { MetaService } from '@/core/MetaService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { DI } from '@/di-symbols.js';
-import { isNotNull } from '@/misc/is-not-null.js';
 
 export const meta = {
 	tags: ['users'],
@@ -53,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				host: acct.host ?? IsNull(),
 			})));
 
-			return await this.userEntityService.packMany(users.filter(isNotNull), me, { schema: 'UserDetailed' });
+			return await this.userEntityService.packMany(users.filter(x => x != null), me, { schema: 'UserDetailed' });
 		});
 	}
 }

From 811ffbf3a46656a3cb9e41bec89a61cd33341871 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 21 Jun 2024 13:18:19 +0900
Subject: [PATCH 035/206] remove unused file

---
 packages/backend/src/misc/prelude/maybe.ts | 25 ----------------------
 packages/backend/test/prelude/maybe.ts     | 23 --------------------
 2 files changed, 48 deletions(-)
 delete mode 100644 packages/backend/src/misc/prelude/maybe.ts
 delete mode 100644 packages/backend/test/prelude/maybe.ts

diff --git a/packages/backend/src/misc/prelude/maybe.ts b/packages/backend/src/misc/prelude/maybe.ts
deleted file mode 100644
index 1c58ccb9c7..0000000000
--- a/packages/backend/src/misc/prelude/maybe.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export interface IMaybe<T> {
-	isJust(): this is IJust<T>;
-}
-
-export interface IJust<T> extends IMaybe<T> {
-	get(): T;
-}
-
-export function just<T>(value: T): IJust<T> {
-	return {
-		isJust: () => true,
-		get: () => value,
-	};
-}
-
-export function nothing<T>(): IMaybe<T> {
-	return {
-		isJust: () => false,
-	};
-}
diff --git a/packages/backend/test/prelude/maybe.ts b/packages/backend/test/prelude/maybe.ts
deleted file mode 100644
index 16e92216d4..0000000000
--- a/packages/backend/test/prelude/maybe.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import * as assert from 'assert';
-import { just, nothing } from '../../src/misc/prelude/maybe.js';
-
-describe('just', () => {
-	test('has a value', () => {
-		assert.deepStrictEqual(just(3).isJust(), true);
-	});
-
-	test('has the inverse called get', () => {
-		assert.deepStrictEqual(just(3).get(), 3);
-	});
-});
-
-describe('nothing', () => {
-	test('has no value', () => {
-		assert.deepStrictEqual(nothing().isJust(), false);
-	});
-});

From 1d6ccd97812900444a665fadf689a818d777245c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 21 Jun 2024 13:21:27 +0900
Subject: [PATCH 036/206] remove unused files

---
 packages/backend/src/misc/prelude/math.ts   |  8 --------
 packages/backend/src/misc/prelude/string.ts | 20 --------------------
 packages/backend/src/misc/prelude/symbol.ts |  6 ------
 3 files changed, 34 deletions(-)
 delete mode 100644 packages/backend/src/misc/prelude/math.ts
 delete mode 100644 packages/backend/src/misc/prelude/string.ts
 delete mode 100644 packages/backend/src/misc/prelude/symbol.ts

diff --git a/packages/backend/src/misc/prelude/math.ts b/packages/backend/src/misc/prelude/math.ts
deleted file mode 100644
index 38556def2d..0000000000
--- a/packages/backend/src/misc/prelude/math.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export function gcd(a: number, b: number): number {
-	return b === 0 ? a : gcd(b, a % b);
-}
diff --git a/packages/backend/src/misc/prelude/string.ts b/packages/backend/src/misc/prelude/string.ts
deleted file mode 100644
index 67ea529961..0000000000
--- a/packages/backend/src/misc/prelude/string.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export function concat(xs: string[]): string {
-	return xs.join('');
-}
-
-export function capitalize(s: string): string {
-	return toUpperCase(s.charAt(0)) + toLowerCase(s.slice(1));
-}
-
-export function toUpperCase(s: string): string {
-	return s.toUpperCase();
-}
-
-export function toLowerCase(s: string): string {
-	return s.toLowerCase();
-}
diff --git a/packages/backend/src/misc/prelude/symbol.ts b/packages/backend/src/misc/prelude/symbol.ts
deleted file mode 100644
index 7e8d39bdb6..0000000000
--- a/packages/backend/src/misc/prelude/symbol.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export const fallback = Symbol('fallback');

From e88f08ad7deeba74f5a196fe3afd52da6982f251 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 21 Jun 2024 13:31:02 +0900
Subject: [PATCH 037/206] refactor

---
 .../src/server/api/endpoints/drive/folders/update.ts     | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts
index 52b8b335b5..62b04e1df3 100644
--- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts
+++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts
@@ -95,15 +95,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 					// Check if the circular reference will occur
 					const checkCircle = async (folderId: string): Promise<boolean> => {
-						// Fetch folder
-						const folder2 = await this.driveFoldersRepository.findOneBy({
+						const folder2 = await this.driveFoldersRepository.findOneByOrFail({
 							id: folderId,
 						});
 
-						if (folder2!.id === folder!.id) {
+						if (folder2.id === folder.id) {
 							return true;
-						} else if (folder2!.parentId) {
-							return await checkCircle(folder2!.parentId);
+						} else if (folder2.parentId) {
+							return await checkCircle(folder2.parentId);
 						} else {
 							return false;
 						}

From 2c84d06a66c00991f51de664571c1ade203fcd56 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Fri, 21 Jun 2024 13:48:04 +0900
Subject: [PATCH 038/206] Fix type checking (#14052)

---
 packages/frontend/src/components/MkNotification.vue | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 73cd7cd5b3..3fa2eb254e 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -6,14 +6,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div :class="$style.root">
 	<div :class="$style.head">
-		<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/>
+		<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
 		<MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
 		<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div>
 		<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
 		<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
 		<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
-		<MkAvatar v-else-if="notification.user" :class="$style.icon" :user="notification.user" link preview/>
-		<img v-else-if="notification.icon" :class="[$style.icon, $style.icon_app]" :src="notification.icon" alt=""/>
+		<MkAvatar v-else-if="'user' in notification" :class="$style.icon" :user="notification.user" link preview/>
+		<img v-else-if="'icon' in notification" :class="[$style.icon, $style.icon_app]" :src="notification.icon" alt=""/>
 		<div
 			:class="[$style.subIcon, {
 				[$style.t_follow]: notification.type === 'follow',
@@ -164,13 +164,13 @@ const props = withDefaults(defineProps<{
 const followRequestDone = ref(false);
 
 const acceptFollowRequest = () => {
-	if (props.notification.user == null) return;
+	if (!('user' in props.notification)) return;
 	followRequestDone.value = true;
 	misskeyApi('following/requests/accept', { userId: props.notification.user.id });
 };
 
 const rejectFollowRequest = () => {
-	if (props.notification.user == null) return;
+	if (!('user' in props.notification)) return;
 	followRequestDone.value = true;
 	misskeyApi('following/requests/reject', { userId: props.notification.user.id });
 };

From a9012d3d0c2036b06d7b313fef7a3867f4559517 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Fri, 21 Jun 2024 17:29:37 +0900
Subject: [PATCH 039/206] test(frontend): fix component error in `MkChart`
 story (#14056)

---
 packages/frontend/.storybook/charts.ts        | 48 +++++++++++++++++++
 .../src/components/MkChart.stories.impl.ts    | 45 +----------------
 .../MkInstanceCardMini.stories.impl.ts        |  3 +-
 3 files changed, 52 insertions(+), 44 deletions(-)
 create mode 100644 packages/frontend/.storybook/charts.ts

diff --git a/packages/frontend/.storybook/charts.ts b/packages/frontend/.storybook/charts.ts
new file mode 100644
index 0000000000..5015012a82
--- /dev/null
+++ b/packages/frontend/.storybook/charts.ts
@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { DefaultBodyType, HttpResponse, HttpResponseResolver, JsonBodyType, PathParams, http } from 'msw';
+import seedrandom from 'seedrandom';
+import { action } from '@storybook/addon-actions';
+
+function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] {
+	const rng = seedrandom(seed);
+	const max = Math.floor(option?.mul ?? 250 * rng());
+	let accumulation = 0;
+	const array: number[] = [];
+	for (let i = 0; i < limit; i++) {
+		const num = Math.floor((max + 1) * rng());
+		if (option?.accumulate) {
+			accumulation += num;
+			array.unshift(accumulation);
+		} else {
+			array.push(num);
+		}
+	}
+	return array;
+}
+
+export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
+	return ({ request }) => {
+		action(`GET ${request.url}`)();
+		const limitParam = new URL(request.url).searchParams.get('limit');
+		const limit = limitParam ? parseInt(limitParam) : 30;
+		const res = {};
+		for (const field of fields) {
+			const layers = field.split('.');
+			let current = res;
+			while (layers.length > 1) {
+				const currentKey = layers.shift()!;
+				if (current[currentKey] == null) current[currentKey] = {};
+				current = current[currentKey];
+			}
+			current[layers[0]] = getChartArray(field, limit, {
+				accumulate: option?.accumulate,
+				mul: option?.mulMap != null && field in option.mulMap ? option.mulMap[field] : undefined,
+			});
+		}
+		return HttpResponse.json(res);
+	};
+}
diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts
index 3bae703245..1bcb9c30d8 100644
--- a/packages/frontend/src/components/MkChart.stories.impl.ts
+++ b/packages/frontend/src/components/MkChart.stories.impl.ts
@@ -6,52 +6,11 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 /* eslint-disable import/no-default-export */
 import { StoryObj } from '@storybook/vue3';
-import { DefaultBodyType, HttpResponse, HttpResponseResolver, JsonBodyType, PathParams, http } from 'msw';
-import seedrandom from 'seedrandom';
-import { action } from '@storybook/addon-actions';
+import { http } from 'msw';
 import { commonHandlers } from '../../.storybook/mocks.js';
+import { getChartResolver } from '../../.storybook/charts.js';
 import MkChart from './MkChart.vue';
 
-function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] {
-	const rng = seedrandom(seed);
-	const max = Math.floor(option?.mul ?? 250 * rng());
-	let accumulation = 0;
-	const array: number[] = [];
-	for (let i = 0; i < limit; i++) {
-		const num = Math.floor((max + 1) * rng());
-		if (option?.accumulate) {
-			accumulation += num;
-			array.unshift(accumulation);
-		} else {
-			array.push(num);
-		}
-	}
-	return array;
-}
-
-export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
-	return ({ request }) => {
-		action(`GET ${request.url}`)();
-		const limitParam = new URL(request.url).searchParams.get('limit');
-		const limit = limitParam ? parseInt(limitParam) : 30;
-		const res = {};
-		for (const field of fields) {
-			const layers = field.split('.');
-			let current = res;
-			while (layers.length > 1) {
-				const currentKey = layers.shift()!;
-				if (current[currentKey] == null) current[currentKey] = {};
-				current = current[currentKey];
-			}
-			current[layers[0]] = getChartArray(field, limit, {
-				accumulate: option?.accumulate,
-				mul: option?.mulMap != null && field in option.mulMap ? option.mulMap[field] : undefined,
-			});
-		}
-		return HttpResponse.json(res);
-	};
-}
-
 const Base = {
 	render(args) {
 		return {
diff --git a/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts
index a069a0eb8e..9e8de9d878 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts
+++ b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts
@@ -8,8 +8,9 @@ import { StoryObj } from '@storybook/vue3';
 import { HttpResponse, http } from 'msw';
 import { federationInstance } from '../../.storybook/fakes.js';
 import { commonHandlers } from '../../.storybook/mocks.js';
+import { getChartResolver } from '../../.storybook/charts.js';
 import MkInstanceCardMini from './MkInstanceCardMini.vue';
-import { getChartResolver } from './MkChart.stories.impl.js';
+
 export const Default = {
 	render(args) {
 		return {

From 4d2eddec2e95587f7e4ec4ed79b6392a203d8789 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Sat, 22 Jun 2024 12:40:00 +0900
Subject: [PATCH 040/206] Replace with `vue/no-setup-props-reactivity-loss`
 rule (#14062)

---
 packages/frontend/.eslintrc.cjs                          | 2 +-
 packages/frontend/src/components/MkChart.vue             | 2 +-
 packages/frontend/src/components/MkDateSeparatedList.vue | 2 +-
 packages/frontend/src/components/MkMediaAudio.vue        | 2 +-
 packages/frontend/src/components/MkMediaVideo.vue        | 2 +-
 packages/frontend/src/components/MkTutorialDialog.vue    | 2 +-
 packages/frontend/src/components/MkUserSetupDialog.vue   | 2 +-
 packages/frontend/src/components/global/MkTime.vue       | 4 ++--
 packages/frontend/src/pages/reversi/game.board.vue       | 2 +-
 9 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/packages/frontend/.eslintrc.cjs b/packages/frontend/.eslintrc.cjs
index 20f88dc078..fd562e1c40 100644
--- a/packages/frontend/.eslintrc.cjs
+++ b/packages/frontend/.eslintrc.cjs
@@ -56,7 +56,7 @@ module.exports = {
 		'vue/no-dupe-keys': 'warn',
 		'vue/valid-v-for': 'warn',
 		'vue/return-in-computed-property': 'warn',
-		'vue/no-setup-props-destructure': 'warn',
+		'vue/no-setup-props-reactivity-loss': 'warn',
 		'vue/max-attributes-per-line': 'off',
 		'vue/html-self-closing': 'off',
 		'vue/singleline-html-element-content-newline': 'off',
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 3816bca348..4b24562249 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -114,7 +114,7 @@ const getColor = (i) => {
 	return colorSets[i % colorSets.length];
 };
 
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const now = props.nowForChromatic != null ? new Date(props.nowForChromatic) : new Date();
 let chartInstance: Chart | null = null;
 let chartData: {
diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue
index 85e131cf9b..f16981716c 100644
--- a/packages/frontend/src/components/MkDateSeparatedList.vue
+++ b/packages/frontend/src/components/MkDateSeparatedList.vue
@@ -130,7 +130,7 @@ export default defineComponent({
 			el.style.left = '';
 		}
 
-		// eslint-disable-next-line vue/no-setup-props-destructure
+		// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 		const classes = {
 			[$style['date-separated-list']]: true,
 			[$style['date-separated-list-nogap']]: props.noGap,
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index 5d2edf467e..ebd4fc9ca4 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -126,7 +126,7 @@ function hasFocus() {
 const playerEl = shallowRef<HTMLDivElement>();
 const audioEl = shallowRef<HTMLAudioElement>();
 
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore'));
 
 // Menu
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 1e3868bc36..707d7c1501 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -160,7 +160,7 @@ function hasFocus() {
 	return playerEl.value === document.activeElement || playerEl.value.contains(document.activeElement);
 }
 
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
 
 // Menu
diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue
index d2711e4ec5..9adc8d466c 100644
--- a/packages/frontend/src/components/MkTutorialDialog.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.vue
@@ -172,7 +172,7 @@ const emit = defineEmits<{
 
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const page = ref(props.initialPage ?? 0);
 
 watch(page, (to) => {
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index 1d376382ca..cab0067813 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -148,7 +148,7 @@ const emit = defineEmits<{
 
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const page = ref(defaultStore.state.accountSetupWizard);
 
 watch(page, () => {
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 23fe99bd9c..027b226f3f 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -41,12 +41,12 @@ function getDateSafe(n: Date | string | number) {
 	}
 }
 
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const _time = props.time == null ? NaN : getDateSafe(props.time).getTime();
 const invalid = Number.isNaN(_time);
 const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
 
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const now = ref(props.origin?.getTime() ?? Date.now());
 const ago = computed(() => (now.value - _time) / 1000/*ms*/);
 
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index 175ea62411..7d9cefa5c9 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -169,7 +169,7 @@ const props = defineProps<{
 const showBoardLabels = ref<boolean>(false);
 const useAvatarAsStone = ref<boolean>(true);
 const autoplaying = ref<boolean>(false);
-// eslint-disable-next-line vue/no-setup-props-destructure
+// eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const game = ref<Misskey.entities.ReversiGameDetailed & { logs: Reversi.Serializer.SerializedLog[] }>(deepClone(props.game));
 const logPos = ref<number>(game.value.logs.length);
 const engine = shallowRef<Reversi.Game>(Reversi.Serializer.restoreGame({

From 8a9de081f1efd78117c30bdc721785ca323b202c Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 22 Jun 2024 12:43:03 +0900
Subject: [PATCH 041/206] fix(backend): fallback if `sinceId` is older than the
 oldest in cache when using FTT (#14061)

* fix(backend): fallback if `sinceId` is older than the oldest in cache when using FTT

* Update CHANGELOG.md

* chore: fix description of test
---
 CHANGELOG.md                                  |  1 +
 .../src/core/FanoutTimelineEndpointService.ts | 12 +++----
 packages/backend/test/e2e/timelines.ts        | 35 +++++++++++++++++++
 3 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3341d4afff..8ce566370b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
 - Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
+- Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
 
 ## 2024.5.0
 
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index d5058f37c2..b05af99c5e 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -55,9 +55,6 @@ export class FanoutTimelineEndpointService {
 
 	@bindThis
 	private async getMiNotes(ps: TimelineOptions): Promise<MiNote[]> {
-		let noteIds: string[];
-		let shouldFallbackToDb = false;
-
 		// 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える
 		if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]);
 
@@ -67,12 +64,11 @@ export class FanoutTimelineEndpointService {
 		const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
 
 		// TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい
-		const redisResultIds = Array.from(new Set(redisResult.flat(1)));
+		const redisResultIds = Array.from(new Set(redisResult.flat(1))).sort(idCompare);
 
-		redisResultIds.sort(idCompare);
-		noteIds = redisResultIds.slice(0, ps.limit);
-
-		shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
+		let noteIds = redisResultIds.slice(0, ps.limit);
+		const oldestNoteId = ascending ? redisResultIds[0] : redisResultIds[redisResultIds.length - 1];
+		const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId;
 
 		if (!shouldFallbackToDb) {
 			let filter = ps.noteFilter ?? (_note => true);
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index 5487292afc..f6cc2bac28 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -7,6 +7,8 @@
 // pnpm jest -- e2e/timelines.ts
 
 import * as assert from 'assert';
+import { Redis } from 'ioredis';
+import { loadConfig } from '@/config.js';
 import { api, post, randomString, sendEnvUpdateRequest, signup, sleep, uploadUrl } from '../utils.js';
 
 function genHost() {
@@ -17,7 +19,13 @@ function waitForPushToTl() {
 	return sleep(500);
 }
 
+let redisForTimelines: Redis;
+
 describe('Timelines', () => {
+	beforeAll(() => {
+		redisForTimelines = new Redis(loadConfig().redisForTimelines);
+	});
+
 	describe('Home TL', () => {
 		test.concurrent('自分の visibility: followers なノートが含まれる', async () => {
 			const [alice] = await Promise.all([signup()]);
@@ -1272,6 +1280,33 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
 		});
+
+		/** @see https://github.com/misskey-dev/misskey/issues/14000 */
+		test.concurrent('FTT: sinceId にキャッシュより古いノートを指定しても、sinceId による絞り込みが正しく動作する', async () => {
+			const alice = await signup();
+			const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
+			const note1 = await post(alice, { text: '1' });
+			const note2 = await post(alice, { text: '2' });
+			await redisForTimelines.del('list:userTimeline:' + alice.id);
+			const note3 = await post(alice, { text: '3' });
+
+			const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id });
+			assert.deepStrictEqual(res.body, [note1, note2, note3]);
+		});
+
+		test.concurrent('FTT: sinceId にキャッシュより古いノートを指定しても、sinceId と untilId による絞り込みが正しく動作する', async () => {
+			const alice = await signup();
+			const noteSince = await post(alice, { text: 'Note where id will be `sinceId`.' });
+			const note1 = await post(alice, { text: '1' });
+			const note2 = await post(alice, { text: '2' });
+			await redisForTimelines.del('list:userTimeline:' + alice.id);
+			const note3 = await post(alice, { text: '3' });
+			const noteUntil = await post(alice, { text: 'Note where id will be `untilId`.' });
+			await post(alice, { text: '4' });
+
+			const res = await api('users/notes', { userId: alice.id, sinceId: noteSince.id, untilId: noteUntil.id });
+			assert.deepStrictEqual(res.body, [note3, note2, note1]);
+		});
 	});
 
 	// TODO: リノートミュート済みユーザーのテスト

From 1e78ef1cb847da719be23d55c87aaac0294665db Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 22 Jun 2024 12:44:01 +0900
Subject: [PATCH 042/206] =?UTF-8?q?fix:=20notRespondingSince=E3=81=8C?=
 =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=81=95=E3=82=8C=E3=82=8B=E5=89=8D=E3=81=AB?=
 =?UTF-8?q?=E4=B8=8D=E9=80=9A=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E3=82=A4?=
 =?UTF-8?q?=E3=83=B3=E3=82=B9=E3=82=BF=E3=83=B3=E3=82=B9=E3=81=8C=E8=87=AA?=
 =?UTF-8?q?=E5=8B=95=E7=9A=84=E3=81=AB=E9=85=8D=E4=BF=A1=E5=81=9C=E6=AD=A2?=
 =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=82=89=E3=81=AA=E3=81=84=20(#14059)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                                | 1 +
 .../backend/src/queue/processors/DeliverProcessorService.ts | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ce566370b..facc613b73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
 - Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
+- Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
 - Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
 
 ## 2024.5.0
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index b73195afc3..d665945861 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -109,6 +109,12 @@ export class DeliverProcessorService {
 							suspensionState: 'autoSuspendedForNotResponding',
 						});
 					}
+				} else {
+					// isNotRespondingがtrueでnotRespondingSinceがnullの場合はnotRespondingSinceをセット
+					// notRespondingSinceは新たな機能なので、それ以前のデータにはnotRespondingSinceがない場合がある
+					this.federatedInstanceService.update(i.id, {
+						notRespondingSince: new Date(),
+					});
 				}
 
 				this.apRequestChart.deliverFail();

From 7e21497edc97b401026193b033b4a85675431907 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 22 Jun 2024 12:45:37 +0900
Subject: [PATCH 043/206] =?UTF-8?q?fix(frontend):=20=E3=83=AA=E3=83=90?=
 =?UTF-8?q?=E3=83=BC=E3=82=B7=E9=96=8B=E5=A7=8B=E6=99=82=E3=81=AE=E8=87=AA?=
 =?UTF-8?q?=E5=8B=95=E6=8A=95=E7=A8=BF=E3=81=AEURL=E3=81=8C=E6=AD=A3?=
 =?UTF-8?q?=E3=81=97=E3=81=8F=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=81=8C?=
 =?UTF-8?q?=E3=81=82=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1404?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): リバーシ開始時の自動投稿のURLが正しくない場合があるのを修正

* :v:
---
 CHANGELOG.md                                 | 1 +
 packages/frontend/src/pages/reversi/game.vue | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index facc613b73..c5eb698385 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 ### Client
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
+- Fix: リバーシの対局を正しく共有できないことがある問題を修正
 
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
diff --git a/packages/frontend/src/pages/reversi/game.vue b/packages/frontend/src/pages/reversi/game.vue
index eadc51881c..97a793753d 100644
--- a/packages/frontend/src/pages/reversi/game.vue
+++ b/packages/frontend/src/pages/reversi/game.vue
@@ -20,6 +20,7 @@ import { useStream } from '@/stream.js';
 import { signinRequired } from '@/account.js';
 import { useRouter } from '@/router/supplier.js';
 import * as os from '@/os.js';
+import { url } from '@/config.js';
 import { i18n } from '@/i18n.js';
 import { useInterval } from '@/scripts/use-interval.js';
 
@@ -44,7 +45,7 @@ function start(_game: Misskey.entities.ReversiGameDetailed) {
 
 	if (shareWhenStart.value) {
 		misskeyApi('notes/create', {
-			text: i18n.ts._reversi.iStartedAGame + '\n' + location.href,
+			text: `${i18n.ts._reversi.iStartedAGame}\n${url}/reversi/g/${props.gameId}`,
 			visibility: 'home',
 		});
 	}

From 3254f7c5cd13c56d023f9fc1ed50f1b602c8359d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 22 Jun 2024 12:45:52 +0900
Subject: [PATCH 044/206] chore(deps): bump docker/build-push-action from 5 to
 6 (#14039)

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/docker-develop.yml | 2 +-
 .github/workflows/docker.yml         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml
index cb84849580..ac2b1b4d35 100644
--- a/.github/workflows/docker-develop.yml
+++ b/.github/workflows/docker-develop.yml
@@ -37,7 +37,7 @@ jobs:
           password: ${{ secrets.DOCKER_PASSWORD }}
       - name: Build and push by digest
         id: build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: .
           push: true
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 23c1bdbc16..db899ba386 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -48,7 +48,7 @@ jobs:
           password: ${{ secrets.DOCKER_PASSWORD }}
       - name: Build and Push to Docker Hub
         id: build
-        uses: docker/build-push-action@v5
+        uses: docker/build-push-action@v6
         with:
           context: .
           push: true

From ef205fb60ece6a4e7bd985b62692afc299ce39ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=AC=E3=82=8B=E3=81=8D=E3=82=83=E3=81=A3=E3=81=A8?=
 <nullnyat@nca10.moe>
Date: Sat, 22 Jun 2024 12:46:30 +0900
Subject: [PATCH 045/206] =?UTF-8?q?enhance(frontend):=20WidgetInstanceInfo?=
 =?UTF-8?q?.vue=20=E3=81=A8=20WidgetProfile.vue=20=E3=81=AE=E3=82=B9?=
 =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=AB=E8=AA=BF=E6=95=B4=20(#14028)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 🎨 WidgetInstanceInfo.vue and WidgetProfile.vue

* 🎨 WidgetInstanceInfo.vue and WidgetProfile.vue

* 🎨 WidgetInstanceInfo.vue and WidgetProfile.vue

* 🎨 WidgetInstanceInfo.vue and WidgetProfile.vue

* 🎨 home.vue
---
 packages/frontend/src/pages/user/home.vue            | 5 +++--
 packages/frontend/src/widgets/WidgetInstanceInfo.vue | 7 +++++--
 packages/frontend/src/widgets/WidgetProfile.vue      | 7 +++++--
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index d0be973552..834d799072 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -392,11 +392,12 @@ onUnmounted(() => {
 
 						> .name {
 							display: block;
-							margin: 0;
+							margin: -10px;
+							padding: 10px;
 							line-height: 32px;
 							font-weight: bold;
 							font-size: 1.8em;
-							text-shadow: 0 0 8px #000;
+							filter: drop-shadow(0 0 4px #000);
 						}
 
 						> .bottom {
diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
index 25d824c8ae..5d8beaf9a9 100644
--- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
@@ -81,16 +81,19 @@ defineExpose<WidgetComponentExpose>({
 .body {
 	text-overflow: ellipsis;
 	overflow: clip;
+	margin-left: -10px;
+	padding: 10px;
 }
 
 .name {
 	color: #fff;
-	filter: drop-shadow(0 0 4px #000);
+	filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
 	font-weight: bold;
 }
 
 .host {
 	color: #fff;
-	filter: drop-shadow(0 0 4px #000);
+	filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
+
 }
 </style>
diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue
index a5578d4de6..ae39098305 100644
--- a/packages/frontend/src/widgets/WidgetProfile.vue
+++ b/packages/frontend/src/widgets/WidgetProfile.vue
@@ -82,16 +82,19 @@ defineExpose<WidgetComponentExpose>({
 .body {
 	text-overflow: ellipsis;
 	overflow: clip;
+	margin-left: -10px;
+	padding: 10px;
 }
 
 .name {
 	color: #fff;
-	filter: drop-shadow(0 0 4px #000);
+	filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
 	font-weight: bold;
 }
 
 .username {
 	color: #fff;
-	filter: drop-shadow(0 0 4px #000);
+	filter: drop-shadow(0 0 4px #000) drop-shadow(0 0 0.1px rgba(0, 0, 0, 0.5));
+	font-weight: normal;
 }
 </style>

From ac12ab8629f0a0172250f949a98ee1efb1d0890d Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sat, 22 Jun 2024 12:51:02 +0900
Subject: [PATCH 046/206] =?UTF-8?q?fix(backend):=20=E3=83=95=E3=82=A3?=
 =?UTF-8?q?=E3=83=BC=E3=83=89=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E3=81=AE?=
 =?UTF-8?q?MFM=E3=81=AFHTML=E3=81=AB=E3=83=AC=E3=83=B3=E3=83=80=E3=83=BC?=
 =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=8B=E3=82=89=E8=BF=94=E3=81=99=20(#1400?=
 =?UTF-8?q?6)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): フィードのノートのMFMはHTMLにレンダーしてから返す (test wip)

* chore: beforeEachを使う?

* fix: プレーンテキストにフォールバックしてMFMが含まれていないか調べる方針を実装

* fix: application/jsonだとパースされるのでその作用をキャンセル

* build: fix lint error

* docs: update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                   |  1 +
 packages/backend/src/server/web/FeedService.ts |  6 +++++-
 packages/backend/test/e2e/fetch-resource.ts    | 17 +++++++++++++++++
 packages/backend/test/utils.ts                 |  5 +++--
 4 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c5eb698385..ab9f5f8000 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
+- Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
 - Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
 - Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
 - Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts
index 10e3ed2682..9d810ddc84 100644
--- a/packages/backend/src/server/web/FeedService.ts
+++ b/packages/backend/src/server/web/FeedService.ts
@@ -14,6 +14,8 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
+import { MfmService } from "@/core/MfmService.js";
+import { parse as mfmParse } from 'mfm-js';
 
 @Injectable()
 export class FeedService {
@@ -33,6 +35,7 @@ export class FeedService {
 		private userEntityService: UserEntityService,
 		private driveFileEntityService: DriveFileEntityService,
 		private idService: IdService,
+		private mfmService: MfmService,
 	) {
 	}
 
@@ -76,13 +79,14 @@ export class FeedService {
 				id: In(note.fileIds),
 			}) : [];
 			const file = files.find(file => file.type.startsWith('image/'));
+			const text = note.text;
 
 			feed.addItem({
 				title: `New note by ${author.name}`,
 				link: `${this.config.url}/notes/${note.id}`,
 				date: this.idService.parse(note.id).date,
 				description: note.cw ?? undefined,
-				content: note.text ?? undefined,
+				content: text ? this.mfmService.toHtml(mfmParse(text), JSON.parse(note.mentionedRemoteUsers)) ?? undefined : undefined,
 				image: file ? this.driveFileEntityService.getPublicUrl(file) : undefined,
 			});
 		}
diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts
index 4851ed14be..7efd688ec2 100644
--- a/packages/backend/test/e2e/fetch-resource.ts
+++ b/packages/backend/test/e2e/fetch-resource.ts
@@ -153,6 +153,23 @@ describe('Webリソース', () => {
 			path: path('nonexisting'),
 			status: 404,
 		}));
+
+		describe(' has entry such ', () => {
+			beforeEach(() => {
+				post(alice, { text: "**a**" })
+			});
+
+			test('MFMを含まない。', async () => {
+				const content = await simpleGet(path(alice.username), "*/*", undefined, res => res.text());
+				const _body: unknown = content.body;
+				// JSONフィードのときは改めて文字列化する
+				const body: string = typeof (_body) === "object" ? JSON.stringify(_body) : _body as string;
+
+				if (body.includes("**a**")) {
+					throw new Error("MFM shouldn't be included");
+				}
+			});
+		})
 	});
 
 	describe.each([{ path: '/api/foo' }])('$path', ({ path }) => {
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index 86814fffe0..aad4ab37c9 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -17,6 +17,7 @@ import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/val
 import { entities } from '../src/postgres.js';
 import { loadConfig } from '../src/config.js';
 import type * as misskey from 'misskey-js';
+import { type Response } from 'node-fetch';
 
 export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
 
@@ -454,7 +455,7 @@ export type SimpleGetResponse = {
 	type: string | null,
 	location: string | null
 };
-export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined): Promise<SimpleGetResponse> => {
+export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined, bodyExtractor: (res: Response) => Promise<string | null> = _ => Promise.resolve(null)): Promise<SimpleGetResponse> => {
 	const res = await relativeFetch(path, {
 		headers: {
 			Accept: accept,
@@ -482,7 +483,7 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde
 	const body =
 		jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() :
 		htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) :
-		null;
+		await bodyExtractor(res);
 
 	return {
 		status: res.status,

From b50eb511b0cf6fb05d37c3370726f940c1438a99 Mon Sep 17 00:00:00 2001
From: yupix <yupi0982@outlook.jp>
Date: Sat, 22 Jun 2024 14:52:27 +0900
Subject: [PATCH 047/206] =?UTF-8?q?refactor:=20api/*/update=E7=B3=BB?=
 =?UTF-8?q?=E3=81=AE=E5=BF=85=E9=A0=88=E3=82=AD=E3=83=BC=E3=82=92=E6=9C=80?=
 =?UTF-8?q?=E4=BD=8E=E9=99=90=E3=81=AB=20(#13824)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: clips/updateの必須キーをclipIdのみに

* refactor: admin/roles/update の必須キーをroleIdのみに

* feat: pages/update の必須キーをpageIdのみに

* refactor: gallery/posts/update の必須キーをpostidのみに

* feat: misskey-jsの型を更新

* feat: i/webhooks/updateの必須キーをwebhookIdのみに

* feat: admin/ad/updateの必須キーをidのみに

* feat: misskey-jsの型を更新

* chore: update CHANGELOG.md

* docs: update CHANGELOG.md

* fix: secretが更新できなくなる場合がある

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>

* Update packages/backend/src/server/api/endpoints/gallery/posts/update.ts

---------

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  6 ++
 .../server/api/endpoints/admin/ad/update.ts   |  6 +-
 .../api/endpoints/admin/roles/update.ts       | 14 ----
 .../src/server/api/endpoints/clips/update.ts  |  4 +-
 .../api/endpoints/gallery/posts/update.ts     | 24 ++++---
 .../server/api/endpoints/i/webhooks/update.ts |  6 +-
 .../src/server/api/endpoints/pages/update.ts  | 23 ++----
 packages/misskey-js/src/autogen/types.ts      | 71 +++++++++----------
 8 files changed, 70 insertions(+), 84 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab9f5f8000..c1af63ad23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,12 @@
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
 - Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
 - Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
+- Enhance: エンドポイント`clips/update`の必須項目を`clipId`のみに
+- Enhance: エンドポイント`admin/roles/update`の必須項目を`roleId`のみに
+- Enhance: エンドポイント`pages/update`の必須項目を`pageId`のみに
+- Enhance: エンドポイント`gallery/posts/update`の必須項目を`postId`のみに
+- Enhance: エンドポイント`i/webhook/update`の必須項目を`webhookId`のみに
+- Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに
 - Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
 - Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
 
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
index 62358457ff..4e3d731aca 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
@@ -40,7 +40,7 @@ export const paramDef = {
 		startsAt: { type: 'integer' },
 		dayOfWeek: { type: 'integer' },
 	},
-	required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'dayOfWeek'],
+	required: ['id'],
 } as const;
 
 @Injectable()
@@ -63,8 +63,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				ratio: ps.ratio,
 				memo: ps.memo,
 				imageUrl: ps.imageUrl,
-				expiresAt: new Date(ps.expiresAt),
-				startsAt: new Date(ps.startsAt),
+				expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : undefined,
+				startsAt: ps.startsAt ? new Date(ps.startsAt) : undefined,
 				dayOfWeek: ps.dayOfWeek,
 			});
 
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
index 5242e0be2f..465ad7aaaf 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -6,7 +6,6 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { RolesRepository } from '@/models/_.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '@/server/api/error.js';
 import { RoleService } from '@/core/RoleService.js';
@@ -50,19 +49,6 @@ export const paramDef = {
 	},
 	required: [
 		'roleId',
-		'name',
-		'description',
-		'color',
-		'iconUrl',
-		'target',
-		'condFormula',
-		'isPublic',
-		'isModerator',
-		'isAdministrator',
-		'asBadge',
-		'canEditMembersByModerator',
-		'displayOrder',
-		'policies',
 	],
 } as const;
 
diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts
index 3b44ba81b3..603a3ccf3d 100644
--- a/packages/backend/src/server/api/endpoints/clips/update.ts
+++ b/packages/backend/src/server/api/endpoints/clips/update.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Inject, Injectable } from '@nestjs/common';
+import { Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
 import { ClipService } from '@/core/ClipService.js';
@@ -41,7 +41,7 @@ export const paramDef = {
 		isPublic: { type: 'boolean' },
 		description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
 	},
-	required: ['clipId', 'name'],
+	required: ['clipId'],
 } as const;
 
 @Injectable()
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
index 2f977784ec..5243ee9603 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts
@@ -47,7 +47,7 @@ export const paramDef = {
 		} },
 		isSensitive: { type: 'boolean', default: false },
 	},
-	required: ['postId', 'title', 'fileIds'],
+	required: ['postId'],
 } as const;
 
 @Injectable()
@@ -62,15 +62,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private galleryPostEntityService: GalleryPostEntityService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const files = (await Promise.all(ps.fileIds.map(fileId =>
-				this.driveFilesRepository.findOneBy({
-					id: fileId,
-					userId: me.id,
-				}),
-			))).filter(x => x != null);
+			let files: Array<MiDriveFile> | undefined;
 
-			if (files.length === 0) {
-				throw new Error();
+			if (ps.fileIds) {
+				files = (await Promise.all(ps.fileIds.map(fileId =>
+					this.driveFilesRepository.findOneBy({
+						id: fileId,
+						userId: me.id,
+					}),
+				))).filter(x => x != null);
+
+				if (files.length === 0) {
+					throw new Error();
+				}
 			}
 
 			await this.galleryPostsRepository.update({
@@ -81,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				title: ps.title,
 				description: ps.description,
 				isSensitive: ps.isSensitive,
-				fileIds: files.map(file => file.id),
+				fileIds: files ? files.map(file => file.id) : undefined,
 			});
 
 			const post = await this.galleryPostsRepository.findOneByOrFail({ id: ps.postId });
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts
index 6e380d76f8..07a25bd82a 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts
@@ -34,13 +34,13 @@ export const paramDef = {
 		webhookId: { type: 'string', format: 'misskey:id' },
 		name: { type: 'string', minLength: 1, maxLength: 100 },
 		url: { type: 'string', minLength: 1, maxLength: 1024 },
-		secret: { type: 'string', maxLength: 1024, default: '' },
+		secret: { type: 'string', nullable: true, maxLength: 1024 },
 		on: { type: 'array', items: {
 			type: 'string', enum: webhookEventTypes,
 		} },
 		active: { type: 'boolean' },
 	},
-	required: ['webhookId', 'name', 'url', 'on', 'active'],
+	required: ['webhookId'],
 } as const;
 
 // TODO: ロジックをサービスに切り出す
@@ -66,7 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			await this.webhooksRepository.update(webhook.id, {
 				name: ps.name,
 				url: ps.url,
-				secret: ps.secret,
+				secret: ps.secret === null ? '' : ps.secret,
 				on: ps.on,
 				active: ps.active,
 			});
diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts
index b8e5e70a25..f11bbbcb1a 100644
--- a/packages/backend/src/server/api/endpoints/pages/update.ts
+++ b/packages/backend/src/server/api/endpoints/pages/update.ts
@@ -70,7 +70,7 @@ export const paramDef = {
 		alignCenter: { type: 'boolean' },
 		hideTitleWhenPinned: { type: 'boolean' },
 	},
-	required: ['pageId', 'title', 'name', 'content', 'variables', 'script'],
+	required: ['pageId'],
 } as const;
 
 @Injectable()
@@ -91,9 +91,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.accessDenied);
 			}
 
-			let eyeCatchingImage = null;
 			if (ps.eyeCatchingImageId != null) {
-				eyeCatchingImage = await this.driveFilesRepository.findOneBy({
+				const eyeCatchingImage = await this.driveFilesRepository.findOneBy({
 					id: ps.eyeCatchingImageId,
 					userId: me.id,
 				});
@@ -116,23 +115,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			await this.pagesRepository.update(page.id, {
 				updatedAt: new Date(),
 				title: ps.title,
-				// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-				name: ps.name === undefined ? page.name : ps.name,
+				name: ps.name,
 				summary: ps.summary === undefined ? page.summary : ps.summary,
 				content: ps.content,
 				variables: ps.variables,
 				script: ps.script,
-				// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-				alignCenter: ps.alignCenter === undefined ? page.alignCenter : ps.alignCenter,
-				// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-				hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned,
-				// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-				font: ps.font === undefined ? page.font : ps.font,
-				eyeCatchingImageId: ps.eyeCatchingImageId === null
-					? null
-					: ps.eyeCatchingImageId === undefined
-						? page.eyeCatchingImageId
-						: eyeCatchingImage!.id,
+				alignCenter: ps.alignCenter,
+				hideTitleWhenPinned: ps.hideTitleWhenPinned,
+				font: ps.font,
+				eyeCatchingImageId: ps.eyeCatchingImageId,
 			});
 		});
 	}
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index bdcc1dfd77..72aca4dee2 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -5881,15 +5881,15 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           id: string;
-          memo: string;
-          url: string;
-          imageUrl: string;
-          place: string;
-          priority: string;
-          ratio: number;
-          expiresAt: number;
-          startsAt: number;
-          dayOfWeek: number;
+          memo?: string;
+          url?: string;
+          imageUrl?: string;
+          place?: string;
+          priority?: string;
+          ratio?: number;
+          expiresAt?: number;
+          startsAt?: number;
+          dayOfWeek?: number;
         };
       };
     };
@@ -9744,21 +9744,21 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           roleId: string;
-          name: string;
-          description: string;
-          color: string | null;
-          iconUrl: string | null;
+          name?: string;
+          description?: string;
+          color?: string | null;
+          iconUrl?: string | null;
           /** @enum {string} */
-          target: 'manual' | 'conditional';
-          condFormula: Record<string, never>;
-          isPublic: boolean;
-          isModerator: boolean;
-          isAdministrator: boolean;
+          target?: 'manual' | 'conditional';
+          condFormula?: Record<string, never>;
+          isPublic?: boolean;
+          isModerator?: boolean;
+          isAdministrator?: boolean;
           isExplorable?: boolean;
-          asBadge: boolean;
-          canEditMembersByModerator: boolean;
-          displayOrder: number;
-          policies: Record<string, never>;
+          asBadge?: boolean;
+          canEditMembersByModerator?: boolean;
+          displayOrder?: number;
+          policies?: Record<string, never>;
         };
       };
     };
@@ -13400,7 +13400,7 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           clipId: string;
-          name: string;
+          name?: string;
           isPublic?: boolean;
           description?: string | null;
         };
@@ -16247,9 +16247,9 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           postId: string;
-          title: string;
+          title?: string;
           description?: string | null;
-          fileIds: string[];
+          fileIds?: string[];
           /** @default false */
           isSensitive?: boolean;
         };
@@ -20030,12 +20030,11 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           webhookId: string;
-          name: string;
-          url: string;
-          /** @default */
-          secret?: string;
-          on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
-          active: boolean;
+          name?: string;
+          url?: string;
+          secret?: string | null;
+          on?: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
+          active?: boolean;
         };
       };
     };
@@ -23404,16 +23403,16 @@ export type operations = {
         'application/json': {
           /** Format: misskey:id */
           pageId: string;
-          title: string;
-          name: string;
+          title?: string;
+          name?: string;
           summary?: string | null;
-          content: {
+          content?: {
               [key: string]: unknown;
             }[];
-          variables: {
+          variables?: {
               [key: string]: unknown;
             }[];
-          script: string;
+          script?: string;
           /** Format: misskey:id */
           eyeCatchingImageId?: string | null;
           /** @enum {string} */

From faeab96e01c7c7be5dfc85716b4a0b05b93f50ab Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sat, 22 Jun 2024 14:55:24 +0900
Subject: [PATCH 048/206] ci: add quote (#13990)

---
 .github/workflows/storybook.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml
index c52883ffdd..daa76509c8 100644
--- a/.github/workflows/storybook.yml
+++ b/.github/workflows/storybook.yml
@@ -88,7 +88,7 @@ jobs:
         if [ "$BRANCH" = "misskey-dev:$HEAD_REF" ]; then
           BRANCH="$HEAD_REF"
         fi
-        pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name $BRANCH $(echo "$CHROMATIC_PARAMETER")
+        pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name "$BRANCH" $(echo "$CHROMATIC_PARAMETER")
       env:
         HEAD_REF: ${{ github.event.pull_request.head.ref }}
         CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

From bf403aa656627fc4b29aed329aa044d42a791acf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 22 Jun 2024 15:35:54 +0900
Subject: [PATCH 049/206] =?UTF-8?q?fix(frontend):=20=E3=83=99=E3=83=BC?=
 =?UTF-8?q?=E3=82=B9=E3=83=AD=E3=83=BC=E3=83=AB=E3=82=92=E7=B7=A8=E9=9B=86?=
 =?UTF-8?q?=E3=81=97=E3=81=A6=E3=82=82UI=E4=B8=8A=E3=81=A7=E3=81=AF?=
 =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=8C=E5=8F=8D=E6=98=A0=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#13995)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): ベースロールを変更してもUI上では変更が反映されない問題を修正

* Update CHANGELOG.md
---
 CHANGELOG.md                                | 1 +
 packages/frontend/src/pages/admin/roles.vue | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1af63ad23..a913e42500 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
+- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正 
 
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 9753d9f6cb..50323e3de5 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -243,7 +243,7 @@ import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { instance } from '@/instance.js';
+import { instance, fetchInstance } from '@/instance.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import { ROLE_POLICIES } from '@/const.js';
 import { useRouter } from '@/router/supplier.js';
@@ -267,6 +267,7 @@ async function updateBaseRole() {
 	await os.apiWithDialog('admin/roles/update-default-policies', {
 		policies,
 	});
+	fetchInstance(true);
 }
 
 function create() {

From 7c22a64b8c505f6e6c9da0fec16902fcd9af773f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 22 Jun 2024 16:52:27 +0900
Subject: [PATCH 050/206] =?UTF-8?q?fix(backend):=20=E8=87=AA=E5=88=86?=
 =?UTF-8?q?=E4=BB=A5=E5=A4=96=E3=81=AE=E3=82=AF=E3=83=AA=E3=83=83=E3=83=97?=
 =?UTF-8?q?=E5=86=85=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E5=80=8B=E6=95=B0?=
 =?UTF-8?q?=E3=81=8C=E8=A6=8B=E3=81=88=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#14065)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): 自分以外のクリップ内のノート個数が見えることがあるのを修正

* Update Changelog

* fix
---
 CHANGELOG.md                                            | 1 +
 packages/backend/src/core/entities/ClipEntityService.ts | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a913e42500..e4c4cfe1f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 - Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに
 - Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
 - Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
+- Fix: 自分以外のクリップ内のノート個数が見えることがあるのを修正
 
 ## 2024.5.0
 
diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts
index 3855a28436..d915645906 100644
--- a/packages/backend/src/core/entities/ClipEntityService.ts
+++ b/packages/backend/src/core/entities/ClipEntityService.ts
@@ -53,7 +53,7 @@ export class ClipEntityService {
 			isPublic: clip.isPublic,
 			favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }),
 			isFavorited: meId ? await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: meId } }) : undefined,
-			notesCount: meId ? await this.clipNotesRepository.countBy({ clipId: clip.id }) : undefined,
+			notesCount: (meId === clip.userId) ? await this.clipNotesRepository.countBy({ clipId: clip.id }) : undefined,
 		});
 	}
 

From 9368eb3038d5f655b924d53800daaa7e54e08c47 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sat, 22 Jun 2024 19:40:55 +0900
Subject: [PATCH 051/206] refactor: say bye to the weird groupBy friends
 (#13975)

* refactor(frontend): say bye to the weird groupBy friends

* refactor(backend): say bye to the weird groupBy friends
---
 packages/backend/src/misc/prelude/array.ts | 38 ----------------------
 packages/frontend/src/scripts/array.ts     | 38 ----------------------
 2 files changed, 76 deletions(-)

diff --git a/packages/backend/src/misc/prelude/array.ts b/packages/backend/src/misc/prelude/array.ts
index dbfe1fff18..f741a0c913 100644
--- a/packages/backend/src/misc/prelude/array.ts
+++ b/packages/backend/src/misc/prelude/array.ts
@@ -65,44 +65,6 @@ export function maximum(xs: number[]): number {
 	return Math.max(...xs);
 }
 
-/**
- * Splits an array based on the equivalence relation.
- * The concatenation of the result is equal to the argument.
- */
-export function groupBy<T>(f: EndoRelation<T>, xs: T[]): T[][] {
-	const groups = [] as T[][];
-	for (const x of xs) {
-		const lastGroup = groups.at(-1);
-		if (lastGroup !== undefined && f(lastGroup[0], x)) {
-			lastGroup.push(x);
-		} else {
-			groups.push([x]);
-		}
-	}
-	return groups;
-}
-
-/**
- * Splits an array based on the equivalence relation induced by the function.
- * The concatenation of the result is equal to the argument.
- */
-export function groupOn<T, S>(f: (x: T) => S, xs: T[]): T[][] {
-	return groupBy((a, b) => f(a) === f(b), xs);
-}
-
-export function groupByX<T>(collections: T[], keySelector: (x: T) => string) {
-	return collections.reduce((obj: Record<string, T[]>, item: T) => {
-		const key = keySelector(item);
-		if (!Object.prototype.hasOwnProperty.call(obj, key)) {
-			obj[key] = [];
-		}
-
-		obj[key].push(item);
-
-		return obj;
-	}, {});
-}
-
 /**
  * Compare two arrays by lexicographical order
  */
diff --git a/packages/frontend/src/scripts/array.ts b/packages/frontend/src/scripts/array.ts
index b3d76e149f..f2feb29dfc 100644
--- a/packages/frontend/src/scripts/array.ts
+++ b/packages/frontend/src/scripts/array.ts
@@ -77,44 +77,6 @@ export function maximum(xs: number[]): number {
 	return Math.max(...xs);
 }
 
-/**
- * Splits an array based on the equivalence relation.
- * The concatenation of the result is equal to the argument.
- */
-export function groupBy<T>(f: EndoRelation<T>, xs: T[]): T[][] {
-	const groups = [] as T[][];
-	for (const x of xs) {
-		const lastGroup = groups.at(-1);
-		if (lastGroup !== undefined && f(lastGroup[0], x)) {
-			lastGroup.push(x);
-		} else {
-			groups.push([x]);
-		}
-	}
-	return groups;
-}
-
-/**
- * Splits an array based on the equivalence relation induced by the function.
- * The concatenation of the result is equal to the argument.
- */
-export function groupOn<T, S>(f: (x: T) => S, xs: T[]): T[][] {
-	return groupBy((a, b) => f(a) === f(b), xs);
-}
-
-export function groupByX<T>(collections: T[], keySelector: (x: T) => string) {
-	return collections.reduce((obj: Record<string, T[]>, item: T) => {
-		const key = keySelector(item);
-		if (typeof obj[key] === 'undefined') {
-			obj[key] = [];
-		}
-
-		obj[key].push(item);
-
-		return obj;
-	}, {});
-}
-
 /**
  * Compare two arrays by lexicographical order
  */

From b8b4dc50384aa3f146d90b10d7f13f87a4a2232c Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 22 Jun 2024 19:45:08 +0900
Subject: [PATCH 052/206] build: install pnpm with corepack on docker build
 (#13926)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* build: install pnpm with corepack on build

* docs(changelog): Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
---
 CHANGELOG.md | 1 +
 Dockerfile   | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4c4cfe1f0..ca74d71719 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 ### General
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
+- Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 
 ### Client
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
diff --git a/Dockerfile b/Dockerfile
index 9fc2d611cd..d6ca6b8cdf 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -82,6 +82,10 @@ RUN apt-get update \
 USER misskey
 WORKDIR /misskey
 
+# add package.json to add pnpm
+COPY --chown=misskey:misskey ./package.json ./package.json
+RUN corepack install
+
 COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
 COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
 COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules

From 00b213373bd3f60a6144344ab8cbda08418ca1e4 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Sat, 22 Jun 2024 19:46:29 +0900
Subject: [PATCH 053/206] Remove @types/node-fetch (#13948)

---
 packages/backend/package.json |  1 -
 pnpm-lock.yaml                | 11 -----------
 2 files changed, 12 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 15134b1ca8..0467ab0bee 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -207,7 +207,6 @@
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
 		"@types/node": "20.12.7",
-		"@types/node-fetch": "3.0.3",
 		"@types/nodemailer": "6.4.15",
 		"@types/oauth": "0.9.4",
 		"@types/oauth2orize": "1.11.5",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1281f7eefe..09df15853b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -586,9 +586,6 @@ importers:
       '@types/node':
         specifier: 20.12.7
         version: 20.12.7
-      '@types/node-fetch':
-        specifier: 3.0.3
-        version: 3.0.3
       '@types/nodemailer':
         specifier: 6.4.15
         version: 6.4.15
@@ -4625,10 +4622,6 @@ packages:
   '@types/node-fetch@2.6.4':
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
 
-  '@types/node-fetch@3.0.3':
-    resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==}
-    deprecated: This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.
-
   '@types/node@18.17.15':
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
 
@@ -16025,10 +16018,6 @@ snapshots:
       '@types/node': 20.12.7
       form-data: 3.0.1
 
-  '@types/node-fetch@3.0.3':
-    dependencies:
-      node-fetch: 3.3.2
-
   '@types/node@18.17.15': {}
 
   '@types/node@20.11.5':

From 961cb6c5eeb7745dc156327d2041241b70098b70 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 22 Jun 2024 19:49:38 +0900
Subject: [PATCH 054/206] fix(backend): fix creating reactions bugs (#13901)

* fix(backend): add fallback for empty string when creating reaction

* fix(backend): prohibit reactions to Renote

* test(backend): add some tests for `notes/reactions/create` endpoint

* Update CHANGELOG.md

* lint

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  2 +
 packages/backend/src/core/ReactionService.ts  |  8 ++-
 .../api/endpoints/notes/reactions/create.ts   |  7 +++
 packages/backend/test/e2e/endpoints.ts        | 61 +++++++++++++++++++
 4 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ca74d71719..354bbd20fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@
 - Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
 - Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
 - Fix: 自分以外のクリップ内のノート個数が見えることがあるのを修正
+- Fix: 空文字列のリアクションはフォールバックされるように
+- Fix: リノートにリアクションできないように
 
 ## 2024.5.0
 
diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts
index cb0b079df0..64c7b2ed03 100644
--- a/packages/backend/src/core/ReactionService.ts
+++ b/packages/backend/src/core/ReactionService.ts
@@ -29,6 +29,7 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 import { RoleService } from '@/core/RoleService.js';
 import { FeaturedService } from '@/core/FeaturedService.js';
 import { trackPromise } from '@/misc/promise-tracker.js';
+import { isQuote, isRenote } from '@/misc/is-renote.js';
 
 const FALLBACK = '\u2764';
 const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16;
@@ -117,11 +118,16 @@ export class ReactionService {
 			throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.');
 		}
 
+		// Check if note is Renote
+		if (isRenote(note) && !isQuote(note)) {
+			throw new IdentifiableError('12c35529-3c79-4327-b1cc-e2cf63a71925', 'You cannot react to Renote.');
+		}
+
 		let reaction = _reaction ?? FALLBACK;
 
 		if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && (user.host != null))) {
 			reaction = '\u2764';
-		} else if (_reaction) {
+		} else if (_reaction != null) {
 			const custom = reaction.match(isCustomEmojiRegexp);
 			if (custom) {
 				const reacterHost = this.utilityService.toPunyNullable(user.host);
diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts
index b9899608bf..0f0dcca605 100644
--- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts
@@ -36,6 +36,12 @@ export const meta = {
 			code: 'YOU_HAVE_BEEN_BLOCKED',
 			id: '20ef5475-9f38-4e4c-bd33-de6d979498ec',
 		},
+
+		cannotReactToRenote: {
+			message: 'You cannot react to Renote.',
+			code: 'CANNOT_REACT_TO_RENOTE',
+			id: 'eaccdc08-ddef-43fe-908f-d108faad57f5',
+		},
 	},
 } as const;
 
@@ -62,6 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			await this.reactionService.create(me, note, ps.reaction).catch(err => {
 				if (err.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted);
 				if (err.id === 'e70412a4-7197-4726-8e74-f3e0deb92aa7') throw new ApiError(meta.errors.youHaveBeenBlocked);
+				if (err.id === '12c35529-3c79-4327-b1cc-e2cf63a71925') throw new ApiError(meta.errors.cannotReactToRenote);
 				throw err;
 			});
 			return;
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index bc89dc37f4..de5e8ba95e 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -266,6 +266,67 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.status, 400);
 		});
 
+		test('リノートにリアクションできない', async () => {
+			const bobNote = await post(bob, { text: 'hi' });
+			const bobRenote = await post(bob, { renoteId: bobNote.id });
+
+			const res = await api('notes/reactions/create', {
+				noteId: bobRenote.id,
+				reaction: '🚀',
+			}, alice);
+
+			assert.strictEqual(res.status, 400);
+			assert.strictEqual(res.body.error.code, 'CANNOT_REACT_TO_RENOTE');
+		});
+
+		test('引用にリアクションできる', async () => {
+			const bobNote = await post(bob, { text: 'hi' });
+			const bobRenote = await post(bob, { text: 'hi again', renoteId: bobNote.id });
+
+			const res = await api('notes/reactions/create', {
+				noteId: bobRenote.id,
+				reaction: '🚀',
+			}, alice);
+
+			assert.strictEqual(res.status, 204);
+		});
+
+		test('空文字列のリアクションは\u2764にフォールバックされる', async () => {
+			const bobNote = await post(bob, { text: 'hi' });
+
+			const res = await api('notes/reactions/create', {
+				noteId: bobNote.id,
+				reaction: '',
+			}, alice);
+
+			assert.strictEqual(res.status, 204);
+
+			const reaction = await api('notes/reactions', {
+				noteId: bobNote.id,
+			});
+
+			assert.strictEqual(reaction.body.length, 1);
+			assert.strictEqual(reaction.body[0].type, '\u2764');
+		});
+
+		test('絵文字ではない文字列のリアクションは\u2764にフォールバックされる', async () => {
+			const bobNote = await post(bob, { text: 'hi' });
+
+			const res = await api('notes/reactions/create', {
+				noteId: bobNote.id,
+				reaction: 'Hello!',
+			}, alice);
+
+			assert.strictEqual(res.status, 204);
+
+			const reaction = await api('notes/reactions', {
+				noteId: bobNote.id,
+			});
+
+			assert.strictEqual(reaction.body.length, 1);
+			assert.strictEqual(reaction.body[0].type, '\u2764');
+		});
+
 		test('空のパラメータで怒られる', async () => {
 			// @ts-expect-error param must not be empty
 			const res = await api('notes/reactions/create', {}, alice);

From 2acbec6891a94dc9291b3e0c3d2e24d13367ba1c Mon Sep 17 00:00:00 2001
From: Ibuki Sugiyama <main@fuwa.dev>
Date: Sat, 22 Jun 2024 19:50:32 +0900
Subject: [PATCH 055/206] enhance: update datasaver switch titles (#12834)

---
 locales/ja-JP.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3ac1ce82a3..0d89d33abe 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2599,16 +2599,16 @@ _externalResourceInstaller:
 
 _dataSaver:
   _media:
-    title: "メディアの読み込み"
+    title: "メディアの読み込みを無効化"
     description: "画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。"
   _avatar:
-    title: "アイコン画像"
+    title: "アイコン画像のアニメーションを無効化"
     description: "アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。"
   _urlPreview:
-    title: "URLプレビューのサムネイル"
+    title: "URLプレビューのサムネイルを非表示"
     description: "URLプレビューのサムネイル画像が読み込まれなくなります。"
   _code:
-    title: "コードハイライト"
+    title: "コードハイライトを非表示"
     description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。"
 
 _hemisphere:

From b269c431686b04fbfc256c5a1fe621bc70f7ea06 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Sun, 23 Jun 2024 01:00:12 +0900
Subject: [PATCH 056/206] Fix type annotations (#14071)

---
 packages/frontend/src/filters/user.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/filters/user.ts b/packages/frontend/src/filters/user.ts
index b713d41789..a87766764d 100644
--- a/packages/frontend/src/filters/user.ts
+++ b/packages/frontend/src/filters/user.ts
@@ -6,7 +6,7 @@
 import * as Misskey from 'misskey-js';
 import { url } from '@/config.js';
 
-export const acct = (user: misskey.Acct) => {
+export const acct = (user: Misskey.Acct) => {
 	return Misskey.acct.toString(user);
 };
 
@@ -14,6 +14,6 @@ export const userName = (user: Misskey.entities.User) => {
 	return user.name || user.username;
 };
 
-export const userPage = (user: misskey.Acct, path?, absolute = false) => {
+export const userPage = (user: Misskey.Acct, path?: string, absolute = false) => {
 	return `${absolute ? url : ''}/@${acct(user)}${(path ? `/${path}` : '')}`;
 };

From b95a0457a94b135df9b9511ef77558d1a81962f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 23 Jun 2024 19:04:01 +0900
Subject: [PATCH 057/206] fix(frontend): run `pnpm build-assets` (#14077)

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

diff --git a/locales/index.d.ts b/locales/index.d.ts
index acdc1fc421..ebd980ed85 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -9761,7 +9761,7 @@ export interface Locale extends ILocale {
     "_dataSaver": {
         "_media": {
             /**
-             * メディアの読み込み
+             * メディアの読み込みを無効化
              */
             "title": string;
             /**
@@ -9771,7 +9771,7 @@ export interface Locale extends ILocale {
         };
         "_avatar": {
             /**
-             * アイコン画像
+             * アイコン画像のアニメーションを無効化
              */
             "title": string;
             /**
@@ -9781,7 +9781,7 @@ export interface Locale extends ILocale {
         };
         "_urlPreview": {
             /**
-             * URLプレビューのサムネイル
+             * URLプレビューのサムネイルを非表示
              */
             "title": string;
             /**
@@ -9791,7 +9791,7 @@ export interface Locale extends ILocale {
         };
         "_code": {
             /**
-             * コードハイライト
+             * コードハイライトを非表示
              */
             "title": string;
             /**

From 634764e1a6e06ea9c117b720d58685fe99cae81a Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Mon, 24 Jun 2024 21:32:12 +0900
Subject: [PATCH 058/206] refactor(frontend): Remove unused directives (#14085)

---
 packages/frontend/src/components/MkCaptcha.vue | 1 -
 packages/frontend/src/i18n.ts                  | 1 -
 2 files changed, 2 deletions(-)

diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue
index c64bb47e77..c5b6e0caed 100644
--- a/packages/frontend/src/components/MkCaptcha.vue
+++ b/packages/frontend/src/components/MkCaptcha.vue
@@ -104,7 +104,6 @@ async function requestRender() {
 		});
 	} else if (props.provider === 'mcaptcha' && props.instanceUrl && props.sitekey) {
 		const { default: Widget } = await import('@mcaptcha/vanilla-glue');
-		// @ts-expect-error avoid typecheck error
 		new Widget({
 			siteKey: {
 				instanceUrl: new URL(props.instanceUrl),
diff --git a/packages/frontend/src/i18n.ts b/packages/frontend/src/i18n.ts
index cc9faddb20..10d6adbcd0 100644
--- a/packages/frontend/src/i18n.ts
+++ b/packages/frontend/src/i18n.ts
@@ -11,6 +11,5 @@ import { I18n } from '@/scripts/i18n.js';
 export const i18n = markRaw(new I18n<Locale>(locale));
 
 export function updateI18n(newLocale: Locale) {
-	// @ts-expect-error -- private field
 	i18n.locale = newLocale;
 }

From 1c5d0cf5364ed841bd181b75682503a648a90bd6 Mon Sep 17 00:00:00 2001
From: yupix <yupi0982@outlook.jp>
Date: Wed, 26 Jun 2024 10:25:18 +0900
Subject: [PATCH 059/206] =?UTF-8?q?feat:=20=E3=82=A2=E3=83=B3=E3=83=86?=
 =?UTF-8?q?=E3=83=8A=E3=81=AE=E7=B7=A8=E9=9B=86=E7=94=BB=E9=9D=A2=E3=81=AE?=
 =?UTF-8?q?=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=ABgap=E3=82=92=E8=BF=BD?=
 =?UTF-8?q?=E5=8A=A0=20(#14091)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                       | 1 +
 packages/frontend/src/pages/my-antennas/editor.vue | 6 ++++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 354bbd20fd..290b13ab36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
 - Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正 
+- Fix: アンテナの編集画面のボタンに隙間を追加
 
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 2949bfc02c..02e8f98265 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -41,8 +41,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkSwitch v-model="withFile">{{ i18n.ts.withFileAntenna }}</MkSwitch>
 		</div>
 		<div :class="$style.actions">
-			<MkButton inline primary @click="saveAntenna()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
-			<MkButton v-if="antenna.id != null" inline danger @click="deleteAntenna()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+			<div class="_buttons">
+				<MkButton inline primary @click="saveAntenna()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+				<MkButton v-if="antenna.id != null" inline danger @click="deleteAntenna()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+			</div>
 		</div>
 	</div>
 </MkSpacer>

From 77012f2f2925c93978a3a5844d1adddd330d777b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=AC=E3=82=8B=E3=81=8D=E3=82=83=E3=81=A3=E3=81=A8?=
 <nullnyat@nca10.moe>
Date: Thu, 27 Jun 2024 10:40:46 +0900
Subject: [PATCH 060/206] =?UTF-8?q?fix(frontend):=20=E3=83=86=E3=83=BC?=
 =?UTF-8?q?=E3=83=9E=E3=83=97=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E3=81=8C?=
 =?UTF-8?q?=E8=A6=8B=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#14097)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): テーマプレビューが見れない問題を修正

* fix: MkPreview.vue, preview.vue
---
 .../frontend/src/components/MkPreview.vue     | 150 ++++++++++++++++++
 packages/frontend/src/pages/preview.vue       |  26 +++
 packages/frontend/src/router/definition.ts    |   3 +
 3 files changed, 179 insertions(+)
 create mode 100644 packages/frontend/src/components/MkPreview.vue
 create mode 100644 packages/frontend/src/pages/preview.vue

diff --git a/packages/frontend/src/components/MkPreview.vue b/packages/frontend/src/components/MkPreview.vue
new file mode 100644
index 0000000000..d950d66c6e
--- /dev/null
+++ b/packages/frontend/src/components/MkPreview.vue
@@ -0,0 +1,150 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.preview">
+	<div :class="$style.preview__content1">
+		<MkInput v-model="text">
+			<template #label>Text</template>
+		</MkInput>
+		<MkSwitch v-model="flag" :class="$style.preview__content1__switch_button">
+			<span>Switch is now {{ flag ? 'on' : 'off' }}</span>
+		</MkSwitch>
+		<div :class="$style.preview__content1__input">
+			<MkRadio v-model="radio" value="misskey">Misskey</MkRadio>
+			<MkRadio v-model="radio" value="mastodon">Mastodon</MkRadio>
+			<MkRadio v-model="radio" value="pleroma">Pleroma</MkRadio>
+		</div>
+		<div :class="$style.preview__content1__button">
+		<MkButton inline>This is</MkButton>
+		<MkButton inline primary>the button</MkButton>
+		</div>
+	</div>
+	<div :class="$style.preview__content2" style="pointer-events: none;">
+		<Mfm :text="mfm"/>
+	</div>
+	<div :class="$style.preview__content3">
+		<MkButton inline primary @click="openMenu">Open menu</MkButton>
+		<MkButton inline primary @click="openDialog">Open dialog</MkButton>
+		<MkButton inline primary @click="openForm">Open form</MkButton>
+		<MkButton inline primary @click="openDrive">Open drive</MkButton>
+	</div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
+import MkRadio from '@/components/MkRadio.vue';
+import * as os from '@/os.js';
+import * as config from '@/config.js';
+import { $i } from '@/account.js';
+
+const text = ref('');
+const flag = ref(true);
+const radio = ref('misskey');
+const mfm = ref(`Hello world! This is an @example mention. BTW you are @${$i ? $i.username : 'guest'}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.`);
+
+const openDialog = async () => {
+	await os.alert({
+		type: 'warning',
+		title: 'Oh my Aichan',
+		text: 'Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
+	});
+};
+
+const openForm = async () => {
+	await os.form('Example form', {
+		foo: {
+			type: 'boolean',
+			default: true,
+			label: 'This is a boolean property',
+		},
+		bar: {
+			type: 'number',
+			default: 300,
+			label: 'This is a number property',
+		},
+		baz: {
+			type: 'string',
+			default: 'Misskey makes you happy.',
+			label: 'This is a string property',
+		},
+	});
+};
+
+const openDrive = async () => {
+	await os.selectDriveFile(false);
+};
+
+const selectUser = async () => {
+	await os.selectUser();
+};
+
+const openMenu = async (ev: Event) => {
+	os.popupMenu([{
+		type: 'label',
+		text: 'Fruits',
+	}, {
+		text: 'Create some apples',
+		action: () => {},
+	}, {
+		text: 'Read some oranges',
+		action: () => {},
+	}, {
+		text: 'Update some melons',
+		action: () => {},
+	}, {
+		text: 'Delete some bananas',
+		danger: true,
+		action: () => {},
+	}], ev.currentTarget ?? ev.target);
+};
+</script>
+
+<style lang="scss" module>
+.preview {
+	padding: 16px;
+
+	&__content1 {
+
+		&__switch_button {
+			padding: 16px 0 8px 0;
+		}
+
+		&__input {
+			padding: 8px 0 8px 0;
+
+			div {
+				margin: 0 8px 8px 0;
+			}
+		}
+
+		&__button {
+			padding: 4px 0 8px 0;
+
+			button {
+				margin: 0 8px 8px 0;
+			}
+		}
+	}
+
+	&__content2 {
+		padding: 8px 0 8px 0;
+	}
+
+	&__content3 {
+		padding: 8px 0 8px 0;
+
+		button {
+			margin: 0 8px 8px 0;
+
+		}
+	}
+}
+</style>
diff --git a/packages/frontend/src/pages/preview.vue b/packages/frontend/src/pages/preview.vue
new file mode 100644
index 0000000000..8e07b190aa
--- /dev/null
+++ b/packages/frontend/src/pages/preview.vue
@@ -0,0 +1,26 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div>
+	<MkSample/>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue';
+import MkSample from '@/components/MkPreview.vue';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+
+const headerActions = computed(() => []);
+
+const headerTabs = computed(() => []);
+
+definePageMetadata(computed(() => ({
+	title: i18n.ts.preview,
+	icon: 'ti ti-eye',
+})));
+</script>
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index 8a443f627b..12ab633af1 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -251,6 +251,9 @@ const routes: RouteDef[] = [{
 }, {
 	path: '/scratchpad',
 	component: page(() => import('@/pages/scratchpad.vue')),
+}, {
+	path: '/preview',
+	component: page(() => import('@/pages/preview.vue')),
 }, {
 	path: '/auth/:token',
 	component: page(() => import('@/pages/auth.vue')),

From 0e512d4ff6d2a7c56ac6295bf26d1101a3b6a317 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=AC=E3=82=8B=E3=81=8D=E3=82=83=E3=81=A3=E3=81=A8?=
 <nullnyat@nca10.moe>
Date: Thu, 27 Jun 2024 18:23:47 +0900
Subject: [PATCH 061/206] update: CHANGELOG.md for #14097 (#14099)

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 290b13ab36..3a28c9ef64 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
 - Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正 
 - Fix: アンテナの編集画面のボタンに隙間を追加
+- Fix: テーマプレビューが見れない問題を修正
 
 ### Server
 - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正

From 4096dabe1e4b6ebb43e47fbee19954fb92adbdc7 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Thu, 27 Jun 2024 21:59:19 +0900
Subject: [PATCH 062/206] Add null checking (#14089)

---
 packages/frontend/src/components/MkFollowButton.vue | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index 636e61db8f..6a4081079c 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -121,6 +121,8 @@ async function onClick() {
 				});
 				hasPendingFollowRequestFromYou.value = true;
 
+				if ($i == null) return;
+
 				claimAchievement('following1');
 
 				if ($i.followingCount >= 10) {

From a6edd50a5d292e29e6292754a7be95205ac7dbc1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=AC=E3=82=8B=E3=81=8D=E3=82=83=E3=81=A3=E3=81=A8?=
 <nullnyat@nca10.moe>
Date: Fri, 28 Jun 2024 11:16:12 +0900
Subject: [PATCH 063/206] =?UTF-8?q?chore(docker-compose):=20=E6=8E=A8?=
 =?UTF-8?q?=E5=A5=A8=E3=81=AE=E5=90=8D=E5=89=8D=E3=81=AB=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=20(#14096)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore(docker-compose): 推奨の名前にする

https://github.com/compose-spec/compose-spec/blob/5c18e329d5a15a15e4b636ed093b256b96615e33/spec.md#compose-file

* yaml to yml

* fix

* fix
---
 .devcontainer/{docker-compose.yml => compose.yml}         | 2 --
 .devcontainer/devcontainer.json                           | 2 +-
 .dockerignore                                             | 4 ++--
 .github/workflows/dockle.yml                              | 2 +-
 .gitignore                                                | 4 ++--
 CONTRIBUTING.md                                           | 2 +-
 docker-compose.local-db.yml => compose.local-db.yml       | 2 --
 docker-compose_example.yml => compose_example.yml         | 2 --
 packages/backend/test/{docker-compose.yml => compose.yml} | 2 --
 9 files changed, 7 insertions(+), 15 deletions(-)
 rename .devcontainer/{docker-compose.yml => compose.yml} (98%)
 rename docker-compose.local-db.yml => compose.local-db.yml (98%)
 rename docker-compose_example.yml => compose_example.yml (99%)
 rename packages/backend/test/{docker-compose.yml => compose.yml} (94%)

diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/compose.yml
similarity index 98%
rename from .devcontainer/docker-compose.yml
rename to .devcontainer/compose.yml
index a52d086fb6..d02d2a8f4a 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/compose.yml
@@ -1,5 +1,3 @@
-version: '3.8'
-
 services:
   app:
     build:
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 344edbd65d..7ea23e314e 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,6 +1,6 @@
 {
 	"name": "Misskey",
-	"dockerComposeFile": "docker-compose.yml",
+	"dockerComposeFile": "compose.yml",
 	"service": "app",
 	"workspaceFolder": "/workspace",
 	"features": {
diff --git a/.dockerignore b/.dockerignore
index 1de0c7982b..7dbb06e1d0 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -7,7 +7,7 @@ Dockerfile
 build/
 built/
 db/
-docker-compose.yml
+.devcontainer/compose.yml
 node_modules/
 packages/*/node_modules
 redis/
@@ -28,4 +28,4 @@ fluent-emojis/
 
 .idea/
 packages/*/.vscode/
-packages/backend/test/docker-compose.yml
+packages/backend/test/compose.yml
diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml
index 968971dd8d..c3dba4213d 100644
--- a/.github/workflows/dockle.yml
+++ b/.github/workflows/dockle.yml
@@ -22,7 +22,7 @@ jobs:
           sudo dpkg -i dockle.deb
       - run: |
           cp .config/docker_example.env .config/docker.env
-          cp ./docker-compose_example.yml ./docker-compose.yml
+          cp ./compose_example.yml ./compose.yml
       - run: |
           docker compose up -d web
           docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest
diff --git a/.gitignore b/.gitignore
index bdc14fea0a..3466984cf6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,8 +35,8 @@ coverage
 !/.config/example.yml
 !/.config/docker_example.yml
 !/.config/docker_example.env
-docker-compose.yml
-!/.devcontainer/docker-compose.yml
+.devcontainer/compose.yml
+!/.devcontainer/compose.yml
 
 # misskey
 /build
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dcb625626d..06c2d2f21d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -165,7 +165,7 @@ cp .github/misskey/test.yml .config/
 ```
 Prepare DB/Redis for testing.
 ```
-docker compose -f packages/backend/test/docker-compose.yml up
+docker compose -f packages/backend/test/compose.yaml up
 ```
 Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`.
 
diff --git a/docker-compose.local-db.yml b/compose.local-db.yml
similarity index 98%
rename from docker-compose.local-db.yml
rename to compose.local-db.yml
index 16ba4b49e1..3835cb23db 100644
--- a/docker-compose.local-db.yml
+++ b/compose.local-db.yml
@@ -1,5 +1,3 @@
-version: "3"
-
 # このconfigは、 dockerでMisskey本体を起動せず、 redisとpostgresql などだけを起動します
 
 services:
diff --git a/docker-compose_example.yml b/compose_example.yml
similarity index 99%
rename from docker-compose_example.yml
rename to compose_example.yml
index 5cebbe4164..75d0d3a59c 100644
--- a/docker-compose_example.yml
+++ b/compose_example.yml
@@ -1,5 +1,3 @@
-version: "3"
-
 services:
   web:
     build: .
diff --git a/packages/backend/test/docker-compose.yml b/packages/backend/test/compose.yml
similarity index 94%
rename from packages/backend/test/docker-compose.yml
rename to packages/backend/test/compose.yml
index f2d8990758..6593fc33dd 100644
--- a/packages/backend/test/docker-compose.yml
+++ b/packages/backend/test/compose.yml
@@ -1,5 +1,3 @@
-version: "3"
-
 services:
   redistest:
     image: redis:7

From f1b1e2a7cca3d69eb6162d4c16746968d855ea40 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Tue, 2 Jul 2024 10:57:20 +0900
Subject: [PATCH 064/206] fix(storybook): prevent infinite remount of component
 (#14101)

* fix(storybook): prevent infinite remount of component

* fix: disable flaky `.toMatch()` test
---
 packages/frontend/.storybook/preview.ts       |  6 +-
 .../MkChannelFollowButton.stories.impl.ts     |  6 --
 .../components/MkClickerGame.stories.impl.ts  | 12 ++-
 .../src/components/MkCwButton.stories.impl.ts | 10 ---
 .../src/components/global/MkA.stories.impl.ts |  2 -
 .../components/global/MkAd.stories.impl.ts    | 87 +++++++------------
 packages/frontend/src/scripts/test-utils.ts   | 10 ---
 7 files changed, 41 insertions(+), 92 deletions(-)

diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts
index 73ee007fb8..d000a28232 100644
--- a/packages/frontend/.storybook/preview.ts
+++ b/packages/frontend/.storybook/preview.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { FORCE_REMOUNT } from '@storybook/core-events';
+import { FORCE_RE_RENDER, FORCE_REMOUNT } from '@storybook/core-events';
 import { addons } from '@storybook/preview-api';
 import { type Preview, setup } from '@storybook/vue3';
 import isChromatic from 'chromatic/isChromatic';
@@ -16,7 +16,7 @@ import '../src/style.scss';
 
 const appInitialized = Symbol();
 
-let lastStory = null;
+let lastStory: string | null = null;
 let moduleInitialized = false;
 let unobserve = () => {};
 let misskeyOS = null;
@@ -110,7 +110,7 @@ const preview = {
 				}).catch(() => {});
 				Promise.all([resetIndexedDBPromise, resetDefaultStorePromise]).then(() => {
 					initLocalStorage();
-					channel.emit(FORCE_REMOUNT, { storyId: context.id });
+					channel.emit(FORCE_RE_RENDER, { storyId: context.id });
 				});
 			}
 			const story = Story();
diff --git a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
index b99620da22..b9770670dc 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
+++ b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
@@ -12,14 +12,12 @@ import { expect, userEvent, within } from '@storybook/test';
 import { channel } from '../../.storybook/fakes.js';
 import { commonHandlers } from '../../.storybook/mocks.js';
 import MkChannelFollowButton from './MkChannelFollowButton.vue';
-import { semaphore } from '@/scripts/test-utils.js';
 import { i18n } from '@/i18n.js';
 
 function sleep(ms: number) {
 	return new Promise(resolve => setTimeout(resolve, ms));
 }
 
-const s = semaphore();
 export const Default = {
 	render(args) {
 		return {
@@ -46,17 +44,13 @@ export const Default = {
 		full: true,
 	},
 	async play({ canvasElement }) {
-		await s.acquire();
-		await sleep(1000);
 		const canvas = within(canvasElement);
 		const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
 		await expect(buttonElement).toHaveTextContent(i18n.ts.follow);
 		await userEvent.click(buttonElement);
 		await sleep(1000);
 		await expect(buttonElement).toHaveTextContent(i18n.ts.unfollow);
-		await sleep(100);
 		await userEvent.click(buttonElement);
-		s.release();
 	},
 	parameters: {
 		layout: 'centered',
diff --git a/packages/frontend/src/components/MkClickerGame.stories.impl.ts b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
index 8378010f8b..36313f965d 100644
--- a/packages/frontend/src/components/MkClickerGame.stories.impl.ts
+++ b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
@@ -8,7 +8,7 @@
 import { StoryObj } from '@storybook/vue3';
 import { HttpResponse, http } from 'msw';
 import { action } from '@storybook/addon-actions';
-import { expect, within } from '@storybook/test';
+import { expect, userEvent, within } from '@storybook/test';
 import { commonHandlers } from '../../.storybook/mocks.js';
 import MkClickerGame from './MkClickerGame.vue';
 
@@ -41,12 +41,10 @@ export const Default = {
 		await sleep(1000);
 		const canvas = within(canvasElement);
 		const count = canvas.getByTestId('count');
-		// NOTE: flaky なので N/A も通しておく
-		await expect(count).toHaveTextContent(/^(0|N\/A)$/);
-		// FIXME: flaky
-		// const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
-		// await userEvent.click(buttonElement);
-		// await expect(count).toHaveTextContent('1');
+		await expect(count).toHaveTextContent('0');
+		const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+		await userEvent.click(buttonElement);
+		await expect(count).toHaveTextContent('1');
 	},
 	parameters: {
 		layout: 'centered',
diff --git a/packages/frontend/src/components/MkCwButton.stories.impl.ts b/packages/frontend/src/components/MkCwButton.stories.impl.ts
index 05c6001552..5d6ea56da9 100644
--- a/packages/frontend/src/components/MkCwButton.stories.impl.ts
+++ b/packages/frontend/src/components/MkCwButton.stories.impl.ts
@@ -11,13 +11,6 @@ import { expect, userEvent, within } from '@storybook/test';
 import { file } from '../../.storybook/fakes.js';
 import MkCwButton from './MkCwButton.vue';
 import { i18n } from '@/i18n.js';
-import { semaphore } from '@/scripts/test-utils.js';
-
-function sleep(ms: number) {
-	return new Promise(resolve => setTimeout(resolve, ms));
-}
-
-const s = semaphore();
 
 export const Default = {
 	render(args) {
@@ -54,8 +47,6 @@ export const Default = {
 		text: 'Some CW content',
 	},
 	async play({ canvasElement }) {
-		await s.acquire();
-		await sleep(1000);
 		const canvas = within(canvasElement);
 		const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
 		await expect(buttonElement).toHaveTextContent(i18n.ts._cw.show);
@@ -63,7 +54,6 @@ export const Default = {
 		await userEvent.click(buttonElement);
 		await expect(buttonElement).toHaveTextContent(i18n.ts._cw.hide);
 		await userEvent.click(buttonElement);
-		s.release();
 	},
 	parameters: {
 		chromatic: {
diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts
index c1d8cf0ca6..02e5a7f98c 100644
--- a/packages/frontend/src/components/global/MkA.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkA.stories.impl.ts
@@ -35,12 +35,10 @@ export const Default = {
 		// FIXME: 通るけどその後落ちるのでコメントアウト
 		// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
 		await userEvent.pointer({ keys: '[MouseRight]', target: a });
-		await tick();
 		const menu = canvas.getByRole('menu');
 		await expect(menu).toBeInTheDocument();
 		await userEvent.click(a);
 		a.blur();
-		await tick();
 		await expect(menu).not.toBeInTheDocument();
 	},
 	args: {
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index aef26ab92d..8c0b7ef52f 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -9,12 +9,6 @@ import { StoryObj } from '@storybook/vue3';
 import MkAd from './MkAd.vue';
 import { i18n } from '@/i18n.js';
 
-let lock: Promise<undefined> | undefined;
-
-function sleep(ms: number) {
-	return new Promise(resolve => setTimeout(resolve, ms));
-}
-
 const common = {
 	render(args) {
 		return {
@@ -37,56 +31,41 @@ const common = {
 		};
 	},
 	async play({ canvasElement, args }) {
-		if (lock) {
-			console.warn('This test is unexpectedly running twice in parallel, fix it!');
-			console.warn('See also: https://github.com/misskey-dev/misskey/issues/11267');
-			await lock;
+		const canvas = within(canvasElement);
+		const a = canvas.getByRole<HTMLAnchorElement>('link');
+		// FIXME: 通るけどその後落ちるのでコメントアウト
+		// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
+		const img = within(a).getByRole('img');
+		await expect(img).toBeInTheDocument();
+		let buttons = canvas.getAllByRole<HTMLButtonElement>('button');
+		await expect(buttons).toHaveLength(1);
+		const i = buttons[0];
+		await expect(i).toBeInTheDocument();
+		await userEvent.click(i);
+		await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
+		await expect(a).not.toBeInTheDocument();
+		await expect(i).not.toBeInTheDocument();
+		buttons = canvas.getAllByRole<HTMLButtonElement>('button');
+		const hasReduceFrequency = args.specify?.ratio !== 0;
+		await expect(buttons).toHaveLength(hasReduceFrequency ? 2 : 1);
+		const reduce = hasReduceFrequency ? buttons[0] : null;
+		const back = buttons[hasReduceFrequency ? 1 : 0];
+		if (reduce) {
+			await expect(reduce).toBeInTheDocument();
+			await expect(reduce).toHaveTextContent(i18n.ts._ad.reduceFrequencyOfThisAd);
 		}
-
-		let resolve: (value?: any) => void;
-		lock = new Promise(r => resolve = r);
-
-		try {
-			// NOTE: sleep しないと何故か落ちる
-			await sleep(100);
-			const canvas = within(canvasElement);
-			const a = canvas.getByRole<HTMLAnchorElement>('link');
-			// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
-			const img = within(a).getByRole('img');
-			await expect(img).toBeInTheDocument();
-			let buttons = canvas.getAllByRole<HTMLButtonElement>('button');
-			await expect(buttons).toHaveLength(1);
-			const i = buttons[0];
-			await expect(i).toBeInTheDocument();
-			await userEvent.click(i);
-			await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
-			await expect(a).not.toBeInTheDocument();
-			await expect(i).not.toBeInTheDocument();
-			buttons = canvas.getAllByRole<HTMLButtonElement>('button');
-			const hasReduceFrequency = args.specify?.ratio !== 0;
-			await expect(buttons).toHaveLength(hasReduceFrequency ? 2 : 1);
-			const reduce = hasReduceFrequency ? buttons[0] : null;
-			const back = buttons[hasReduceFrequency ? 1 : 0];
-			if (reduce) {
-				await expect(reduce).toBeInTheDocument();
-				await expect(reduce).toHaveTextContent(i18n.ts._ad.reduceFrequencyOfThisAd);
-			}
-			await expect(back).toBeInTheDocument();
-			await expect(back).toHaveTextContent(i18n.ts._ad.back);
-			await userEvent.click(back);
-			await waitFor(() => expect(canvas.queryByRole('img')).toBeTruthy());
-			if (reduce) {
-				await expect(reduce).not.toBeInTheDocument();
-			}
-			await expect(back).not.toBeInTheDocument();
-			const aAgain = canvas.getByRole<HTMLAnchorElement>('link');
-			await expect(aAgain).toBeInTheDocument();
-			const imgAgain = within(aAgain).getByRole('img');
-			await expect(imgAgain).toBeInTheDocument();
-		} finally {
-			resolve!();
-			lock = undefined;
+		await expect(back).toBeInTheDocument();
+		await expect(back).toHaveTextContent(i18n.ts._ad.back);
+		await userEvent.click(back);
+		await waitFor(() => expect(canvas.queryByRole('img')).toBeTruthy());
+		if (reduce) {
+			await expect(reduce).not.toBeInTheDocument();
 		}
+		await expect(back).not.toBeInTheDocument();
+		const aAgain = canvas.getByRole<HTMLAnchorElement>('link');
+		await expect(aAgain).toBeInTheDocument();
+		const imgAgain = within(aAgain).getByRole('img');
+		await expect(imgAgain).toBeInTheDocument();
 	},
 	args: {
 		prefer: [],
diff --git a/packages/frontend/src/scripts/test-utils.ts b/packages/frontend/src/scripts/test-utils.ts
index a32315f4df..52bb2d94e0 100644
--- a/packages/frontend/src/scripts/test-utils.ts
+++ b/packages/frontend/src/scripts/test-utils.ts
@@ -7,13 +7,3 @@ export async function tick(): Promise<void> {
 	// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 	await new Promise((globalThis.requestIdleCallback ?? setTimeout) as never);
 }
-
-/**
- * @see https://github.com/misskey-dev/misskey/issues/11267
- */
-export function semaphore(counter = 0, waiting: (() => void)[] = []) {
-	return {
-		acquire: () => ++counter > 1 && new Promise<void>(resolve => waiting.push(resolve)),
-		release: () => --counter && waiting.pop()?.(),
-	};
-}

From 427648c4b8c5b7699c92afa95a14097bb9329ee8 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 2 Jul 2024 11:38:34 +0900
Subject: [PATCH 065/206] update deps (#14057)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* wip

* locales/index.jsのymlファイル取得ロジックを調節

* regenerate pnpm-lock.yaml

* fix(backend): typecheck fails

* chore(deps): bump ip-cidr from 4.0.0 to 4.0.1 in /packages/backend

* chore: migrate ESLint configs to flat config (#14094)

* chore: migrate ESLint configs to flat config

* fix: update paths

* fix: frontend lint fails

* refactor(misskey-js): lint build.js

* update deps

---------

Co-authored-by: samunohito <46447427+samunohito@users.noreply.github.com>
Co-authored-by: zyoshoka <root@zyoshoka.com>
Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
---
 .github/workflows/lint.yml                    |    4 +-
 locales/index.js                              |    6 +-
 package.json                                  |   20 +-
 packages/backend/.eslintignore                |    4 -
 packages/backend/.eslintrc.cjs                |   32 -
 packages/backend/eslint.config.js             |   46 +
 packages/backend/package.json                 |   98 +-
 packages/backend/test-server/.eslintrc.cjs    |   32 -
 packages/backend/test-server/eslint.config.js |   43 +
 packages/backend/test/.eslintrc.cjs           |   11 -
 packages/backend/test/eslint.config.js        |   22 +
 packages/frontend/.eslintrc.cjs               |   82 -
 packages/frontend/eslint.config.js            |   95 +
 packages/frontend/package.json                |  116 +-
 packages/frontend/tsconfig.json               |    1 -
 packages/misskey-bubble-game/.eslintignore    |    8 -
 packages/misskey-bubble-game/.eslintrc.cjs    |    9 -
 packages/misskey-bubble-game/eslint.config.js |   27 +
 packages/misskey-bubble-game/package.json     |    4 +-
 packages/misskey-js/.eslintignore             |    8 -
 packages/misskey-js/.eslintrc.cjs             |    9 -
 packages/misskey-js/build.js                  |   32 +-
 packages/misskey-js/eslint.config.js          |   28 +
 packages/misskey-js/generator/.eslintrc.cjs   |    9 -
 .../misskey-js/generator/eslint.config.js     |   17 +
 packages/misskey-js/generator/package.json    |    4 +-
 packages/misskey-js/package.json              |   24 +-
 packages/misskey-reversi/.eslintignore        |    8 -
 packages/misskey-reversi/.eslintrc.cjs        |   10 -
 packages/misskey-reversi/eslint.config.js     |   23 +
 packages/misskey-reversi/package.json         |    4 +-
 packages/shared/.eslintrc.js                  |    7 -
 packages/shared/eslint.config.js              |   28 +
 packages/shared/package.json                  |    3 +
 packages/sw/.eslintrc.cjs                     |   20 -
 packages/sw/eslint.config.js                  |   32 +
 packages/sw/package.json                      |   10 +-
 pnpm-lock.yaml                                | 9005 +++++++++--------
 scripts/changelog-checker/.eslintrc.cjs       |    9 -
 scripts/changelog-checker/eslint.config.js    |   17 +
 40 files changed, 5556 insertions(+), 4411 deletions(-)
 delete mode 100644 packages/backend/.eslintignore
 delete mode 100644 packages/backend/.eslintrc.cjs
 create mode 100644 packages/backend/eslint.config.js
 delete mode 100644 packages/backend/test-server/.eslintrc.cjs
 create mode 100644 packages/backend/test-server/eslint.config.js
 delete mode 100644 packages/backend/test/.eslintrc.cjs
 create mode 100644 packages/backend/test/eslint.config.js
 delete mode 100644 packages/frontend/.eslintrc.cjs
 create mode 100644 packages/frontend/eslint.config.js
 delete mode 100644 packages/misskey-bubble-game/.eslintignore
 delete mode 100644 packages/misskey-bubble-game/.eslintrc.cjs
 create mode 100644 packages/misskey-bubble-game/eslint.config.js
 delete mode 100644 packages/misskey-js/.eslintignore
 delete mode 100644 packages/misskey-js/.eslintrc.cjs
 create mode 100644 packages/misskey-js/eslint.config.js
 delete mode 100644 packages/misskey-js/generator/.eslintrc.cjs
 create mode 100644 packages/misskey-js/generator/eslint.config.js
 delete mode 100644 packages/misskey-reversi/.eslintignore
 delete mode 100644 packages/misskey-reversi/.eslintrc.cjs
 create mode 100644 packages/misskey-reversi/eslint.config.js
 delete mode 100644 packages/shared/.eslintrc.js
 create mode 100644 packages/shared/eslint.config.js
 create mode 100644 packages/shared/package.json
 delete mode 100644 packages/sw/.eslintrc.cjs
 create mode 100644 packages/sw/eslint.config.js
 delete mode 100644 scripts/changelog-checker/.eslintrc.cjs
 create mode 100644 scripts/changelog-checker/eslint.config.js

diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 76616ec5a7..1a1b30168a 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -10,14 +10,14 @@ on:
       - packages/frontend/**
       - packages/sw/**
       - packages/misskey-js/**
-      - packages/shared/.eslintrc.js
+      - packages/shared/eslint.config.js
   pull_request:
     paths:
       - packages/backend/**
       - packages/frontend/**
       - packages/sw/**
       - packages/misskey-js/**
-      - packages/shared/.eslintrc.js
+      - packages/shared/eslint.config.js
 
 jobs:
   pnpm_install:
diff --git a/locales/index.js b/locales/index.js
index 650e552337..c2738884eb 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -52,7 +52,11 @@ const primaries = {
 const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
 
 export function build() {
-	const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, import.meta.url), 'utf-8'))) || {}, a), {});
+	// vitestの挙動を調整するため、一度ローカル変数化する必要がある
+	// https://github.com/vitest-dev/vitest/issues/3988#issuecomment-1686599577
+	// https://github.com/misskey-dev/misskey/pull/14057#issuecomment-2192833785
+	const metaUrl = import.meta.url;
+	const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, metaUrl), 'utf-8'))) || {}, a), {});
 
 	// 空文字列が入ることがあり、フォールバックが動作しなくなるのでプロパティごと消す
 	const removeEmpty = (obj) => {
diff --git a/package.json b/package.json
index 5adce65415..bf8415d212 100644
--- a/package.json
+++ b/package.json
@@ -55,20 +55,22 @@
 		"js-yaml": "4.1.0",
 		"postcss": "8.4.38",
 		"tar": "6.2.1",
-		"terser": "5.30.3",
-		"typescript": "5.5.2",
-		"esbuild": "0.20.2",
+		"terser": "5.31.1",
+		"typescript": "5.5.3",
+		"esbuild": "0.22.0",
 		"glob": "10.3.12"
 	},
 	"devDependencies": {
-		"@types/node": "20.12.7",
-		"@typescript-eslint/eslint-plugin": "7.7.1",
-		"@typescript-eslint/parser": "7.7.1",
+		"@misskey-dev/eslint-plugin": "2.0.2",
+		"@types/node": "20.14.9",
+		"@typescript-eslint/eslint-plugin": "7.15.0",
+		"@typescript-eslint/parser": "7.15.0",
 		"cross-env": "7.0.3",
-		"cypress": "13.7.3",
-		"eslint": "8.57.0",
+		"cypress": "13.13.0",
+		"eslint": "9.6.0",
+		"globals": "15.7.0",
 		"ncp": "2.0.0",
-		"start-server-and-test": "2.0.3"
+		"start-server-and-test": "2.0.4"
 	},
 	"optionalDependencies": {
 		"@tensorflow/tfjs-core": "4.4.0"
diff --git a/packages/backend/.eslintignore b/packages/backend/.eslintignore
deleted file mode 100644
index 790eb90145..0000000000
--- a/packages/backend/.eslintignore
+++ /dev/null
@@ -1,4 +0,0 @@
-node_modules
-/built
-/.eslintrc.js
-/@types/**/*
diff --git a/packages/backend/.eslintrc.cjs b/packages/backend/.eslintrc.cjs
deleted file mode 100644
index f9fe4814e6..0000000000
--- a/packages/backend/.eslintrc.cjs
+++ /dev/null
@@ -1,32 +0,0 @@
-module.exports = {
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json', './test/tsconfig.json'],
-	},
-	extends: [
-		'../shared/.eslintrc.js',
-	],
-	rules: {
-		'import/order': ['warn', {
-			'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
-			'pathGroups': [
-				{
-					'pattern': '@/**',
-					'group': 'external',
-					'position': 'after'
-				}
-			],
-		}],
-		'no-restricted-globals': [
-			'error',
-			{
-				'name': '__dirname',
-				'message': 'Not in ESModule. Use `import.meta.url` instead.'
-			},
-			{
-				'name': '__filename',
-				'message': 'Not in ESModule. Use `import.meta.url` instead.'
-			}
-	]
-	},
-};
diff --git a/packages/backend/eslint.config.js b/packages/backend/eslint.config.js
new file mode 100644
index 0000000000..318b7fd340
--- /dev/null
+++ b/packages/backend/eslint.config.js
@@ -0,0 +1,46 @@
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		ignores: ['**/node_modules', 'built', '@types/**/*'],
+	},
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json', './test/tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+		rules: {
+			'import/order': ['warn', {
+				groups: [
+					'builtin',
+					'external',
+					'internal',
+					'parent',
+					'sibling',
+					'index',
+					'object',
+					'type',
+				],
+				pathGroups: [{
+					pattern: '@/**',
+					group: 'external',
+					position: 'after',
+				}],
+			}],
+			'no-restricted-globals': ['error', {
+				name: '__dirname',
+				message: 'Not in ESModule. Use `import.meta.url` instead.',
+			}, {
+				name: '__filename',
+				message: 'Not in ESModule. Use `import.meta.url` instead.',
+			}],
+		},
+	},
+];
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 0467ab0bee..22fdc5cf16 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -65,43 +65,43 @@
 		"utf-8-validate": "6.0.3"
 	},
 	"dependencies": {
-		"@aws-sdk/client-s3": "3.412.0",
-		"@aws-sdk/lib-storage": "3.412.0",
-		"@bull-board/api": "5.17.0",
-		"@bull-board/fastify": "5.17.0",
-		"@bull-board/ui": "5.17.0",
+		"@aws-sdk/client-s3": "3.600.0",
+		"@aws-sdk/lib-storage": "3.600.0",
+		"@bull-board/api": "5.20.5",
+		"@bull-board/fastify": "5.20.5",
+		"@bull-board/ui": "5.20.5",
 		"@discordapp/twemoji": "15.0.3",
 		"@fastify/accepts": "4.3.0",
 		"@fastify/cookie": "9.3.1",
 		"@fastify/cors": "9.0.1",
 		"@fastify/express": "3.0.0",
 		"@fastify/http-proxy": "9.5.0",
-		"@fastify/multipart": "8.2.0",
-		"@fastify/static": "7.0.3",
+		"@fastify/multipart": "8.3.0",
+		"@fastify/static": "7.0.4",
 		"@fastify/view": "9.1.0",
 		"@misskey-dev/sharp-read-bmp": "1.2.0",
 		"@misskey-dev/summaly": "5.1.0",
-		"@napi-rs/canvas": "^0.1.52",
-		"@nestjs/common": "10.3.8",
-		"@nestjs/core": "10.3.8",
-		"@nestjs/testing": "10.3.8",
+		"@napi-rs/canvas": "^0.1.53",
+		"@nestjs/common": "10.3.10",
+		"@nestjs/core": "10.3.10",
+		"@nestjs/testing": "10.3.10",
 		"@peertube/http-signature": "1.7.0",
-		"@sentry/node": "^8.5.0",
-		"@sentry/profiling-node": "^8.5.0",
+		"@sentry/node": "8.13.0",
+		"@sentry/profiling-node": "8.13.0",
 		"@simplewebauthn/server": "10.0.0",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.5.0",
 		"@swc/cli": "0.3.12",
-		"@swc/core": "1.4.17",
+		"@swc/core": "1.6.6",
 		"@twemoji/parser": "15.1.1",
 		"accepts": "1.3.8",
-		"ajv": "8.13.0",
+		"ajv": "8.16.0",
 		"archiver": "7.0.1",
 		"async-mutex": "0.5.0",
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "5.7.8",
+		"bullmq": "5.8.3",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.2",
 		"chalk": "5.3.0",
@@ -112,27 +112,27 @@
 		"content-disposition": "0.5.4",
 		"date-fns": "2.30.0",
 		"deep-email-validator": "0.1.21",
-		"fastify": "4.26.2",
+		"fastify": "4.28.1",
 		"fastify-raw-body": "4.3.0",
 		"feed": "4.2.2",
 		"file-type": "19.0.0",
-		"fluent-ffmpeg": "2.1.2",
+		"fluent-ffmpeg": "2.1.3",
 		"form-data": "4.0.0",
-		"got": "14.2.1",
+		"got": "14.4.1",
 		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
 		"htmlescape": "1.1.1",
 		"http-link-header": "1.1.3",
 		"ioredis": "5.4.1",
-		"ip-cidr": "3.1.0",
+		"ip-cidr": "4.0.1",
 		"ipaddr.js": "2.2.0",
-		"is-svg": "5.0.0",
+		"is-svg": "5.0.1",
 		"js-yaml": "4.1.0",
-		"jsdom": "24.0.0",
+		"jsdom": "24.1.0",
 		"json5": "2.2.3",
 		"jsonld": "8.3.2",
 		"jsrsasign": "11.1.0",
-		"meilisearch": "0.38.0",
+		"meilisearch": "0.41.0",
 		"mfm-js": "0.24.0",
 		"microformats-parser": "2.0.2",
 		"mime-types": "2.1.35",
@@ -142,24 +142,24 @@
 		"nanoid": "5.0.7",
 		"nested-property": "4.0.0",
 		"node-fetch": "3.3.2",
-		"nodemailer": "6.9.13",
+		"nodemailer": "6.9.14",
 		"nsfwjs": "2.4.2",
 		"oauth": "0.10.0",
 		"oauth2orize": "1.12.0",
 		"oauth2orize-pkce": "0.1.2",
 		"os-utils": "0.0.14",
-		"otpauth": "9.2.3",
+		"otpauth": "9.3.1",
 		"parse5": "7.1.2",
-		"pg": "8.11.5",
+		"pg": "8.12.0",
 		"pkce-challenge": "4.1.0",
 		"probe-image-size": "7.2.3",
 		"promise-limit": "2.7.0",
-		"pug": "3.0.2",
+		"pug": "3.0.3",
 		"punycode": "2.3.1",
 		"qrcode": "1.5.3",
 		"random-seed": "0.3.0",
 		"ratelimiter": "3.4.1",
-		"re2": "1.21.2",
+		"re2": "1.21.3",
 		"redis-lock": "0.1.4",
 		"reflect-metadata": "0.2.2",
 		"rename": "1.0.4",
@@ -167,27 +167,26 @@
 		"rxjs": "7.8.1",
 		"sanitize-html": "2.13.0",
 		"secure-json-parse": "2.7.0",
-		"sharp": "0.33.3",
+		"sharp": "0.33.4",
 		"slacc": "0.0.10",
 		"strict-event-emitter-types": "2.0.0",
 		"stringz": "2.1.0",
-		"systeminformation": "5.22.7",
+		"systeminformation": "5.22.11",
 		"tinycolor2": "1.6.0",
 		"tmp": "0.2.3",
-		"tsc-alias": "1.8.8",
+		"tsc-alias": "1.8.10",
 		"tsconfig-paths": "4.2.0",
 		"typeorm": "0.3.20",
-		"typescript": "5.5.2",
+		"typescript": "5.5.3",
 		"ulid": "2.3.0",
 		"vary": "1.1.2",
 		"web-push": "3.6.7",
-		"ws": "8.17.0",
+		"ws": "8.17.1",
 		"xev": "3.0.2"
 	},
 	"devDependencies": {
 		"@jest/globals": "29.7.0",
-		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@nestjs/platform-express": "10.3.8",
+		"@nestjs/platform-express": "10.3.10",
 		"@simplewebauthn/types": "10.0.0",
 		"@swc/jest": "0.2.36",
 		"@types/accepts": "1.3.7",
@@ -197,21 +196,21 @@
 		"@types/color-convert": "2.0.3",
 		"@types/content-disposition": "0.5.8",
 		"@types/fluent-ffmpeg": "2.1.24",
-		"@types/htmlescape": "^1.1.3",
-		"@types/http-link-header": "1.0.5",
+		"@types/htmlescape": "1.1.3",
+		"@types/http-link-header": "1.0.7",
 		"@types/jest": "29.5.12",
 		"@types/js-yaml": "4.0.9",
-		"@types/jsdom": "21.1.6",
-		"@types/jsonld": "1.5.13",
+		"@types/jsdom": "21.1.7",
+		"@types/jsonld": "1.5.14",
 		"@types/jsrsasign": "10.5.14",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.12.7",
+		"@types/node": "20.14.9",
 		"@types/nodemailer": "6.4.15",
-		"@types/oauth": "0.9.4",
+		"@types/oauth": "0.9.5",
 		"@types/oauth2orize": "1.11.5",
 		"@types/oauth2orize-pkce": "0.1.2",
-		"@types/pg": "8.11.5",
+		"@types/pg": "8.11.6",
 		"@types/pug": "2.0.10",
 		"@types/punycode": "2.1.4",
 		"@types/qrcode": "1.5.5",
@@ -227,18 +226,17 @@
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "7.7.1",
-		"@typescript-eslint/parser": "7.7.1",
-		"aws-sdk-client-mock": "3.0.1",
+		"@typescript-eslint/eslint-plugin": "7.15.0",
+		"@typescript-eslint/parser": "7.15.0",
+		"aws-sdk-client-mock": "4.0.1",
 		"cross-env": "7.0.3",
-		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
-		"execa": "8.0.1",
-		"fkill": "^9.0.0",
+		"execa": "9.2.0",
+		"fkill": "9.0.0",
 		"jest": "29.7.0",
 		"jest-mock": "29.7.0",
-		"nodemon": "3.1.0",
+		"nodemon": "3.1.4",
 		"pid-port": "1.0.0",
-		"simple-oauth2": "5.0.0"
+		"simple-oauth2": "5.0.1"
 	}
 }
diff --git a/packages/backend/test-server/.eslintrc.cjs b/packages/backend/test-server/.eslintrc.cjs
deleted file mode 100644
index c261741a36..0000000000
--- a/packages/backend/test-server/.eslintrc.cjs
+++ /dev/null
@@ -1,32 +0,0 @@
-module.exports = {
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: [
-		'../../shared/.eslintrc.js',
-	],
-	rules: {
-		'import/order': ['warn', {
-			'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
-			'pathGroups': [
-				{
-					'pattern': '@/**',
-					'group': 'external',
-					'position': 'after'
-				}
-			],
-		}],
-		'no-restricted-globals': [
-			'error',
-			{
-				'name': '__dirname',
-				'message': 'Not in ESModule. Use `import.meta.url` instead.'
-			},
-			{
-				'name': '__filename',
-				'message': 'Not in ESModule. Use `import.meta.url` instead.'
-			}
-	]
-	},
-};
diff --git a/packages/backend/test-server/eslint.config.js b/packages/backend/test-server/eslint.config.js
new file mode 100644
index 0000000000..b9c16d469f
--- /dev/null
+++ b/packages/backend/test-server/eslint.config.js
@@ -0,0 +1,43 @@
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+		rules: {
+			'import/order': ['warn', {
+				groups: [
+					'builtin',
+					'external',
+					'internal',
+					'parent',
+					'sibling',
+					'index',
+					'object',
+					'type',
+				],
+				pathGroups: [{
+					pattern: '@/**',
+					group: 'external',
+					position: 'after',
+				}],
+			}],
+			'no-restricted-globals': ['error', {
+				name: '__dirname',
+				message: 'Not in ESModule. Use `import.meta.url` instead.',
+			}, {
+				name: '__filename',
+				message: 'Not in ESModule. Use `import.meta.url` instead.',
+			}],
+		},
+	},
+];
diff --git a/packages/backend/test/.eslintrc.cjs b/packages/backend/test/.eslintrc.cjs
deleted file mode 100644
index 41ecea0c3f..0000000000
--- a/packages/backend/test/.eslintrc.cjs
+++ /dev/null
@@ -1,11 +0,0 @@
-module.exports = {
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: ['../.eslintrc.cjs'],
-	env: {
-		node: true,
-		jest: true,
-	},
-};
diff --git a/packages/backend/test/eslint.config.js b/packages/backend/test/eslint.config.js
new file mode 100644
index 0000000000..a0f43babad
--- /dev/null
+++ b/packages/backend/test/eslint.config.js
@@ -0,0 +1,22 @@
+import globals from 'globals';
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			globals: {
+				...globals.node,
+				...globals.jest,
+			},
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+	},
+];
diff --git a/packages/frontend/.eslintrc.cjs b/packages/frontend/.eslintrc.cjs
deleted file mode 100644
index fd562e1c40..0000000000
--- a/packages/frontend/.eslintrc.cjs
+++ /dev/null
@@ -1,82 +0,0 @@
-module.exports = {
-	root: true,
-	env: {
-		'node': false,
-	},
-	parser: 'vue-eslint-parser',
-	parserOptions: {
-		'parser': '@typescript-eslint/parser',
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-		extraFileExtensions: ['.vue'],
-	},
-	extends: [
-		'../shared/.eslintrc.js',
-		'plugin:vue/vue3-recommended',
-	],
-	rules: {
-		'@typescript-eslint/no-empty-interface': [
-			'error',
-			{
-				'allowSingleExtends': true,
-			},
-		],
-		// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
-		// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
-		'id-denylist': ['error', 'window', 'e'],
-		'no-shadow': ['warn'],
-		'vue/attributes-order': ['error', {
-			'alphabetical': false,
-		}],
-		'vue/no-use-v-if-with-v-for': ['error', {
-			'allowUsingIterationVar': false,
-		}],
-		'vue/no-ref-as-operand': 'error',
-		'vue/no-multi-spaces': ['error', {
-			'ignoreProperties': false,
-		}],
-		'vue/no-v-html': 'warn',
-		'vue/order-in-components': 'error',
-		'vue/html-indent': ['warn', 'tab', {
-			'attribute': 1,
-			'baseIndent': 0,
-			'closeBracket': 0,
-			'alignAttributesVertically': true,
-			'ignores': [],
-		}],
-		'vue/html-closing-bracket-spacing': ['warn', {
-			'startTag': 'never',
-			'endTag': 'never',
-			'selfClosingTag': 'never',
-		}],
-		'vue/multi-word-component-names': 'warn',
-		'vue/require-v-for-key': 'warn',
-		'vue/no-unused-components': 'warn',
-		'vue/no-unused-vars': 'warn',
-		'vue/no-dupe-keys': 'warn',
-		'vue/valid-v-for': 'warn',
-		'vue/return-in-computed-property': 'warn',
-		'vue/no-setup-props-reactivity-loss': 'warn',
-		'vue/max-attributes-per-line': 'off',
-		'vue/html-self-closing': 'off',
-		'vue/singleline-html-element-content-newline': 'off',
-		'vue/v-on-event-hyphenation': ['error', 'never', { autofix: true }],
-		'vue/attribute-hyphenation': ['error', 'never'],
-	},
-	globals: {
-		// Node.js
-		'module': false,
-		'require': false,
-		'__dirname': false,
-
-		// Misskey
-		'_DEV_': false,
-		'_LANGS_': false,
-		'_VERSION_': false,
-		'_ENV_': false,
-		'_PERF_PREFIX_': false,
-		'_DATA_TRANSFER_DRIVE_FILE_': false,
-		'_DATA_TRANSFER_DRIVE_FOLDER_': false,
-		'_DATA_TRANSFER_DECK_COLUMN_': false,
-	},
-};
diff --git a/packages/frontend/eslint.config.js b/packages/frontend/eslint.config.js
new file mode 100644
index 0000000000..dd8f03dac5
--- /dev/null
+++ b/packages/frontend/eslint.config.js
@@ -0,0 +1,95 @@
+import globals from 'globals';
+import tsParser from '@typescript-eslint/parser';
+import parser from 'vue-eslint-parser';
+import pluginVue from 'eslint-plugin-vue';
+import pluginMisskey from '@misskey-dev/eslint-plugin';
+import sharedConfig from '../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		files: ['src/**/*.vue'],
+		...pluginMisskey.configs.typescript,
+	},
+	...pluginVue.configs['flat/recommended'],
+	{
+		files: ['src/**/*.{ts,vue}'],
+		languageOptions: {
+			globals: {
+				...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, 'off'])),
+				...globals.browser,
+
+				// Node.js
+				module: false,
+				require: false,
+				__dirname: false,
+
+				// Misskey
+				_DEV_: false,
+				_LANGS_: false,
+				_VERSION_: false,
+				_ENV_: false,
+				_PERF_PREFIX_: false,
+				_DATA_TRANSFER_DRIVE_FILE_: false,
+				_DATA_TRANSFER_DRIVE_FOLDER_: false,
+				_DATA_TRANSFER_DECK_COLUMN_: false,
+			},
+			parser,
+			parserOptions: {
+				extraFileExtensions: ['.vue'],
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+		rules: {
+			'@typescript-eslint/no-empty-interface': ['error', {
+				allowSingleExtends: true,
+			}],
+			// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
+			// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため
+			'id-denylist': ['error', 'window', 'e'],
+			'no-shadow': ['warn'],
+			'vue/attributes-order': ['error', {
+				alphabetical: false,
+			}],
+			'vue/no-use-v-if-with-v-for': ['error', {
+				allowUsingIterationVar: false,
+			}],
+			'vue/no-ref-as-operand': 'error',
+			'vue/no-multi-spaces': ['error', {
+				ignoreProperties: false,
+			}],
+			'vue/no-v-html': 'warn',
+			'vue/order-in-components': 'error',
+			'vue/html-indent': ['warn', 'tab', {
+				attribute: 1,
+				baseIndent: 0,
+				closeBracket: 0,
+				alignAttributesVertically: true,
+				ignores: [],
+			}],
+			'vue/html-closing-bracket-spacing': ['warn', {
+				startTag: 'never',
+				endTag: 'never',
+				selfClosingTag: 'never',
+			}],
+			'vue/multi-word-component-names': 'warn',
+			'vue/require-v-for-key': 'warn',
+			'vue/no-unused-components': 'warn',
+			'vue/no-unused-vars': 'warn',
+			'vue/no-dupe-keys': 'warn',
+			'vue/valid-v-for': 'warn',
+			'vue/return-in-computed-property': 'warn',
+			'vue/no-setup-props-reactivity-loss': 'warn',
+			'vue/max-attributes-per-line': 'off',
+			'vue/html-self-closing': 'off',
+			'vue/singleline-html-element-content-newline': 'off',
+			'vue/v-on-event-hyphenation': ['error', 'never', {
+				autofix: true,
+			}],
+			'vue/attribute-hyphenation': ['error', 'never'],
+		},
+	},
+];
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index a63d97658b..743722c231 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -22,24 +22,24 @@
 		"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
 		"@misskey-dev/browser-image-resizer": "2024.1.0",
 		"@rollup/plugin-json": "6.1.0",
-		"@rollup/plugin-replace": "5.0.5",
+		"@rollup/plugin-replace": "5.0.7",
 		"@rollup/pluginutils": "5.1.0",
 		"@syuilo/aiscript": "0.18.0",
 		"@tabler/icons-webfont": "3.3.0",
 		"@twemoji/parser": "15.1.1",
-		"@vitejs/plugin-vue": "5.0.4",
-		"@vue/compiler-sfc": "3.4.26",
+		"@vitejs/plugin-vue": "5.0.5",
+		"@vue/compiler-sfc": "3.4.31",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.9",
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
 		"buraha": "0.0.1",
 		"canvas-confetti": "1.9.3",
-		"chart.js": "4.4.2",
+		"chart.js": "4.4.3",
 		"chartjs-adapter-date-fns": "3.0.0",
 		"chartjs-chart-matrix": "2.0.1",
 		"chartjs-plugin-gradient": "0.6.1",
 		"chartjs-plugin-zoom": "2.0.1",
-		"chromatic": "11.3.0",
+		"chromatic": "11.5.4",
 		"compare-versions": "6.1.0",
 		"cropperjs": "2.0.0-beta.5",
 		"date-fns": "2.30.0",
@@ -55,89 +55,87 @@
 		"misskey-bubble-game": "workspace:*",
 		"misskey-js": "workspace:*",
 		"misskey-reversi": "workspace:*",
-		"photoswipe": "5.4.3",
+		"photoswipe": "5.4.4",
 		"punycode": "2.3.1",
-		"rollup": "4.17.2",
+		"rollup": "4.18.0",
 		"sanitize-html": "2.13.0",
-		"sass": "1.76.0",
-		"shiki": "1.4.0",
+		"sass": "1.77.6",
+		"shiki": "1.10.0",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
-		"three": "0.164.1",
-		"throttle-debounce": "5.0.0",
+		"three": "0.165.0",
+		"throttle-debounce": "5.0.2",
 		"tinycolor2": "1.6.0",
-		"tsc-alias": "1.8.8",
+		"tsc-alias": "1.8.10",
 		"tsconfig-paths": "4.2.0",
-		"typescript": "5.5.2",
-		"uuid": "9.0.1",
-		"v-code-diff": "1.11.0",
-		"vite": "5.2.11",
-		"vue": "3.4.26",
+		"typescript": "5.5.3",
+		"uuid": "10.0.0",
+		"v-code-diff": "1.12.0",
+		"vite": "5.3.2",
+		"vue": "3.4.31",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
-		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@misskey-dev/summaly": "5.1.0",
-		"@storybook/addon-actions": "8.0.9",
-		"@storybook/addon-essentials": "8.0.9",
-		"@storybook/addon-interactions": "8.0.9",
-		"@storybook/addon-links": "8.0.9",
-		"@storybook/addon-mdx-gfm": "8.0.9",
-		"@storybook/addon-storysource": "8.0.9",
-		"@storybook/blocks": "8.0.9",
-		"@storybook/components": "8.0.9",
-		"@storybook/core-events": "8.0.9",
-		"@storybook/manager-api": "8.0.9",
-		"@storybook/preview-api": "8.0.9",
-		"@storybook/react": "8.0.9",
-		"@storybook/react-vite": "8.0.9",
-		"@storybook/test": "8.0.9",
-		"@storybook/theming": "8.0.9",
-		"@storybook/types": "8.0.9",
-		"@storybook/vue3": "8.0.9",
-		"@storybook/vue3-vite": "8.0.9",
-		"@testing-library/vue": "8.0.3",
+		"@storybook/addon-actions": "8.1.11",
+		"@storybook/addon-essentials": "8.1.11",
+		"@storybook/addon-interactions": "8.1.11",
+		"@storybook/addon-links": "8.1.11",
+		"@storybook/addon-mdx-gfm": "8.1.11",
+		"@storybook/addon-storysource": "8.1.11",
+		"@storybook/blocks": "8.1.11",
+		"@storybook/components": "8.1.11",
+		"@storybook/core-events": "8.1.11",
+		"@storybook/manager-api": "8.1.11",
+		"@storybook/preview-api": "8.1.11",
+		"@storybook/react": "8.1.11",
+		"@storybook/react-vite": "8.1.11",
+		"@storybook/test": "8.1.11",
+		"@storybook/theming": "8.1.11",
+		"@storybook/types": "8.1.11",
+		"@storybook/vue3": "8.1.11",
+		"@storybook/vue3-vite": "8.1.11",
+		"@testing-library/vue": "8.1.0",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
 		"@types/matter-js": "0.19.6",
-		"@types/micromatch": "4.0.7",
-		"@types/node": "20.12.7",
+		"@types/micromatch": "4.0.9",
+		"@types/node": "20.14.9",
 		"@types/punycode": "2.1.4",
 		"@types/sanitize-html": "2.11.0",
 		"@types/seedrandom": "3.0.8",
 		"@types/throttle-debounce": "5.0.2",
 		"@types/tinycolor2": "1.4.6",
-		"@types/uuid": "9.0.8",
+		"@types/uuid": "10.0.0",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "7.7.1",
-		"@typescript-eslint/parser": "7.7.1",
-		"@vitest/coverage-v8": "0.34.6",
-		"@vue/runtime-core": "3.4.26",
-		"acorn": "8.11.3",
+		"@typescript-eslint/eslint-plugin": "7.15.0",
+		"@typescript-eslint/parser": "7.15.0",
+		"@vitest/coverage-v8": "1.6.0",
+		"@vue/runtime-core": "3.4.31",
+		"acorn": "8.12.0",
 		"cross-env": "7.0.3",
-		"cypress": "13.8.1",
-		"eslint": "8.57.0",
+		"cypress": "13.13.0",
 		"eslint-plugin-import": "2.29.1",
-		"eslint-plugin-vue": "9.25.0",
+		"eslint-plugin-vue": "9.26.0",
 		"fast-glob": "3.3.2",
 		"happy-dom": "10.0.3",
 		"intersection-observer": "0.12.2",
-		"micromatch": "4.0.5",
-		"msw": "2.2.14",
-		"msw-storybook-addon": "2.0.1",
-		"nodemon": "3.1.0",
-		"prettier": "3.2.5",
+		"micromatch": "4.0.7",
+		"msw": "2.3.1",
+		"msw-storybook-addon": "2.0.2",
+		"nodemon": "3.1.4",
+		"prettier": "3.3.2",
 		"react": "18.3.1",
 		"react-dom": "18.3.1",
 		"seedrandom": "3.0.5",
-		"start-server-and-test": "2.0.3",
-		"storybook": "8.0.9",
+		"start-server-and-test": "2.0.4",
+		"storybook": "8.1.11",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
 		"vite-plugin-turbosnap": "1.0.3",
-		"vitest": "0.34.6",
+		"vitest": "1.6.0",
 		"vitest-fetch-mock": "0.2.2",
-		"vue-component-type-helpers": "2.0.16",
-		"vue-eslint-parser": "9.4.2",
-		"vue-tsc": "2.0.16"
+		"vue-component-type-helpers": "2.0.24",
+		"vue-eslint-parser": "9.4.3",
+		"vue-tsc": "2.0.24"
 	}
 }
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 187a2473ba..fe4d202894 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -44,7 +44,6 @@
 	},
 	"compileOnSave": false,
 	"include": [
-		".eslintrc.js",
 		"./**/*.ts",
 		"./**/*.vue"
 	],
diff --git a/packages/misskey-bubble-game/.eslintignore b/packages/misskey-bubble-game/.eslintignore
deleted file mode 100644
index 52ea8b3362..0000000000
--- a/packages/misskey-bubble-game/.eslintignore
+++ /dev/null
@@ -1,8 +0,0 @@
-node_modules
-/built
-/coverage
-/.eslintrc.js
-/jest.config.ts
-/test
-/test-d
-build.js
diff --git a/packages/misskey-bubble-game/.eslintrc.cjs b/packages/misskey-bubble-game/.eslintrc.cjs
deleted file mode 100644
index e2e31e9e33..0000000000
--- a/packages/misskey-bubble-game/.eslintrc.cjs
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: [
-		'../shared/.eslintrc.js',
-	],
-};
diff --git a/packages/misskey-bubble-game/eslint.config.js b/packages/misskey-bubble-game/eslint.config.js
new file mode 100644
index 0000000000..86c21a22a3
--- /dev/null
+++ b/packages/misskey-bubble-game/eslint.config.js
@@ -0,0 +1,27 @@
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		ignores: [
+			'**/node_modules',
+			'built',
+			'coverage',
+			'jest.config.ts',
+			'test',
+			'test-d',
+		],
+	},
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+	},
+];
diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json
index a3aad147a9..528eb00b74 100644
--- a/packages/misskey-bubble-game/package.json
+++ b/packages/misskey-bubble-game/package.json
@@ -17,18 +17,16 @@
 	"scripts": {
 		"build": "node ./build.js",
 		"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
-		"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
+		"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
 		"typecheck": "tsc --noEmit",
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"devDependencies": {
-		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@types/matter-js": "0.19.6",
 		"@types/seedrandom": "3.0.8",
 		"@types/node": "20.11.5",
 		"@typescript-eslint/eslint-plugin": "7.1.0",
 		"@typescript-eslint/parser": "7.1.0",
-		"eslint": "8.57.0",
 		"nodemon": "3.0.2",
 		"execa": "8.0.1",
 		"typescript": "5.3.3",
diff --git a/packages/misskey-js/.eslintignore b/packages/misskey-js/.eslintignore
deleted file mode 100644
index 52ea8b3362..0000000000
--- a/packages/misskey-js/.eslintignore
+++ /dev/null
@@ -1,8 +0,0 @@
-node_modules
-/built
-/coverage
-/.eslintrc.js
-/jest.config.ts
-/test
-/test-d
-build.js
diff --git a/packages/misskey-js/.eslintrc.cjs b/packages/misskey-js/.eslintrc.cjs
deleted file mode 100644
index e2e31e9e33..0000000000
--- a/packages/misskey-js/.eslintrc.cjs
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: [
-		'../shared/.eslintrc.js',
-	],
-};
diff --git a/packages/misskey-js/build.js b/packages/misskey-js/build.js
index 0b79f4b915..a13d9c1186 100644
--- a/packages/misskey-js/build.js
+++ b/packages/misskey-js/build.js
@@ -1,32 +1,32 @@
-import * as esbuild from "esbuild";
-import { build } from "esbuild";
-import { globSync } from "glob";
-import { execa } from "execa";
-import fs from "node:fs";
-import { fileURLToPath } from "node:url";
-import { dirname } from "node:path";
+import fs from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname } from 'node:path';
+import * as esbuild from 'esbuild';
+import { build } from 'esbuild';
+import { globSync } from 'glob';
+import { execa } from 'execa';
 
 const _filename = fileURLToPath(import.meta.url);
 const _dirname = dirname(_filename);
 const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
 
-const entryPoints = globSync("./src/**/**.{ts,tsx}");
+const entryPoints = globSync('./src/**/**.{ts,tsx}');
 
 /** @type {import('esbuild').BuildOptions} */
 const options = {
 	entryPoints,
 	minify: process.env.NODE_ENV === 'production',
-	outdir: "./built",
-	target: "es2022",
-	platform: "browser",
-	format: "esm",
+	outdir: './built',
+	target: 'es2022',
+	platform: 'browser',
+	format: 'esm',
 	sourcemap: 'linked',
 };
 
 // built配下をすべて削除する
 fs.rmSync('./built', { recursive: true, force: true });
 
-if (process.argv.map(arg => arg.toLowerCase()).includes("--watch")) {
+if (process.argv.map(arg => arg.toLowerCase()).includes('--watch')) {
 	await watchSrc();
 } else {
 	await buildSrc();
@@ -36,7 +36,7 @@ async function buildSrc() {
 	console.log(`[${_package.name}] start building...`);
 
 	await build(options)
-		.then(it => {
+		.then(() => {
 			console.log(`[${_package.name}] build succeeded.`);
 		})
 		.catch((err) => {
@@ -65,7 +65,7 @@ function buildDts() {
 		{
 			stdout: process.stdout,
 			stderr: process.stderr,
-		}
+		},
 	);
 }
 
@@ -86,7 +86,7 @@ async function watchSrc() {
 		},
 	}];
 
-	console.log(`[${_package.name}] start watching...`)
+	console.log(`[${_package.name}] start watching...`);
 
 	const context = await esbuild.context({ ...options, plugins });
 	await context.watch();
diff --git a/packages/misskey-js/eslint.config.js b/packages/misskey-js/eslint.config.js
new file mode 100644
index 0000000000..e34e7510b2
--- /dev/null
+++ b/packages/misskey-js/eslint.config.js
@@ -0,0 +1,28 @@
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		ignores: [
+			'**/node_modules',
+			'built',
+			'coverage',
+			'jest.config.ts',
+			'test',
+			'test-d',
+			'generator',
+		],
+	},
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+	},
+];
diff --git a/packages/misskey-js/generator/.eslintrc.cjs b/packages/misskey-js/generator/.eslintrc.cjs
deleted file mode 100644
index 6a8b31da9c..0000000000
--- a/packages/misskey-js/generator/.eslintrc.cjs
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: [
-		'../../shared/.eslintrc.js',
-	],
-};
diff --git a/packages/misskey-js/generator/eslint.config.js b/packages/misskey-js/generator/eslint.config.js
new file mode 100644
index 0000000000..4bf78c3b91
--- /dev/null
+++ b/packages/misskey-js/generator/eslint.config.js
@@ -0,0 +1,17 @@
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+	},
+];
diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json
index a1c0f41cb2..4a02bcd8ff 100644
--- a/packages/misskey-js/generator/package.json
+++ b/packages/misskey-js/generator/package.json
@@ -4,15 +4,13 @@
 	"description": "Misskey TypeGenerator",
 	"type": "module",
 	"scripts": {
-		"generate": "tsx src/generator.ts && eslint ./built/**/* --ext .ts --fix"
+		"generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix"
 	},
 	"devDependencies": {
-		"@misskey-dev/eslint-plugin": "^1.0.0",
 		"@readme/openapi-parser": "2.5.0",
 		"@types/node": "20.9.1",
 		"@typescript-eslint/eslint-plugin": "6.11.0",
 		"@typescript-eslint/parser": "6.11.0",
-		"eslint": "8.53.0",
 		"openapi-types": "12.1.3",
 		"openapi-typescript": "6.7.3",
 		"ts-case-convert": "2.0.2",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index b99d0dd260..00342d3dbc 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -22,7 +22,7 @@
 		"tsd": "tsd",
 		"api": "pnpm api-extractor run --local --verbose",
 		"api-prod": "pnpm api-extractor run --verbose",
-		"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
+		"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
 		"typecheck": "tsc --noEmit",
 		"lint": "pnpm typecheck && pnpm eslint",
 		"jest": "jest --coverage --detectOpenHandles",
@@ -35,25 +35,23 @@
 		"directory": "packages/misskey-js"
 	},
 	"devDependencies": {
-		"@microsoft/api-extractor": "7.43.1",
-		"@misskey-dev/eslint-plugin": "1.0.0",
+		"@microsoft/api-extractor": "7.47.0",
 		"@swc/jest": "0.2.36",
 		"@types/jest": "29.5.12",
-		"@types/node": "20.12.7",
-		"@typescript-eslint/eslint-plugin": "7.7.1",
-		"@typescript-eslint/parser": "7.7.1",
-		"eslint": "8.57.0",
+		"@types/node": "20.14.9",
+		"@typescript-eslint/eslint-plugin": "7.15.0",
+		"@typescript-eslint/parser": "7.15.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
 		"mock-socket": "9.3.1",
 		"ncp": "2.0.0",
-		"nodemon": "3.1.0",
-		"execa": "8.0.1",
-		"tsd": "0.30.7",
-		"typescript": "5.5.2",
-		"esbuild": "0.19.11",
-		"glob": "10.3.12"
+		"nodemon": "3.1.4",
+		"execa": "9.2.0",
+		"tsd": "0.31.1",
+		"typescript": "5.5.3",
+		"esbuild": "0.22.0",
+		"glob": "10.4.2"
 	},
 	"files": [
 		"built"
diff --git a/packages/misskey-reversi/.eslintignore b/packages/misskey-reversi/.eslintignore
deleted file mode 100644
index 52ea8b3362..0000000000
--- a/packages/misskey-reversi/.eslintignore
+++ /dev/null
@@ -1,8 +0,0 @@
-node_modules
-/built
-/coverage
-/.eslintrc.js
-/jest.config.ts
-/test
-/test-d
-build.js
diff --git a/packages/misskey-reversi/.eslintrc.cjs b/packages/misskey-reversi/.eslintrc.cjs
deleted file mode 100644
index db37a01098..0000000000
--- a/packages/misskey-reversi/.eslintrc.cjs
+++ /dev/null
@@ -1,10 +0,0 @@
-module.exports = {
-	root: true,
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: [
-		'../shared/.eslintrc.js',
-	],
-};
diff --git a/packages/misskey-reversi/eslint.config.js b/packages/misskey-reversi/eslint.config.js
new file mode 100644
index 0000000000..3f81df7145
--- /dev/null
+++ b/packages/misskey-reversi/eslint.config.js
@@ -0,0 +1,23 @@
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		ignores: [
+			'**/node_modules',
+			'built',
+		],
+	},
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+	},
+];
diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json
index 45a6120861..c6db6e6221 100644
--- a/packages/misskey-reversi/package.json
+++ b/packages/misskey-reversi/package.json
@@ -17,16 +17,14 @@
 	"scripts": {
 		"build": "node ./build.js",
 		"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
-		"eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
+		"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
 		"typecheck": "tsc --noEmit",
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"devDependencies": {
-		"@misskey-dev/eslint-plugin": "1.0.0",
 		"@types/node": "20.11.5",
 		"@typescript-eslint/eslint-plugin": "7.1.0",
 		"@typescript-eslint/parser": "7.1.0",
-		"eslint": "8.57.0",
 		"execa": "8.0.1",
 		"nodemon": "3.0.2",
 		"typescript": "5.3.3",
diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js
deleted file mode 100644
index 58247877ae..0000000000
--- a/packages/shared/.eslintrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-module.exports = {
-	root: true,
-	ignorePatterns: ['**/.eslintrc.cjs'],
-	extends: [
-		'plugin:@misskey-dev/recommended',
-	],
-};
diff --git a/packages/shared/eslint.config.js b/packages/shared/eslint.config.js
new file mode 100644
index 0000000000..e9d27c4a72
--- /dev/null
+++ b/packages/shared/eslint.config.js
@@ -0,0 +1,28 @@
+import globals from 'globals';
+import pluginMisskey from '@misskey-dev/eslint-plugin';
+
+export default [
+	...pluginMisskey.configs['recommended'],
+	{
+		files: ['**/*.cjs'],
+		languageOptions: {
+			parserOptions: {
+				sourceType: 'commonjs',
+			},
+		},
+	},
+	{
+		files: ['**/*.js', '**/*.jsx'],
+		languageOptions: {
+			parserOptions: {
+				sourceType: 'module',
+			},
+		},
+	},
+	{
+		files: ['build.js'],
+		languageOptions: {
+			globals: globals.node,
+		},
+	},
+];
diff --git a/packages/shared/package.json b/packages/shared/package.json
new file mode 100644
index 0000000000..bedb411a91
--- /dev/null
+++ b/packages/shared/package.json
@@ -0,0 +1,3 @@
+{
+	"type": "module"
+}
diff --git a/packages/sw/.eslintrc.cjs b/packages/sw/.eslintrc.cjs
deleted file mode 100644
index b1fd6b5edc..0000000000
--- a/packages/sw/.eslintrc.cjs
+++ /dev/null
@@ -1,20 +0,0 @@
-module.exports = {
-	root: true,
-	env: {
-		node: false,
-	},
-	parserOptions: {
-		parser: '@typescript-eslint/parser',
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: ['../shared/.eslintrc.js'],
-	globals: {
-		require: false,
-		_DEV_: false,
-		_LANGS_: false,
-		_VERSION_: false,
-		_ENV_: false,
-		_PERF_PREFIX_: false,
-	},
-};
diff --git a/packages/sw/eslint.config.js b/packages/sw/eslint.config.js
new file mode 100644
index 0000000000..c62a2eadc6
--- /dev/null
+++ b/packages/sw/eslint.config.js
@@ -0,0 +1,32 @@
+import globals from 'globals';
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		ignores: ['build.js'],
+		languageOptions: {
+			globals: {
+				...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, 'off'])),
+				require: false,
+				_DEV_: false,
+				_LANGS_: false,
+				_VERSION_: false,
+				_ENV_: false,
+				_PERF_PREFIX_: false,
+			},
+		},
+	},
+	{
+		files: ['**/*.ts', '**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+	},
+];
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 2deda47369..bcd642ffc4 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,18 +9,16 @@
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"dependencies": {
-		"esbuild": "0.20.2",
+		"esbuild": "0.22.0",
 		"idb-keyval": "6.2.1",
 		"misskey-js": "workspace:*"
 	},
 	"devDependencies": {
-		"@misskey-dev/eslint-plugin": "1.0.0",
-		"@typescript-eslint/parser": "7.7.1",
+		"@typescript-eslint/parser": "7.15.0",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
-		"eslint": "8.57.0",
 		"eslint-plugin-import": "2.29.1",
-		"nodemon": "3.1.0",
-		"typescript": "5.5.2"
+		"nodemon": "3.1.4",
+		"typescript": "5.5.3"
 	},
 	"type": "module"
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 09df15853b..10968f3e82 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -16,8 +16,8 @@ importers:
         specifier: 6.1.2
         version: 6.1.2(postcss@8.4.38)
       esbuild:
-        specifier: 0.20.2
-        version: 0.20.2
+        specifier: 0.22.0
+        version: 0.22.0
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -40,58 +40,64 @@ importers:
         specifier: 6.2.1
         version: 6.2.1
       terser:
-        specifier: 5.30.3
-        version: 5.30.3
+        specifier: 5.31.1
+        version: 5.31.1
       typescript:
-        specifier: 5.5.2
-        version: 5.5.2
+        specifier: 5.5.3
+        version: 5.5.3
     optionalDependencies:
       '@tensorflow/tfjs-core':
         specifier: 4.4.0
         version: 4.4.0(encoding@0.1.13)
     devDependencies:
+      '@misskey-dev/eslint-plugin':
+        specifier: 2.0.2
+        version: 2.0.2(@eslint/compat@1.1.0)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)
       '@types/node':
-        specifier: 20.12.7
-        version: 20.12.7
+        specifier: 20.14.9
+        version: 20.14.9
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
       '@typescript-eslint/parser':
-        specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.7.3
-        version: 13.7.3
+        specifier: 13.13.0
+        version: 13.13.0
       eslint:
-        specifier: 8.57.0
-        version: 8.57.0
+        specifier: 9.6.0
+        version: 9.6.0
+      globals:
+        specifier: 15.7.0
+        version: 15.7.0
       ncp:
         specifier: 2.0.0
         version: 2.0.0
       start-server-and-test:
-        specifier: 2.0.3
-        version: 2.0.3
+        specifier: 2.0.4
+        version: 2.0.4
 
   packages/backend:
     dependencies:
       '@aws-sdk/client-s3':
-        specifier: 3.412.0
-        version: 3.412.0
+        specifier: 3.600.0
+        version: 3.600.0
       '@aws-sdk/lib-storage':
-        specifier: 3.412.0
-        version: 3.412.0(@aws-sdk/client-s3@3.412.0)
+        specifier: 3.600.0
+        version: 3.600.0(@aws-sdk/client-s3@3.600.0)
       '@bull-board/api':
-        specifier: 5.17.0
-        version: 5.17.0(@bull-board/ui@5.17.0)
+        specifier: 5.20.5
+        version: 5.20.5(@bull-board/ui@5.20.5)
       '@bull-board/fastify':
-        specifier: 5.17.0
-        version: 5.17.0
+        specifier: 5.20.5
+        version: 5.20.5
       '@bull-board/ui':
-        specifier: 5.17.0
-        version: 5.17.0
+        specifier: 5.20.5
+        version: 5.20.5
       '@discordapp/twemoji':
         specifier: 15.0.3
         version: 15.0.3
@@ -111,11 +117,11 @@ importers:
         specifier: 9.5.0
         version: 9.5.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       '@fastify/multipart':
-        specifier: 8.2.0
-        version: 8.2.0
+        specifier: 8.3.0
+        version: 8.3.0
       '@fastify/static':
-        specifier: 7.0.3
-        version: 7.0.3
+        specifier: 7.0.4
+        version: 7.0.4
       '@fastify/view':
         specifier: 9.1.0
         version: 9.1.0
@@ -126,26 +132,26 @@ importers:
         specifier: 5.1.0
         version: 5.1.0
       '@napi-rs/canvas':
-        specifier: ^0.1.52
-        version: 0.1.52
+        specifier: ^0.1.53
+        version: 0.1.53
       '@nestjs/common':
-        specifier: 10.3.8
-        version: 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
+        specifier: 10.3.10
+        version: 10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1)
       '@nestjs/core':
-        specifier: 10.3.8
-        version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
+        specifier: 10.3.10
+        version: 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
       '@nestjs/testing':
-        specifier: 10.3.8
-        version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8))
+        specifier: 10.3.10
+        version: 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10))
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
       '@sentry/node':
-        specifier: ^8.5.0
-        version: 8.5.0
+        specifier: 8.13.0
+        version: 8.13.0
       '@sentry/profiling-node':
-        specifier: ^8.5.0
-        version: 8.5.0
+        specifier: 8.13.0
+        version: 8.13.0
       '@simplewebauthn/server':
         specifier: 10.0.0
         version: 10.0.0(encoding@0.1.13)
@@ -157,10 +163,10 @@ importers:
         version: 2.5.0
       '@swc/cli':
         specifier: 0.3.12
-        version: 0.3.12(@swc/core@1.4.17)(chokidar@3.5.3)
+        version: 0.3.12(@swc/core@1.6.6)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.4.17
-        version: 1.4.17
+        specifier: 1.6.6
+        version: 1.6.6
       '@twemoji/parser':
         specifier: 15.1.1
         version: 15.1.1
@@ -168,8 +174,8 @@ importers:
         specifier: 1.3.8
         version: 1.3.8
       ajv:
-        specifier: 8.13.0
-        version: 8.13.0
+        specifier: 8.16.0
+        version: 8.16.0
       archiver:
         specifier: 7.0.1
         version: 7.0.1
@@ -186,8 +192,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 5.7.8
-        version: 5.7.8
+        specifier: 5.8.3
+        version: 5.8.3
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -219,8 +225,8 @@ importers:
         specifier: 0.1.21
         version: 0.1.21
       fastify:
-        specifier: 4.26.2
-        version: 4.26.2
+        specifier: 4.28.1
+        version: 4.28.1
       fastify-raw-body:
         specifier: 4.3.0
         version: 4.3.0
@@ -231,14 +237,14 @@ importers:
         specifier: 19.0.0
         version: 19.0.0
       fluent-ffmpeg:
-        specifier: 2.1.2
-        version: 2.1.2
+        specifier: 2.1.3
+        version: 2.1.3
       form-data:
         specifier: 4.0.0
         version: 4.0.0
       got:
-        specifier: 14.2.1
-        version: 14.2.1
+        specifier: 14.4.1
+        version: 14.4.1
       happy-dom:
         specifier: 10.0.3
         version: 10.0.3
@@ -255,20 +261,20 @@ importers:
         specifier: 5.4.1
         version: 5.4.1
       ip-cidr:
-        specifier: 3.1.0
-        version: 3.1.0
+        specifier: 4.0.1
+        version: 4.0.1
       ipaddr.js:
         specifier: 2.2.0
         version: 2.2.0
       is-svg:
-        specifier: 5.0.0
-        version: 5.0.0
+        specifier: 5.0.1
+        version: 5.0.1
       js-yaml:
         specifier: 4.1.0
         version: 4.1.0
       jsdom:
-        specifier: 24.0.0
-        version: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 24.1.0
+        version: 24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       json5:
         specifier: 2.2.3
         version: 2.2.3
@@ -279,8 +285,8 @@ importers:
         specifier: 11.1.0
         version: 11.1.0
       meilisearch:
-        specifier: 0.38.0
-        version: 0.38.0(encoding@0.1.13)
+        specifier: 0.41.0
+        version: 0.41.0(encoding@0.1.13)
       mfm-js:
         specifier: 0.24.0
         version: 0.24.0
@@ -309,8 +315,8 @@ importers:
         specifier: 3.3.2
         version: 3.3.2
       nodemailer:
-        specifier: 6.9.13
-        version: 6.9.13
+        specifier: 6.9.14
+        version: 6.9.14
       nsfwjs:
         specifier: 2.4.2
         version: 2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5))
@@ -327,14 +333,14 @@ importers:
         specifier: 0.0.14
         version: 0.0.14
       otpauth:
-        specifier: 9.2.3
-        version: 9.2.3
+        specifier: 9.3.1
+        version: 9.3.1
       parse5:
         specifier: 7.1.2
         version: 7.1.2
       pg:
-        specifier: 8.11.5
-        version: 8.11.5
+        specifier: 8.12.0
+        version: 8.12.0
       pkce-challenge:
         specifier: 4.1.0
         version: 4.1.0
@@ -345,8 +351,8 @@ importers:
         specifier: 2.7.0
         version: 2.7.0
       pug:
-        specifier: 3.0.2
-        version: 3.0.2
+        specifier: 3.0.3
+        version: 3.0.3
       punycode:
         specifier: 2.3.1
         version: 2.3.1
@@ -360,8 +366,8 @@ importers:
         specifier: 3.4.1
         version: 3.4.1
       re2:
-        specifier: 1.21.2
-        version: 1.21.2
+        specifier: 1.21.3
+        version: 1.21.3
       redis-lock:
         specifier: 0.1.4
         version: 0.1.4
@@ -384,8 +390,8 @@ importers:
         specifier: 2.7.0
         version: 2.7.0
       sharp:
-        specifier: 0.33.3
-        version: 0.33.3
+        specifier: 0.33.4
+        version: 0.33.4
       slacc:
         specifier: 0.0.10
         version: 0.0.10
@@ -396,8 +402,8 @@ importers:
         specifier: 2.1.0
         version: 2.1.0
       systeminformation:
-        specifier: 5.22.7
-        version: 5.22.7
+        specifier: 5.22.11
+        version: 5.22.11
       tinycolor2:
         specifier: 1.6.0
         version: 1.6.0
@@ -405,17 +411,17 @@ importers:
         specifier: 0.2.3
         version: 0.2.3
       tsc-alias:
-        specifier: 1.8.8
-        version: 1.8.8
+        specifier: 1.8.10
+        version: 1.8.10
       tsconfig-paths:
         specifier: 4.2.0
         version: 4.2.0
       typeorm:
         specifier: 0.3.20
-        version: 0.3.20(ioredis@5.4.1)(pg@8.11.5)
+        version: 0.3.20(ioredis@5.4.1)(pg@8.12.0)
       typescript:
-        specifier: 5.5.2
-        version: 5.5.2
+        specifier: 5.5.3
+        version: 5.5.3
       ulid:
         specifier: 2.3.0
         version: 2.3.0
@@ -426,8 +432,8 @@ importers:
         specifier: 3.6.7
         version: 3.6.7
       ws:
-        specifier: 8.17.0
-        version: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 8.17.1
+        version: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xev:
         specifier: 3.0.2
         version: 3.0.2
@@ -523,18 +529,15 @@ importers:
       '@jest/globals':
         specifier: 29.7.0
         version: 29.7.0
-      '@misskey-dev/eslint-plugin':
-        specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
       '@nestjs/platform-express':
-        specifier: 10.3.8
-        version: 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)
+        specifier: 10.3.10
+        version: 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10)
       '@simplewebauthn/types':
         specifier: 10.0.0
         version: 10.0.0
       '@swc/jest':
         specifier: 0.2.36
-        version: 0.2.36(@swc/core@1.4.17)
+        version: 0.2.36(@swc/core@1.6.6)
       '@types/accepts':
         specifier: 1.3.7
         version: 1.3.7
@@ -557,11 +560,11 @@ importers:
         specifier: 2.1.24
         version: 2.1.24
       '@types/htmlescape':
-        specifier: ^1.1.3
+        specifier: 1.1.3
         version: 1.1.3
       '@types/http-link-header':
-        specifier: 1.0.5
-        version: 1.0.5
+        specifier: 1.0.7
+        version: 1.0.7
       '@types/jest':
         specifier: 29.5.12
         version: 29.5.12
@@ -569,11 +572,11 @@ importers:
         specifier: 4.0.9
         version: 4.0.9
       '@types/jsdom':
-        specifier: 21.1.6
-        version: 21.1.6
+        specifier: 21.1.7
+        version: 21.1.7
       '@types/jsonld':
-        specifier: 1.5.13
-        version: 1.5.13
+        specifier: 1.5.14
+        version: 1.5.14
       '@types/jsrsasign':
         specifier: 10.5.14
         version: 10.5.14
@@ -584,14 +587,14 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.12.7
-        version: 20.12.7
+        specifier: 20.14.9
+        version: 20.14.9
       '@types/nodemailer':
         specifier: 6.4.15
         version: 6.4.15
       '@types/oauth':
-        specifier: 0.9.4
-        version: 0.9.4
+        specifier: 0.9.5
+        version: 0.9.5
       '@types/oauth2orize':
         specifier: 1.11.5
         version: 1.11.5
@@ -599,8 +602,8 @@ importers:
         specifier: 0.1.2
         version: 0.1.2
       '@types/pg':
-        specifier: 8.11.5
-        version: 8.11.5
+        specifier: 8.11.6
+        version: 8.11.6
       '@types/pug':
         specifier: 2.0.10
         version: 2.0.10
@@ -647,44 +650,41 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
       '@typescript-eslint/parser':
-        specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
       aws-sdk-client-mock:
-        specifier: 3.0.1
-        version: 3.0.1
+        specifier: 4.0.1
+        version: 4.0.1
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
-      eslint:
-        specifier: 8.57.0
-        version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
       execa:
-        specifier: 8.0.1
-        version: 8.0.1
+        specifier: 9.2.0
+        version: 9.2.0
       fkill:
-        specifier: ^9.0.0
+        specifier: 9.0.0
         version: 9.0.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.12.7)
+        version: 29.7.0(@types/node@20.14.9)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
       nodemon:
-        specifier: 3.1.0
-        version: 3.1.0
+        specifier: 3.1.4
+        version: 3.1.4
       pid-port:
         specifier: 1.0.0
         version: 1.0.0
       simple-oauth2:
-        specifier: 5.0.0
-        version: 5.0.0
+        specifier: 5.0.1
+        version: 5.0.1
 
   packages/frontend:
     dependencies:
@@ -702,13 +702,13 @@ importers:
         version: 2024.1.0
       '@rollup/plugin-json':
         specifier: 6.1.0
-        version: 6.1.0(rollup@4.17.2)
+        version: 6.1.0(rollup@4.18.0)
       '@rollup/plugin-replace':
-        specifier: 5.0.5
-        version: 5.0.5(rollup@4.17.2)
+        specifier: 5.0.7
+        version: 5.0.7(rollup@4.18.0)
       '@rollup/pluginutils':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.17.2)
+        version: 5.1.0(rollup@4.18.0)
       '@syuilo/aiscript':
         specifier: 0.18.0
         version: 0.18.0
@@ -719,11 +719,11 @@ importers:
         specifier: 15.1.1
         version: 15.1.1
       '@vitejs/plugin-vue':
-        specifier: 5.0.4
-        version: 5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))
+        specifier: 5.0.5
+        version: 5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
       '@vue/compiler-sfc':
-        specifier: 3.4.26
-        version: 3.4.26
+        specifier: 3.4.31
+        version: 3.4.31
       aiscript-vscode:
         specifier: github:aiscript-dev/aiscript-vscode#v0.1.9
         version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02
@@ -740,23 +740,23 @@ importers:
         specifier: 1.9.3
         version: 1.9.3
       chart.js:
-        specifier: 4.4.2
-        version: 4.4.2
+        specifier: 4.4.3
+        version: 4.4.3
       chartjs-adapter-date-fns:
         specifier: 3.0.0
-        version: 3.0.0(chart.js@4.4.2)(date-fns@2.30.0)
+        version: 3.0.0(chart.js@4.4.3)(date-fns@2.30.0)
       chartjs-chart-matrix:
         specifier: 2.0.1
-        version: 2.0.1(chart.js@4.4.2)
+        version: 2.0.1(chart.js@4.4.3)
       chartjs-plugin-gradient:
         specifier: 0.6.1
-        version: 0.6.1(chart.js@4.4.2)
+        version: 0.6.1(chart.js@4.4.3)
       chartjs-plugin-zoom:
         specifier: 2.0.1
-        version: 2.0.1(chart.js@4.4.2)
+        version: 2.0.1(chart.js@4.4.3)
       chromatic:
-        specifier: 11.3.0
-        version: 11.3.0
+        specifier: 11.5.4
+        version: 11.5.4
       compare-versions:
         specifier: 6.1.0
         version: 6.1.0
@@ -803,23 +803,23 @@ importers:
         specifier: workspace:*
         version: link:../misskey-reversi
       photoswipe:
-        specifier: 5.4.3
-        version: 5.4.3
+        specifier: 5.4.4
+        version: 5.4.4
       punycode:
         specifier: 2.3.1
         version: 2.3.1
       rollup:
-        specifier: 4.17.2
-        version: 4.17.2
+        specifier: 4.18.0
+        version: 4.18.0
       sanitize-html:
         specifier: 2.13.0
         version: 2.13.0
       sass:
-        specifier: 1.76.0
-        version: 1.76.0
+        specifier: 1.77.6
+        version: 1.77.6
       shiki:
-        specifier: 1.4.0
-        version: 1.4.0
+        specifier: 1.10.0
+        version: 1.10.0
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -827,102 +827,99 @@ importers:
         specifier: 3.1.0
         version: 3.1.0
       three:
-        specifier: 0.164.1
-        version: 0.164.1
+        specifier: 0.165.0
+        version: 0.165.0
       throttle-debounce:
-        specifier: 5.0.0
-        version: 5.0.0
+        specifier: 5.0.2
+        version: 5.0.2
       tinycolor2:
         specifier: 1.6.0
         version: 1.6.0
       tsc-alias:
-        specifier: 1.8.8
-        version: 1.8.8
+        specifier: 1.8.10
+        version: 1.8.10
       tsconfig-paths:
         specifier: 4.2.0
         version: 4.2.0
       typescript:
-        specifier: 5.5.2
-        version: 5.5.2
+        specifier: 5.5.3
+        version: 5.5.3
       uuid:
-        specifier: 9.0.1
-        version: 9.0.1
+        specifier: 10.0.0
+        version: 10.0.0
       v-code-diff:
-        specifier: 1.11.0
-        version: 1.11.0(vue@3.4.26(typescript@5.5.2))
+        specifier: 1.12.0
+        version: 1.12.0(vue@3.4.31(typescript@5.5.3))
       vite:
-        specifier: 5.2.11
-        version: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+        specifier: 5.3.2
+        version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
       vue:
-        specifier: 3.4.26
-        version: 3.4.26(typescript@5.5.2)
+        specifier: 3.4.31
+        version: 3.4.31(typescript@5.5.3)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.4.26(typescript@5.5.2))
+        version: 4.1.0(vue@3.4.31(typescript@5.5.3))
     devDependencies:
-      '@misskey-dev/eslint-plugin':
-        specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
       '@misskey-dev/summaly':
         specifier: 5.1.0
         version: 5.1.0
       '@storybook/addon-actions':
-        specifier: 8.0.9
-        version: 8.0.9
+        specifier: 8.1.11
+        version: 8.1.11
       '@storybook/addon-essentials':
-        specifier: 8.0.9
-        version: 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.1.11
+        version: 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/addon-interactions':
-        specifier: 8.0.9
-        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        specifier: 8.1.11
+        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
       '@storybook/addon-links':
-        specifier: 8.0.9
-        version: 8.0.9(react@18.3.1)
+        specifier: 8.1.11
+        version: 8.1.11(react@18.3.1)
       '@storybook/addon-mdx-gfm':
-        specifier: 8.0.9
-        version: 8.0.9
+        specifier: 8.1.11
+        version: 8.1.11
       '@storybook/addon-storysource':
-        specifier: 8.0.9
-        version: 8.0.9
+        specifier: 8.1.11
+        version: 8.1.11
       '@storybook/blocks':
-        specifier: 8.0.9
-        version: 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.1.11
+        version: 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/components':
-        specifier: 8.0.9
-        version: 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.1.11
+        version: 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/core-events':
-        specifier: 8.0.9
-        version: 8.0.9
+        specifier: 8.1.11
+        version: 8.1.11
       '@storybook/manager-api':
-        specifier: 8.0.9
-        version: 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.1.11
+        version: 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/preview-api':
-        specifier: 8.0.9
-        version: 8.0.9
+        specifier: 8.1.11
+        version: 8.1.11
       '@storybook/react':
-        specifier: 8.0.9
-        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2)
+        specifier: 8.1.11
+        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)
       '@storybook/react-vite':
-        specifier: 8.0.9
-        version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
+        specifier: 8.1.11
+        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
       '@storybook/test':
-        specifier: 8.0.9
-        version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        specifier: 8.1.11
+        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
       '@storybook/theming':
-        specifier: 8.0.9
-        version: 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.1.11
+        version: 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/types':
-        specifier: 8.0.9
-        version: 8.0.9
+        specifier: 8.1.11
+        version: 8.1.11
       '@storybook/vue3':
-        specifier: 8.0.9
-        version: 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.5.2))
+        specifier: 8.1.11
+        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))
       '@storybook/vue3-vite':
-        specifier: 8.0.9
-        version: 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))
+        specifier: 8.1.11
+        version: 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
       '@testing-library/vue':
-        specifier: 8.0.3
-        version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))
+        specifier: 8.1.0
+        version: 8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -933,11 +930,11 @@ importers:
         specifier: 0.19.6
         version: 0.19.6
       '@types/micromatch':
-        specifier: 4.0.7
-        version: 4.0.7
+        specifier: 4.0.9
+        version: 4.0.9
       '@types/node':
-        specifier: 20.12.7
-        version: 20.12.7
+        specifier: 20.14.9
+        version: 20.14.9
       '@types/punycode':
         specifier: 2.1.4
         version: 2.1.4
@@ -954,41 +951,38 @@ importers:
         specifier: 1.4.6
         version: 1.4.6
       '@types/uuid':
-        specifier: 9.0.8
-        version: 9.0.8
+        specifier: 10.0.0
+        version: 10.0.0
       '@types/ws':
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
       '@typescript-eslint/parser':
-        specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
       '@vitest/coverage-v8':
-        specifier: 0.34.6
-        version: 0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        specifier: 1.6.0
+        version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
       '@vue/runtime-core':
-        specifier: 3.4.26
-        version: 3.4.26
+        specifier: 3.4.31
+        version: 3.4.31
       acorn:
-        specifier: 8.11.3
-        version: 8.11.3
+        specifier: 8.12.0
+        version: 8.12.0
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.8.1
-        version: 13.8.1
-      eslint:
-        specifier: 8.57.0
-        version: 8.57.0
+        specifier: 13.13.0
+        version: 13.13.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
       eslint-plugin-vue:
-        specifier: 9.25.0
-        version: 9.25.0(eslint@8.57.0)
+        specifier: 9.26.0
+        version: 9.26.0(eslint@9.6.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
@@ -999,20 +993,20 @@ importers:
         specifier: 0.12.2
         version: 0.12.2
       micromatch:
-        specifier: 4.0.5
-        version: 4.0.5
+        specifier: 4.0.7
+        version: 4.0.7
       msw:
-        specifier: 2.2.14
-        version: 2.2.14(typescript@5.5.2)
+        specifier: 2.3.1
+        version: 2.3.1(typescript@5.5.3)
       msw-storybook-addon:
-        specifier: 2.0.1
-        version: 2.0.1(msw@2.2.14(typescript@5.5.2))
+        specifier: 2.0.2
+        version: 2.0.2(msw@2.3.1(typescript@5.5.3))
       nodemon:
-        specifier: 3.1.0
-        version: 3.1.0
+        specifier: 3.1.4
+        version: 3.1.4
       prettier:
-        specifier: 3.2.5
-        version: 3.2.5
+        specifier: 3.3.2
+        version: 3.3.2
       react:
         specifier: 18.3.1
         version: 18.3.1
@@ -1023,32 +1017,32 @@ importers:
         specifier: 3.0.5
         version: 3.0.5
       start-server-and-test:
-        specifier: 2.0.3
-        version: 2.0.3
+        specifier: 2.0.4
+        version: 2.0.4
       storybook:
-        specifier: 8.0.9
-        version: 8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+        specifier: 8.1.11
+        version: 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
-        version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.1.11)(@storybook/manager-api@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.1.11)(@storybook/theming@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.1.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       vite-plugin-turbosnap:
         specifier: 1.0.3
         version: 1.0.3
       vitest:
-        specifier: 0.34.6
-        version: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+        specifier: 1.6.0
+        version: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
       vitest-fetch-mock:
         specifier: 0.2.2
-        version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+        version: 0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
       vue-component-type-helpers:
-        specifier: 2.0.16
-        version: 2.0.16
+        specifier: 2.0.24
+        version: 2.0.24
       vue-eslint-parser:
-        specifier: 9.4.2
-        version: 9.4.2(eslint@8.57.0)
+        specifier: 9.4.3
+        version: 9.4.3(eslint@9.6.0)
       vue-tsc:
-        specifier: 2.0.16
-        version: 2.0.16(typescript@5.5.2)
+        specifier: 2.0.24
+        version: 2.0.24(typescript@5.5.3)
 
   packages/misskey-bubble-game:
     dependencies:
@@ -1062,9 +1056,6 @@ importers:
         specifier: 3.0.5
         version: 3.0.5
     devDependencies:
-      '@misskey-dev/eslint-plugin':
-        specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0))(eslint@8.57.0)
       '@types/matter-js':
         specifier: 0.19.6
         version: 0.19.6
@@ -1076,16 +1067,13 @@ importers:
         version: 3.0.8
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
-        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+        version: 7.1.0(eslint@9.6.0)(typescript@5.3.3)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
-      eslint:
-        specifier: 8.57.0
-        version: 8.57.0
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -1109,41 +1097,35 @@ importers:
         version: 4.4.0
     devDependencies:
       '@microsoft/api-extractor':
-        specifier: 7.43.1
-        version: 7.43.1(@types/node@20.12.7)
-      '@misskey-dev/eslint-plugin':
-        specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
+        specifier: 7.47.0
+        version: 7.47.0(@types/node@20.14.9)
       '@swc/jest':
         specifier: 0.2.36
-        version: 0.2.36(@swc/core@1.4.17)
+        version: 0.2.36(@swc/core@1.6.6)
       '@types/jest':
         specifier: 29.5.12
         version: 29.5.12
       '@types/node':
-        specifier: 20.12.7
-        version: 20.12.7
+        specifier: 20.14.9
+        version: 20.14.9
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.7.1
-        version: 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
       '@typescript-eslint/parser':
-        specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
       esbuild:
-        specifier: 0.19.11
-        version: 0.19.11
-      eslint:
-        specifier: 8.57.0
-        version: 8.57.0
+        specifier: 0.22.0
+        version: 0.22.0
       execa:
-        specifier: 8.0.1
-        version: 8.0.1
+        specifier: 9.2.0
+        version: 9.2.0
       glob:
-        specifier: 10.3.12
-        version: 10.3.12
+        specifier: 10.4.2
+        version: 10.4.2
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.12.7)
+        version: 29.7.0(@types/node@20.14.9)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3(encoding@0.1.13)
@@ -1157,20 +1139,17 @@ importers:
         specifier: 2.0.0
         version: 2.0.0
       nodemon:
-        specifier: 3.1.0
-        version: 3.1.0
+        specifier: 3.1.4
+        version: 3.1.4
       tsd:
-        specifier: 0.30.7
-        version: 0.30.7
+        specifier: 0.31.1
+        version: 0.31.1
       typescript:
-        specifier: 5.5.2
-        version: 5.5.2
+        specifier: 5.5.3
+        version: 5.5.3
 
   packages/misskey-js/generator:
     devDependencies:
-      '@misskey-dev/eslint-plugin':
-        specifier: ^1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3))(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0))(eslint@8.53.0)
       '@readme/openapi-parser':
         specifier: 2.5.0
         version: 2.5.0(openapi-types@12.1.3)
@@ -1179,13 +1158,10 @@ importers:
         version: 20.9.1
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      eslint:
-        specifier: 8.53.0
-        version: 8.53.0
+        version: 6.11.0(eslint@9.6.0)(typescript@5.3.3)
       openapi-types:
         specifier: 12.1.3
         version: 12.1.3
@@ -1208,24 +1184,18 @@ importers:
         specifier: 1.2.2
         version: 1.2.2
     devDependencies:
-      '@misskey-dev/eslint-plugin':
-        specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0))(eslint@8.57.0)
       '@types/node':
         specifier: 20.11.5
         version: 20.11.5
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
-        version: 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+        version: 7.1.0(eslint@9.6.0)(typescript@5.3.3)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
-      eslint:
-        specifier: 8.57.0
-        version: 8.57.0
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -1242,8 +1212,8 @@ importers:
   packages/sw:
     dependencies:
       esbuild:
-        specifier: 0.20.2
-        version: 0.20.2
+        specifier: 0.22.0
+        version: 0.22.0
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -1251,27 +1221,21 @@ importers:
         specifier: workspace:*
         version: link:../misskey-js
     devDependencies:
-      '@misskey-dev/eslint-plugin':
-        specifier: 1.0.0
-        version: 1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)
       '@typescript-eslint/parser':
-        specifier: 7.7.1
-        version: 7.7.1(eslint@8.57.0)(typescript@5.5.2)
+        specifier: 7.15.0
+        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: '@types/serviceworker@0.0.67'
-      eslint:
-        specifier: 8.57.0
-        version: 8.57.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
       nodemon:
-        specifier: 3.1.0
-        version: 3.1.0
+        specifier: 3.1.4
+        version: 3.1.4
       typescript:
-        specifier: 5.5.2
-        version: 5.5.2
+        specifier: 5.5.3
+        version: 5.5.3
 
 packages:
 
@@ -1302,221 +1266,239 @@ packages:
     resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==}
     hasBin: true
 
-  '@aws-crypto/crc32@3.0.0':
-    resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==}
+  '@aws-crypto/crc32@5.2.0':
+    resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-crypto/crc32c@3.0.0':
-    resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==}
+  '@aws-crypto/crc32c@5.2.0':
+    resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==}
 
-  '@aws-crypto/ie11-detection@3.0.0':
-    resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==}
+  '@aws-crypto/sha1-browser@5.2.0':
+    resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==}
 
-  '@aws-crypto/sha1-browser@3.0.0':
-    resolution: {integrity: sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==}
+  '@aws-crypto/sha256-browser@5.2.0':
+    resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==}
 
-  '@aws-crypto/sha256-browser@3.0.0':
-    resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==}
+  '@aws-crypto/sha256-js@5.2.0':
+    resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-crypto/sha256-js@3.0.0':
-    resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==}
+  '@aws-crypto/supports-web-crypto@5.2.0':
+    resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==}
 
-  '@aws-crypto/supports-web-crypto@3.0.0':
-    resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==}
+  '@aws-crypto/util@5.2.0':
+    resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
 
-  '@aws-crypto/util@3.0.0':
-    resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==}
+  '@aws-sdk/client-s3@3.600.0':
+    resolution: {integrity: sha512-iYoKbJTputbf+ubkX6gSK/y/4uJEBRaXZ18jykLdBQ8UJuGrk2gqvV8h7OlGAhToCeysmmMqM0vDWyLt6lP8nw==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/client-s3@3.412.0':
-    resolution: {integrity: sha512-sNrlx9sSBmFUCqMgTznwk9Fee3PJat0nZ3RIDR5Crhsld/eexxrqb6TYKsxzFfBfXTL/oPh+/S5driRV2xsB8A==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/client-sso-oidc@3.600.0':
+    resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/client-sso@3.410.0':
-    resolution: {integrity: sha512-MC9GrgwtlOuSL2WS3DRM3dQ/5y+49KSMMJRH6JiEcU5vE0dX/OtEcX+VfEwpi73x5pSfIjm7xnzjzOFx+sQBIg==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/client-sso@3.598.0':
+    resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/client-sts@3.410.0':
-    resolution: {integrity: sha512-e6VMrBJtnTxxUXwDmkADGIvyppmDMFf4+cGGA68tVCUm1cFNlCI6M/67bVSIPN/WVKAAfhEL5O2vVXCM7aatYg==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/client-sts@3.600.0':
+    resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/credential-provider-env@3.410.0':
-    resolution: {integrity: sha512-c7TB9LbN0PkFOsXI0lcRJnqPNOmc4VBvrHf8jP/BkTDg4YUoKQKOFd4d0SqzODmlZiAyoMQVZTR4ISZo95Zj4Q==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/core@3.598.0':
+    resolution: {integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/credential-provider-ini@3.410.0':
-    resolution: {integrity: sha512-D8rcr5bRCFD0f42MPQ7K6TWZq5d3pfqrKINL1/bpfkK5BJbvq1BGYmR88UC6CLpTRtZ1LHY2HgYG0fp/2zjjww==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-env@3.598.0':
+    resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/credential-provider-node@3.410.0':
-    resolution: {integrity: sha512-0wmVm33T/j1FS7MZ/j+WsPlgSc0YnCXnpbWSov1Mn6R86SHI2b2JhdIPRRE4XbGfyW2QGNUl2CwoZVaqhXeF5g==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-http@3.598.0':
+    resolution: {integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/credential-provider-process@3.410.0':
-    resolution: {integrity: sha512-BMju1hlDCDNkkSZpKF5SQ8G0WCLRj6/Jvw9QmudLHJuVwYJXEW1r2AsVMg98OZ3hB9G+MAvHruHZIbMiNmUMXQ==}
-    engines: {node: '>=14.0.0'}
-
-  '@aws-sdk/credential-provider-sso@3.410.0':
-    resolution: {integrity: sha512-zEaoY/sY+KYTlQUkp9dvveAHf175b8RIt0DsQkDrRPtrg/RBHR00r5rFvz9+nrwsR8546RaBU7h/zzTaQGhmcA==}
-    engines: {node: '>=14.0.0'}
-
-  '@aws-sdk/credential-provider-web-identity@3.410.0':
-    resolution: {integrity: sha512-cE0l8LmEHdWbDkdPNgrfdYSgp4/cIVXrjUKI1QCATA729CrHZ/OQjB/maOBOrMHO9YTiggko887NkslVvwVB7w==}
-    engines: {node: '>=14.0.0'}
-
-  '@aws-sdk/lib-storage@3.412.0':
-    resolution: {integrity: sha512-uAdVtNuip06rJOs28zVrYXLNeHfKraxvJRTzTA+DW1dXkzh70GTKqDKHWH9IJkW/xMTE6wGSM+fDs8jsMOn/yA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-ini@3.598.0':
+    resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==}
+    engines: {node: '>=16.0.0'}
     peerDependencies:
-      '@aws-sdk/client-s3': ^3.0.0
+      '@aws-sdk/client-sts': ^3.598.0
 
-  '@aws-sdk/middleware-bucket-endpoint@3.410.0':
-    resolution: {integrity: sha512-pUGrpFgCKf9fDHu01JJhhw+MUImheS0HFlZwNG37OMubkxUAbCdmYGewGxfTCUvWyZJtx9bVjrSu6gG7w+RARg==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-node@3.600.0':
+    resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-expect-continue@3.410.0':
-    resolution: {integrity: sha512-e5YqGCNmW99GZjEPPujJ02RlEZql19U40oORysBhVF7mKz8BBvF3s8l37tvu37oxebDEkh1u/2cm2+ggOXxLjQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-process@3.598.0':
+    resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-flexible-checksums@3.410.0':
-    resolution: {integrity: sha512-IK7KlvEKtrQVBfmAp/MmGd0wbWLuN2GZwwfAmsU0qFb0f5vOVUbKDsu6tudtDKCBG9uXyTEsx3/QGvoK2zDy+g==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-sso@3.598.0':
+    resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-host-header@3.410.0':
-    resolution: {integrity: sha512-ED/OVcyITln5rrxnajZP+V0PN1nug+gSDHJDqdDo/oLy7eiDr/ZWn3nlWW7WcMplQ1/Jnb+hK0UetBp/25XooA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/credential-provider-web-identity@3.598.0':
+    resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      '@aws-sdk/client-sts': ^3.598.0
 
-  '@aws-sdk/middleware-location-constraint@3.410.0':
-    resolution: {integrity: sha512-jAftSpOpw/5AdpOJ/cGiXCb+Vv22KXR5QZmxmllUDsnlm18672tpRaI2plmu/1d98CVvqhY61eSklFMrIf2c4w==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/lib-storage@3.600.0':
+    resolution: {integrity: sha512-jjgGMmFykXBAs8YO3ghgnVSjM/uf99jvVQqKJfDjwXUCLPrsZqk14v2WcDCWAXzeAroDvIOVQO1V/RR8fK18Pw==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      '@aws-sdk/client-s3': ^3.600.0
 
-  '@aws-sdk/middleware-logger@3.410.0':
-    resolution: {integrity: sha512-YtmKYCVtBfScq3/UFJk+aSZOktKJBNZL9DaSc2aPcy/goCVsYDOkGwtHk0jIkC1JRSNCkVTqL7ya60sSr8zaQQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-bucket-endpoint@3.598.0':
+    resolution: {integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-recursion-detection@3.410.0':
-    resolution: {integrity: sha512-KWaes5FLzRqj28vaIEE4Bimpga2E596WdPF2HaH6zsVMJddoRDsc3ZX9ZhLOGrXzIO1RqBd0QxbLrM0S/B2aOQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-expect-continue@3.598.0':
+    resolution: {integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-sdk-s3@3.410.0':
-    resolution: {integrity: sha512-K2sG2V1ZkezYMCIy3uMt0MwtflcfIwLptwm0iFLaYitiINZQ1tcslk9ggAjyTHg0rslDSI4/zjkhy8VHFOV7HA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-flexible-checksums@3.598.0':
+    resolution: {integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-sdk-sts@3.410.0':
-    resolution: {integrity: sha512-YfBpctDocRR4CcROoDueJA7D+aMLBV8nTFfmVNdLLLgyuLZ/AUR11VQSu1lf9gQZKl8IpKE/BLf2fRE/qV1ZuA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-host-header@3.598.0':
+    resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-signing@3.410.0':
-    resolution: {integrity: sha512-KBAZ/eoAJUSJv5us2HsKwK2OszG2s9FEyKpEhgnHLcbbKzW873zHBH5GcOGEQu4AWArTy2ndzJu3FF+9/J9hJQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-location-constraint@3.598.0':
+    resolution: {integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-ssec@3.410.0':
-    resolution: {integrity: sha512-DNsjVTXoxIh+PuW9o45CFaMiconbuZRm19MC3NA1yNCaCj3ZxD5OdXAutq6UjQdrx8UG4EjUlCJEEvBKmboITw==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-logger@3.598.0':
+    resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/middleware-user-agent@3.410.0':
-    resolution: {integrity: sha512-ZayDtLfvCZUohSxQc/49BfoU/y6bDHLfLdyyUJbJ54Sv8zQcrmdyKvCBFUZwE6tHQgAmv9/ZT18xECMl+xiONA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-recursion-detection@3.598.0':
+    resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/signature-v4-multi-region@3.412.0':
-    resolution: {integrity: sha512-ijxOeYpNDuk2T940S9HYcZ1C+wTP9vqp1Cw37zw9whVY2mKV3Vr7i+44D4FQ5HhWULgdwhjD7IctbNxPIPzUZQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-sdk-s3@3.598.0':
+    resolution: {integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/token-providers@3.410.0':
-    resolution: {integrity: sha512-d5Nc0xydkH/X0LA1HDyhGY5sEv4LuADFk+QpDtT8ogLilcre+b1jpdY8Sih/gd1KoGS1H+d1tz2hSGwUHAbUbw==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-signing@3.598.0':
+    resolution: {integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/types@3.410.0':
-    resolution: {integrity: sha512-D7iaUCszv/v04NDaZUmCmekamy6VD/lKozm/3gS9+dkfU6cC2CsNoUfPV8BlV6dPdw0oWgF91am3I1stdvfVrQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-ssec@3.598.0':
+    resolution: {integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/types@3.413.0':
-    resolution: {integrity: sha512-j1xib0f/TazIFc5ySIKOlT1ujntRbaoG4LJFeEezz4ji03/wSJMI8Vi4KjzpBp8J1tTu0oRDnsxRIGixsUBeYQ==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/middleware-user-agent@3.598.0':
+    resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/util-arn-parser@3.310.0':
-    resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/region-config-resolver@3.598.0':
+    resolution: {integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==}
+    engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/util-endpoints@3.410.0':
-    resolution: {integrity: sha512-iNiqJyC7N3+8zFwnXUqcWSxrZecVZLToo1iTQQdeYL2af1IcOtRgb7n8jpAI/hmXhBSx2+3RI+Y7pxyFo1vu+w==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/signature-v4-multi-region@3.598.0':
+    resolution: {integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/token-providers@3.598.0':
+    resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      '@aws-sdk/client-sso-oidc': ^3.598.0
+
+  '@aws-sdk/types@3.598.0':
+    resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/util-arn-parser@3.568.0':
+    resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/util-endpoints@3.598.0':
+    resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==}
+    engines: {node: '>=16.0.0'}
 
   '@aws-sdk/util-locate-window@3.208.0':
     resolution: {integrity: sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==}
     engines: {node: '>=14.0.0'}
 
-  '@aws-sdk/util-user-agent-browser@3.410.0':
-    resolution: {integrity: sha512-i1G/XGpXGMRT2zEiAhi1xucJsfCWk8nNYjk/LbC0sA+7B9Huri96YAzVib12wkHPsJQvZxZC6CpQDIHWm4lXMA==}
+  '@aws-sdk/util-user-agent-browser@3.598.0':
+    resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==}
 
-  '@aws-sdk/util-user-agent-node@3.410.0':
-    resolution: {integrity: sha512-bK70t1jHRl8HrJXd4hEIwc5PBZ7U0w+81AKFnanIVKZwZedd6nLibUXDTK14z/Jp2GFcBqd4zkt2YLGkRt/U4A==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/util-user-agent-node@3.598.0':
+    resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==}
+    engines: {node: '>=16.0.0'}
     peerDependencies:
       aws-crt: '>=1.0.0'
     peerDependenciesMeta:
       aws-crt:
         optional: true
 
-  '@aws-sdk/util-utf8-browser@3.259.0':
-    resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==}
-
-  '@aws-sdk/xml-builder@3.310.0':
-    resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==}
-    engines: {node: '>=14.0.0'}
+  '@aws-sdk/xml-builder@3.598.0':
+    resolution: {integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q==}
+    engines: {node: '>=16.0.0'}
 
   '@babel/code-frame@7.23.5':
     resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/code-frame@7.24.7':
+    resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/compat-data@7.23.5':
     resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/compat-data@7.24.7':
+    resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/core@7.23.5':
     resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/core@7.24.0':
-    resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==}
+  '@babel/core@7.24.7':
+    resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==}
     engines: {node: '>=6.9.0'}
 
   '@babel/generator@7.23.5':
     resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/generator@7.23.6':
-    resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
+  '@babel/generator@7.24.7':
+    resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-annotate-as-pure@7.22.5':
-    resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
+  '@babel/helper-annotate-as-pure@7.24.7':
+    resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-builder-binary-assignment-operator-visitor@7.22.15':
-    resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==}
+  '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
+    resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==}
     engines: {node: '>=6.9.0'}
 
   '@babel/helper-compilation-targets@7.22.15':
     resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-compilation-targets@7.23.6':
-    resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
+  '@babel/helper-compilation-targets@7.24.7':
+    resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-create-class-features-plugin@7.23.5':
-    resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
+  '@babel/helper-create-class-features-plugin@7.24.7':
+    resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/helper-create-regexp-features-plugin@7.22.15':
-    resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==}
+  '@babel/helper-create-regexp-features-plugin@7.24.7':
+    resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/helper-define-polyfill-provider@0.4.3':
-    resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==}
+  '@babel/helper-define-polyfill-provider@0.6.2':
+    resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
 
@@ -1524,44 +1506,70 @@ packages:
     resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-environment-visitor@7.24.7':
+    resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-function-name@7.23.0':
     resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-function-name@7.24.7':
+    resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-hoist-variables@7.22.5':
     resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-member-expression-to-functions@7.23.0':
-    resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
+  '@babel/helper-hoist-variables@7.24.7':
+    resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-member-expression-to-functions@7.24.7':
+    resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==}
     engines: {node: '>=6.9.0'}
 
   '@babel/helper-module-imports@7.22.15':
     resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-module-imports@7.24.7':
+    resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-module-transforms@7.23.3':
     resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/helper-optimise-call-expression@7.22.5':
-    resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
+  '@babel/helper-module-transforms@7.24.7':
+    resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-optimise-call-expression@7.24.7':
+    resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==}
     engines: {node: '>=6.9.0'}
 
   '@babel/helper-plugin-utils@7.22.5':
     resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-remap-async-to-generator@7.22.20':
-    resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==}
+  '@babel/helper-plugin-utils@7.24.7':
+    resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-remap-async-to-generator@7.24.7':
+    resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/helper-replace-supers@7.22.20':
-    resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
+  '@babel/helper-replace-supers@7.24.7':
+    resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
@@ -1570,71 +1578,101 @@ packages:
     resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-skip-transparent-expression-wrappers@7.22.5':
-    resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
+  '@babel/helper-simple-access@7.24.7':
+    resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
+    resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==}
     engines: {node: '>=6.9.0'}
 
   '@babel/helper-split-export-declaration@7.22.6':
     resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-split-export-declaration@7.24.7':
+    resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-string-parser@7.23.4':
     resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-string-parser@7.24.7':
+    resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-validator-identifier@7.22.20':
     resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-validator-identifier@7.24.7':
+    resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-validator-option@7.23.5':
     resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helper-wrap-function@7.22.20':
-    resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==}
+  '@babel/helper-validator-option@7.24.7':
+    resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-wrap-function@7.24.7':
+    resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==}
     engines: {node: '>=6.9.0'}
 
   '@babel/helpers@7.23.5':
     resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/helpers@7.24.0':
-    resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==}
+  '@babel/helpers@7.24.7':
+    resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==}
     engines: {node: '>=6.9.0'}
 
   '@babel/highlight@7.23.4':
     resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/highlight@7.24.7':
+    resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/parser@7.23.9':
     resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==}
     engines: {node: '>=6.0.0'}
     hasBin: true
 
-  '@babel/parser@7.24.0':
-    resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-
   '@babel/parser@7.24.5':
     resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==}
     engines: {node: '>=6.0.0'}
     hasBin: true
 
-  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3':
-    resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
+  '@babel/parser@7.24.7':
+    resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7':
+    resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3':
-    resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==}
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7':
+    resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7':
+    resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.13.0
 
-  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3':
-    resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==}
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7':
+    resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
@@ -1682,14 +1720,14 @@ packages:
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-syntax-import-assertions@7.23.3':
-    resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==}
+  '@babel/plugin-syntax-import-assertions@7.24.7':
+    resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-syntax-import-attributes@7.23.3':
-    resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==}
+  '@babel/plugin-syntax-import-attributes@7.24.7':
+    resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
@@ -1764,92 +1802,92 @@ packages:
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/plugin-transform-arrow-functions@7.23.3':
-    resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==}
+  '@babel/plugin-transform-arrow-functions@7.24.7':
+    resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-async-generator-functions@7.23.4':
-    resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==}
+  '@babel/plugin-transform-async-generator-functions@7.24.7':
+    resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-async-to-generator@7.23.3':
-    resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==}
+  '@babel/plugin-transform-async-to-generator@7.24.7':
+    resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-block-scoped-functions@7.23.3':
-    resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==}
+  '@babel/plugin-transform-block-scoped-functions@7.24.7':
+    resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-block-scoping@7.23.4':
-    resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==}
+  '@babel/plugin-transform-block-scoping@7.24.7':
+    resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-class-properties@7.23.3':
-    resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==}
+  '@babel/plugin-transform-class-properties@7.24.7':
+    resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-class-static-block@7.23.4':
-    resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==}
+  '@babel/plugin-transform-class-static-block@7.24.7':
+    resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.12.0
 
-  '@babel/plugin-transform-classes@7.23.5':
-    resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==}
+  '@babel/plugin-transform-classes@7.24.7':
+    resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-computed-properties@7.23.3':
-    resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==}
+  '@babel/plugin-transform-computed-properties@7.24.7':
+    resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-destructuring@7.23.3':
-    resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==}
+  '@babel/plugin-transform-destructuring@7.24.7':
+    resolution: {integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-dotall-regex@7.23.3':
-    resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==}
+  '@babel/plugin-transform-dotall-regex@7.24.7':
+    resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-duplicate-keys@7.23.3':
-    resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==}
+  '@babel/plugin-transform-duplicate-keys@7.24.7':
+    resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-dynamic-import@7.23.4':
-    resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==}
+  '@babel/plugin-transform-dynamic-import@7.24.7':
+    resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-exponentiation-operator@7.23.3':
-    resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==}
+  '@babel/plugin-transform-exponentiation-operator@7.24.7':
+    resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-export-namespace-from@7.23.4':
-    resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==}
+  '@babel/plugin-transform-export-namespace-from@7.24.7':
+    resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
@@ -1860,176 +1898,176 @@ packages:
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-for-of@7.23.3':
-    resolution: {integrity: sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==}
+  '@babel/plugin-transform-for-of@7.24.7':
+    resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-function-name@7.23.3':
-    resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==}
+  '@babel/plugin-transform-function-name@7.24.7':
+    resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-json-strings@7.23.4':
-    resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==}
+  '@babel/plugin-transform-json-strings@7.24.7':
+    resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-literals@7.23.3':
-    resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==}
+  '@babel/plugin-transform-literals@7.24.7':
+    resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-logical-assignment-operators@7.23.4':
-    resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==}
+  '@babel/plugin-transform-logical-assignment-operators@7.24.7':
+    resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-member-expression-literals@7.23.3':
-    resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==}
+  '@babel/plugin-transform-member-expression-literals@7.24.7':
+    resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-modules-amd@7.23.3':
-    resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==}
+  '@babel/plugin-transform-modules-amd@7.24.7':
+    resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-modules-commonjs@7.23.3':
-    resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
+  '@babel/plugin-transform-modules-commonjs@7.24.7':
+    resolution: {integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-modules-systemjs@7.23.3':
-    resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==}
+  '@babel/plugin-transform-modules-systemjs@7.24.7':
+    resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-modules-umd@7.23.3':
-    resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==}
+  '@babel/plugin-transform-modules-umd@7.24.7':
+    resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-named-capturing-groups-regex@7.22.5':
-    resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==}
+  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7':
+    resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/plugin-transform-new-target@7.23.3':
-    resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==}
+  '@babel/plugin-transform-new-target@7.24.7':
+    resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-nullish-coalescing-operator@7.23.4':
-    resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==}
+  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7':
+    resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-numeric-separator@7.23.4':
-    resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==}
+  '@babel/plugin-transform-numeric-separator@7.24.7':
+    resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-object-rest-spread@7.23.4':
-    resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==}
+  '@babel/plugin-transform-object-rest-spread@7.24.7':
+    resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-object-super@7.23.3':
-    resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==}
+  '@babel/plugin-transform-object-super@7.24.7':
+    resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-optional-catch-binding@7.23.4':
-    resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==}
+  '@babel/plugin-transform-optional-catch-binding@7.24.7':
+    resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-optional-chaining@7.23.4':
-    resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==}
+  '@babel/plugin-transform-optional-chaining@7.24.7':
+    resolution: {integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-parameters@7.23.3':
-    resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==}
+  '@babel/plugin-transform-parameters@7.24.7':
+    resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-private-methods@7.23.3':
-    resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==}
+  '@babel/plugin-transform-private-methods@7.24.7':
+    resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-private-property-in-object@7.23.4':
-    resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==}
+  '@babel/plugin-transform-private-property-in-object@7.24.7':
+    resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-property-literals@7.23.3':
-    resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==}
+  '@babel/plugin-transform-property-literals@7.24.7':
+    resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-regenerator@7.23.3':
-    resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==}
+  '@babel/plugin-transform-regenerator@7.24.7':
+    resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-reserved-words@7.23.3':
-    resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==}
+  '@babel/plugin-transform-reserved-words@7.24.7':
+    resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-shorthand-properties@7.23.3':
-    resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==}
+  '@babel/plugin-transform-shorthand-properties@7.24.7':
+    resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-spread@7.23.3':
-    resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==}
+  '@babel/plugin-transform-spread@7.24.7':
+    resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-sticky-regex@7.23.3':
-    resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==}
+  '@babel/plugin-transform-sticky-regex@7.24.7':
+    resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-template-literals@7.23.3':
-    resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==}
+  '@babel/plugin-transform-template-literals@7.24.7':
+    resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-typeof-symbol@7.23.3':
-    resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==}
+  '@babel/plugin-transform-typeof-symbol@7.24.7':
+    resolution: {integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
@@ -2040,32 +2078,32 @@ packages:
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-unicode-escapes@7.23.3':
-    resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==}
+  '@babel/plugin-transform-unicode-escapes@7.24.7':
+    resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-unicode-property-regex@7.23.3':
-    resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==}
+  '@babel/plugin-transform-unicode-property-regex@7.24.7':
+    resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-unicode-regex@7.23.3':
-    resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==}
+  '@babel/plugin-transform-unicode-regex@7.24.7':
+    resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
 
-  '@babel/plugin-transform-unicode-sets-regex@7.23.3':
-    resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==}
+  '@babel/plugin-transform-unicode-sets-regex@7.24.7':
+    resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
 
-  '@babel/preset-env@7.23.5':
-    resolution: {integrity: sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A==}
+  '@babel/preset-env@7.24.7':
+    resolution: {integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
@@ -2108,12 +2146,16 @@ packages:
     resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/template@7.24.7':
+    resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/traverse@7.23.5':
     resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
     engines: {node: '>=6.9.0'}
 
-  '@babel/traverse@7.24.0':
-    resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==}
+  '@babel/traverse@7.24.7':
+    resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==}
     engines: {node: '>=6.9.0'}
 
   '@babel/types@7.23.5':
@@ -2124,22 +2166,26 @@ packages:
     resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/types@7.24.7':
+    resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==}
+    engines: {node: '>=6.9.0'}
+
   '@base2/pretty-print-object@1.0.1':
     resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==}
 
   '@bcoe/v8-coverage@0.2.3':
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
 
-  '@bull-board/api@5.17.0':
-    resolution: {integrity: sha512-qU+AiZIaYa//rkt1x7jDowtYa8u7/dLsDfEWgenZMkgvUszZ1kxJszdCtGapsDTVyPmnXgTRxpOWcR6sAYwSNQ==}
+  '@bull-board/api@5.20.5':
+    resolution: {integrity: sha512-YI95JK5A4/K4KB5VWbQn/CYNB+AO5cZ/BnZ77LxAhsaJ3ssHBN3Au0n3Z4wD7O+78+W3ON9uqGjKnHV6rXBGcQ==}
     peerDependencies:
-      '@bull-board/ui': 5.17.0
+      '@bull-board/ui': 5.20.5
 
-  '@bull-board/fastify@5.17.0':
-    resolution: {integrity: sha512-73YrPc7ERTWSOQRgBP6a7BPscWfcHd8U+Zq0auMdL/KkjPhG9GxapbfnovGZDDahJL/p/4YQb6ULu03zdtOrEA==}
+  '@bull-board/fastify@5.20.5':
+    resolution: {integrity: sha512-tdMR97xbzEzBbMJiJQreJHGdhfOocQn61K/WqM9I038Dk1dBHM5phQJxRJhspvwEJV4jwAayNOZbzuETI7QKwA==}
 
-  '@bull-board/ui@5.17.0':
-    resolution: {integrity: sha512-Vj+yWPjrjx3Iqh2N/ZBDhK2d2yJD44dfvIxm+SnXQb4ne312j117TpViInceysxGtbbAOlAW6hq6JvsDoRl7KQ==}
+  '@bull-board/ui@5.20.5':
+    resolution: {integrity: sha512-RV9VlW4qVL1A0Dewpsor4z7ZL9D56OW9LcRYjvXrIU5FSzvTvYKofmrUYoVrNQDs6jGMwJic+dMiW9K8GUU15A==}
 
   '@bundled-es-modules/cookie@2.0.0':
     resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==}
@@ -2219,12 +2265,18 @@ packages:
     cpu: [ppc64]
     os: [aix]
 
-  '@esbuild/aix-ppc64@0.20.2':
-    resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
+  '@esbuild/aix-ppc64@0.21.5':
+    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [aix]
 
+  '@esbuild/aix-ppc64@0.22.0':
+    resolution: {integrity: sha512-uvQR2crZ/zgzSHDvdygHyNI+ze9zwS8mqz0YtGXotSqvEE0UkYE9s+FZKQNTt1VtT719mfP3vHrUdCpxBNQZhQ==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
   '@esbuild/android-arm64@0.18.20':
     resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
     engines: {node: '>=12'}
@@ -2237,12 +2289,18 @@ packages:
     cpu: [arm64]
     os: [android]
 
-  '@esbuild/android-arm64@0.20.2':
-    resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
+  '@esbuild/android-arm64@0.21.5':
+    resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [android]
 
+  '@esbuild/android-arm64@0.22.0':
+    resolution: {integrity: sha512-UKhPb3o2gAB/bfXcl58ZXTn1q2oVu1rEu/bKrCtmm+Nj5MKUbrOwR5WAixE2v+lk0amWuwPvhnPpBRLIGiq7ig==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
   '@esbuild/android-arm@0.18.20':
     resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
     engines: {node: '>=12'}
@@ -2255,12 +2313,18 @@ packages:
     cpu: [arm]
     os: [android]
 
-  '@esbuild/android-arm@0.20.2':
-    resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
+  '@esbuild/android-arm@0.21.5':
+    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [android]
 
+  '@esbuild/android-arm@0.22.0':
+    resolution: {integrity: sha512-PBnyP+r8vJE4ifxsWys9l+Mc2UY/yYZOpX82eoyGISXXb3dRr0M21v+s4fgRKWMFPMSf/iyowqPW/u7ScSUkjQ==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
   '@esbuild/android-x64@0.18.20':
     resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
     engines: {node: '>=12'}
@@ -2273,12 +2337,18 @@ packages:
     cpu: [x64]
     os: [android]
 
-  '@esbuild/android-x64@0.20.2':
-    resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
+  '@esbuild/android-x64@0.21.5':
+    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [android]
 
+  '@esbuild/android-x64@0.22.0':
+    resolution: {integrity: sha512-IjTYtvIrjhR41Ijy2dDPgYjQHWG/x/A4KXYbs1fiU3efpRdoxMChK3oEZV6GPzVEzJqxFgcuBaiX1kwEvWUxSw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
   '@esbuild/darwin-arm64@0.18.20':
     resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
     engines: {node: '>=12'}
@@ -2291,12 +2361,18 @@ packages:
     cpu: [arm64]
     os: [darwin]
 
-  '@esbuild/darwin-arm64@0.20.2':
-    resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
+  '@esbuild/darwin-arm64@0.21.5':
+    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [darwin]
 
+  '@esbuild/darwin-arm64@0.22.0':
+    resolution: {integrity: sha512-mqt+Go4y9wRvEz81bhKd9RpHsQR1LwU8Xm6jZRUV/xpM7cIQFbFH6wBCLPTNsdELBvfoHeumud7X78jQQJv2TA==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
   '@esbuild/darwin-x64@0.18.20':
     resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
     engines: {node: '>=12'}
@@ -2309,12 +2385,18 @@ packages:
     cpu: [x64]
     os: [darwin]
 
-  '@esbuild/darwin-x64@0.20.2':
-    resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
+  '@esbuild/darwin-x64@0.21.5':
+    resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [darwin]
 
+  '@esbuild/darwin-x64@0.22.0':
+    resolution: {integrity: sha512-vTaTQ9OgYc3VTaWtOE5pSuDT6H3d/qSRFRfSBbnxFfzAvYoB3pqKXA0LEbi/oT8GUOEAutspfRMqPj2ezdFaMw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
   '@esbuild/freebsd-arm64@0.18.20':
     resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
     engines: {node: '>=12'}
@@ -2327,12 +2409,18 @@ packages:
     cpu: [arm64]
     os: [freebsd]
 
-  '@esbuild/freebsd-arm64@0.20.2':
-    resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
+  '@esbuild/freebsd-arm64@0.21.5':
+    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [freebsd]
 
+  '@esbuild/freebsd-arm64@0.22.0':
+    resolution: {integrity: sha512-0e1ZgoobJzaGnR4reD7I9rYZ7ttqdh1KPvJWnquUoDJhL0rYwdneeLailBzd2/4g/U5p4e5TIHEWa68NF2hFpQ==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
   '@esbuild/freebsd-x64@0.18.20':
     resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
     engines: {node: '>=12'}
@@ -2345,12 +2433,18 @@ packages:
     cpu: [x64]
     os: [freebsd]
 
-  '@esbuild/freebsd-x64@0.20.2':
-    resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
+  '@esbuild/freebsd-x64@0.21.5':
+    resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [freebsd]
 
+  '@esbuild/freebsd-x64@0.22.0':
+    resolution: {integrity: sha512-BFgyYwlCwRWyPQJtkzqq2p6pJbiiWgp0P9PNf7a5FQ1itKY4czPuOMAlFVItirSmEpRPCeImuwePNScZS0pL5Q==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
   '@esbuild/linux-arm64@0.18.20':
     resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
     engines: {node: '>=12'}
@@ -2363,12 +2457,18 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@esbuild/linux-arm64@0.20.2':
-    resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
+  '@esbuild/linux-arm64@0.21.5':
+    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [linux]
 
+  '@esbuild/linux-arm64@0.22.0':
+    resolution: {integrity: sha512-V/K2rctCUgC0PCXpN7AqT4hoazXKgIYugFGu/myk2+pfe6jTW2guz/TBwq4cZ7ESqusR/IzkcQaBkcjquuBWsw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
   '@esbuild/linux-arm@0.18.20':
     resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
     engines: {node: '>=12'}
@@ -2381,12 +2481,18 @@ packages:
     cpu: [arm]
     os: [linux]
 
-  '@esbuild/linux-arm@0.20.2':
-    resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
+  '@esbuild/linux-arm@0.21.5':
+    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [linux]
 
+  '@esbuild/linux-arm@0.22.0':
+    resolution: {integrity: sha512-KEMWiA9aGuPUD4BH5yjlhElLgaRXe+Eri6gKBoDazoPBTo1BXc/e6IW5FcJO9DoL19FBeCxgONyh95hLDNepIg==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
   '@esbuild/linux-ia32@0.18.20':
     resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
     engines: {node: '>=12'}
@@ -2399,12 +2505,18 @@ packages:
     cpu: [ia32]
     os: [linux]
 
-  '@esbuild/linux-ia32@0.20.2':
-    resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
+  '@esbuild/linux-ia32@0.21.5':
+    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [linux]
 
+  '@esbuild/linux-ia32@0.22.0':
+    resolution: {integrity: sha512-r2ZZqkOMOrpUhzNwxI7uLAHIDwkfeqmTnrv1cjpL/rjllPWszgqmprd/om9oviKXUBpMqHbXmppvjAYgISb26Q==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
   '@esbuild/linux-loong64@0.18.20':
     resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
     engines: {node: '>=12'}
@@ -2417,12 +2529,18 @@ packages:
     cpu: [loong64]
     os: [linux]
 
-  '@esbuild/linux-loong64@0.20.2':
-    resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
+  '@esbuild/linux-loong64@0.21.5':
+    resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
     engines: {node: '>=12'}
     cpu: [loong64]
     os: [linux]
 
+  '@esbuild/linux-loong64@0.22.0':
+    resolution: {integrity: sha512-qaowLrV/YOMAL2RfKQ4C/VaDzAuLDuylM2sd/LH+4OFirMl6CuDpRlCq4u49ZBaVV8pkI/Y+hTdiibvQRhojCA==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
   '@esbuild/linux-mips64el@0.18.20':
     resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
     engines: {node: '>=12'}
@@ -2435,12 +2553,18 @@ packages:
     cpu: [mips64el]
     os: [linux]
 
-  '@esbuild/linux-mips64el@0.20.2':
-    resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
+  '@esbuild/linux-mips64el@0.21.5':
+    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
     engines: {node: '>=12'}
     cpu: [mips64el]
     os: [linux]
 
+  '@esbuild/linux-mips64el@0.22.0':
+    resolution: {integrity: sha512-hgrezzjQTRxjkQ5k08J6rtZN5PNnkWx/Rz6Kmj9gnsdCAX1I4Dn4ZPqvFRkXo55Q3pnVQJBwbdtrTO7tMGtyVA==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
   '@esbuild/linux-ppc64@0.18.20':
     resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
     engines: {node: '>=12'}
@@ -2453,12 +2577,18 @@ packages:
     cpu: [ppc64]
     os: [linux]
 
-  '@esbuild/linux-ppc64@0.20.2':
-    resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
+  '@esbuild/linux-ppc64@0.21.5':
+    resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [linux]
 
+  '@esbuild/linux-ppc64@0.22.0':
+    resolution: {integrity: sha512-ewxg6FLLUio883XgSjfULEmDl3VPv/TYNnRprVAS3QeGFLdCYdx1tIudBcd7n9jIdk82v1Ajov4jx87qW7h9+g==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
   '@esbuild/linux-riscv64@0.18.20':
     resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
     engines: {node: '>=12'}
@@ -2471,12 +2601,18 @@ packages:
     cpu: [riscv64]
     os: [linux]
 
-  '@esbuild/linux-riscv64@0.20.2':
-    resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
+  '@esbuild/linux-riscv64@0.21.5':
+    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
     engines: {node: '>=12'}
     cpu: [riscv64]
     os: [linux]
 
+  '@esbuild/linux-riscv64@0.22.0':
+    resolution: {integrity: sha512-Az5XbgSJC2lE8XK8pdcutsf9RgdafWdTpUK/+6uaDdfkviw/B4JCwAfh1qVeRWwOohwdsl4ywZrWBNWxwrPLFg==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
   '@esbuild/linux-s390x@0.18.20':
     resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
     engines: {node: '>=12'}
@@ -2489,12 +2625,18 @@ packages:
     cpu: [s390x]
     os: [linux]
 
-  '@esbuild/linux-s390x@0.20.2':
-    resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
+  '@esbuild/linux-s390x@0.21.5':
+    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
     engines: {node: '>=12'}
     cpu: [s390x]
     os: [linux]
 
+  '@esbuild/linux-s390x@0.22.0':
+    resolution: {integrity: sha512-8j4a2ChT9+V34NNNY9c/gMldutaJFmfMacTPq4KfNKwv2fitBCLYjee7c+Vxaha2nUhPK7cXcZpJtJ3+Y7ZdVQ==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
   '@esbuild/linux-x64@0.18.20':
     resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
     engines: {node: '>=12'}
@@ -2507,12 +2649,18 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@esbuild/linux-x64@0.20.2':
-    resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
+  '@esbuild/linux-x64@0.21.5':
+    resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [linux]
 
+  '@esbuild/linux-x64@0.22.0':
+    resolution: {integrity: sha512-JUQyOnpbAkkRFOk/AhsEemz5TfWN4FJZxVObUlnlNCbe7QBl61ZNfM4cwBXayQA6laMJMUcqLHaYQHAB6YQ95Q==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
   '@esbuild/netbsd-x64@0.18.20':
     resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
     engines: {node: '>=12'}
@@ -2525,12 +2673,24 @@ packages:
     cpu: [x64]
     os: [netbsd]
 
-  '@esbuild/netbsd-x64@0.20.2':
-    resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
+  '@esbuild/netbsd-x64@0.21.5':
+    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [netbsd]
 
+  '@esbuild/netbsd-x64@0.22.0':
+    resolution: {integrity: sha512-11PoCoHXo4HFNbLsXuMB6bpMPWGDiw7xETji6COdJss4SQZLvcgNoeSqWtATRm10Jj1uEHiaIk4N0PiN6x4Fcg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-arm64@0.22.0':
+    resolution: {integrity: sha512-Ezlhu/YyITmXwKSB+Zu/QqD7cxrjrpiw85cc0Rbd3AWr2wsgp+dWbWOE8MqHaLW9NKMZvuL0DhbJbvzR7F6Zvg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
   '@esbuild/openbsd-x64@0.18.20':
     resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
     engines: {node: '>=12'}
@@ -2543,12 +2703,18 @@ packages:
     cpu: [x64]
     os: [openbsd]
 
-  '@esbuild/openbsd-x64@0.20.2':
-    resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
+  '@esbuild/openbsd-x64@0.21.5':
+    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [openbsd]
 
+  '@esbuild/openbsd-x64@0.22.0':
+    resolution: {integrity: sha512-ufjdW5tFJGUjlH9j/5cCE9lrwRffyZh+T4vYvoDKoYsC6IXbwaFeV/ENxeNXcxotF0P8CDzoICXVSbJaGBhkrw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
   '@esbuild/sunos-x64@0.18.20':
     resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
     engines: {node: '>=12'}
@@ -2561,12 +2727,18 @@ packages:
     cpu: [x64]
     os: [sunos]
 
-  '@esbuild/sunos-x64@0.20.2':
-    resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
+  '@esbuild/sunos-x64@0.21.5':
+    resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [sunos]
 
+  '@esbuild/sunos-x64@0.22.0':
+    resolution: {integrity: sha512-zY6ly/AoSmKnmNTowDJsK5ehra153/5ZhqxNLfq9NRsTTltetr+yHHcQ4RW7QDqw4JC8A1uC1YmeSfK9NRcK1w==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
   '@esbuild/win32-arm64@0.18.20':
     resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
     engines: {node: '>=12'}
@@ -2579,12 +2751,18 @@ packages:
     cpu: [arm64]
     os: [win32]
 
-  '@esbuild/win32-arm64@0.20.2':
-    resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
+  '@esbuild/win32-arm64@0.21.5':
+    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [win32]
 
+  '@esbuild/win32-arm64@0.22.0':
+    resolution: {integrity: sha512-Kml5F7tv/1Maam0pbbCrvkk9vj046dPej30kFzlhXnhuCtYYBP6FGy/cLbc5yUT1lkZznGLf2OvuvmLjscO5rw==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
   '@esbuild/win32-ia32@0.18.20':
     resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
     engines: {node: '>=12'}
@@ -2597,12 +2775,18 @@ packages:
     cpu: [ia32]
     os: [win32]
 
-  '@esbuild/win32-ia32@0.20.2':
-    resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
+  '@esbuild/win32-ia32@0.21.5':
+    resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [win32]
 
+  '@esbuild/win32-ia32@0.22.0':
+    resolution: {integrity: sha512-IOgwn+mYTM3RrcydP4Og5IpXh+ftN8oF+HELTXSmbWBlujuci4Qa3DTeO+LEErceisI7KUSfEIiX+WOUlpELkw==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
   '@esbuild/win32-x64@0.18.20':
     resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
     engines: {node: '>=12'}
@@ -2615,12 +2799,18 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@esbuild/win32-x64@0.20.2':
-    resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
+  '@esbuild/win32-x64@0.21.5':
+    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [win32]
 
+  '@esbuild/win32-x64@0.22.0':
+    resolution: {integrity: sha512-4bDHJrk2WHBXJPhy1y80X7/5b5iZTZP3LGcKIlAP1J+KqZ4zQAPMLEzftGyjjfcKbA4JDlPt/+2R/F1ZTeRgrw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
   '@eslint-community/eslint-utils@4.4.0':
     resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2635,17 +2825,25 @@ packages:
     resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==}
     engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
 
-  '@eslint/eslintrc@2.1.4':
-    resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  '@eslint/compat@1.1.0':
+    resolution: {integrity: sha512-s9Wi/p25+KbzxKlDm3VshQdImhWk+cbdblhwGNnyCU5lpSwtWa4v7VQCxSki0FAUrGA3s8nCWgYzAH41mwQVKQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@eslint/js@8.53.0':
-    resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  '@eslint/config-array@0.17.0':
+    resolution: {integrity: sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@eslint/js@8.57.0':
-    resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  '@eslint/eslintrc@3.1.0':
+    resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/js@9.6.0':
+    resolution: {integrity: sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/object-schema@2.1.4':
+    resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@fal-works/esbuild-plugin-global-externals@2.1.2':
     resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==}
@@ -2685,8 +2883,8 @@ packages:
   '@fastify/http-proxy@9.5.0':
     resolution: {integrity: sha512-1iqIdV10d5k9YtfHq9ylX5zt1NiM50fG+rIX40qt00R694sqWso3ukyTFZVk33SDoSiBW8roB7n11RUVUoN+Ag==}
 
-  '@fastify/multipart@8.2.0':
-    resolution: {integrity: sha512-OZ8nsyyoS2TV7Yeu3ZdrdDGsKUTAbfjrKC9jSxGgT2qdgek+BxpWX31ZubTrWMNZyU5xwk4ox6AvTjAbYWjrWg==}
+  '@fastify/multipart@8.3.0':
+    resolution: {integrity: sha512-A8h80TTyqUzaMVH0Cr9Qcm6RxSkVqmhK/MVBYHYeRRSUbUYv08WecjWKSlG2aSnD4aGI841pVxAjC+G1GafUeQ==}
 
   '@fastify/reply-from@9.0.1':
     resolution: {integrity: sha512-q9vFNUiXZTY1x8omDPe59os2MYq+3y7KgO/kZoXpZlnud+45Nd8Ot/svEvrUATzjkizIggfS4K8LR9zXDyZZKg==}
@@ -2697,8 +2895,8 @@ packages:
   '@fastify/static@6.12.0':
     resolution: {integrity: sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==}
 
-  '@fastify/static@7.0.3':
-    resolution: {integrity: sha512-2tmTdF+uFCykasutaO6k4/wOt7eXyi7m3dGuCPo5micXzv0qt6ttb/nWnDYL/BlXjYGfp1JI4a1gyluTIylvQA==}
+  '@fastify/static@7.0.4':
+    resolution: {integrity: sha512-p2uKtaf8BMOZWLs6wu+Ihg7bWNBdjNgCwDza4MJtTqg+5ovKmcbgbR9Xs5/smZ1YISfzKOCNYmZV8LaCj+eJ1Q==}
 
   '@fastify/view@8.2.0':
     resolution: {integrity: sha512-hBSiBofCnJNlPHEMZWpO1SL84eqOaqujJ1hR3jntFyZZCkweH5jMs12DKYyGesjVll7SJFRRxPUBB8kmUmneRQ==}
@@ -2716,11 +2914,8 @@ packages:
   '@hapi/bourne@3.0.0':
     resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
 
-  '@hapi/hoek@10.0.1':
-    resolution: {integrity: sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw==}
-
-  '@hapi/hoek@11.0.2':
-    resolution: {integrity: sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==}
+  '@hapi/hoek@11.0.4':
+    resolution: {integrity: sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==}
 
   '@hapi/hoek@9.3.0':
     resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
@@ -2734,14 +2929,6 @@ packages:
   '@hexagon/base64@1.1.27':
     resolution: {integrity: sha512-PdUmzpvcUM3Rh39kvz9RdbPVYhMjBjdV7Suw7ZduP7urRLsZR8l5tzgSWKm7TExwBYDFwTnYrZbnE0rQ3N5NLQ==}
 
-  '@humanwhocodes/config-array@0.11.13':
-    resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
-    engines: {node: '>=10.10.0'}
-
-  '@humanwhocodes/config-array@0.11.14':
-    resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
-    engines: {node: '>=10.10.0'}
-
   '@humanwhocodes/module-importer@1.0.1':
     resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
     engines: {node: '>=12.22'}
@@ -2750,20 +2937,18 @@ packages:
     resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==}
     engines: {node: '>=10.10.0'}
 
-  '@humanwhocodes/object-schema@2.0.1':
-    resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
+  '@humanwhocodes/retry@0.3.0':
+    resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
+    engines: {node: '>=18.18'}
 
-  '@humanwhocodes/object-schema@2.0.2':
-    resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
-
-  '@img/sharp-darwin-arm64@0.33.3':
-    resolution: {integrity: sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==}
+  '@img/sharp-darwin-arm64@0.33.4':
+    resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==}
     engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [arm64]
     os: [darwin]
 
-  '@img/sharp-darwin-x64@0.33.3':
-    resolution: {integrity: sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==}
+  '@img/sharp-darwin-x64@0.33.4':
+    resolution: {integrity: sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==}
     engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [x64]
     os: [darwin]
@@ -2816,55 +3001,55 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@img/sharp-linux-arm64@0.33.3':
-    resolution: {integrity: sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==}
+  '@img/sharp-linux-arm64@0.33.4':
+    resolution: {integrity: sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==}
     engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [arm64]
     os: [linux]
 
-  '@img/sharp-linux-arm@0.33.3':
-    resolution: {integrity: sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==}
+  '@img/sharp-linux-arm@0.33.4':
+    resolution: {integrity: sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==}
     engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [arm]
     os: [linux]
 
-  '@img/sharp-linux-s390x@0.33.3':
-    resolution: {integrity: sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==}
-    engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
+  '@img/sharp-linux-s390x@0.33.4':
+    resolution: {integrity: sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==}
+    engines: {glibc: '>=2.31', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [s390x]
     os: [linux]
 
-  '@img/sharp-linux-x64@0.33.3':
-    resolution: {integrity: sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==}
+  '@img/sharp-linux-x64@0.33.4':
+    resolution: {integrity: sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==}
     engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [x64]
     os: [linux]
 
-  '@img/sharp-linuxmusl-arm64@0.33.3':
-    resolution: {integrity: sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==}
+  '@img/sharp-linuxmusl-arm64@0.33.4':
+    resolution: {integrity: sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==}
     engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [arm64]
     os: [linux]
 
-  '@img/sharp-linuxmusl-x64@0.33.3':
-    resolution: {integrity: sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==}
+  '@img/sharp-linuxmusl-x64@0.33.4':
+    resolution: {integrity: sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==}
     engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [x64]
     os: [linux]
 
-  '@img/sharp-wasm32@0.33.3':
-    resolution: {integrity: sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==}
+  '@img/sharp-wasm32@0.33.4':
+    resolution: {integrity: sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [wasm32]
 
-  '@img/sharp-win32-ia32@0.33.3':
-    resolution: {integrity: sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==}
+  '@img/sharp-win32-ia32@0.33.4':
+    resolution: {integrity: sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [ia32]
     os: [win32]
 
-  '@img/sharp-win32-x64@0.33.3':
-    resolution: {integrity: sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==}
+  '@img/sharp-win32-x64@0.33.4':
+    resolution: {integrity: sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'}
     cpu: [x64]
     os: [win32]
@@ -2982,8 +3167,8 @@ packages:
     resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
 
-  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.0':
-    resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
+  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.1':
+    resolution: {integrity: sha512-pdoMZ9QaPnVlSM+SdU/wgg0nyD/8wQ7y90ttO2CMCyrrm7RxveYIJ5eNfjPaoMFqW41LZra7QO9j+xV4Y18Glw==}
     peerDependencies:
       typescript: '>= 4.3.x'
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
@@ -2995,6 +3180,10 @@ packages:
     resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
     engines: {node: '>=6.0.0'}
 
+  '@jridgewell/gen-mapping@0.3.5':
+    resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+    engines: {node: '>=6.0.0'}
+
   '@jridgewell/resolve-uri@3.1.0':
     resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
     engines: {node: '>=6.0.0'}
@@ -3003,6 +3192,10 @@ packages:
     resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
     engines: {node: '>=6.0.0'}
 
+  '@jridgewell/set-array@1.2.1':
+    resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+    engines: {node: '>=6.0.0'}
+
   '@jridgewell/source-map@0.3.5':
     resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
 
@@ -3015,6 +3208,9 @@ packages:
   '@jridgewell/trace-mapping@0.3.18':
     resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
 
+  '@jridgewell/trace-mapping@0.3.25':
+    resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
   '@jsdevtools/ono@7.1.3':
     resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
 
@@ -3048,29 +3244,31 @@ packages:
       '@types/react': '>=16'
       react: '>=16'
 
-  '@microsoft/api-extractor-model@7.28.14':
-    resolution: {integrity: sha512-Bery/c8A8SsKPSvA82cTTuy/+OcxZbLRmKhPkk91/AJOQzxZsShcrmHFAGeiEqSIrv1nPZ3tKq9kfMLdCHmsqg==}
+  '@microsoft/api-extractor-model@7.29.2':
+    resolution: {integrity: sha512-hAYajOjQan3uslhKJRwvvHIdLJ+ZByKqdSsJ/dgHFxPtEbdKpzMDO8zuW4K5gkSMYl5D0LbNwxkhxr51P2zsmw==}
 
-  '@microsoft/api-extractor@7.43.1':
-    resolution: {integrity: sha512-ohg40SsvFFgzHFAtYq5wKJc8ZDyY46bphjtnSvhSSlXpPTG7GHwyyXkn48UZiUCBwr2WC7TRC1Jfwz7nreuiyQ==}
+  '@microsoft/api-extractor@7.47.0':
+    resolution: {integrity: sha512-LT8yvcWNf76EpDC+8/ArTVSYePvuDQ+YbAUrsTcpg3ptiZ93HIcMCozP/JOxDt+rrsFfFHcpfoselKfPyRI0GQ==}
     hasBin: true
 
-  '@microsoft/tsdoc-config@0.16.2':
-    resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==}
+  '@microsoft/tsdoc-config@0.17.0':
+    resolution: {integrity: sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==}
 
-  '@microsoft/tsdoc@0.14.2':
-    resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
+  '@microsoft/tsdoc@0.15.0':
+    resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==}
 
   '@misskey-dev/browser-image-resizer@2024.1.0':
     resolution: {integrity: sha512-4EnO0zLW5NDtng3Gaz5MuT761uiuoOuplwX18wBqgj8w56LTU5BjLn/vbHwDIIe0j2gwqDYhMb7bDjmr1/Fomg==}
 
-  '@misskey-dev/eslint-plugin@1.0.0':
-    resolution: {integrity: sha512-dh6UbcrNDVg5DD8k8Qh4ab30OPpuEYIlJCqaBV/lkIV8wNN/AfCJ2V7iTP8V8KjryM4t+sf5IqzQLQnT0mWI4A==}
+  '@misskey-dev/eslint-plugin@2.0.2':
+    resolution: {integrity: sha512-bnTqxCSP0CIN0xSpIGib13bz+K8/3e4h8OlQjuCPlhZF7oFwtn339EZM8yJkHg6gdfciV8KOr3gzlLyG3jiVEQ==}
     peerDependencies:
-      '@typescript-eslint/eslint-plugin': '>= 6'
-      '@typescript-eslint/parser': '>= 6'
-      eslint: '>= 3'
+      '@eslint/compat': '>= 1'
+      '@typescript-eslint/eslint-plugin': '>= 7'
+      '@typescript-eslint/parser': '>= 7'
+      eslint: '>= 8'
       eslint-plugin-import: '>= 2'
+      globals: '>= 15'
 
   '@misskey-dev/sharp-read-bmp@1.2.0':
     resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
@@ -3116,73 +3314,73 @@ packages:
     resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==}
     engines: {node: '>=18'}
 
-  '@mswjs/interceptors@0.26.15':
-    resolution: {integrity: sha512-HM47Lu1YFmnYHKMBynFfjCp0U/yRskHj/8QEJW0CBEPOlw8Gkmjfll+S9b8M7V5CNDw2/ciRxjjnWeaCiblSIQ==}
+  '@mswjs/interceptors@0.29.1':
+    resolution: {integrity: sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==}
     engines: {node: '>=18'}
 
-  '@napi-rs/canvas-android-arm64@0.1.52':
-    resolution: {integrity: sha512-x/K471KbASPVh5mfBUxokza66J0FNIlOgMNANWAf5C8HiATb487KecEhSkUQvvTS3WLYC9uSqIPHFgwF+tir3w==}
+  '@napi-rs/canvas-android-arm64@0.1.53':
+    resolution: {integrity: sha512-2YhxfVsZguATlRWE0fZdTx35SE9+r5D7HV5GPNDataZOKmHf+zZ5//dspuuBSbOriQdoicaFrgXKCUqI0pK3WQ==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [android]
 
-  '@napi-rs/canvas-darwin-arm64@0.1.52':
-    resolution: {integrity: sha512-4OgVRD7TW02q5Q7lWLLjT+pYJ9ZHkQUTBOuXbPQ5wB0Wnh3RIq/aMY6thoXDZDzdR5vV3a5TUtbZUJ0aqLq3NA==}
+  '@napi-rs/canvas-darwin-arm64@0.1.53':
+    resolution: {integrity: sha512-ls+CWLMusf4RAGo5BvIIzA6dNcc0elwVp6LKjHfQECHA8KKmvdB58YuE5BQcTlb2rzk0SEKtBC/Th3NI2oNdfg==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [darwin]
 
-  '@napi-rs/canvas-darwin-x64@0.1.52':
-    resolution: {integrity: sha512-3fgeGJ3j2X6Mtmn0QYf3iA+A6y1ePnsayakc2emEokzf03ErrPczONw3vjnTQo53JLPMzEnfPGAffdktU/ssPA==}
+  '@napi-rs/canvas-darwin-x64@0.1.53':
+    resolution: {integrity: sha512-ZAgcoCH5+5OKS2P8Lxx+jbkAPKkyLD2x6OvSrHg1U6ppdxmLA+CkJlRl8w45HCXwuyIiP7OeymECRtiNYTwznQ==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [darwin]
 
-  '@napi-rs/canvas-linux-arm-gnueabihf@0.1.52':
-    resolution: {integrity: sha512-aaDEEK5XwHUrPt0q4SR8l7Va0vtn50KmSs+itxP+o7RNk3Nuch8fINHOXyhMyhwNYgv1tfiJVyHsJhD0E6lXGA==}
+  '@napi-rs/canvas-linux-arm-gnueabihf@0.1.53':
+    resolution: {integrity: sha512-p9km/3C/loDxu3AvA8/vtpIS1BGMd/Ehkl2Iu/v/Gw8N/KUIt3HUvTS7AKApyVE28bxTfq96wJQjtcT8jzDncw==}
     engines: {node: '>= 10'}
     cpu: [arm]
     os: [linux]
 
-  '@napi-rs/canvas-linux-arm64-gnu@0.1.52':
-    resolution: {integrity: sha512-tzuwM7Amt5mkrp4csQjYWkFzwFdiCm7RNdJ5usX8syzKSXmozqWzLHjzo/2ozdSQNUy6wyzRrxkG4Rh6g0OpOA==}
+  '@napi-rs/canvas-linux-arm64-gnu@0.1.53':
+    resolution: {integrity: sha512-QKK+sykEiYwjwd+ogyLcpcnH38DNZ8KViBlnfEpoGA2Wa+21/cWQKfMxnbgb/rbvm5tazJinZcihFvH577WQ5g==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@napi-rs/canvas-linux-arm64-musl@0.1.52':
-    resolution: {integrity: sha512-HQCtJlDT0dFp3uUZVzZOZ1VLMO7lbLRc548MjMxPpojit2ZdGopFzJ8jDSr4iszHrTO1SM1AxPaCM3pRvCAtjw==}
+  '@napi-rs/canvas-linux-arm64-musl@0.1.53':
+    resolution: {integrity: sha512-2N41U0X8RnrTKzpTtPv1ozlYkJtPsUdbfF3uP/KEd/BsULGd8Y8ghkGMS6CM+821au4ex0dPrWOOdT9wC1rSqQ==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@napi-rs/canvas-linux-x64-gnu@0.1.52':
-    resolution: {integrity: sha512-z5sBEw0PVWPH/MIQL8hOR8C3YYVlu8lqtRUcYajigMfXAhbMiNqDWTjuIWGMz3nIydDjZmn8KTxw/D4a0HFPqQ==}
+  '@napi-rs/canvas-linux-x64-gnu@0.1.53':
+    resolution: {integrity: sha512-7XjuTvDKCODtf/vMwF43VGDrjfgwYKgS91ggdcX3UrJaBYWyWu/+eqNvNj+zdXSe/0x+YOjf5jG4m8xIXdBMQA==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@napi-rs/canvas-linux-x64-musl@0.1.52':
-    resolution: {integrity: sha512-G1+JdWFhHLyHhULJS51xTEhB7EL0ZiAUQwQaRi4/w75OOYDQ91O+o4miaxDHiV0hZuxBhHtZU6ftV2Zl3RMguw==}
+  '@napi-rs/canvas-linux-x64-musl@0.1.53':
+    resolution: {integrity: sha512-970WEvB8vmj+uxvgdBZ+AGFV7uq9GJhXrqG5PGQ5lWciHX0P0d/OhS2F7TITgFR0LsKDQZ7XQgzMxsYOfwZ0FQ==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@napi-rs/canvas-win32-x64-msvc@0.1.52':
-    resolution: {integrity: sha512-hMI626VsCC/wv29qHF78N7TSG+auatOp08DHln0Zdif5y1NJ14NU/rNUhzlTW8Zc6ssw+AMDJ3KKYYWYYg1aoA==}
+  '@napi-rs/canvas-win32-x64-msvc@0.1.53':
+    resolution: {integrity: sha512-rLFQCSJaWg/sv54Aap9nAhaodi4Vyb4un50EgW+PNkk8icMziU6KLRKirGBdQr9ZdxnshAPeQXD1g2ArStujKA==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [win32]
 
-  '@napi-rs/canvas@0.1.52':
-    resolution: {integrity: sha512-xeW9EghZLDPZuqWJ4l1+eG3ld0i9J7SpV2zlgi34MPt/FE9K2XWGCfnLr0gHGOBkcI3YOVhI13I0HqRAkMPdVw==}
+  '@napi-rs/canvas@0.1.53':
+    resolution: {integrity: sha512-XsEZi97+kKykmAiPpY+IpZoHxJY1srqFZp8jDt1/RySzC0kB0iZYt/VMIFqQKpLCARZjD7SOAz2AULtwYlesCA==}
     engines: {node: '>= 10'}
 
   '@ndelangen/get-tarball@3.0.7':
     resolution: {integrity: sha512-NqGfTZIZpRFef1GoVaShSSRwDC3vde3ThtTeqFdcYd6ipKqnfEVhjK2hUeHjCQUcptyZr2TONqcloFXM+5QBrQ==}
 
-  '@nestjs/common@10.3.8':
-    resolution: {integrity: sha512-P+vPEIvqx2e+fonsYVlFXKvoChyJ8Tq+lfpqdVFqblovHbFr3kZ/nYX0cPs+XuW6bnRT8tz0SSR9XBGU43kJhw==}
+  '@nestjs/common@10.3.10':
+    resolution: {integrity: sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg==}
     peerDependencies:
       class-transformer: '*'
       class-validator: '*'
@@ -3194,8 +3392,8 @@ packages:
       class-validator:
         optional: true
 
-  '@nestjs/core@10.3.8':
-    resolution: {integrity: sha512-AxF4tpYLDNn5Wfb3C4bNaaHJ4pREH5FJrSisR2A5zkYpQFORFs0Tc36lOFPMwBTy8Iv2wUwWLUVc5ftBnxEv4w==}
+  '@nestjs/core@10.3.10':
+    resolution: {integrity: sha512-ZbQ4jovQyzHtCGCrzK5NdtW1SYO2fHSsgSY1+/9WdruYCUra+JDkWEXgZ4M3Hv480Dl3OXehAmY1wCOojeMyMQ==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/microservices': ^10.0.0
@@ -3211,14 +3409,14 @@ packages:
       '@nestjs/websockets':
         optional: true
 
-  '@nestjs/platform-express@10.3.8':
-    resolution: {integrity: sha512-sifLoxgEJvAgbim1UuW6wyScMfkS9SVQRH+lN33N/9ZvZSjO6NSDLOe+wxqsnZkia+QrjFC0qy0ITRAsggfqbg==}
+  '@nestjs/platform-express@10.3.10':
+    resolution: {integrity: sha512-wK2ow3CZI2KFqWeEpPmoR300OB6BcBLxARV1EiClJLCj4S1mZsoCmS0YWgpk3j1j6mo0SI8vNLi/cC2iZPEPQA==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/core': ^10.0.0
 
-  '@nestjs/testing@10.3.8':
-    resolution: {integrity: sha512-hpX9das2TdFTKQ4/2ojhjI6YgXtCfXRKui3A4Qaj54VVzc5+mtK502Jj18Vzji98o9MVS6skmYu+S/UvW3U6Fw==}
+  '@nestjs/testing@10.3.10':
+    resolution: {integrity: sha512-i3HAtVQJijxNxJq1k39aelyJlyEIBRONys7IipH/4r8W0J+M1V+y5EKDOyi4j1SdNSb/vmNyWpZ2/ewZjl3kRA==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/core': ^10.0.0
@@ -3230,6 +3428,10 @@ packages:
       '@nestjs/platform-express':
         optional: true
 
+  '@noble/hashes@1.4.0':
+    resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
+    engines: {node: '>= 16'}
+
   '@nodelib/fs.scandir@2.1.5':
     resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
     engines: {node: '>= 8'}
@@ -3273,19 +3475,19 @@ packages:
   '@open-draft/until@2.1.0':
     resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
 
-  '@opentelemetry/api-logs@0.51.1':
-    resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==}
+  '@opentelemetry/api-logs@0.52.1':
+    resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==}
     engines: {node: '>=14'}
 
-  '@opentelemetry/api@1.8.0':
-    resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==}
+  '@opentelemetry/api@1.9.0':
+    resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
     engines: {node: '>=8.0.0'}
 
-  '@opentelemetry/context-async-hooks@1.24.1':
-    resolution: {integrity: sha512-R5r6DO4kgEOVBxFXhXjwospLQkv+sYxwCfjvoZBe7Zm6KKXAV9kDSJhi/D1BweowdZmO+sdbENLs374gER8hpQ==}
+  '@opentelemetry/context-async-hooks@1.25.1':
+    resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==}
     engines: {node: '>=14'}
     peerDependencies:
-      '@opentelemetry/api': '>=1.0.0 <1.9.0'
+      '@opentelemetry/api': '>=1.0.0 <1.10.0'
 
   '@opentelemetry/core@1.24.1':
     resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==}
@@ -3293,86 +3495,98 @@ packages:
     peerDependencies:
       '@opentelemetry/api': '>=1.0.0 <1.9.0'
 
-  '@opentelemetry/instrumentation-connect@0.36.0':
-    resolution: {integrity: sha512-k9++bmJZ9zDEs3u3DnKTn2l7QTiNFg3gPx7G9rW0TPnP+xZoBSBTrEcGYBaqflQlrFG23Q58+X1sM2ayWPv5Fg==}
+  '@opentelemetry/core@1.25.1':
+    resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+  '@opentelemetry/instrumentation-connect@0.37.0':
+    resolution: {integrity: sha512-SeQktDIH5rNzjiEiazWiJAIXkmnLOnNV7wwHpahrqE0Ph+Z3heqMfxRtoMtbdJSIYLfcNZYO51AjxZ00IXufdw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-express@0.39.0':
-    resolution: {integrity: sha512-AG8U7z7D0JcBu/7dDcwb47UMEzj9/FMiJV2iQZqrsZnxR3FjB9J9oIH2iszJYci2eUdp2WbdvtpD9RV/zmME5A==}
+  '@opentelemetry/instrumentation-express@0.40.1':
+    resolution: {integrity: sha512-+RKMvVe2zw3kIXRup9c1jFu3T4d0fs5aKy015TpiMyoCKX1UMu3Z0lfgYtuyiSTANvg5hZnDbWmQmqSPj9VTvg==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-fastify@0.36.1':
-    resolution: {integrity: sha512-3Nfm43PI0I+3EX+1YbSy6xbDu276R1Dh1tqAk68yd4yirnIh52Kd5B+nJ8CgHA7o3UKakpBjj6vSzi5vNCzJIA==}
+  '@opentelemetry/instrumentation-fastify@0.37.0':
+    resolution: {integrity: sha512-WRjwzNZgupSzbEYvo9s+QuHJRqZJjVdNxSEpGBwWK8RKLlHGwGVAu0gcc2gPamJWUJsGqPGvahAPWM18ZkWj6A==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-graphql@0.40.0':
-    resolution: {integrity: sha512-LVRdEHWACWOczv2imD+mhUrLMxsEjPPi32vIZJT57zygR5aUiA4em8X3aiGOCycgbMWkIu8xOSGSxdx3JmzN+w==}
+  '@opentelemetry/instrumentation-graphql@0.41.0':
+    resolution: {integrity: sha512-R/gXeljgIhaRDKquVkKYT5QHPnFouM8ooyePZEP0kqyaVAedtR1V7NfAUJbxfTG5fBQa5wdmLjvu63+tzRXZCA==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-hapi@0.38.0':
-    resolution: {integrity: sha512-ZcOqEuwuutTDYIjhDIStix22ECblG/i9pHje23QGs4Q4YS4RMaZ5hKCoQJxW88Z4K7T53rQkdISmoXFKDV8xMg==}
+  '@opentelemetry/instrumentation-hapi@0.39.0':
+    resolution: {integrity: sha512-ik2nA9Yj2s2ay+aNY+tJsKCsEx6Tsc2g/MK0iWBW5tibwrWKTy1pdVt5sB3kd5Gkimqj23UV5+FH2JFcQLeKug==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-http@0.51.1':
-    resolution: {integrity: sha512-6b3nZnFFEz/3xZ6w8bVxctPUWIPWiXuPQ725530JgxnN1cvYFd8CJ75PrHZNjynmzSSnqBkN3ef4R9N+RpMh8Q==}
+  '@opentelemetry/instrumentation-http@0.52.1':
+    resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-ioredis@0.40.0':
-    resolution: {integrity: sha512-Jv/fH7KhpWe4KBirsiqeUJIYrsdR2iu2l4nWhfOlRvaZ+zYIiLEzTQR6QhBbyRoAbU4OuYJzjWusOmmpGBnwng==}
+  '@opentelemetry/instrumentation-ioredis@0.41.0':
+    resolution: {integrity: sha512-rxiLloU8VyeJGm5j2fZS8ShVdB82n7VNP8wTwfUQqDwRfHCnkzGr+buKoxuhGD91gtwJ91RHkjHA1Eg6RqsUTg==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-koa@0.40.0':
-    resolution: {integrity: sha512-dJc3H/bKMcgUYcQpLF+1IbmUKus0e5Fnn/+ru/3voIRHwMADT3rFSUcGLWSczkg68BCgz0vFWGDTvPtcWIFr7A==}
+  '@opentelemetry/instrumentation-koa@0.41.0':
+    resolution: {integrity: sha512-mbPnDt7ELvpM2S0vixYUsde7122lgegLOJQxx8iJQbB8YHal/xnTh9v7IfArSVzIDo+E+080hxZyUZD4boOWkw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mongodb@0.43.0':
-    resolution: {integrity: sha512-bMKej7Y76QVUD3l55Q9YqizXybHUzF3pujsBFjqbZrRn2WYqtsDtTUlbCK7fvXNPwFInqZ2KhnTqd0gwo8MzaQ==}
+  '@opentelemetry/instrumentation-mongodb@0.45.0':
+    resolution: {integrity: sha512-xnZP9+ayeB1JJyNE9cIiwhOJTzNEsRhXVdLgfzmrs48Chhhk026mQdM5CITfyXSCfN73FGAIB8d91+pflJEfWQ==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mongoose@0.38.1':
-    resolution: {integrity: sha512-zaeiasdnRjXe6VhYCBMdkmAVh1S5MmXC/0spet+yqoaViGnYst/DOxPvhwg3yT4Yag5crZNWsVXnA538UjP6Ow==}
+  '@opentelemetry/instrumentation-mongoose@0.39.0':
+    resolution: {integrity: sha512-J1r66A7zJklPPhMtrFOO7/Ud2p0Pv5u8+r23Cd1JUH6fYPmftNJVsLp2urAt6PHK4jVqpP/YegN8wzjJ2mZNPQ==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mysql2@0.38.1':
-    resolution: {integrity: sha512-qkpHMgWSDTYVB1vlZ9sspf7l2wdS5DDq/rbIepDwX5BA0N0068JTQqh0CgAh34tdFqSCnWXIhcyOXC2TtRb0sg==}
+  '@opentelemetry/instrumentation-mysql2@0.39.0':
+    resolution: {integrity: sha512-Iypuq2z6TCfriAXCIZjRq8GTFCKhQv5SpXbmI+e60rYdXw8NHtMH4NXcGF0eKTuoCsC59IYSTUvDQYDKReaszA==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mysql@0.38.1':
-    resolution: {integrity: sha512-+iBAawUaTfX/HAlvySwozx0C2B6LBfNPXX1W8Z2On1Uva33AGkw2UjL9XgIg1Pj4eLZ9R4EoJ/aFz+Xj4E/7Fw==}
+  '@opentelemetry/instrumentation-mysql@0.39.0':
+    resolution: {integrity: sha512-8snHPh83rhrDf31v9Kq0Nf+ts8hdr7NguuszRqZomZBHgE0+UyXZSkXHAAFZoBPPRMGyM68uaFE5hVtFl+wOcA==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-nestjs-core@0.37.1':
-    resolution: {integrity: sha512-ebYQjHZEmGHWEALwwDGhSQVLBaurFnuLIkZD5igPXrt7ohfF4lc5/4al1LO+vKc0NHk8SJWStuRueT86ISA8Vg==}
+  '@opentelemetry/instrumentation-nestjs-core@0.38.0':
+    resolution: {integrity: sha512-M381Df1dM8aqihZz2yK+ugvMFK5vlHG/835dc67Sx2hH4pQEQYDA2PpFPTgc9AYYOydQaj7ClFQunESimjXDgg==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-pg@0.41.0':
-    resolution: {integrity: sha512-BSlhpivzBD77meQNZY9fS4aKgydA8AJBzv2dqvxXFy/Hq64b7HURgw/ztbmwFeYwdF5raZZUifiiNSMLpOJoSA==}
+  '@opentelemetry/instrumentation-pg@0.42.0':
+    resolution: {integrity: sha512-sjgcM8CswYy8zxHgXv4RAZ09DlYhQ+9TdlourUs63Df/ek5RrB1ZbjznqW7PB6c3TyJJmX6AVtPTjAsROovEjA==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.3.0
+
+  '@opentelemetry/instrumentation-redis-4@0.40.0':
+    resolution: {integrity: sha512-0ieQYJb6yl35kXA75LQUPhHtGjtQU9L85KlWa7d4ohBbk/iQKZ3X3CFl5jC5vNMq/GGPB3+w3IxNvALlHtrp7A==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
@@ -3383,8 +3597,8 @@ packages:
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation@0.51.1':
-    resolution: {integrity: sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==}
+  '@opentelemetry/instrumentation@0.52.1':
+    resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
@@ -3399,22 +3613,32 @@ packages:
     peerDependencies:
       '@opentelemetry/api': '>=1.0.0 <1.9.0'
 
+  '@opentelemetry/resources@1.25.1':
+    resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
   '@opentelemetry/sdk-metrics@1.24.1':
     resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': '>=1.3.0 <1.9.0'
 
-  '@opentelemetry/sdk-trace-base@1.24.1':
-    resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==}
+  '@opentelemetry/sdk-trace-base@1.25.1':
+    resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==}
     engines: {node: '>=14'}
     peerDependencies:
-      '@opentelemetry/api': '>=1.0.0 <1.9.0'
+      '@opentelemetry/api': '>=1.0.0 <1.10.0'
 
   '@opentelemetry/semantic-conventions@1.24.1':
     resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==}
     engines: {node: '>=14'}
 
+  '@opentelemetry/semantic-conventions@1.25.1':
+    resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==}
+    engines: {node: '>=14'}
+
   '@opentelemetry/sql-common@0.40.1':
     resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==}
     engines: {node: '>=14'}
@@ -3444,23 +3668,167 @@ packages:
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
-  '@prisma/instrumentation@5.14.0':
-    resolution: {integrity: sha512-DeybWvIZzu/mUsOYP9MVd6AyBj+MP7xIMrcuIn25MX8FiQX39QBnET5KhszTAip/ToctUuDwSJ46QkIoyo3RFA==}
+  '@prisma/instrumentation@5.16.0':
+    resolution: {integrity: sha512-MVzNRW2ikWvVNnMIEgQMcwWxpFD+XF2U2h0Qz7MjutRqJxrhWexWV2aSi2OXRaU8UL5wzWw7pnjdKUzYhWauLg==}
 
-  '@radix-ui/react-compose-refs@1.0.1':
-    resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
+  '@radix-ui/primitive@1.1.0':
+    resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==}
+
+  '@radix-ui/react-compose-refs@1.1.0':
+    resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==}
     peerDependencies:
       '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
     peerDependenciesMeta:
       '@types/react':
         optional: true
 
-  '@radix-ui/react-slot@1.0.2':
-    resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
+  '@radix-ui/react-context@1.1.0':
+    resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==}
     peerDependencies:
       '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-dialog@1.1.1':
+    resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
+  '@radix-ui/react-dismissable-layer@1.1.0':
+    resolution: {integrity: sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
+  '@radix-ui/react-focus-guards@1.1.0':
+    resolution: {integrity: sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-focus-scope@1.1.0':
+    resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
+  '@radix-ui/react-id@1.1.0':
+    resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-portal@1.1.1':
+    resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
+  '@radix-ui/react-presence@1.1.0':
+    resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
+  '@radix-ui/react-primitive@2.0.0':
+    resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
+  '@radix-ui/react-slot@1.1.0':
+    resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-use-callback-ref@1.1.0':
+    resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-use-controllable-state@1.1.0':
+    resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-use-escape-keydown@1.1.0':
+    resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  '@radix-ui/react-use-layout-effect@1.1.0':
+    resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
+    peerDependencies:
+      '@types/react': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
     peerDependenciesMeta:
       '@types/react':
         optional: true
@@ -3489,8 +3857,8 @@ packages:
       rollup:
         optional: true
 
-  '@rollup/plugin-replace@5.0.5':
-    resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
+  '@rollup/plugin-replace@5.0.7':
+    resolution: {integrity: sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
       rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
@@ -3507,88 +3875,88 @@ packages:
       rollup:
         optional: true
 
-  '@rollup/rollup-android-arm-eabi@4.17.2':
-    resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==}
+  '@rollup/rollup-android-arm-eabi@4.18.0':
+    resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==}
     cpu: [arm]
     os: [android]
 
-  '@rollup/rollup-android-arm64@4.17.2':
-    resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==}
+  '@rollup/rollup-android-arm64@4.18.0':
+    resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==}
     cpu: [arm64]
     os: [android]
 
-  '@rollup/rollup-darwin-arm64@4.17.2':
-    resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==}
+  '@rollup/rollup-darwin-arm64@4.18.0':
+    resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==}
     cpu: [arm64]
     os: [darwin]
 
-  '@rollup/rollup-darwin-x64@4.17.2':
-    resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==}
+  '@rollup/rollup-darwin-x64@4.18.0':
+    resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==}
     cpu: [x64]
     os: [darwin]
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.17.2':
-    resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==}
+  '@rollup/rollup-linux-arm-gnueabihf@4.18.0':
+    resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm-musleabihf@4.17.2':
-    resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==}
+  '@rollup/rollup-linux-arm-musleabihf@4.18.0':
+    resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-gnu@4.17.2':
-    resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==}
+  '@rollup/rollup-linux-arm64-gnu@4.18.0':
+    resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-musl@4.17.2':
-    resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==}
+  '@rollup/rollup-linux-arm64-musl@4.18.0':
+    resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.17.2':
-    resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==}
+  '@rollup/rollup-linux-powerpc64le-gnu@4.18.0':
+    resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==}
     cpu: [ppc64]
     os: [linux]
 
-  '@rollup/rollup-linux-riscv64-gnu@4.17.2':
-    resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==}
+  '@rollup/rollup-linux-riscv64-gnu@4.18.0':
+    resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==}
     cpu: [riscv64]
     os: [linux]
 
-  '@rollup/rollup-linux-s390x-gnu@4.17.2':
-    resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==}
+  '@rollup/rollup-linux-s390x-gnu@4.18.0':
+    resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==}
     cpu: [s390x]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-gnu@4.17.2':
-    resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==}
+  '@rollup/rollup-linux-x64-gnu@4.18.0':
+    resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-musl@4.17.2':
-    resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==}
+  '@rollup/rollup-linux-x64-musl@4.18.0':
+    resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-win32-arm64-msvc@4.17.2':
-    resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==}
+  '@rollup/rollup-win32-arm64-msvc@4.18.0':
+    resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==}
     cpu: [arm64]
     os: [win32]
 
-  '@rollup/rollup-win32-ia32-msvc@4.17.2':
-    resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==}
+  '@rollup/rollup-win32-ia32-msvc@4.18.0':
+    resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==}
     cpu: [ia32]
     os: [win32]
 
-  '@rollup/rollup-win32-x64-msvc@4.17.2':
-    resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==}
+  '@rollup/rollup-win32-x64-msvc@4.18.0':
+    resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==}
     cpu: [x64]
     os: [win32]
 
-  '@rushstack/node-core-library@4.1.0':
-    resolution: {integrity: sha512-qz4JFBZJCf1YN5cAXa1dP6Mki/HrsQxc/oYGAGx29dF2cwF2YMxHoly0FBhMw3IEnxo5fMj0boVfoHVBkpkx/w==}
+  '@rushstack/node-core-library@5.4.1':
+    resolution: {integrity: sha512-WNnwdS8r9NZ/2K3u29tNoSRldscFa7SxU0RT+82B6Dy2I4Hl2MeCSKm4EXLXPKeNzLGvJ1cqbUhTLviSF8E6iA==}
     peerDependencies:
       '@types/node': '*'
     peerDependenciesMeta:
@@ -3598,50 +3966,53 @@ packages:
   '@rushstack/rig-package@0.5.2':
     resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==}
 
-  '@rushstack/terminal@0.10.1':
-    resolution: {integrity: sha512-C6Vi/m/84IYJTkfzmXr1+W8Wi3MmBjVF/q3za91Gb3VYjKbpALHVxY6FgH625AnDe5Z0Kh4MHKWA3Z7bqgAezA==}
+  '@rushstack/terminal@0.13.0':
+    resolution: {integrity: sha512-Ou44Q2s81BqJu3dpYedAX54am9vn245F0HzqVrfJCMQk5pGgoKKOBOjkbfZC9QKcGNaECh6pwH2s5noJt7X6ew==}
     peerDependencies:
       '@types/node': '*'
     peerDependenciesMeta:
       '@types/node':
         optional: true
 
-  '@rushstack/ts-command-line@4.19.2':
-    resolution: {integrity: sha512-cqmXXmBEBlzo9WtyUrHtF9e6kl0LvBY7aTSVX4jfnBfXWZQWnPq9JTFPlQZ+L/ZwjZ4HrNwQsOVvhe9oOucZkw==}
+  '@rushstack/ts-command-line@4.22.0':
+    resolution: {integrity: sha512-Qj28t6MO3HRgAZ72FDeFsrpdE6wBWxF3VENgvrXh7JF2qIT+CrXiOJIesW80VFZB9QwObSpkB1ilx794fGQg6g==}
 
-  '@sentry/core@8.5.0':
-    resolution: {integrity: sha512-SO3ddBzGdha+Oflp+IKwBxj+7ds1q69OAT3VsypTd+WUFQdI9DIhR92Bjf+QQZCIzUNOi79VWOh3aOi3f6hMnw==}
+  '@sec-ant/readable-stream@0.4.1':
+    resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
+
+  '@sentry/core@8.13.0':
+    resolution: {integrity: sha512-N9Qg4ZGxZWp8eb2eUUHVVKgjBLtFIjS805nG92s6yJmkvOpKm6mLtcUaT/iDf3Hta6nG+xRkhbE3r+Z4cbXG8w==}
     engines: {node: '>=14.18'}
 
-  '@sentry/node@8.5.0':
-    resolution: {integrity: sha512-t9cHAx/wLJYtdVf2XlzKlRJGvwdAp1wjzG0tC4E1Znx74OuUS1cFNo5WrGuOi0/YcWSxiJaxBvtUcsWK86fIgw==}
+  '@sentry/node@8.13.0':
+    resolution: {integrity: sha512-OeZ7K90RhyxfwfreerIi4cszzHrPRRH36STJno2+p3sIGbG5VScOccqXzYEOAqHpByxnti4KQN34BLAT2BFOEA==}
     engines: {node: '>=14.18'}
 
-  '@sentry/opentelemetry@8.5.0':
-    resolution: {integrity: sha512-AbxFUNjuTKQ9ugZrssmGtPxWkBr4USNoP7GjaaGCNwNzvIVYCa+i8dv7BROJiW2lsxNAremULEbh+nbVmhGxDA==}
+  '@sentry/opentelemetry@8.13.0':
+    resolution: {integrity: sha512-NYn/HNE/SxFXe8pfnxJknhrrRzYRMHNssCoi5M1CeR5G7F2BGxxVmaGsd8j0WyTCpUS4i97G4vhYtDGxHvWN6w==}
     engines: {node: '>=14.18'}
     peerDependencies:
-      '@opentelemetry/api': ^1.8.0
-      '@opentelemetry/core': ^1.24.1
-      '@opentelemetry/instrumentation': ^0.51.1
-      '@opentelemetry/sdk-trace-base': ^1.23.0
-      '@opentelemetry/semantic-conventions': ^1.23.0
+      '@opentelemetry/api': ^1.9.0
+      '@opentelemetry/core': ^1.25.1
+      '@opentelemetry/instrumentation': ^0.52.1
+      '@opentelemetry/sdk-trace-base': ^1.25.1
+      '@opentelemetry/semantic-conventions': ^1.25.1
 
-  '@sentry/profiling-node@8.5.0':
-    resolution: {integrity: sha512-nEXJqVNfZWYi4PakQXBZCJeH59UlnBv+zaYftDNUUXttCmzRXpL1ujNm5mJrJHlWjV7tgIFw02HW3nh2yyKOkw==}
+  '@sentry/profiling-node@8.13.0':
+    resolution: {integrity: sha512-6qirN71xlMahcm4m25XZLnjQdvs0EaFym/9MdLqcsAa3gAHZtw6h+IDapUzBWRXVOrU1OR5oQdh2tlFthsDtew==}
     engines: {node: '>=14.18'}
     hasBin: true
 
-  '@sentry/types@8.5.0':
-    resolution: {integrity: sha512-eDgkSmKI4+XL0QZm4H3j/n1RgnrbnjXZmjj+LsfccRZQwbPu9bWlc8q7Y7Ty1gOsoUpX+TecNLp2a8CRID4KHA==}
+  '@sentry/types@8.13.0':
+    resolution: {integrity: sha512-r63s/H5gvQnQM9tTGBXz2xErUbxZALh4e2Lg/1aHj4zIvGLBjA2z5qWsh6TEZYbpmgAyGShLDr6+rWeUVf9yBQ==}
     engines: {node: '>=14.18'}
 
-  '@sentry/utils@8.5.0':
-    resolution: {integrity: sha512-fdrCzo8SAYiw9JBhkJPqYqJkDXZ/wICzN7+zcXIuzKNhE1hdoFjeKcPnpUI3bKZCG6e3hT1PTYQXhVw7GIZV9w==}
+  '@sentry/utils@8.13.0':
+    resolution: {integrity: sha512-PxV0v9VbGWH9zP37P5w2msLUFDr287nYjoY2XVF+RSolyiTs1CQNI5ZMUO3o4MsSac/dpXxjyrZXQd72t/jRYA==}
     engines: {node: '>=14.18'}
 
-  '@shikijs/core@1.4.0':
-    resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==}
+  '@shikijs/core@1.10.0':
+    resolution: {integrity: sha512-BZcr6FCmPfP6TXaekvujZcnkFmJHZ/Yglu97r/9VjzVndQA56/F4WjUKtJRQUnK59Wi7p/UTAOekMfCJv7jnYg==}
 
   '@sideway/address@4.1.4':
     resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
@@ -3670,10 +4041,18 @@ packages:
     resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
     engines: {node: '>=14.16'}
 
-  '@sindresorhus/is@6.1.0':
-    resolution: {integrity: sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==}
+  '@sindresorhus/is@6.3.1':
+    resolution: {integrity: sha512-FX4MfcifwJyFOI2lPoX7PQxCqx8BG1HCho7WdiXwpEQx1Ycij0JxkfYtGK7yqNScrZGSlt6RE6sw8QYoH7eKnQ==}
     engines: {node: '>=16'}
 
+  '@sindresorhus/merge-streams@2.3.0':
+    resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
+    engines: {node: '>=18'}
+
+  '@sindresorhus/merge-streams@4.0.0':
+    resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
+    engines: {node: '>=18'}
+
   '@sinonjs/commons@2.0.0':
     resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==}
 
@@ -3692,275 +4071,299 @@ packages:
   '@sinonjs/text-encoding@0.7.2':
     resolution: {integrity: sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==}
 
-  '@smithy/abort-controller@2.0.14':
-    resolution: {integrity: sha512-zXtteuYLWbSXnzI3O6xq3FYvigYZFW8mdytGibfarLL2lxHto9L3ILtGVnVGmFZa7SDh62l39EnU5hesLN87Fw==}
-    engines: {node: '>=14.0.0'}
-
   '@smithy/abort-controller@2.2.0':
     resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/chunked-blob-reader-native@2.0.0':
-    resolution: {integrity: sha512-HM8V2Rp1y8+1343tkZUKZllFhEQPNmpNdgFAncbTsxkZ18/gqjk23XXv3qGyXWp412f3o43ZZ1UZHVcHrpRnCQ==}
+  '@smithy/abort-controller@3.1.1':
+    resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/chunked-blob-reader@2.0.0':
-    resolution: {integrity: sha512-k+J4GHJsMSAIQPChGBrjEmGS+WbPonCXesoqP9fynIqjn7rdOThdH8FAeCmokP9mxTYKQAKoHCLPzNlm6gh7Wg==}
+  '@smithy/chunked-blob-reader-native@3.0.0':
+    resolution: {integrity: sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==}
 
-  '@smithy/config-resolver@2.0.9':
-    resolution: {integrity: sha512-QBkGPLUqyPmis9Erz8v4q5lo/ErnF7+GD5WZHa6JZiXopUPfaaM+B21n8gzS5xCkIXZmnwzNQhObP9xQPu8oqQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/chunked-blob-reader@3.0.0':
+    resolution: {integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==}
 
-  '@smithy/credential-provider-imds@2.0.11':
-    resolution: {integrity: sha512-uJJs8dnM5iXkn8a2GaKvlKMhcOJ+oJPYqY9gY3CM/EieCVObIDjxUtR/g8lU/k/A+OauA78GzScAfulmFjPOYA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/config-resolver@3.0.4':
+    resolution: {integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/eventstream-codec@2.0.8':
-    resolution: {integrity: sha512-onO4to8ujCKn4m5XagReT9Nc6FlNG5vveuvjp1H7AtaG7njdet1LOl6/jmUOkskF2C/w+9jNw3r9Ak+ghOvN0A==}
+  '@smithy/core@2.2.4':
+    resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/eventstream-serde-browser@2.0.8':
-    resolution: {integrity: sha512-/RGlkKUnC0sd+xKBKH/2APSBRmVMZTeLOKZMhrZmrO+ONoU+DwyMr/RLJ6WnmBKN+2ebjffM4pcIJTKLNNDD8g==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/credential-provider-imds@3.1.3':
+    resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/eventstream-serde-config-resolver@2.0.8':
-    resolution: {integrity: sha512-EyAEj258eMUv9zcMvBbqrInh2eHRYuiwQAjXDMxZFCyP+JePzQB6O++3wFwjQeRKMFFgZipNgnEXfReII4+NAw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/eventstream-codec@3.1.2':
+    resolution: {integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==}
 
-  '@smithy/eventstream-serde-node@2.0.8':
-    resolution: {integrity: sha512-FMBatSUSKwh6aguKVJokXfJaV8nqsuCkCZHb9MP9zah0ZF+ohbTLeeed7DQGeTVBueVIVWEzIsShPxtxBv7MMQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/eventstream-serde-browser@3.0.4':
+    resolution: {integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/eventstream-serde-universal@2.0.8':
-    resolution: {integrity: sha512-6InMXH8BUKoEDa6CAuxR4Gn8Gf2vBfVtjA9A6zDKZClYHT+ANUJS+2EtOBc5wECJJGk4KLn5ajQyrt9MBv5lcw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/eventstream-serde-config-resolver@3.0.3':
+    resolution: {integrity: sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/fetch-http-handler@2.1.4':
-    resolution: {integrity: sha512-SL24M9W5ERByoXaVicRx+bj9GJVujDnPn+QO7GY7adhY0mPGa6DSF58pVKsgIh4r5Tx/k3SWCPlH4BxxSxA/fQ==}
+  '@smithy/eventstream-serde-node@3.0.4':
+    resolution: {integrity: sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/hash-blob-browser@2.0.8':
-    resolution: {integrity: sha512-IgvRlBMfg/qLg321a59T1yTdEEbaizLrEVsU3DHj65DAO4lFRMF5f+l7vuV+je6m1G9wSD5GQXLturX8qlGb4g==}
+  '@smithy/eventstream-serde-universal@3.0.4':
+    resolution: {integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/hash-node@2.0.8':
-    resolution: {integrity: sha512-yZL/nmxZzjZV5/QX5JWSgXlt0HxuMTwFO89CS++jOMMPiCMZngf6VYmtNdccs8IIIAMmfQeTzwu07XgUE/Zd3Q==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/fetch-http-handler@3.2.0':
+    resolution: {integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==}
 
-  '@smithy/hash-stream-node@2.0.8':
-    resolution: {integrity: sha512-82zC6I9ZJycbEZH8TVyXyBx9c2ZIPQDgBvM0x5AFPUl/i1AxwKKX+lwYRnzgkF//cYhIIoJaCfJ9mjSMPRGvCQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/hash-blob-browser@3.1.2':
+    resolution: {integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==}
 
-  '@smithy/invalid-dependency@2.0.8':
-    resolution: {integrity: sha512-88VOS7W3KzUz/bNRc+Sl/F/CDIasFspEE4G39YZRHIh9YmsXF7GUyVaAKURfMNulTie62ayk6BHC9O0nOBAVgQ==}
+  '@smithy/hash-node@3.0.3':
+    resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==}
+    engines: {node: '>=16.0.0'}
+
+  '@smithy/hash-stream-node@3.1.2':
+    resolution: {integrity: sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==}
+    engines: {node: '>=16.0.0'}
+
+  '@smithy/invalid-dependency@3.0.3':
+    resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==}
 
   '@smithy/is-array-buffer@2.0.0':
     resolution: {integrity: sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/md5-js@2.0.8':
-    resolution: {integrity: sha512-1VVECXEiuJvjXv+mudiaUFKYwgDLOWz5MTTy8RzbrPiU3GiOb3/o5/urdkYpqmgoMfxdvxxOw/Adjv2dV2q2Yg==}
+  '@smithy/is-array-buffer@3.0.0':
+    resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/middleware-content-length@2.0.10':
-    resolution: {integrity: sha512-EGSbysyA4jH0p3xI6G0jdXoj9Iz9GUnAta6aEaHtXm3wVWtenRf80y2TeVvNkVSr5jwKOdSCjKIRI2l1A/oZLA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/md5-js@3.0.3':
+    resolution: {integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==}
 
-  '@smithy/middleware-endpoint@2.0.8':
-    resolution: {integrity: sha512-yOpogfG2d2V0cbJdAJ6GLAWkNOc9pVsL5hZUfXcxJu408N3CUCsXzIAFF6+70ZKSE+lCfG3GFErcSXv/UfUbjw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-content-length@3.0.3':
+    resolution: {integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/middleware-retry@2.0.11':
-    resolution: {integrity: sha512-pknfokumZ+wvBERSuKAI2vVr+aK3ZgPiWRg6+0ZG4kKJogBRpPmDGWw+Jht0izS9ZaEbIobNzueIb4wD33JJVg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-endpoint@3.0.4':
+    resolution: {integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/middleware-serde@2.0.8':
-    resolution: {integrity: sha512-Is0sm+LiNlgsc0QpstDzifugzL9ehno1wXp109GgBgpnKTK3j+KphiparBDI4hWTtH9/7OUsxuspNqai2yyhcg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-retry@3.0.7':
+    resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/middleware-stack@2.0.1':
-    resolution: {integrity: sha512-UexsfY6/oQZRjTQL56s9AKtMcR60tBNibSgNYX1I2WXaUaXg97W9JCkFyth85TzBWKDBTyhLfenrukS/kyu54A==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-serde@3.0.3':
+    resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/node-config-provider@2.0.11':
-    resolution: {integrity: sha512-CaR1dciSSGKttjhcefpytYjsfI/Yd5mqL8am4wfmyFCDxSiPsvnEWHl8UjM/RbcAjX0klt+CeIKPSHEc0wGvJA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/middleware-stack@3.0.3':
+    resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==}
+    engines: {node: '>=16.0.0'}
+
+  '@smithy/node-config-provider@3.1.3':
+    resolution: {integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==}
+    engines: {node: '>=16.0.0'}
 
   '@smithy/node-http-handler@2.5.0':
     resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/property-provider@2.0.9':
-    resolution: {integrity: sha512-25pPZ8f8DeRwYI5wbPRZaoMoR+3vrw8DwbA0TjP+GsdiB2KxScndr4HQehiJ5+WJ0giOTWhLz0bd+7Djv1qpUQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/node-http-handler@3.1.1':
+    resolution: {integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/protocol-http@3.0.10':
-    resolution: {integrity: sha512-6+tjNk7rXW7YTeGo9qwxXj/2BFpJTe37kTj3EnZCoX/nH+NP/WLA7O83fz8XhkGqsaAhLUPo/bB12vvd47nsmg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/property-provider@3.1.3':
+    resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==}
+    engines: {node: '>=16.0.0'}
 
   '@smithy/protocol-http@3.3.0':
     resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/querystring-builder@2.0.14':
-    resolution: {integrity: sha512-lQ4pm9vTv9nIhl5jt6uVMPludr6syE2FyJmHsIJJuOD7QPIJnrf9HhUGf1iHh9KJ4CUv21tpOU3X6s0rB6uJ0g==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/protocol-http@4.0.3':
+    resolution: {integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==}
+    engines: {node: '>=16.0.0'}
 
   '@smithy/querystring-builder@2.2.0':
     resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/querystring-parser@2.0.8':
-    resolution: {integrity: sha512-ArbanNuR7O/MmTd90ZqhDqGOPPDYmxx3huHxD+R3cuCnazcK/1tGQA+SnnR5307T7ZRb5WTpB6qBggERuibVSA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/querystring-builder@3.0.3':
+    resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/service-error-classification@2.0.1':
-    resolution: {integrity: sha512-QHa9+t+v4s0cMuDCcbjIJN67mNZ42/+fc3jKe8P6ZMPXZl5ksKk6a8vhZ/m494GZng5eFTc3OePv+NF9cG83yg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/querystring-parser@3.0.3':
+    resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/shared-ini-file-loader@2.0.10':
-    resolution: {integrity: sha512-jWASteSezRKohJ7GdA7pHDvmr7Q7tw3b5mu3xLHIkZy/ICftJ+O7aqNaF8wklhI7UNFoQ7flFRM3Rd0KA+1BbQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/service-error-classification@3.0.3':
+    resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/signature-v4@2.0.5':
-    resolution: {integrity: sha512-ABIzXmUDXK4n2c9cXjQLELgH2RdtABpYKT+U131e2I6RbCypFZmxIHmIBufJzU2kdMCQ3+thBGDWorAITFW04A==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/shared-ini-file-loader@3.1.3':
+    resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/smithy-client@2.1.5':
-    resolution: {integrity: sha512-7S865uKzsxApM8W8Q6zkij7tcUFgaG8PuADMFdMt1yL/ku3d0+s6Zwrg3N7iXCPM08Gu/mf0BIfTXIu/9i450Q==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/signature-v4@3.1.2':
+    resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==}
+    engines: {node: '>=16.0.0'}
+
+  '@smithy/smithy-client@3.1.5':
+    resolution: {integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==}
+    engines: {node: '>=16.0.0'}
 
   '@smithy/types@2.12.0':
     resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/types@2.6.0':
-    resolution: {integrity: sha512-PgqxJq2IcdMF9iAasxcqZqqoOXBHufEfmbEUdN1pmJrJltT42b0Sc8UiYSWWzKkciIp9/mZDpzYi4qYG1qqg6g==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/types@3.3.0':
+    resolution: {integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/url-parser@2.0.8':
-    resolution: {integrity: sha512-wQw7j004ScCrBRJ+oNPXlLE9mtofxyadSZ9D8ov/rHkyurS7z1HTNuyaGRj6OvKsEk0SVQsuY0C9+EfM75XTkw==}
+  '@smithy/url-parser@3.0.3':
+    resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==}
 
-  '@smithy/util-base64@2.0.0':
-    resolution: {integrity: sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-base64@3.0.0':
+    resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/util-body-length-browser@2.0.0':
-    resolution: {integrity: sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==}
+  '@smithy/util-body-length-browser@3.0.0':
+    resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==}
 
-  '@smithy/util-body-length-node@2.1.0':
-    resolution: {integrity: sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-body-length-node@3.0.0':
+    resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==}
+    engines: {node: '>=16.0.0'}
 
   '@smithy/util-buffer-from@2.0.0':
     resolution: {integrity: sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/util-config-provider@2.0.0':
-    resolution: {integrity: sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-buffer-from@3.0.0':
+    resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/util-defaults-mode-browser@2.0.9':
-    resolution: {integrity: sha512-JONLJVQWT8165XoSV36ERn3SVlZLJJ4D6IeGsCSePv65Uxa93pzSLE0UMSR9Jwm4zix7rst9AS8W5QIypZWP8Q==}
+  '@smithy/util-config-provider@3.0.0':
+    resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==}
+    engines: {node: '>=16.0.0'}
+
+  '@smithy/util-defaults-mode-browser@3.0.7':
+    resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==}
     engines: {node: '>= 10.0.0'}
 
-  '@smithy/util-defaults-mode-node@2.0.11':
-    resolution: {integrity: sha512-tmqjNsfj+bgZN6jXBe6efZnukzILA7BUytHkzqikuRLNtR+0VVchQHvawD0w6vManh76rO81ydhioe7i4oBzuA==}
+  '@smithy/util-defaults-mode-node@3.0.7':
+    resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==}
     engines: {node: '>= 10.0.0'}
 
-  '@smithy/util-hex-encoding@2.0.0':
-    resolution: {integrity: sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-endpoints@2.0.4':
+    resolution: {integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/util-middleware@2.0.1':
-    resolution: {integrity: sha512-LnsBMi0Mg3gfz/TpNGLv2Jjcz2ra1OX5HR/4IaCepIYmtPQzqMWDdhX/XTW1LS8OZ0xbQuyQPcHkQ+2XkhWOVQ==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-hex-encoding@3.0.0':
+    resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/util-retry@2.0.1':
-    resolution: {integrity: sha512-naj4X0IafJ9yJnVJ58QgSMkCNLjyQOnyrnKh/T0f+0UOUxJiT8vuFn/hS7B/pNqbo2STY7PyJ4J4f+5YqxwNtA==}
-    engines: {node: '>= 14.0.0'}
+  '@smithy/util-middleware@3.0.3':
+    resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/util-stream@2.0.11':
-    resolution: {integrity: sha512-2MeWfqSpZKdmEJ+tH8CJQSgzLWhH5cmdE24X7JB0hiamXrOmswWGGuPvyj/9sQCTclo57pNxLR2p7KrP8Ahiyg==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-retry@3.0.3':
+    resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==}
+    engines: {node: '>=16.0.0'}
 
-  '@smithy/util-uri-escape@2.0.0':
-    resolution: {integrity: sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-stream@3.0.5':
+    resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==}
+    engines: {node: '>=16.0.0'}
 
   '@smithy/util-uri-escape@2.2.0':
     resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==}
     engines: {node: '>=14.0.0'}
 
+  '@smithy/util-uri-escape@3.0.0':
+    resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==}
+    engines: {node: '>=16.0.0'}
+
   '@smithy/util-utf8@2.0.0':
     resolution: {integrity: sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/util-waiter@2.0.8':
-    resolution: {integrity: sha512-t9yaoofNhdEhNlyDeV5al/JJEFJ62HIQBGktgCUE63MvKn6imnbkh1qISsYMyMYVLwhWCpZ3Xa3R1LA+SnWcng==}
-    engines: {node: '>=14.0.0'}
+  '@smithy/util-utf8@3.0.0':
+    resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==}
+    engines: {node: '>=16.0.0'}
+
+  '@smithy/util-waiter@3.1.2':
+    resolution: {integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==}
+    engines: {node: '>=16.0.0'}
 
   '@sqltools/formatter@1.2.5':
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
 
-  '@storybook/addon-actions@8.0.9':
-    resolution: {integrity: sha512-+I3VTvlKdj8puHeS2tyaOVv9syDiNLneVZbTfqN+UDOK2i42NwvZr8PVwjTzMlEj9eePJdCZgiipz55xwts5bw==}
+  '@storybook/addon-actions@8.1.11':
+    resolution: {integrity: sha512-jqYXgBgOVInStOCk//AA+dGkrfN8R7rDXA4lyu82zM59kvICtG9iqgmkSRDn0Z3zUkM+lIHZGoz0aLVQ8pxsgw==}
 
-  '@storybook/addon-backgrounds@8.0.9':
-    resolution: {integrity: sha512-pCDecACrVyxPaJKEWS0sHsRb8xw+IPCSxDM1TkjaAQ6zZ468A/dcUnqW+LVK8bSXgQwWzn23wqnqPFSy5yptuQ==}
+  '@storybook/addon-backgrounds@8.1.11':
+    resolution: {integrity: sha512-naGf1ovmsU2pSWb270yRO1IidnO+0YCZ5Tcb8I4rPhZ0vsdXNURYKS1LPSk1OZkvaUXdeB4Im9HhHfUBJOW9oQ==}
 
-  '@storybook/addon-controls@8.0.9':
-    resolution: {integrity: sha512-wWdmd62UP/sfPm8M7aJjEA+kEXTUIR/QsYi9PoYBhBZcXiikZ4kNan7oD7GfsnzGGKHrBVfwQhO+TqaENGYytA==}
+  '@storybook/addon-controls@8.1.11':
+    resolution: {integrity: sha512-q/Vt4meNVlFlBWIMCJhx6r+bqiiYocCta2RoUK5nyIZUiLzHncKHX6JnCU36EmJzRyah9zkwjfCb2G1r9cjnoQ==}
 
-  '@storybook/addon-docs@8.0.9':
-    resolution: {integrity: sha512-x7hX7UuzJtClu6XwU3SfpyFhuckVcgqgD6BU6Ihxl0zs+i4xp6iKVXYSnHFMRM1sgoeT8TjPxab35Ke8w8BVRw==}
+  '@storybook/addon-docs@8.1.11':
+    resolution: {integrity: sha512-69dv+CE4R5wFU7xnJmhuyEbLN2PEVDV3N/BbgJqeucIYPmm6zDV83Q66teCHKYtRln3BFUqPH5mxsjiHobxfJQ==}
 
-  '@storybook/addon-essentials@8.0.9':
-    resolution: {integrity: sha512-mwAgdfrOsTuTDcagvM7veBh+iayZIWmKOazzkhrIWbhYcrXOsweigD2UOVeHgAiAzJK49znr4FXTCKcE1hOWcw==}
+  '@storybook/addon-essentials@8.1.11':
+    resolution: {integrity: sha512-uRTpcIZQnflML8H+2onicUNIIssKfuviW8Lyrs/KFwSZ1rMcYzhwzCNbGlIbAv04tgHe5NqEyNhb+DVQcZQBzg==}
 
-  '@storybook/addon-highlight@8.0.9':
-    resolution: {integrity: sha512-vaRHGDbx7dpNpQECAHk5wczlZO3ntstprGlqnZt0o7ylz6xB5+pTQwTuIFty0hwKv+3TPcskzzifATUyEOEmyg==}
+  '@storybook/addon-highlight@8.1.11':
+    resolution: {integrity: sha512-Iu8FCAd4ETsB6QF4xDE/OLLZY3HOFopuLM5KE0f58jnccF5zAVGr1Rj/54p6TeK0PEou0tLRPFuZs+LPlEzrSw==}
 
-  '@storybook/addon-interactions@8.0.9':
-    resolution: {integrity: sha512-AMIdNcyM6DDAWvMitBJMqp1iPZND8AXB4QT4VZHGMKG2ngHNKktriEKpTfcRkfKPGTJs9T+71dWfm6/R4tticw==}
+  '@storybook/addon-interactions@8.1.11':
+    resolution: {integrity: sha512-nkc01z61mYM1kxf0ncBQLlFnnwW4RAVPfRSxK9BdbFN3AAvFiHCwVZdn71mi+C3L8oTqYR6o32e0RlXk+AjhHA==}
 
-  '@storybook/addon-links@8.0.9':
-    resolution: {integrity: sha512-FVt+AdW3JFSqbJzkKiqKsMRWqHXqEvCBqFs7lNfk3OW0w0jfv1iREtrxE0dVdJoUFQC9V/2Im/EpJ7UB3C2bNQ==}
+  '@storybook/addon-links@8.1.11':
+    resolution: {integrity: sha512-HlV2RQSrZyi+55W1B1a9eWNuJdNpWx0g3j7s2arNlNmbd6/kfWAp84axBstI1tL0nW4svut7bWlCsMSOIden+A==}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
     peerDependenciesMeta:
       react:
         optional: true
 
-  '@storybook/addon-mdx-gfm@8.0.9':
-    resolution: {integrity: sha512-AoEx+OGKANtVZgKyWKrQhGpMpDuc2S7PnOlNLUiDYzmj8ABAGPmEJmqeb/VHVgqLQSjhOW1fMsQ4fYsecvMxTQ==}
+  '@storybook/addon-mdx-gfm@8.1.11':
+    resolution: {integrity: sha512-0/4Xaisvmoi26iK1ezTOB9dN2b0JbgWKzO2PO6att2Jh7lplLCf1QeoE8Y4SgCh0brage+mA8mKI8NrT7d18pg==}
 
-  '@storybook/addon-measure@8.0.9':
-    resolution: {integrity: sha512-91svOOGEXmGG4USglwXLE3wtlUVgtbKJVxTKX7xRI+AC5JEEaKByVzP17/X8Qn/8HilUL7AfSQ0kCoqtPSJ5cA==}
+  '@storybook/addon-measure@8.1.11':
+    resolution: {integrity: sha512-LkQD3SiLWaWt53aLB3EnmhD9Im8EOO+HKSUE+XGnIJRUcHHRqHfvDkN9KX7T1DCWbfRE5WzMHF5o23b3UiAANw==}
 
-  '@storybook/addon-outline@8.0.9':
-    resolution: {integrity: sha512-fQ+jm356TgUnz81IxsC99/aOesbLw3N5OQRJpo/A6kqbLMzlq3ybVzuXYCKC3f0ArgQRNh4NoMeJBMRFMtaWRw==}
+  '@storybook/addon-outline@8.1.11':
+    resolution: {integrity: sha512-vco3RLVjkcS25dNtj1lxmjq4fC0Nq08KNLMS5cbNPVJWNTuSUi/2EthSTQQCdpfMV/p6u+D5uF20A9Pl0xJFXw==}
 
-  '@storybook/addon-storysource@8.0.9':
-    resolution: {integrity: sha512-5m3K2Rs4fQtKtqwrq4CDS1jK2wzWOlnxhE2ArX5XTWytb1am65CEPxfYTEQkvZH9oPGwX3cXytPCziynqysFMQ==}
+  '@storybook/addon-storysource@8.1.11':
+    resolution: {integrity: sha512-b2K3+ZzfANDTTeN1jnqNgAQ5ZIhnhIAv89gC/36cOhSK5NLyKmyVKLGQmR3fVqX3URpnz9xccst2JNXopvtccw==}
 
-  '@storybook/addon-toolbars@8.0.9':
-    resolution: {integrity: sha512-nNSBnnBOhQ+EJwkrIkK4ZBYPcozNmEH770CZ/6NK85SUJ6WEBZapE6ru33jIUokFGEvlOlNCeai0GUc++cQP8w==}
+  '@storybook/addon-toolbars@8.1.11':
+    resolution: {integrity: sha512-reIKB0+JTiP+GNzynlDcRf4xmv9+j/DQ94qiXl2ZG5+ufKilH8DiRZpVA/i0x+4+TxdGdOJr1/pOf8tAmhNEoQ==}
 
-  '@storybook/addon-viewport@8.0.9':
-    resolution: {integrity: sha512-Ao4+D56cO7biaw+iTlMU1FBec1idX0cmdosDeCFZin06MSawcPkeBlRBeruaSQYdLes8TBMdZPFgfuqI5yIk6g==}
+  '@storybook/addon-viewport@8.1.11':
+    resolution: {integrity: sha512-qk4IcGnAgiAUQxt8l5PIQ293Za+w6wxlJQIpxr7+QM8OVkADPzXY0MmQfYWU9EQplrxAC2MSx3/C1gZeq+MDOQ==}
 
-  '@storybook/blocks@8.0.9':
-    resolution: {integrity: sha512-F2zSrfSwzTFN7qW3zB80tG+EXtmfmCDC6Ird0F7tolszb6tOqJcAcBOwQbE2O0wI63sLu21qxzXgaKBMkiWvJg==}
+  '@storybook/blocks@8.1.11':
+    resolution: {integrity: sha512-eMed7PpL/hAVM6tBS7h70bEAyzbiSU9I/kye4jZ7DkCbAsrX6OKmC7pcHSDn712WTcf3vVqxy5jOKUmOXpc0eg==}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
     peerDependenciesMeta:
       react:
         optional: true
       react-dom:
         optional: true
 
-  '@storybook/builder-manager@8.0.9':
-    resolution: {integrity: sha512-/PxDwZIfMc/PSRZcasb6SIdGr3azIlenzx7dBF7Imt8i4jLHiAf1t00GvghlfJsvsrn4DNp95rbRbXTDyTj7tQ==}
+  '@storybook/builder-manager@8.1.11':
+    resolution: {integrity: sha512-U7bmed4Ayg+OlJ8HPmLeGxLTHzDY7rxmxM4aAs4YL01fufYfBcjkIP9kFhJm+GJOvGm+YJEUAPe5mbM1P/bn0Q==}
 
-  '@storybook/builder-vite@8.0.9':
-    resolution: {integrity: sha512-7hEQFZIIz7VvxdySDpPE96iMvZxQvRZcRdhaNGeE+8Y2pyc3DgYE4WY3sjr+LUoB0a6TYLpAIKqbXwtLz0R+PQ==}
+  '@storybook/builder-vite@8.1.11':
+    resolution: {integrity: sha512-hG4eoNMCPgjZ2Ai+zSmk69zjsyEihe75XbJXtYfGRqjMWtz2+SAUFO54fLc2BD5svcUiTeN+ukWcTrwApyPsKg==}
     peerDependencies:
       '@preact/preset-vite': '*'
       typescript: '>= 4.3.x'
@@ -3974,48 +4377,53 @@ packages:
       vite-plugin-glimmerx:
         optional: true
 
-  '@storybook/channels@8.0.9':
-    resolution: {integrity: sha512-7Lcfyy5CsLWWGhMPO9WG4jZ/Alzp0AjepFhEreYHRPtQrfttp6qMAjE/g1aHgun0qHCYWxwqIG4NLR/hqDNrXQ==}
+  '@storybook/channels@8.1.11':
+    resolution: {integrity: sha512-fu5FTqo6duOqtJFa6gFzKbiSLJoia+8Tibn3xFfB6BeifWrH81hc+AZq0lTmHo5qax2G5t8ZN8JooHjMw6k2RA==}
 
-  '@storybook/cli@8.0.9':
-    resolution: {integrity: sha512-lilYTKn8F5YOePijqfRYFa5v2mHVIJxPCIgTn+OXAmAFbcizZ6P8P6niU4J/NXulgx68Ln1M7hYhFtTP25hVTw==}
+  '@storybook/cli@8.1.11':
+    resolution: {integrity: sha512-4U48w9C7mVEKrykcPcfHwJkRyCqJ28XipbElACbjIIkQEqaHaOVtP3GeKIrgkoOXe/HK3O4zKWRP2SqlVS0r4A==}
     hasBin: true
 
-  '@storybook/client-logger@8.0.9':
-    resolution: {integrity: sha512-LzV/RHkbf07sRc1Jc0ff36RlapKf9Ul7/+9VMvVbI3hshH1CpmrZK4t/tsIdpX/EVOdJ1Gg5cES06PnleOAIPA==}
+  '@storybook/client-logger@8.1.11':
+    resolution: {integrity: sha512-DVMh2usz3yYmlqCLCiCKy5fT8/UR9aTh+gSqwyNFkGZrIM4otC5A8eMXajXifzotQLT5SaOEnM3WzHwmpvMIEA==}
 
-  '@storybook/codemod@8.0.9':
-    resolution: {integrity: sha512-VBeGpSZSQpL6iyLLqceJSNGhdCqcNwv+xC/aWdDFOkmuE1YfbmNNwpa9QYv4ZFJ2QjUsm4iTWG60qK+9NXeSKA==}
+  '@storybook/codemod@8.1.11':
+    resolution: {integrity: sha512-/LCozjH1IQ1TOs9UQV59BE0X6UZ9q+C0NEUz7qmJZPrwAii3FkW4l7D/fwxblpMExaoxv0oE8NQfUz49U/5Ymg==}
 
-  '@storybook/components@8.0.9':
-    resolution: {integrity: sha512-JcwBGADzIJs0PSzqykrrD2KHzNG9wtexUOKuidt+FSv9szpUhe3qBAXIHpdfBRl7mOJ9TRZ5rt+mukEnfncdzA==}
+  '@storybook/components@8.1.11':
+    resolution: {integrity: sha512-iXKsNu7VmrLBtjMfPj7S4yJ6T13GU6joKcVcrcw8wfrQJGlPFp4YaURPBUEDxvCt1XWi5JkaqJBvb48kIrROEQ==}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
 
-  '@storybook/core-common@8.0.9':
-    resolution: {integrity: sha512-Jmue+sfHFb4GTYBzyWYw1MygoJiQSfISIrKmNIzAmZ+oR9EOr+jpu/i/bH+uetZ2Hqg1AGhj1VB7OtJp9HQyWw==}
+  '@storybook/core-common@8.1.11':
+    resolution: {integrity: sha512-Ix0nplD4I4DrV2t9B+62jaw1baKES9UbR/Jz9LVKFF9nsua3ON0aVe73dOjMxFWBngpzBYWe+zYBTZ7aQtDH4Q==}
+    peerDependencies:
+      prettier: ^2 || ^3
+    peerDependenciesMeta:
+      prettier:
+        optional: true
 
-  '@storybook/core-events@8.0.9':
-    resolution: {integrity: sha512-DxSUx7wG9Qe3OFUBnv3OrYq48J8UWNo2DUR5/JecJCtp3n++L4fAEW3J0IF5FfxpQDMQSp1yTNjZ2PaWCMd2ag==}
+  '@storybook/core-events@8.1.11':
+    resolution: {integrity: sha512-vXaNe2KEW9BGlLrg0lzmf5cJ0xt+suPjWmEODH5JqBbrdZ67X6ApA2nb6WcxDQhykesWCuFN5gp1l+JuDOBi7A==}
 
-  '@storybook/core-server@8.0.9':
-    resolution: {integrity: sha512-BIe1T5YUBl0GYxEjRoTQsvXD2pyuzL8rPTUD41zlzSQM0R8U6Iant9SzRms4u0+rKUm2mGxxKuODlUo5ewqaGA==}
+  '@storybook/core-server@8.1.11':
+    resolution: {integrity: sha512-L6dzQTmR0np/kagNONvvlm6lSvF1FNc9js3vxsEEPnEypLbhx8bDZaHmuhmBpYUzKyUMpRVQTE/WgjHLuBBuxA==}
 
-  '@storybook/csf-plugin@8.0.9':
-    resolution: {integrity: sha512-pXaNCNi++kxKsqSWwvx215fPx8cNqvepLVxQ7B69qXLHj80DHn0Q3DFBO3sLXNiQMJ2JK4OYcTxMfuOiyzszKw==}
+  '@storybook/csf-plugin@8.1.11':
+    resolution: {integrity: sha512-hkA8gjFtSN/tabG0cuvmEqanMXtxPr3qTkp4UNSt1R6jBEgFHRG2y/KYLl367kDwOSFTT987ZgRfJJruU66Fvw==}
 
-  '@storybook/csf-tools@8.0.9':
-    resolution: {integrity: sha512-PiNMhL97giLytTdQwuhsZ92buVk4gy9H/8DtrDhUc45/1OmF95gogm6T2Yap729SIFwgpOcuq/U3aVo6d6swVQ==}
+  '@storybook/csf-tools@8.1.11':
+    resolution: {integrity: sha512-6qMWAg/dBwCVIHzANM9lSHoirwqSS+wWmv+NwAs0t9S94M75IttHYxD3IyzwaSYCC5llp0EQFvtXXAuSfFbibg==}
 
-  '@storybook/csf@0.1.6':
-    resolution: {integrity: sha512-JjWnBptVhBYJ14yq+cHs66BXjykRUWQ5TlD1RhPxMOtavynYyV/Q+QR98/N+XB+mcPtFMm5I2DvNkpj0/Dk8Mw==}
+  '@storybook/csf@0.1.9':
+    resolution: {integrity: sha512-JlZ6v/iFn+iKohKGpYXnMeNeTiiAMeFoDhYnPLIC8GnyyIWqEI9wJYrOK9i9rxlJ8NZAH/ojGC/u/xVC41qSgQ==}
 
-  '@storybook/docs-mdx@3.0.0':
-    resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==}
+  '@storybook/docs-mdx@3.1.0-next.0':
+    resolution: {integrity: sha512-t4syFIeSyufieNovZbLruPt2DmRKpbwL4fERCZ1MifWDRIORCKLc4NCEHy+IqvIqd71/SJV2k4B51nF7vlJfmQ==}
 
-  '@storybook/docs-tools@8.0.9':
-    resolution: {integrity: sha512-OzogAeOmeHea/MxSPKRBWtOQVNSpoq+OOpimO9YRA5h5GBRJ2TUOGT44Gny6QT4ll5AvQA8fIiq9KezKcLekAg==}
+  '@storybook/docs-tools@8.1.11':
+    resolution: {integrity: sha512-mEXtR9rS7Y+OdKtT/QG6JBGYR1L41mcDhIqhnk7RmYl9qJstVAegrCKWR53sPKFdTVOHU7dmu6k+BD+TqHpyyw==}
 
   '@storybook/global@5.0.0':
     resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
@@ -4027,83 +4435,83 @@ packages:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
 
-  '@storybook/instrumenter@8.0.9':
-    resolution: {integrity: sha512-Gw74dgpTU/2p7FG0s7DuVdqCbJ2MEcSuRJjDo7HcXRYcvWp7I6Ly+C0v7N5VaoS+kbBVerAhLKIHZgG/LZf1og==}
+  '@storybook/instrumenter@8.1.11':
+    resolution: {integrity: sha512-r/U9hcqnodNMHuzRt1g56mWrVsDazR85Djz64M3KOwBhrTj5d46DF4/EE80w/5zR5JOrT7p8WmjJRowiVteOCQ==}
 
-  '@storybook/manager-api@8.0.9':
-    resolution: {integrity: sha512-99b3yKArDSvfabXL7QE3nA95e4DdW/5H/ZCcr6/E2qCQJayZ6G1v/WWamKXbiaTpkndulFmcb/+ZmnDXcweIIQ==}
+  '@storybook/manager-api@8.1.11':
+    resolution: {integrity: sha512-QSgwKfAw01K9YvvZj30iGBMgQ4YaCT3vojmttuqdH5ukyXkiO7pENLJj4Y+alwUeSi0g+SJeadCI3PXySBHOGg==}
 
-  '@storybook/manager@8.0.9':
-    resolution: {integrity: sha512-+NnRo+5JQFGNqveKrLtC0b+Z08Tae4m44iq292bPeZMpr9OkFsIkU0PBPsHTHPkrqC/zZXRNsCsTEgvu3p2OIA==}
+  '@storybook/manager@8.1.11':
+    resolution: {integrity: sha512-e02y9dmxowo7cTKYm9am7UO6NOHoHy6Xi7xZf/UA932qLwFZUtk5pnwIEFaZWI3OQsRUCGhP+FL5zizU7uVZeg==}
 
-  '@storybook/node-logger@8.0.9':
-    resolution: {integrity: sha512-5ajMdZFrYrjGLJOVDq7dlEQNFsgeLHymt4dCK9MulL/ciXykmXUZXE3Bye0wFy+I2qqDVvrvR8uzCvSFvm5MAQ==}
+  '@storybook/node-logger@8.1.11':
+    resolution: {integrity: sha512-wdzFo7B2naGhS52L3n1qBkt5BfvQjs8uax6B741yKRpiGgeAN8nz8+qelkD25MbSukxvbPgDot7WJvsMU/iCzg==}
 
-  '@storybook/preview-api@8.0.9':
-    resolution: {integrity: sha512-zHfX34bkAMzzmE7vbDzaqFwSW6ExiBD0HiO1L/IsHF55f0f7xV7IH8uJyFRrDTvAoW3ReSxZDMvvPpeydFPKGA==}
+  '@storybook/preview-api@8.1.11':
+    resolution: {integrity: sha512-8ZChmFV56GKppCJ0hnBd/kNTfGn2gWVq1242kuet13pbJtBpvOhyq4W01e/Yo14tAPXvgz8dSnMvWLbJx4QfhQ==}
 
-  '@storybook/preview@8.0.9':
-    resolution: {integrity: sha512-tFsR8xc8AYBZZrZw8enklFbSQt7ZAV+rv20BoxwDhd3q7fjXyK7O4moGPqUwBZ7rukTG13nPoISxr+VXAk/HYA==}
+  '@storybook/preview@8.1.11':
+    resolution: {integrity: sha512-K/9NZmjnL0D1BROkTNWNoPqgL2UaocALRSqCARmkBLgU2Rn/FuZgEclHkWlYo6pUrmLNK+bZ+XzpNMu12iTbpg==}
 
-  '@storybook/react-dom-shim@8.0.9':
-    resolution: {integrity: sha512-8011KlRuG3obr5pZZ7bcEyYYNWF3tR596YadoMd267NPoHKvwAbKL1L/DNgb6kiYjZDUf9QfaKSCWW31k0kcRQ==}
+  '@storybook/react-dom-shim@8.1.11':
+    resolution: {integrity: sha512-KVDSuipqkFjpGfldoRM5xR/N1/RNmbr+sVXqMmelr0zV2jGnexEZnoa7wRHk7IuXuivLWe8BxMxzvQWqjIa4GA==}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
 
-  '@storybook/react-vite@8.0.9':
-    resolution: {integrity: sha512-FT5KeulUH6grfzOJOxJCxpv9+81UVDrT9UPcgiFhQT9rKtsgmltezThwbHknByZNw3WWnf+ieidMLEis9hd73A==}
+  '@storybook/react-vite@8.1.11':
+    resolution: {integrity: sha512-QqkE6QKsIDthXtps9+YSBQ39O4VvU7Uu3y6WSA3IPgKTtGnmIvhwXtapjf7WQ2cNb5KY1JksFxHXbDe0i5IL4g==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
       vite: ^4.0.0 || ^5.0.0
 
-  '@storybook/react@8.0.9':
-    resolution: {integrity: sha512-NeQ6suZG3HKikwe3Tx9cAIaRx7uP8FKCmlVvIiBg4LTTI5orCt94PPakvuZukZcbkqvcCnEBkebAzwUpn8PiJw==}
+  '@storybook/react@8.1.11':
+    resolution: {integrity: sha512-t+EYXOkgwg3ropLGS9y8gGvX5/Okffu/6JYL3YWksrBGAZSqVV4NkxCnVJZepS717SyhR0tN741gv/SxxFPJMg==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
       typescript: '>= 4.2.x'
     peerDependenciesMeta:
       typescript:
         optional: true
 
-  '@storybook/router@8.0.9':
-    resolution: {integrity: sha512-aAOWxbM9J4mt+cp4o88T2PB29mgBBTOzU37/pUsTHYnKnR9XI4npXEXdN8Gv+ryqM0kj0AbBpz/llFlnR2MNNA==}
+  '@storybook/router@8.1.11':
+    resolution: {integrity: sha512-nU5lsBvy0L8wBYOkjagh29ztZicDATpZNYrHuavlhQ2jznmmHdJvXKYk+VrMAbthjQ6ZBqfeeMNPR1UlnqR5Rw==}
 
-  '@storybook/source-loader@8.0.9':
-    resolution: {integrity: sha512-FDnpxIGE5nIYT15pvYe6rz95TSBrdLcDll7lOHNyZisWt19MI3wZU3YkVsFNRBuFrebo+FjVU3wHyoV81ur1Qw==}
+  '@storybook/source-loader@8.1.11':
+    resolution: {integrity: sha512-4cfJ7aPjtniIdDGiFjdFpO47byHOl4RKYCJEHf9t+j0xHmlXe4B9aAinxuFfv3GKAXfLvSbbwGO0cDZQRj+brw==}
 
-  '@storybook/telemetry@8.0.9':
-    resolution: {integrity: sha512-AGGfcup06t+wxhBIkHd0iybieOh9PDVZQJ9oPct5JGB39+ni9wvs0WOD+MYlHbsjp8id7+aGkh6mYuYOvfck+Q==}
+  '@storybook/telemetry@8.1.11':
+    resolution: {integrity: sha512-Jqvm7HcZismKzPuebhyLECO6KjGiSk4ycbca1WUM/TUvifxCXqgoUPlHHQEEfaRdHS63/MSqtMNjLsQRLC/vNQ==}
 
-  '@storybook/test@8.0.9':
-    resolution: {integrity: sha512-bRd5tBJnPzR6UKbDXONWnFWtdkNOY99HMLDUWe5fTRo50GwkrpFBVqPflhdkruEeof0kAbBUbnoN2CIYgtnAFw==}
+  '@storybook/test@8.1.11':
+    resolution: {integrity: sha512-k+V3HemF2/I8fkRxRqM8uH8ULrpBSAAdBOtWSHWLvHguVcb2YA4g4kKo6tXBB9256QfyDW4ZiaAj0/9TMxmJPQ==}
 
-  '@storybook/theming@8.0.9':
-    resolution: {integrity: sha512-jgfDuYoiNMMirQiASN3Eg0hGDXsEtpdAcMxyShqYGwu9elxgD9yUnYC2nSckYsM74a3ZQ3JaViZ9ZFSe2FHmeQ==}
+  '@storybook/theming@8.1.11':
+    resolution: {integrity: sha512-Chn/opjO6Rl1isNobutYqAH2PjKNkj09YBw/8noomk6gElSa3JbUTyaG/+JCHA6OG/9kUsqoKDb5cZmAKNq/jA==}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
     peerDependenciesMeta:
       react:
         optional: true
       react-dom:
         optional: true
 
-  '@storybook/types@8.0.9':
-    resolution: {integrity: sha512-ew0EXzk9k4B557P1qIWYrnvUcgaE0WWA5qQS0AU8l+fRTp5nvl9O3SP/zNIB0SN1qDFO7dXr3idTNTyIikTcEQ==}
+  '@storybook/types@8.1.11':
+    resolution: {integrity: sha512-k9N5iRuY2+t7lVRL6xeu6diNsxO3YI3lS4Juv3RZ2K4QsE/b3yG5ElfJB8DjHDSHwRH4ORyrU71KkOCUVfvtnw==}
 
-  '@storybook/vue3-vite@8.0.9':
-    resolution: {integrity: sha512-IkzYsEyCo5HIvLWbJeGrBu/VIN4u+LvdIAz7vcFqVVXBtTUhy+9/8caLx8fdnM0FWgKcBRQs8HnjBB2V0lOFcg==}
+  '@storybook/vue3-vite@8.1.11':
+    resolution: {integrity: sha512-q0bqh8XEEunaTmp4YiDqM2+YZLwEIevTb5PnNe7G7f2qOiSCE1ncBDnBK717UlCd+iYr34NTztgV2/jIhz1i5w==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
 
-  '@storybook/vue3@8.0.9':
-    resolution: {integrity: sha512-EqVdS62YbOCAE0wJrQKW0sHpM90be8N8Mvmj+HzB0QYhJNtFqP9ehwbcTfwEKtaVGudisHgGBOzNoSKDlxFaag==}
+  '@storybook/vue3@8.1.11':
+    resolution: {integrity: sha512-xJtvfLiCOY3UqwDMd0hZdsadPm1q8dwjfM1UN2Q2ssRWNfXzww1oi+Msj902wz9zFZMYVZypfTfgrdRgWmfEjA==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       vue: ^3.0.0
@@ -4131,8 +4539,8 @@ packages:
     cpu: [arm64]
     os: [darwin]
 
-  '@swc/core-darwin-arm64@1.4.17':
-    resolution: {integrity: sha512-HVl+W4LezoqHBAYg2JCqR+s9ife9yPfgWSj37iIawLWzOmuuJ7jVdIB7Ee2B75bEisSEKyxRlTl6Y1Oq3owBgw==}
+  '@swc/core-darwin-arm64@1.6.6':
+    resolution: {integrity: sha512-5DA8NUGECcbcK1YLKJwNDKqdtTYDVnkfDU1WvQSXq/rU+bjYCLtn5gCe8/yzL7ISXA6rwqPU1RDejhbNt4ARLQ==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [darwin]
@@ -4143,8 +4551,8 @@ packages:
     cpu: [x64]
     os: [darwin]
 
-  '@swc/core-darwin-x64@1.4.17':
-    resolution: {integrity: sha512-WYRO9Fdzq4S/he8zjW5I95G1zcvyd9yyD3Tgi4/ic84P5XDlSMpBDpBLbr/dCPjmSg7aUXxNQqKqGkl6dQxYlA==}
+  '@swc/core-darwin-x64@1.6.6':
+    resolution: {integrity: sha512-2nbh/RHpweNRsJiYDFk1KcX7UtaKgzzTNUjwtvK5cp0wWrpbXmPvdlWOx3yzwoiSASDFx78242JHHXCIOlEdsw==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [darwin]
@@ -4161,8 +4569,8 @@ packages:
     cpu: [arm]
     os: [linux]
 
-  '@swc/core-linux-arm-gnueabihf@1.4.17':
-    resolution: {integrity: sha512-cgbvpWOvtMH0XFjvwppUCR+Y+nf6QPaGu6AQ5hqCP+5Lv2zO5PG0RfasC4zBIjF53xgwEaaWmGP5/361P30X8Q==}
+  '@swc/core-linux-arm-gnueabihf@1.6.6':
+    resolution: {integrity: sha512-YgytuyUfR7b0z0SRHKV+ylr83HmgnROgeT7xryEkth6JGpAEHooCspQ4RrWTU8+WKJ7aXiZlGXPgybQ4TiS+TA==}
     engines: {node: '>=10'}
     cpu: [arm]
     os: [linux]
@@ -4173,8 +4581,8 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@swc/core-linux-arm64-gnu@1.4.17':
-    resolution: {integrity: sha512-l7zHgaIY24cF9dyQ/FOWbmZDsEj2a9gRFbmgx2u19e3FzOPuOnaopFj0fRYXXKCmtdx+anD750iBIYnTR+pq/Q==}
+  '@swc/core-linux-arm64-gnu@1.6.6':
+    resolution: {integrity: sha512-yGwx9fddzEE0iURqRVwKBQ4IwRHE6hNhl15WliHpi/PcYhzmYkUIpcbRXjr0dssubXAVPVnx6+jZVDSbutvnfg==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -4185,8 +4593,8 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@swc/core-linux-arm64-musl@1.4.17':
-    resolution: {integrity: sha512-qhH4gr9gAlVk8MBtzXbzTP3BJyqbAfUOATGkyUtohh85fPXQYuzVlbExix3FZXTwFHNidGHY8C+ocscI7uDaYw==}
+  '@swc/core-linux-arm64-musl@1.6.6':
+    resolution: {integrity: sha512-a6fMbqzSAsS5KCxFJyg1mD5kwN3ZFO8qQLyJ75R/htZP/eCt05jrhmOI7h2n+1HjiG332jLnZ9S8lkVE5O8Nqw==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -4197,8 +4605,8 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@swc/core-linux-x64-gnu@1.4.17':
-    resolution: {integrity: sha512-vRDFATL1oN5oZMImkwbgSHEkp8xG1ofEASBypze01W1Tqto8t+yo6gsp69wzCZBlxldsvPpvFZW55Jq0Rn+UnA==}
+  '@swc/core-linux-x64-gnu@1.6.6':
+    resolution: {integrity: sha512-hRGsUKNzzZle28YF0dYIpN0bt9PceR9LaVBq7x8+l9TAaDLFbgksSxcnU/ubTtsy+WsYSYGn+A83w3xWC0O8CQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -4209,8 +4617,8 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@swc/core-linux-x64-musl@1.4.17':
-    resolution: {integrity: sha512-zQNPXAXn3nmPqv54JVEN8k2JMEcMTQ6veVuU0p5O+A7KscJq+AGle/7ZQXzpXSfUCXlLMX4wvd+rwfGhh3J4cw==}
+  '@swc/core-linux-x64-musl@1.6.6':
+    resolution: {integrity: sha512-NokIUtFxJDVv3LzGeEtYMTV3j2dnGKLac59luTeq36DQLZdJQawQIdTbzzWl2jE7lxxTZme+dhsVOH9LxE3ceg==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -4221,8 +4629,8 @@ packages:
     cpu: [arm64]
     os: [win32]
 
-  '@swc/core-win32-arm64-msvc@1.4.17':
-    resolution: {integrity: sha512-z86n7EhOwyzxwm+DLE5NoLkxCTme2lq7QZlDjbQyfCxOt6isWz8rkW5QowTX8w9Rdmk34ncrjSLvnHOeLY17+w==}
+  '@swc/core-win32-arm64-msvc@1.6.6':
+    resolution: {integrity: sha512-lzYdI4qb4k1dFG26yv+9Jaq/bUMAhgs/2JsrLncGjLof86+uj74wKYCQnbzKAsq2hDtS5DqnHnl+//J+miZfGA==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [win32]
@@ -4233,8 +4641,8 @@ packages:
     cpu: [ia32]
     os: [win32]
 
-  '@swc/core-win32-ia32-msvc@1.4.17':
-    resolution: {integrity: sha512-JBwuSTJIgiJJX6wtr4wmXbfvOswHFj223AumUrK544QV69k60FJ9q2adPW9Csk+a8wm1hLxq4HKa2K334UHJ/g==}
+  '@swc/core-win32-ia32-msvc@1.6.6':
+    resolution: {integrity: sha512-bvl7FMaXIJQ76WZU0ER4+RyfKIMGb6S2MgRkBhJOOp0i7VFx4WLOnrmMzaeoPJaJSkityVKAftfNh7NBzTIydQ==}
     engines: {node: '>=10'}
     cpu: [ia32]
     os: [win32]
@@ -4245,17 +4653,17 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@swc/core-win32-x64-msvc@1.4.17':
-    resolution: {integrity: sha512-jFkOnGQamtVDBm3MF5Kq1lgW8vx4Rm1UvJWRUfg+0gx7Uc3Jp3QMFeMNw/rDNQYRDYPG3yunCC+2463ycd5+dg==}
+  '@swc/core-win32-x64-msvc@1.6.6':
+    resolution: {integrity: sha512-WAP0JoCTfgeYKgOeYJoJV4ZS0sQUmU3OwvXa2dYYtMLF7zsNqOiW4niU7QlThBHgUv/qNZm2p6ITEgh3w1cltw==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [win32]
 
-  '@swc/core@1.4.17':
-    resolution: {integrity: sha512-tq+mdWvodMBNBBZbwFIMTVGYHe9N7zvEaycVVjfvAx20k1XozHbHhRv+9pEVFJjwRxLdXmtvFZd3QZHRAOpoNQ==}
+  '@swc/core@1.6.6':
+    resolution: {integrity: sha512-sHfmIUPUXNrQTwFMVCY5V5Ena2GTOeaWjS2GFUpjLhAgVfP90OP67DWow7+cYrfFtqBdILHuWnjkTcd0+uPKlg==}
     engines: {node: '>=10'}
     peerDependencies:
-      '@swc/helpers': ^0.5.0
+      '@swc/helpers': '*'
     peerDependenciesMeta:
       '@swc/helpers':
         optional: true
@@ -4269,8 +4677,8 @@ packages:
     peerDependencies:
       '@swc/core': '*'
 
-  '@swc/types@0.1.5':
-    resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
+  '@swc/types@0.1.9':
+    resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==}
 
   '@swc/wasm@1.2.130':
     resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
@@ -4332,16 +4740,16 @@ packages:
     resolution: {integrity: sha512-EmCsnzdvawyk4b+4JKaLLuicHcJQRZtL1zSy9AWJLiiHTbDDseYgLxfaCEfLk8v2bUe7SBXwl3n3B7OjgvH11Q==}
     hasBin: true
 
-  '@testing-library/dom@9.3.3':
-    resolution: {integrity: sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==}
-    engines: {node: '>=14'}
+  '@testing-library/dom@10.1.0':
+    resolution: {integrity: sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==}
+    engines: {node: '>=18'}
 
   '@testing-library/dom@9.3.4':
     resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==}
     engines: {node: '>=14'}
 
-  '@testing-library/jest-dom@6.4.2':
-    resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==}
+  '@testing-library/jest-dom@6.4.5':
+    resolution: {integrity: sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A==}
     engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
     peerDependencies:
       '@jest/globals': '>= 28'
@@ -4367,8 +4775,8 @@ packages:
     peerDependencies:
       '@testing-library/dom': '>=7.21.4'
 
-  '@testing-library/vue@8.0.3':
-    resolution: {integrity: sha512-wSsbNlZ69ZFQgVlHMtc/ZC/g9BHO7MhyDrd4nHyfEubtMr3kToN/w4/BsSBknGIF8w9UmPbsgbIuq/CbdBHzCA==}
+  '@testing-library/vue@8.1.0':
+    resolution: {integrity: sha512-ls4RiHO1ta4mxqqajWRh8158uFObVrrtAPoxk7cIp4HrnQUj/ScKzqz53HxYpG3X6Zb7H2v+0eTGLSoy8HQ2nA==}
     engines: {node: '>=14'}
     peerDependencies:
       '@vue/compiler-sfc': '>= 3'
@@ -4384,8 +4792,8 @@ packages:
     resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
     engines: {node: '>=10.13.0'}
 
-  '@tsd/typescript@5.3.3':
-    resolution: {integrity: sha512-CQlfzol0ldaU+ftWuG52vH29uRoKboLinLy84wS8TQOu+m+tWoaUfk4svL4ij2V8M5284KymJBlHUusKj6k34w==}
+  '@tsd/typescript@5.4.5':
+    resolution: {integrity: sha512-saiCxzHRhUrRxQV2JhH580aQUZiKQUXI38FcAcikcfOomAil4G4lxT0RfrrKywoAYP/rqAdYXYmNRLppcd+hQQ==}
     engines: {node: '>=14.17'}
 
   '@twemoji/parser@15.0.0':
@@ -4430,12 +4838,6 @@ packages:
   '@types/cacheable-request@6.0.3':
     resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
 
-  '@types/chai-subset@1.3.5':
-    resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==}
-
-  '@types/chai@4.3.11':
-    resolution: {integrity: sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==}
-
   '@types/color-convert@2.0.3':
     resolution: {integrity: sha512-2Q6wzrNiuEvYxVQqhh7sXM2mhIhvZR/Paq4FdsQkOMgWsCIkKvSGj8Le1/XalulrmgOzPMqNa0ix+ePY4hTrfg==}
 
@@ -4466,6 +4868,9 @@ packages:
   '@types/detect-port@1.3.2':
     resolution: {integrity: sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==}
 
+  '@types/diff@5.2.1':
+    resolution: {integrity: sha512-uxpcuwWJGhe2AR1g8hD9F5OYGCqjqWnBUQFD8gMZsDbv8oPHzxJF6iMO6n8Tk0AdzlxoaaoQhOYlIg/PukVU8g==}
+
   '@types/disposable-email-domains@1.0.2':
     resolution: {integrity: sha512-SDKwyYTjk3y5aZBxxc38yRecpJPjsqn57STz1bNxYYlv4k11bBe7QB8w4llXDTmQXKT1mFvgGmJv+8Zdu3YmJw==}
 
@@ -4529,8 +4934,8 @@ packages:
   '@types/http-errors@2.0.4':
     resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
 
-  '@types/http-link-header@1.0.5':
-    resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
+  '@types/http-link-header@1.0.7':
+    resolution: {integrity: sha512-snm5oLckop0K3cTDAiBnZDy6ncx9DJ3mCRDvs42C884MbVYPP74Tiq2hFsSDRTyjK6RyDYDIulPiW23ge+g5Lw==}
 
   '@types/istanbul-lib-coverage@2.0.4':
     resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
@@ -4547,8 +4952,8 @@ packages:
   '@types/js-yaml@4.0.9':
     resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
 
-  '@types/jsdom@21.1.6':
-    resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
+  '@types/jsdom@21.1.7':
+    resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==}
 
   '@types/json-schema@7.0.12':
     resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
@@ -4559,8 +4964,8 @@ packages:
   '@types/json5@0.0.29':
     resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
 
-  '@types/jsonld@1.5.13':
-    resolution: {integrity: sha512-n7fUU6W4kSYK8VQlf/LsE9kddBHPKhODoVOjsZswmve+2qLwBy6naWxs/EiuSZN9NU0N06Ra01FR+j87C62T0A==}
+  '@types/jsonld@1.5.14':
+    resolution: {integrity: sha512-z4IRf5oRgjPTkazDDv94sjzI5iK3DrDEW7Y5Gk4VO4+ANymgtHtNaXWi93+BmiAoG3PB9QTv5DgSpKWGYVvysA==}
 
   '@types/jsrsasign@10.5.14':
     resolution: {integrity: sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==}
@@ -4595,8 +5000,8 @@ packages:
   '@types/mdx@2.0.3':
     resolution: {integrity: sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==}
 
-  '@types/micromatch@4.0.7':
-    resolution: {integrity: sha512-C/FMQ8HJAZhTsDpl4wDKZdMeeW5USjgzOczUwTGbRc1ZopPgOhIEnxY2ZgUrsuyy4DwK1JVOJZKFakv3TbCKiA==}
+  '@types/micromatch@4.0.9':
+    resolution: {integrity: sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==}
 
   '@types/mime-types@2.1.4':
     resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==}
@@ -4628,8 +5033,8 @@ packages:
   '@types/node@20.11.5':
     resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==}
 
-  '@types/node@20.12.7':
-    resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==}
+  '@types/node@20.14.9':
+    resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
 
   '@types/node@20.9.1':
     resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==}
@@ -4646,8 +5051,8 @@ packages:
   '@types/oauth2orize@1.11.5':
     resolution: {integrity: sha512-C6hrRoh9hCnqis39OpeUZSwgw+TIzcV0CsxwJMGfQjTx4I1r+CLmuEPzoDJr5NRTfc7OMwHNLkQwrGFLKrJjMQ==}
 
-  '@types/oauth@0.9.4':
-    resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
+  '@types/oauth@0.9.5':
+    resolution: {integrity: sha512-+oQ3C2Zx6ambINOcdIARF5Z3Tu3x//HipE889/fqo3sgpQZbe9c6ExdQFtN6qlhpR7p83lTZfPJt0tCAW29dog==}
 
   '@types/offscreencanvas@2019.3.0':
     resolution: {integrity: sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==}
@@ -4658,8 +5063,8 @@ packages:
   '@types/pg-pool@2.0.4':
     resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==}
 
-  '@types/pg@8.11.5':
-    resolution: {integrity: sha512-2xMjVviMxneZHDHX5p5S6tsRRs7TpDHeeK7kTTMe/kAC/mRRNjWHjZg0rkiY+e17jXSZV3zJYDxXV8Cy72/Vuw==}
+  '@types/pg@8.11.6':
+    resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==}
 
   '@types/pg@8.6.1':
     resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
@@ -4766,6 +5171,9 @@ packages:
   '@types/unist@3.0.2':
     resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
 
+  '@types/uuid@10.0.0':
+    resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
+
   '@types/uuid@9.0.8':
     resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
 
@@ -4815,8 +5223,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/eslint-plugin@7.7.1':
-    resolution: {integrity: sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==}
+  '@typescript-eslint/eslint-plugin@7.15.0':
+    resolution: {integrity: sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^7.0.0
@@ -4846,8 +5254,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/parser@7.7.1':
-    resolution: {integrity: sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==}
+  '@typescript-eslint/parser@7.15.0':
+    resolution: {integrity: sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -4864,8 +5272,8 @@ packages:
     resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==}
     engines: {node: ^16.0.0 || >=18.0.0}
 
-  '@typescript-eslint/scope-manager@7.7.1':
-    resolution: {integrity: sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==}
+  '@typescript-eslint/scope-manager@7.15.0':
+    resolution: {integrity: sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
   '@typescript-eslint/type-utils@6.11.0':
@@ -4888,8 +5296,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/type-utils@7.7.1':
-    resolution: {integrity: sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==}
+  '@typescript-eslint/type-utils@7.15.0':
+    resolution: {integrity: sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -4906,8 +5314,8 @@ packages:
     resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==}
     engines: {node: ^16.0.0 || >=18.0.0}
 
-  '@typescript-eslint/types@7.7.1':
-    resolution: {integrity: sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==}
+  '@typescript-eslint/types@7.15.0':
+    resolution: {integrity: sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
   '@typescript-eslint/typescript-estree@6.11.0':
@@ -4928,8 +5336,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/typescript-estree@7.7.1':
-    resolution: {integrity: sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==}
+  '@typescript-eslint/typescript-estree@7.15.0':
+    resolution: {integrity: sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       typescript: '*'
@@ -4949,8 +5357,8 @@ packages:
     peerDependencies:
       eslint: ^8.56.0
 
-  '@typescript-eslint/utils@7.7.1':
-    resolution: {integrity: sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==}
+  '@typescript-eslint/utils@7.15.0':
+    resolution: {integrity: sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -4963,87 +5371,78 @@ packages:
     resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==}
     engines: {node: ^16.0.0 || >=18.0.0}
 
-  '@typescript-eslint/visitor-keys@7.7.1':
-    resolution: {integrity: sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==}
+  '@typescript-eslint/visitor-keys@7.15.0':
+    resolution: {integrity: sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
   '@ungap/structured-clone@1.2.0':
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
 
-  '@vitejs/plugin-vue@5.0.4':
-    resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
+  '@vitejs/plugin-vue@5.0.5':
+    resolution: {integrity: sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     peerDependencies:
       vite: ^5.0.0
       vue: ^3.2.25
 
-  '@vitest/coverage-v8@0.34.6':
-    resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
+  '@vitest/coverage-v8@1.6.0':
+    resolution: {integrity: sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==}
     peerDependencies:
-      vitest: '>=0.32.0 <1'
+      vitest: 1.6.0
 
-  '@vitest/expect@0.34.6':
-    resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==}
+  '@vitest/expect@1.6.0':
+    resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==}
 
-  '@vitest/expect@1.3.1':
-    resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
+  '@vitest/runner@1.6.0':
+    resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==}
 
-  '@vitest/runner@0.34.6':
-    resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==}
-
-  '@vitest/snapshot@0.34.6':
-    resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==}
-
-  '@vitest/spy@0.34.6':
-    resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==}
-
-  '@vitest/spy@1.3.1':
-    resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
+  '@vitest/snapshot@1.6.0':
+    resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==}
 
   '@vitest/spy@1.6.0':
     resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==}
 
-  '@vitest/utils@0.34.6':
-    resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==}
-
-  '@vitest/utils@1.3.1':
-    resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
-
   '@vitest/utils@1.6.0':
     resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==}
 
   '@volar/language-core@2.2.0':
     resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==}
 
+  '@volar/language-core@2.4.0-alpha.11':
+    resolution: {integrity: sha512-DtftH0DtpksK1y+de/kLnu8CHcFQ7huKXi7cyxH9R0PbOOTSGXd31kijBeKNzyoXRp8dqGpu/7WhOlCWXQR62w==}
+
   '@volar/source-map@2.2.0':
     resolution: {integrity: sha512-HQlPRlHOVqCCHK8wI76ZldHkEwKsjp7E6idUc36Ekni+KJDNrqgSqPvyHQixybXPHNU7CI9Uxd9/IkxO7LuNBw==}
 
+  '@volar/source-map@2.4.0-alpha.11':
+    resolution: {integrity: sha512-yyjmv8KUkTcxXzwme9qUMl6Szdji9JUQa8eadE4ib/spFXXZGq6QOX8cgSu5UQ0ooyBJFO1zdVH5otBJyZE3Ew==}
+
   '@volar/typescript@2.2.0':
     resolution: {integrity: sha512-wC6l4zLiiCLxF+FGaHCbWlQYf4vMsnRxYhcI6WgvaNppOD6r1g+Ef1RKRJUApALWU46Yy/JDU/TbdV6w/X6Liw==}
 
-  '@vue/compiler-core@3.4.21':
-    resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
+  '@volar/typescript@2.4.0-alpha.11':
+    resolution: {integrity: sha512-N/v+wSddhtsNtfv2w0Bxj2QQWURN5budGzpyBTrlcXxz2dnvB0eAMqrEQbBi6rCOVHlRaXbh+wyTRdAcB/FHrg==}
 
-  '@vue/compiler-core@3.4.25':
-    resolution: {integrity: sha512-Y2pLLopaElgWnMNolgG8w3C5nNUVev80L7hdQ5iIKPtMJvhVpG0zhnBG/g3UajJmZdvW0fktyZTotEHD1Srhbg==}
+  '@vue/compiler-core@3.4.29':
+    resolution: {integrity: sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==}
 
-  '@vue/compiler-core@3.4.26':
-    resolution: {integrity: sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==}
+  '@vue/compiler-core@3.4.31':
+    resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==}
 
-  '@vue/compiler-dom@3.4.21':
-    resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==}
+  '@vue/compiler-dom@3.4.29':
+    resolution: {integrity: sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==}
 
-  '@vue/compiler-dom@3.4.25':
-    resolution: {integrity: sha512-Ugz5DusW57+HjllAugLci19NsDK+VyjGvmbB2TXaTcSlQxwL++2PETHx/+Qv6qFwNLzSt7HKepPe4DcTE3pBWg==}
+  '@vue/compiler-dom@3.4.31':
+    resolution: {integrity: sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==}
 
-  '@vue/compiler-dom@3.4.26':
-    resolution: {integrity: sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==}
+  '@vue/compiler-sfc@3.4.31':
+    resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==}
 
-  '@vue/compiler-sfc@3.4.26':
-    resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==}
+  '@vue/compiler-ssr@3.4.29':
+    resolution: {integrity: sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==}
 
-  '@vue/compiler-ssr@3.4.26':
-    resolution: {integrity: sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==}
+  '@vue/compiler-ssr@3.4.31':
+    resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==}
 
   '@vue/devtools-api@6.6.1':
     resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
@@ -5056,28 +5455,38 @@ packages:
       typescript:
         optional: true
 
-  '@vue/reactivity@3.4.26':
-    resolution: {integrity: sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ==}
-
-  '@vue/runtime-core@3.4.26':
-    resolution: {integrity: sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw==}
-
-  '@vue/runtime-dom@3.4.26':
-    resolution: {integrity: sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==}
-
-  '@vue/server-renderer@3.4.26':
-    resolution: {integrity: sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==}
+  '@vue/language-core@2.0.24':
+    resolution: {integrity: sha512-997YD6Lq/66LXr3ZOLNxDCmyn13z9NP8LU1UZn9hGCDWhzlbXAIP0hOgL3w3x4RKEaWTaaRtsHP9DzHvmduruQ==}
     peerDependencies:
-      vue: 3.4.26
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
 
-  '@vue/shared@3.4.21':
-    resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==}
+  '@vue/reactivity@3.4.31':
+    resolution: {integrity: sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==}
 
-  '@vue/shared@3.4.25':
-    resolution: {integrity: sha512-k0yappJ77g2+KNrIaF0FFnzwLvUBLUYr8VOwz+/6vLsmItFp51AcxLL7Ey3iPd7BIRyWPOcqUjMnm7OkahXllA==}
+  '@vue/runtime-core@3.4.31':
+    resolution: {integrity: sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==}
 
-  '@vue/shared@3.4.26':
-    resolution: {integrity: sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==}
+  '@vue/runtime-dom@3.4.31':
+    resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==}
+
+  '@vue/server-renderer@3.4.29':
+    resolution: {integrity: sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==}
+    peerDependencies:
+      vue: 3.4.29
+
+  '@vue/server-renderer@3.4.31':
+    resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==}
+    peerDependencies:
+      vue: 3.4.31
+
+  '@vue/shared@3.4.29':
+    resolution: {integrity: sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==}
+
+  '@vue/shared@3.4.31':
+    resolution: {integrity: sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==}
 
   '@vue/test-utils@2.4.1':
     resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
@@ -5151,8 +5560,8 @@ packages:
     engines: {node: '>=0.4.0'}
     hasBin: true
 
-  acorn@8.11.3:
-    resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
+  acorn@8.12.0:
+    resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
     engines: {node: '>=0.4.0'}
     hasBin: true
 
@@ -5205,12 +5614,26 @@ packages:
       ajv:
         optional: true
 
+  ajv-formats@3.0.1:
+    resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
+    peerDependencies:
+      ajv: ^8.0.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+
   ajv@6.12.6:
     resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
 
+  ajv@8.12.0:
+    resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
+
   ajv@8.13.0:
     resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
 
+  ajv@8.16.0:
+    resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==}
+
   ansi-colors@4.1.3:
     resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
     engines: {node: '>=6'}
@@ -5290,9 +5713,16 @@ packages:
   argparse@2.0.1:
     resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
 
+  aria-hidden@1.2.4:
+    resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
+    engines: {node: '>=10'}
+
   aria-query@5.1.3:
     resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
 
+  aria-query@5.3.0:
+    resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
   array-buffer-byte-length@1.0.0:
     resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
 
@@ -5368,6 +5798,9 @@ packages:
   async-mutex@0.5.0:
     resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
 
+  async@0.2.10:
+    resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==}
+
   async@3.2.4:
     resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
 
@@ -5389,8 +5822,8 @@ packages:
   avvio@8.3.0:
     resolution: {integrity: sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==}
 
-  aws-sdk-client-mock@3.0.1:
-    resolution: {integrity: sha512-9VAzJLl8mz99KP9HjOm/93d8vznRRUTpJooPBOunRdUAnVYopCe9xmMuu7eVemu8fQ+w6rP7o5bBK1kAFkB2KQ==}
+  aws-sdk-client-mock@4.0.1:
+    resolution: {integrity: sha512-yD2mmgy73Xce097G5hIpr1k7j50qzvJ49/+6osGZiCyk4m6cwhb+2x7kKFY1gEMwTzaS8+m8fXv9SB29SkRYyQ==}
 
   aws-sign2@0.7.0:
     resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
@@ -5426,18 +5859,18 @@ packages:
     resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
 
-  babel-plugin-polyfill-corejs2@0.4.6:
-    resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==}
+  babel-plugin-polyfill-corejs2@0.4.11:
+    resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
 
-  babel-plugin-polyfill-corejs3@0.8.6:
-    resolution: {integrity: sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==}
+  babel-plugin-polyfill-corejs3@0.10.4:
+    resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
 
-  babel-plugin-polyfill-regenerator@0.5.3:
-    resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==}
+  babel-plugin-polyfill-regenerator@0.6.2:
+    resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==}
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
 
@@ -5510,10 +5943,6 @@ packages:
   bn.js@4.12.0:
     resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
 
-  body-parser@1.20.1:
-    resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
-
   body-parser@1.20.2:
     resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
     engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@@ -5538,6 +5967,10 @@ packages:
     resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
     engines: {node: '>=8'}
 
+  braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+
   broadcast-channel@7.0.0:
     resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
 
@@ -5586,8 +6019,8 @@ packages:
     resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
     engines: {node: '>=6.14.2'}
 
-  bullmq@5.7.8:
-    resolution: {integrity: sha512-F/Haeu6AVHkFrfeaU/kLOjhfrH6x3CaKAZlQQ+76fa8l3kfI9oaUHeFMW+1mYVz0NtYPF7PNTWFq4ylAHYcCgA==}
+  bullmq@5.8.3:
+    resolution: {integrity: sha512-RJgQu/vgSZqjOYrZ7F1UJsSAzveNx7FFpR3Tp/1TxOMXXN9TtZMSly5MT+vjzOhQX//3+YWNRbMWpC1mkqBc9w==}
 
   buraha@0.0.1:
     resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==}
@@ -5624,6 +6057,10 @@ packages:
     resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
     engines: {node: '>=14.16'}
 
+  cacheable-request@12.0.1:
+    resolution: {integrity: sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==}
+    engines: {node: '>=18'}
+
   cacheable-request@7.0.2:
     resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==}
     engines: {node: '>=8'}
@@ -5713,8 +6150,8 @@ packages:
   character-parser@2.2.0:
     resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==}
 
-  chart.js@4.4.2:
-    resolution: {integrity: sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==}
+  chart.js@4.4.3:
+    resolution: {integrity: sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==}
     engines: {pnpm: '>=8'}
 
   chartjs-adapter-date-fns@3.0.0:
@@ -5763,8 +6200,8 @@ packages:
     resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
     engines: {node: '>=10'}
 
-  chromatic@11.3.0:
-    resolution: {integrity: sha512-q1ZtJDJrjLGnz60ivpC16gmd7KFzcaA4eTb7gcytCqbaKqlHhCFr1xQmcUDsm14CK7JsqdkFU6S+JQdOd2ZNJg==}
+  chromatic@11.5.4:
+    resolution: {integrity: sha512-+J+CopeUSyGUIQJsU6X7CfvSmeVBs0j6LZ9AgF4+XTjI4pFmUiUXsTc00rH9x9W1jCppOaqDXv2kqJJXGDK3mA==}
     hasBin: true
     peerDependencies:
       '@chromatic-com/cypress': ^0.*.* || ^1.0.0
@@ -5799,10 +6236,6 @@ packages:
     engines: {node: '>=8.0.0', npm: '>=5.0.0'}
     hasBin: true
 
-  cli-spinners@2.7.0:
-    resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==}
-    engines: {node: '>=6'}
-
   cli-spinners@2.9.2:
     resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
     engines: {node: '>=6'}
@@ -5982,8 +6415,8 @@ packages:
     resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
     engines: {node: '>= 0.6'}
 
-  core-js-compat@3.33.3:
-    resolution: {integrity: sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==}
+  core-js-compat@3.37.1:
+    resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==}
 
   core-js@3.29.1:
     resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==}
@@ -6037,9 +6470,9 @@ packages:
     resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
     engines: {node: '>= 8'}
 
-  crypto-random-string@2.0.0:
-    resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
-    engines: {node: '>=8'}
+  crypto-random-string@4.0.0:
+    resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==}
+    engines: {node: '>=12'}
 
   css-declaration-sorter@7.2.0:
     resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==}
@@ -6102,13 +6535,8 @@ packages:
   cwise-compiler@1.1.3:
     resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==}
 
-  cypress@13.7.3:
-    resolution: {integrity: sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==}
-    engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
-    hasBin: true
-
-  cypress@13.8.1:
-    resolution: {integrity: sha512-Uk6ovhRbTg6FmXjeZW/TkbRM07KPtvM5gah1BIMp4Y2s+i/NMxgaLw0+PbYTOdw1+egE0FP3mWRiGcRkjjmhzA==}
+  cypress@13.13.0:
+    resolution: {integrity: sha512-ou/MQUDq4tcDJI2FsPaod2FZpex4kpIK43JJlcBgWrX8WX7R/05ZxGTuxedOuZBfxjZxja+fbijZGyxiLP6CFA==}
     engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
     hasBin: true
 
@@ -6162,6 +6590,15 @@ packages:
       supports-color:
         optional: true
 
+  debug@4.3.5:
+    resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
   decamelize-keys@1.1.1:
     resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
     engines: {node: '>=0.10.0'}
@@ -6235,10 +6672,6 @@ packages:
   defu@6.1.4:
     resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
 
-  del@6.1.1:
-    resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==}
-    engines: {node: '>=10'}
-
   delayed-stream@1.0.0:
     resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
     engines: {node: '>=0.4.0'}
@@ -6278,6 +6711,9 @@ packages:
     resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
     engines: {node: '>=8'}
 
+  detect-node-es@1.1.0:
+    resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+
   detect-package-manager@2.0.1:
     resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==}
     engines: {node: '>=12'}
@@ -6300,6 +6736,10 @@ packages:
     resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
     engines: {node: '>=0.3.1'}
 
+  diff@5.2.0:
+    resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
+    engines: {node: '>=0.3.1'}
+
   dijkstrajs@1.0.2:
     resolution: {integrity: sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==}
 
@@ -6371,8 +6811,8 @@ packages:
   ee-first@1.1.1:
     resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
 
-  ejs@3.1.9:
-    resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
+  ejs@3.1.10:
+    resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
     engines: {node: '>=0.10.0'}
     hasBin: true
 
@@ -6438,8 +6878,8 @@ packages:
   es-get-iterator@1.1.3:
     resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
 
-  es-module-lexer@0.9.3:
-    resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
+  es-module-lexer@1.5.4:
+    resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
 
   es-set-tostringtag@2.0.1:
     resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==}
@@ -6476,11 +6916,16 @@ packages:
     engines: {node: '>=12'}
     hasBin: true
 
-  esbuild@0.20.2:
-    resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
+  esbuild@0.21.5:
+    resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
     engines: {node: '>=12'}
     hasBin: true
 
+  esbuild@0.22.0:
+    resolution: {integrity: sha512-zNYA6bFZsVnsU481FnGAQjLDW0Pl/8BGG7EvAp15RzUvGC+ME7hf1q7LvIfStEQBz/iEHuBJCYcOwPmNCf1Tlw==}
+    engines: {node: '>=18'}
+    hasBin: true
+
   escalade@3.1.1:
     resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
     engines: {node: '>=6'}
@@ -6550,8 +6995,8 @@ packages:
       '@typescript-eslint/parser':
         optional: true
 
-  eslint-plugin-vue@9.25.0:
-    resolution: {integrity: sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA==}
+  eslint-plugin-vue@9.26.0:
+    resolution: {integrity: sha512-eTvlxXgd4ijE1cdur850G6KalZqk65k1JKoOI2d1kT3hr8sPD07j1q98FRFdNnpxBELGPWxZmInxeHGF/GxtqQ==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
@@ -6563,19 +7008,26 @@ packages:
     resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
+  eslint-scope@8.0.1:
+    resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
   eslint-visitor-keys@3.4.3:
     resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
-  eslint@8.53.0:
-    resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+  eslint-visitor-keys@4.0.0:
+    resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint@9.6.0:
+    resolution: {integrity: sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     hasBin: true
 
-  eslint@8.57.0:
-    resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
-    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-    hasBin: true
+  espree@10.1.0:
+    resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   espree@9.6.1:
     resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
@@ -6590,6 +7042,10 @@ packages:
     resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==}
     engines: {node: '>=0.10'}
 
+  esquery@1.5.0:
+    resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
+    engines: {node: '>=0.10'}
+
   esrecurse@4.3.0:
     resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
     engines: {node: '>=4.0'}
@@ -6652,6 +7108,10 @@ packages:
     resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
     engines: {node: '>=16.17'}
 
+  execa@9.2.0:
+    resolution: {integrity: sha512-vpOyYg7UAVKLAWWtRS2gAdgkT7oJbCn0me3gmUmxZih4kd3MF/oo8kNTBTIbkO3yuuF5uB4ZCZfn8BOolITYhg==}
+    engines: {node: ^18.19.0 || >=20.5.0}
+
   executable@4.1.1:
     resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
     engines: {node: '>=4'}
@@ -6667,10 +7127,6 @@ packages:
   exponential-backoff@3.1.1:
     resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
 
-  express@4.18.2:
-    resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
-    engines: {node: '>= 0.10.0'}
-
   express@4.19.2:
     resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
     engines: {node: '>= 0.10.0'}
@@ -6744,11 +7200,8 @@ packages:
     resolution: {integrity: sha512-F4o8ZIMVx4YoxGfwrZys6wyjl40gF3Yv6AWWRy62ozFAyZBSS831/uyyCAqKYw3tR73g180ryG98yih6To1PUQ==}
     engines: {node: '>= 10'}
 
-  fastify@4.26.2:
-    resolution: {integrity: sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==}
-
-  fastq@1.15.0:
-    resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
+  fastify@4.28.1:
+    resolution: {integrity: sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ==}
 
   fastq@1.17.1:
     resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
@@ -6774,9 +7227,13 @@ packages:
     resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
     engines: {node: '>=8'}
 
-  file-entry-cache@6.0.1:
-    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
-    engines: {node: ^10.12.0 || >=12.0.0}
+  figures@6.1.0:
+    resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
+    engines: {node: '>=18'}
+
+  file-entry-cache@8.0.0:
+    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+    engines: {node: '>=16.0.0'}
 
   file-system-cache@2.3.0:
     resolution: {integrity: sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==}
@@ -6804,6 +7261,10 @@ packages:
     resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
     engines: {node: '>=8'}
 
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+
   finalhandler@1.2.0:
     resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
     engines: {node: '>= 0.8'}
@@ -6843,20 +7304,20 @@ packages:
     resolution: {integrity: sha512-MdYSsbdCaIRjzo5edthZtWmEZVMfr1qrtYZUHIdO3swCE+CoZA8S5l0s4jDsYlTa9ZiXv0pTgpzE7s4N8NeUOA==}
     engines: {node: '>=18'}
 
-  flat-cache@3.0.4:
-    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
-    engines: {node: ^10.12.0 || >=12.0.0}
+  flat-cache@4.0.1:
+    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+    engines: {node: '>=16'}
 
-  flatted@3.2.7:
-    resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+  flatted@3.3.1:
+    resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
 
   flow-parser@0.202.0:
     resolution: {integrity: sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw==}
     engines: {node: '>=0.4.0'}
 
-  fluent-ffmpeg@2.1.2:
-    resolution: {integrity: sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==}
-    engines: {node: '>=0.8.0'}
+  fluent-ffmpeg@2.1.3:
+    resolution: {integrity: sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==}
+    engines: {node: '>=18'}
 
   follow-redirects@1.15.2:
     resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
@@ -6978,6 +7439,10 @@ packages:
   get-intrinsic@1.2.1:
     resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
 
+  get-nonce@1.0.1:
+    resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+    engines: {node: '>=6'}
+
   get-npm-tarball-url@2.0.3:
     resolution: {integrity: sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw==}
     engines: {node: '>=12.17'}
@@ -7005,6 +7470,10 @@ packages:
     resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
     engines: {node: '>=16'}
 
+  get-stream@9.0.1:
+    resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
+    engines: {node: '>=18'}
+
   get-symbol-description@1.0.0:
     resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
     engines: {node: '>= 0.4'}
@@ -7056,12 +7525,19 @@ packages:
     engines: {node: '>=16 || 14 >=14.17'}
     hasBin: true
 
+  glob@10.4.2:
+    resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==}
+    engines: {node: '>=16 || 14 >=14.18'}
+    hasBin: true
+
   glob@7.2.3:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    deprecated: Glob versions prior to v9 are no longer supported
 
   glob@8.1.0:
     resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
     engines: {node: '>=12'}
+    deprecated: Glob versions prior to v9 are no longer supported
 
   global-dirs@3.0.1:
     resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
@@ -7071,14 +7547,18 @@ 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'}
-
   globals@13.24.0:
     resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
     engines: {node: '>=8'}
 
+  globals@14.0.0:
+    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+    engines: {node: '>=18'}
+
+  globals@15.7.0:
+    resolution: {integrity: sha512-ivatRXWwKC6ImcdKO7dOwXuXR5XFrdwo45qFwD7D0qOkEPzzJdLXC3BHceBdyrPOD3p1suPaWi4Y4NMm2D++AQ==}
+    engines: {node: '>=18'}
+
   globalthis@1.0.3:
     resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
     engines: {node: '>= 0.4'}
@@ -7087,6 +7567,10 @@ packages:
     resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
     engines: {node: '>=10'}
 
+  globby@14.0.1:
+    resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==}
+    engines: {node: '>=18'}
+
   google-protobuf@3.21.2:
     resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==}
 
@@ -7101,8 +7585,8 @@ packages:
     resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==}
     engines: {node: '>=14.16'}
 
-  got@14.2.1:
-    resolution: {integrity: sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==}
+  got@14.4.1:
+    resolution: {integrity: sha512-IvDJbJBUeexX74xNQuMIVgCRRuNOm5wuK+OC3Dc2pnSoh1AOmgc7JVj7WC+cJ4u0aPcO9KZ2frTXcqK4W/5qTQ==}
     engines: {node: '>=20'}
 
   graceful-fs@4.2.11:
@@ -7260,6 +7744,10 @@ packages:
     resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
     engines: {node: '>= 14'}
 
+  http-proxy-agent@7.0.2:
+    resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+    engines: {node: '>= 14'}
+
   http-signature@1.2.0:
     resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
     engines: {node: '>=0.8', npm: '>=1.3.7'}
@@ -7292,6 +7780,10 @@ packages:
     resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==}
     engines: {node: '>= 14'}
 
+  https-proxy-agent@7.0.4:
+    resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
+    engines: {node: '>= 14'}
+
   human-signals@1.1.1:
     resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
     engines: {node: '>=8.12.0'}
@@ -7308,6 +7800,10 @@ packages:
     resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
     engines: {node: '>=16.17.0'}
 
+  human-signals@7.0.0:
+    resolution: {integrity: sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==}
+    engines: {node: '>=18.18.0'}
+
   iconv-lite@0.4.24:
     resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
     engines: {node: '>=0.10.0'}
@@ -7347,8 +7843,8 @@ packages:
   import-in-the-middle@1.4.2:
     resolution: {integrity: sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==}
 
-  import-in-the-middle@1.7.4:
-    resolution: {integrity: sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==}
+  import-in-the-middle@1.8.1:
+    resolution: {integrity: sha512-yhRwoHtiLGvmSozNOALgjRPFI6uYsds60EoMqqnXyyv+JOIW/BrrLejuTGBt+bq0T5tLzOHrN0T7xYTm4Qt/ng==}
 
   import-lazy@4.0.0:
     resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
@@ -7398,6 +7894,9 @@ packages:
   intersection-observer@0.12.2:
     resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
 
+  invariant@2.2.4:
+    resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
+
   ioredis@5.4.1:
     resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==}
     engines: {node: '>=12.22.0'}
@@ -7405,13 +7904,13 @@ packages:
   iota-array@1.0.0:
     resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==}
 
-  ip-address@7.1.0:
-    resolution: {integrity: sha512-V9pWC/VJf2lsXqP7IWJ+pe3P1/HCYGBMZrrnT62niLGjAfCbeiwXMUxaeHvnVlz19O27pvXP4azs+Pj/A0x+SQ==}
-    engines: {node: '>= 10'}
+  ip-address@9.0.5:
+    resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
+    engines: {node: '>= 12'}
 
-  ip-cidr@3.1.0:
-    resolution: {integrity: sha512-HUCn4snshEX1P8cja/IyU3qk8FVDW8T5zZcegDFbu4w7NojmAhk5NcOgj3M8+0fmumo1afJTPDtJlzsxLdOjtg==}
-    engines: {node: '>=10.0.0'}
+  ip-cidr@4.0.1:
+    resolution: {integrity: sha512-V5Nce94SVJ7NtyT/UKUeTM7sY3V7TEk48hURhtBgTiGduOa5t6p9Hd+zBOGvr4Gu7iWPxFVYNl017p0akQA84w==}
+    engines: {node: '>=16.14.0'}
 
   ip-regex@4.3.0:
     resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==}
@@ -7553,10 +8052,6 @@ packages:
     resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
     engines: {node: '>=0.12.0'}
 
-  is-path-cwd@2.2.0:
-    resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
-    engines: {node: '>=6'}
-
   is-path-inside@3.0.3:
     resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
     engines: {node: '>=8'}
@@ -7605,12 +8100,16 @@ packages:
     resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
+  is-stream@4.0.1:
+    resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
+    engines: {node: '>=18'}
+
   is-string@1.0.7:
     resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
     engines: {node: '>= 0.4'}
 
-  is-svg@5.0.0:
-    resolution: {integrity: sha512-sRl7J0oX9yUNamSdc8cwgzh9KBLnQXNzGmW0RVHwg/jEYjGNYHC6UvnYD8+hAeut9WwxRvhG9biK7g/wDGxcMw==}
+  is-svg@5.0.1:
+    resolution: {integrity: sha512-mLYxDsfisQWdS4+gSblAwhATDoNMS/tx8G7BKA+aBIf7F0m1iUwMvuKAo6mW4WMleQAEE50I1Zqef9yMMfHk3w==}
     engines: {node: '>=14.16'}
 
   is-symbol@1.0.4:
@@ -7628,6 +8127,10 @@ packages:
     resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
     engines: {node: '>=10'}
 
+  is-unicode-supported@2.0.0:
+    resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==}
+    engines: {node: '>=18'}
+
   is-weakmap@2.0.1:
     resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
 
@@ -7684,6 +8187,10 @@ packages:
     resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
     engines: {node: '>=10'}
 
+  istanbul-lib-source-maps@5.0.4:
+    resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==}
+    engines: {node: '>=10'}
+
   istanbul-reports@3.1.6:
     resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==}
     engines: {node: '>=8'}
@@ -7696,6 +8203,10 @@ packages:
     resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
     engines: {node: '>=14'}
 
+  jackspeak@3.4.0:
+    resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==}
+    engines: {node: '>=14'}
+
   jake@10.8.5:
     resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
     engines: {node: '>=10'}
@@ -7856,6 +8367,9 @@ packages:
   js-tokens@4.0.0:
     resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
 
+  js-tokens@9.0.0:
+    resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==}
+
   js-yaml@3.14.1:
     resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
     hasBin: true
@@ -7883,8 +8397,8 @@ packages:
       '@babel/preset-env':
         optional: true
 
-  jsdom@24.0.0:
-    resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==}
+  jsdom@24.1.0:
+    resolution: {integrity: sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==}
     engines: {node: '>=18'}
     peerDependencies:
       canvas: ^2.11.2
@@ -7966,9 +8480,6 @@ packages:
   jsrsasign@11.1.0:
     resolution: {integrity: sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==}
 
-  jssha@3.3.1:
-    resolution: {integrity: sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==}
-
   jstransformer@1.0.0:
     resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==}
 
@@ -8045,8 +8556,8 @@ packages:
       enquirer:
         optional: true
 
-  local-pkg@0.4.3:
-    resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
+  local-pkg@0.5.0:
+    resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
     engines: {node: '>=14'}
 
   locate-path@3.0.0:
@@ -8073,9 +8584,6 @@ packages:
   lodash.isarguments@3.1.0:
     resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
 
-  lodash.isequal@4.5.0:
-    resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
-
   lodash.memoize@4.1.2:
     resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
 
@@ -8157,9 +8665,8 @@ packages:
   magic-string@0.30.10:
     resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
 
-  magic-string@0.30.7:
-    resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
-    engines: {node: '>=12'}
+  magicast@0.3.4:
+    resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==}
 
   mailcheck@1.1.1:
     resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==}
@@ -8252,8 +8759,8 @@ packages:
     resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
     engines: {node: '>= 0.6'}
 
-  meilisearch@0.38.0:
-    resolution: {integrity: sha512-bHaq8nYxSKw9/Qslq1Zes5g9tHgFkxy/I9o8942wv2PqlNOT0CzptIkh/x98N52GikoSZOXSQkgt6oMjtf5uZw==}
+  meilisearch@0.41.0:
+    resolution: {integrity: sha512-5KcGLxEXD7E+uNO7R68rCbGSHgCqeM3Q3RFFLSsN7ZrIgr8HPDXVAIlP4LHggAZfk0FkSzo8VSXifHCwa2k80g==}
 
   memoizerific@1.11.3:
     resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==}
@@ -8367,8 +8874,8 @@ packages:
   micromark@4.0.0:
     resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
 
-  micromatch@4.0.5:
-    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+  micromatch@4.0.7:
+    resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
     engines: {node: '>=8.6'}
 
   mime-db@1.52.0:
@@ -8480,6 +8987,10 @@ packages:
     resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
     engines: {node: '>=16 || 14 >=14.17'}
 
+  minipass@7.1.2:
+    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+    engines: {node: '>=16 || 14 >=14.17'}
+
   minizlib@1.3.3:
     resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==}
 
@@ -8541,13 +9052,13 @@ packages:
   msgpackr@1.10.1:
     resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==}
 
-  msw-storybook-addon@2.0.1:
-    resolution: {integrity: sha512-pZ3JDQ9HkGQ3XDMIHvMcDSI4Vbp/LHmwHwiZu+pHLzimtI1vhAo1swjFEDAEJuBcozljYvREEC4sS7rQHPNtWg==}
+  msw-storybook-addon@2.0.2:
+    resolution: {integrity: sha512-sdw++X+AoUbaG2ku493ViVqCA/LfqnybXsKXyPUrF3ZS/x8BqGBnkBLmT/0SHCC5zIO3Vfm5zlclAxnhqOOikQ==}
     peerDependencies:
       msw: ^2.0.0
 
-  msw@2.2.14:
-    resolution: {integrity: sha512-64i8rNCa1xzDK8ZYsTrVMli05D687jty8+Th+PU5VTbJ2/4P7fkQFVyDQ6ZFT5FrNR8z2BHhbY47fKNvfHrumA==}
+  msw@2.3.1:
+    resolution: {integrity: sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==}
     engines: {node: '>=18'}
     hasBin: true
     peerDependencies:
@@ -8701,8 +9212,8 @@ packages:
   node-releases@2.0.14:
     resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
 
-  nodemailer@6.9.13:
-    resolution: {integrity: sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==}
+  nodemailer@6.9.14:
+    resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==}
     engines: {node: '>=6.0.0'}
 
   nodemon@3.0.2:
@@ -8710,8 +9221,8 @@ packages:
     engines: {node: '>=10'}
     hasBin: true
 
-  nodemon@3.1.0:
-    resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==}
+  nodemon@3.1.4:
+    resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==}
     engines: {node: '>=10'}
     hasBin: true
 
@@ -8757,6 +9268,10 @@ packages:
     resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
     engines: {node: '>=14.16'}
 
+  normalize-url@8.0.1:
+    resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
+    engines: {node: '>=14.16'}
+
   npm-run-path@2.0.2:
     resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
     engines: {node: '>=4'}
@@ -8769,6 +9284,10 @@ packages:
     resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
+  npm-run-path@5.3.0:
+    resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
   npmlog@5.0.1:
     resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
 
@@ -8780,8 +9299,8 @@ packages:
   nth-check@2.1.1:
     resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
 
-  nwsapi@2.2.9:
-    resolution: {integrity: sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==}
+  nwsapi@2.2.10:
+    resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==}
 
   oauth-sign@0.9.0:
     resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
@@ -8894,8 +9413,8 @@ packages:
   ospath@1.2.2:
     resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
 
-  otpauth@9.2.3:
-    resolution: {integrity: sha512-oAG55Ch4MBL5Jdg+RXfKiRCZ2lCwa/UIQKsmSfYbGGLSI4dErY1HPZv0JGPPESIYGyDO3s9iJqM4HU/1IppMoQ==}
+  otpauth@9.3.1:
+    resolution: {integrity: sha512-E6d2tMxPofHNk4sRFp+kqW7vQ+WJGO9VLI2N/W00DnI+ThskU12Qa10kyNSGklrzhN5c+wRUsN4GijVgCU2N9w==}
 
   outvariant@1.4.2:
     resolution: {integrity: sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==}
@@ -8924,9 +9443,9 @@ packages:
     resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
     engines: {node: '>=10'}
 
-  p-limit@4.0.0:
-    resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+  p-limit@5.0.0:
+    resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
+    engines: {node: '>=18'}
 
   p-locate@3.0.0:
     resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
@@ -8956,6 +9475,9 @@ packages:
     resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
     engines: {node: '>=6'}
 
+  package-json-from-dist@1.0.0:
+    resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
+
   pako@0.2.9:
     resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
 
@@ -8970,6 +9492,10 @@ packages:
     resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
     engines: {node: '>=8'}
 
+  parse-ms@4.0.0:
+    resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
+    engines: {node: '>=18'}
+
   parse-srcset@1.0.2:
     resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
 
@@ -9030,6 +9556,10 @@ packages:
     resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==}
     engines: {node: '>=16 || 14 >=14.17'}
 
+  path-scurry@1.11.1:
+    resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+    engines: {node: '>=16 || 14 >=14.18'}
+
   path-to-regexp@0.1.7:
     resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
 
@@ -9046,6 +9576,10 @@ packages:
     resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
     engines: {node: '>=8'}
 
+  path-type@5.0.0:
+    resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==}
+    engines: {node: '>=12'}
+
   pathe@1.1.2:
     resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
 
@@ -9087,9 +9621,6 @@ packages:
     peerDependencies:
       pg: '>=8.0'
 
-  pg-protocol@1.6.0:
-    resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
-
   pg-protocol@1.6.1:
     resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==}
 
@@ -9101,8 +9632,8 @@ packages:
     resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==}
     engines: {node: '>=10'}
 
-  pg@8.11.5:
-    resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==}
+  pg@8.12.0:
+    resolution: {integrity: sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==}
     engines: {node: '>= 8.0.0'}
     peerDependencies:
       pg-native: '>=3.0.1'
@@ -9113,8 +9644,8 @@ packages:
   pgpass@1.0.5:
     resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
 
-  photoswipe@5.4.3:
-    resolution: {integrity: sha512-9UC6oJBK4oXFZ5HcdlcvGkfEHsVrmE4csUdCQhEjHYb3PvPLO3PG7UhnPuOgjxwmhq5s17Un5NUdum01LgBDng==}
+  photoswipe@5.4.4:
+    resolution: {integrity: sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==}
     engines: {node: '>= 0.12.0'}
 
   picocolors@1.0.0:
@@ -9136,14 +9667,14 @@ packages:
     resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
     engines: {node: '>=6'}
 
-  pino-abstract-transport@1.1.0:
-    resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==}
+  pino-abstract-transport@1.2.0:
+    resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==}
 
-  pino-std-serializers@6.1.0:
-    resolution: {integrity: sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==}
+  pino-std-serializers@7.0.0:
+    resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==}
 
-  pino@8.17.0:
-    resolution: {integrity: sha512-ey+Mku+PVPhvxglLXMg1l1zQMwSHuNrKC3MD40EDZbkckJmmuY7DYZLIOwwjZ8ix/Nvhe9dZt5H99cgkot9bAw==}
+  pino@9.2.0:
+    resolution: {integrity: sha512-g3/hpwfujK5a4oVbaefoJxezLzsDgLcNJeITvC6yrfwYeT9la+edCK42j5QpEQSQCZgTKapXvnQIdgZwvRaZug==}
     hasBin: true
 
   pirates@4.0.5:
@@ -9411,8 +9942,8 @@ packages:
     resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
     engines: {node: '>= 0.8.0'}
 
-  prettier@3.2.5:
-    resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
+  prettier@3.3.2:
+    resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
     engines: {node: '>=14'}
     hasBin: true
 
@@ -9432,6 +9963,10 @@ packages:
     resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==}
     engines: {node: '>= 0.8'}
 
+  pretty-ms@9.0.0:
+    resolution: {integrity: sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==}
+    engines: {node: '>=18'}
+
   private-ip@2.3.3:
     resolution: {integrity: sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw==}
 
@@ -9517,12 +10052,15 @@ packages:
   pug-attrs@3.0.0:
     resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==}
 
-  pug-code-gen@3.0.2:
-    resolution: {integrity: sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==}
+  pug-code-gen@3.0.3:
+    resolution: {integrity: sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==}
 
   pug-error@2.0.0:
     resolution: {integrity: sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==}
 
+  pug-error@2.1.0:
+    resolution: {integrity: sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==}
+
   pug-filters@4.0.0:
     resolution: {integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==}
 
@@ -9547,8 +10085,8 @@ packages:
   pug-walk@2.0.0:
     resolution: {integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==}
 
-  pug@3.0.2:
-    resolution: {integrity: sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==}
+  pug@3.0.3:
+    resolution: {integrity: sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==}
 
   pump@2.0.1:
     resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
@@ -9631,10 +10169,6 @@ packages:
   ratelimiter@3.4.1:
     resolution: {integrity: sha512-5FJbRW/Jkkdk29ksedAfWFkQkhbUrMx3QJGwMKAypeIiQf4yrLW+gtPKZiaWt4zPrtw1uGufOjGO7UGM6VllsQ==}
 
-  raw-body@2.5.1:
-    resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
-    engines: {node: '>= 0.8'}
-
   raw-body@2.5.2:
     resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
     engines: {node: '>= 0.8'}
@@ -9643,8 +10177,8 @@ packages:
     resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==}
     engines: {node: '>=12'}
 
-  re2@1.21.2:
-    resolution: {integrity: sha512-f8jqI0vCbwDhzY66Fgx1V2RoNDdmAupKkqRqR/AEF+2/MZNRbtEOjax6oHSht95MU40vx6+2ITsJr/9esukckg==}
+  re2@1.21.3:
+    resolution: {integrity: sha512-GI+KoGkHT4kxTaX+9p0FgNB1XUnCndO9slG5qqeEoZ7kbf6Dk6ohQVpmwKVeSp7LPLn+g6Q3BaCopz4oHuBDuQ==}
 
   react-colorful@5.6.1:
     resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
@@ -9684,6 +10218,36 @@ packages:
   react-is@18.2.0:
     resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
 
+  react-remove-scroll-bar@2.3.6:
+    resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  react-remove-scroll@2.5.7:
+    resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  react-style-singleton@2.2.1:
+    resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
   react@18.3.1:
     resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
     engines: {node: '>=0.10.0'}
@@ -9725,10 +10289,6 @@ packages:
     resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
     engines: {node: '>= 12.13.0'}
 
-  recast@0.23.4:
-    resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==}
-    engines: {node: '>= 4'}
-
   recast@0.23.6:
     resolution: {integrity: sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==}
     engines: {node: '>= 4'}
@@ -9852,9 +10412,6 @@ packages:
     resolution: {integrity: sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==}
     engines: {node: '>=10'}
 
-  resolve@1.19.0:
-    resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==}
-
   resolve@1.22.8:
     resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
     hasBin: true
@@ -9887,6 +10444,7 @@ packages:
 
   rimraf@2.6.3:
     resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
+    deprecated: Rimraf versions prior to v4 are no longer supported
     hasBin: true
 
   rimraf@2.7.1:
@@ -9897,14 +10455,17 @@ packages:
     resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
     hasBin: true
 
-  rollup@4.17.2:
-    resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
+  rollup@4.18.0:
+    resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
 
   rrweb-cssom@0.6.0:
     resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
 
+  rrweb-cssom@0.7.1:
+    resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==}
+
   rss-parser@3.13.0:
     resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==}
 
@@ -9940,8 +10501,8 @@ packages:
   sanitize-html@2.13.0:
     resolution: {integrity: sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==}
 
-  sass@1.76.0:
-    resolution: {integrity: sha512-nc3LeqvF2FNW5xGF1zxZifdW3ffIz5aBb7I7tSvOoNu7z1RQ6pFt9MBuiPtjgaI62YWrM/txjWlOCFiGtf2xpw==}
+  sass@1.77.6:
+    resolution: {integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==}
     engines: {node: '>=14.0.0'}
     hasBin: true
 
@@ -10015,8 +10576,8 @@ packages:
     resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
     engines: {node: '>=8'}
 
-  sharp@0.33.3:
-    resolution: {integrity: sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==}
+  sharp@0.33.4:
+    resolution: {integrity: sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==}
     engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0}
 
   shebang-command@1.2.0:
@@ -10035,8 +10596,8 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
-  shiki@1.4.0:
-    resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==}
+  shiki@1.10.0:
+    resolution: {integrity: sha512-YD2sXQ+TMD/F9BimV9Jn0wj35pqOvywvOG/3PB6hGHyGKlM7TJ9tyJ02jOb2kF8F0HfJwKNYrh3sW7jEcuRlXA==}
 
   shimmer@1.2.1:
     resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
@@ -10054,8 +10615,8 @@ packages:
     resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
     engines: {node: '>=14'}
 
-  simple-oauth2@5.0.0:
-    resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==}
+  simple-oauth2@5.0.1:
+    resolution: {integrity: sha512-JcmGdzvbHKU3GegF3BK6zNi46DqFTxPMjwYddu2bgYqZuy7Gtm8U8wdedkVE4lI4LEqXocmPBLAvC4BIiiBc5w==}
 
   simple-swizzle@0.2.2:
     resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
@@ -10155,6 +10716,10 @@ packages:
     resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
     engines: {node: '>=8'}
 
+  slash@5.1.0:
+    resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
+    engines: {node: '>=14.16'}
+
   slice-ansi@3.0.0:
     resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
     engines: {node: '>=8'}
@@ -10175,8 +10740,8 @@ packages:
     resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==}
     engines: {node: '>= 10.13.0', npm: '>= 3.0.0'}
 
-  sonic-boom@3.7.0:
-    resolution: {integrity: sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==}
+  sonic-boom@4.0.1:
+    resolution: {integrity: sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==}
 
   sort-keys-length@1.0.1:
     resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==}
@@ -10236,8 +10801,8 @@ packages:
   sprintf-js@1.0.3:
     resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
 
-  sprintf-js@1.1.2:
-    resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==}
+  sprintf-js@1.1.3:
+    resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
 
   sshpk@1.17.0:
     resolution: {integrity: sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==}
@@ -10258,8 +10823,8 @@ packages:
   standard-as-callback@2.1.0:
     resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
 
-  start-server-and-test@2.0.3:
-    resolution: {integrity: sha512-QsVObjfjFZKJE6CS6bSKNwWZCKBG6975/jKRPPGFfFh+yOQglSeGXiNWjzgQNXdphcBI9nXbyso9tPfX4YAUhg==}
+  start-server-and-test@2.0.4:
+    resolution: {integrity: sha512-CKNeBTcP0hVqIlNismHMudb9q3lLdAjcVPO13/7gfI66fcJpeIb/o4NzQd1JK/CD+lfWVqr10ZH9Y14+OwlJuw==}
     engines: {node: '>=16'}
     hasBin: true
 
@@ -10296,8 +10861,8 @@ packages:
       react-dom:
         optional: true
 
-  storybook@8.0.9:
-    resolution: {integrity: sha512-/Mvij0Br5bUwJpCvqAUZMEDIWmdRxEyllvVj8Ukw5lIWJePxfpSsz4px5jg9+R6B9tO8sQSqjg4HJvQ/pZk8Tg==}
+  storybook@8.1.11:
+    resolution: {integrity: sha512-3KjIhF8lczXhKKHyHbOqV30dvuRYJSxc0d1as/C8kybuwE7cLaydhWGma7VBv5bTSPv0rDzucx7KcO+achArPg==}
     hasBin: true
 
   stream-browserify@3.0.0:
@@ -10395,6 +10960,10 @@ packages:
     resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
     engines: {node: '>=12'}
 
+  strip-final-newline@4.0.0:
+    resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
+    engines: {node: '>=18'}
+
   strip-indent@3.0.0:
     resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
     engines: {node: '>=8'}
@@ -10407,8 +10976,8 @@ packages:
     resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
     engines: {node: '>=8'}
 
-  strip-literal@1.3.0:
-    resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==}
+  strip-literal@2.1.0:
+    resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==}
 
   strip-outer@2.0.0:
     resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==}
@@ -10459,8 +11028,8 @@ packages:
   symbol-tree@3.2.4:
     resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
 
-  systeminformation@5.22.7:
-    resolution: {integrity: sha512-AWxlP05KeHbpGdgvZkcudJpsmChc2Y5Eo/GvxG/iUA/Aws5LZKHAMSeAo+V+nD+nxWZaxrwpWcnx4SH3oxNL3A==}
+  systeminformation@5.22.11:
+    resolution: {integrity: sha512-aLws5yi4KCHTb0BVvbodQY5bY8eW4asMRDTxTW46hqw9lGjACX6TlLdJrkdoHYRB0qs+MekqEq1zG7WDnWE8Ug==}
     engines: {node: '>=8.0.0'}
     os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
     hasBin: true
@@ -10490,20 +11059,20 @@ packages:
   telejson@7.2.0:
     resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==}
 
-  temp-dir@2.0.0:
-    resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
-    engines: {node: '>=8'}
+  temp-dir@3.0.0:
+    resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==}
+    engines: {node: '>=14.16'}
 
   temp@0.8.4:
     resolution: {integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==}
     engines: {node: '>=6.0.0'}
 
-  tempy@1.0.1:
-    resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==}
-    engines: {node: '>=10'}
+  tempy@3.1.0:
+    resolution: {integrity: sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==}
+    engines: {node: '>=14.16'}
 
-  terser@5.30.3:
-    resolution: {integrity: sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==}
+  terser@5.31.1:
+    resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==}
     engines: {node: '>=10'}
     hasBin: true
 
@@ -10524,14 +11093,14 @@ packages:
   thenify@3.3.1:
     resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
 
-  thread-stream@2.3.0:
-    resolution: {integrity: sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==}
+  thread-stream@3.1.0:
+    resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
 
-  three@0.164.1:
-    resolution: {integrity: sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w==}
+  three@0.165.0:
+    resolution: {integrity: sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA==}
 
-  throttle-debounce@5.0.0:
-    resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==}
+  throttle-debounce@5.0.2:
+    resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
     engines: {node: '>=12.22'}
 
   throttleit@1.0.0:
@@ -10546,9 +11115,6 @@ packages:
   through@2.3.8:
     resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
 
-  tiny-invariant@1.3.1:
-    resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
-
   tiny-invariant@1.3.3:
     resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
 
@@ -10562,8 +11128,8 @@ packages:
   tinycolor2@1.6.0:
     resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
 
-  tinypool@0.7.0:
-    resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
+  tinypool@0.8.4:
+    resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==}
     engines: {node: '>=14.0.0'}
 
   tinyspy@2.2.0:
@@ -10588,10 +11154,6 @@ packages:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
 
-  toad-cache@3.3.0:
-    resolution: {integrity: sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==}
-    engines: {node: '>=12'}
-
   toad-cache@3.7.0:
     resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
     engines: {node: '>=12'}
@@ -10618,8 +11180,8 @@ packages:
     resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
     engines: {node: '>=0.8'}
 
-  tough-cookie@4.1.3:
-    resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
+  tough-cookie@4.1.4:
+    resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
     engines: {node: '>=6'}
 
   tr46@0.0.3:
@@ -10666,8 +11228,8 @@ packages:
   ts-map@1.0.3:
     resolution: {integrity: sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==}
 
-  tsc-alias@1.8.8:
-    resolution: {integrity: sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==}
+  tsc-alias@1.8.10:
+    resolution: {integrity: sha512-Ibv4KAWfFkFdKJxnWfVtdOmB0Zi1RJVxcbPGiCDsFpCQSsmpWyuzHG3rQyI5YkobWwxFPEyQfu1hdo4qLG2zPw==}
     hasBin: true
 
   tsconfig-paths@3.15.0:
@@ -10677,8 +11239,8 @@ packages:
     resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
     engines: {node: '>=6'}
 
-  tsd@0.30.7:
-    resolution: {integrity: sha512-oTiJ28D6B/KXoU3ww/Eji+xqHJojiuPVMwA12g4KYX1O72N93Nb6P3P3h2OAhhf92Xl8NIhb/xFmBZd5zw/xUw==}
+  tsd@0.31.1:
+    resolution: {integrity: sha512-sSL84A0SFwx2xGMWrxlGaarKFSQszWjJS2vgNDDLwatytzg2aq6ShlwHsBYxRNmjzXISODwMva5ZOdAg/4AoOA==}
     engines: {node: '>=14.16'}
     hasBin: true
 
@@ -10688,6 +11250,9 @@ packages:
   tslib@2.6.2:
     resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
 
+  tslib@2.6.3:
+    resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
+
   tsx@4.4.0:
     resolution: {integrity: sha512-4fwcEjRUxW20ciSaMB8zkpGwCPxuRGnadDuj/pBk5S9uT29zvWz15PK36GrKJo45mSJomDxVejZ73c6lr3811Q==}
     engines: {node: '>=18.0.0'}
@@ -10707,10 +11272,6 @@ packages:
     resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
     engines: {node: '>=4'}
 
-  type-fest@0.16.0:
-    resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
-    engines: {node: '>=10'}
-
   type-fest@0.18.1:
     resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}
     engines: {node: '>=10'}
@@ -10731,10 +11292,18 @@ packages:
     resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
     engines: {node: '>=8'}
 
+  type-fest@1.4.0:
+    resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
+    engines: {node: '>=10'}
+
   type-fest@2.19.0:
     resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
     engines: {node: '>=12.20'}
 
+  type-fest@4.20.1:
+    resolution: {integrity: sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg==}
+    engines: {node: '>=16'}
+
   type-fest@4.9.0:
     resolution: {integrity: sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==}
     engines: {node: '>=16'}
@@ -10829,8 +11398,8 @@ packages:
     engines: {node: '>=14.17'}
     hasBin: true
 
-  typescript@5.5.2:
-    resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==}
+  typescript@5.5.3:
+    resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -10882,6 +11451,10 @@ packages:
     resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
     engines: {node: '>=4'}
 
+  unicorn-magic@0.1.0:
+    resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
+    engines: {node: '>=18'}
+
   unified@11.0.4:
     resolution: {integrity: sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==}
 
@@ -10896,9 +11469,9 @@ packages:
     resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
-  unique-string@2.0.0:
-    resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
-    engines: {node: '>=8'}
+  unique-string@3.0.0:
+    resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==}
+    engines: {node: '>=12'}
 
   unist-util-is@6.0.0:
     resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
@@ -10950,6 +11523,26 @@ packages:
   url-parse@1.5.10:
     resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
 
+  use-callback-ref@1.3.2:
+    resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
+  use-sidecar@1.1.2:
+    resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+
   utf-8-validate@6.0.3:
     resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==}
     engines: {node: '>=6.14.2'}
@@ -10964,6 +11557,10 @@ packages:
     resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
     engines: {node: '>= 0.4.0'}
 
+  uuid@10.0.0:
+    resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
+    hasBin: true
+
   uuid@3.4.0:
     resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
     deprecated: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
@@ -10977,8 +11574,8 @@ packages:
     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
     hasBin: true
 
-  v-code-diff@1.11.0:
-    resolution: {integrity: sha512-lBlO+FXw3I3qFKbnlorXZ4sb5cFnrdxlc6lj3Y1CWrbn2LC7PoVbGlwH0W+nvAVX1rdJhhc15rKIQdHyMkXe/w==}
+  v-code-diff@1.12.0:
+    resolution: {integrity: sha512-vvdCBG02mIIiW6Gx6jF119hzxELt+6TlJIwchglR1JYzboHePNxIkVBjR/aoAOVlsGa+5Vtb77cd/N84nrXWPA==}
     peerDependencies:
       '@vue/composition-api': ^1.4.9
       vue: ^2.6.0 || >=3.0.0
@@ -10993,10 +11590,6 @@ packages:
   validate-npm-package-license@3.0.4:
     resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
 
-  validator@13.9.0:
-    resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==}
-    engines: {node: '>= 0.10'}
-
   vary@1.1.2:
     resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
     engines: {node: '>= 0.8'}
@@ -11011,16 +11604,16 @@ packages:
   vfile@6.0.1:
     resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
 
-  vite-node@0.34.6:
-    resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
-    engines: {node: '>=v14.18.0'}
+  vite-node@1.6.0:
+    resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==}
+    engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
 
   vite-plugin-turbosnap@1.0.3:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
 
-  vite@5.2.11:
-    resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==}
+  vite@5.3.2:
+    resolution: {integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -11053,22 +11646,22 @@ packages:
     peerDependencies:
       vitest: '>=0.16.0'
 
-  vitest@0.34.6:
-    resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
-    engines: {node: '>=v14.18.0'}
+  vitest@1.6.0:
+    resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
       '@edge-runtime/vm': '*'
-      '@vitest/browser': '*'
-      '@vitest/ui': '*'
+      '@types/node': ^18.0.0 || >=20.0.0
+      '@vitest/browser': 1.6.0
+      '@vitest/ui': 1.6.0
       happy-dom: '*'
       jsdom: '*'
-      playwright: '*'
-      safaridriver: '*'
-      webdriverio: '*'
     peerDependenciesMeta:
       '@edge-runtime/vm':
         optional: true
+      '@types/node':
+        optional: true
       '@vitest/browser':
         optional: true
       '@vitest/ui':
@@ -11077,12 +11670,6 @@ packages:
         optional: true
       jsdom:
         optional: true
-      playwright:
-        optional: true
-      safaridriver:
-        optional: true
-      webdriverio:
-        optional: true
 
   void-elements@3.1.0:
     resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
@@ -11109,6 +11696,9 @@ packages:
     resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
     hasBin: true
 
+  vscode-uri@3.0.8:
+    resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
+
   vue-component-meta@2.0.16:
     resolution: {integrity: sha512-IyIMClUMYcKxAL34GqdPbR4V45MUeHXqQiZlHxeYMV5Qcqp4M+CEmtGpF//XBSS138heDkYkceHAtJQjLUB1Lw==}
     peerDependencies:
@@ -11123,8 +11713,8 @@ packages:
   vue-component-type-helpers@2.0.16:
     resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
 
-  vue-component-type-helpers@2.0.21:
-    resolution: {integrity: sha512-3NaicyZ7N4B6cft4bfb7dOnPbE9CjLcx+6wZWAg5zwszfO4qXRh+U52dN5r5ZZfc6iMaxKCEcoH9CmxxoFZHLg==}
+  vue-component-type-helpers@2.0.24:
+    resolution: {integrity: sha512-Jr5N8QVYEcbQuMN1LRgvg61758G8HTnzUlQsAFOxx6Y6X8kmhJ7C+jOvWsQruYxi3uHhhS6BghyRlyiwO99DBg==}
 
   vue-demi@0.14.7:
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
@@ -11142,8 +11732,8 @@ packages:
     peerDependencies:
       vue: '>=2'
 
-  vue-eslint-parser@9.4.2:
-    resolution: {integrity: sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==}
+  vue-eslint-parser@9.4.3:
+    resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: '>=6.0.0'
@@ -11162,14 +11752,14 @@ packages:
   vue-template-compiler@2.7.14:
     resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
 
-  vue-tsc@2.0.16:
-    resolution: {integrity: sha512-/gHAWJa216PeEhfxtAToIbxdWgw01wuQzo48ZUqMYVEyNqDp+OYV9xMO5HaPS2P3Ls0+EsjguMZLY4cGobX4Ew==}
+  vue-tsc@2.0.24:
+    resolution: {integrity: sha512-1qi4P8L7yS78A7OJ7CDDxUIZPD6nVxoQEgX3DkRZNi1HI1qOfzOJwQlNpmwkogSVD6S/XcanbW9sktzpSxz6rA==}
     hasBin: true
     peerDependencies:
-      typescript: '*'
+      typescript: '>=5.0.0'
 
-  vue@3.4.26:
-    resolution: {integrity: sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg==}
+  vue@3.4.31:
+    resolution: {integrity: sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
@@ -11310,8 +11900,8 @@ packages:
     resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
 
-  ws@8.17.0:
-    resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==}
+  ws@8.17.1:
+    resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
     engines: {node: '>=10.0.0'}
     peerDependencies:
       bufferutil: ^4.0.1
@@ -11403,10 +11993,9 @@ packages:
     resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
     engines: {node: '>=12.20'}
 
-  z-schema@5.0.5:
-    resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==}
-    engines: {node: '>=8.0.0'}
-    hasBin: true
+  yoctocolors@2.0.2:
+    resolution: {integrity: sha512-Ct97huExsu7cWeEjmrXlofevF8CvzUglJ4iGUet5B8xn1oumtAZBpHU4GzYuoE6PVqcZ5hghtBrSlhwHuR1Jmw==}
+    engines: {node: '>=18'}
 
   zip-stream@6.0.1:
     resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
@@ -11442,458 +12031,510 @@ snapshots:
     dependencies:
       default-browser-id: 3.0.0
 
-  '@aws-crypto/crc32@3.0.0':
+  '@aws-crypto/crc32@5.2.0':
     dependencies:
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      tslib: 1.14.1
+      '@aws-crypto/util': 5.2.0
+      '@aws-sdk/types': 3.598.0
+      tslib: 2.6.2
 
-  '@aws-crypto/crc32c@3.0.0':
+  '@aws-crypto/crc32c@5.2.0':
     dependencies:
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      tslib: 1.14.1
+      '@aws-crypto/util': 5.2.0
+      '@aws-sdk/types': 3.598.0
+      tslib: 2.6.2
 
-  '@aws-crypto/ie11-detection@3.0.0':
+  '@aws-crypto/sha1-browser@5.2.0':
     dependencies:
-      tslib: 1.14.1
-
-  '@aws-crypto/sha1-browser@3.0.0':
-    dependencies:
-      '@aws-crypto/ie11-detection': 3.0.0
-      '@aws-crypto/supports-web-crypto': 3.0.0
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
+      '@aws-crypto/supports-web-crypto': 5.2.0
+      '@aws-crypto/util': 5.2.0
+      '@aws-sdk/types': 3.598.0
       '@aws-sdk/util-locate-window': 3.208.0
-      '@aws-sdk/util-utf8-browser': 3.259.0
-      tslib: 1.14.1
-
-  '@aws-crypto/sha256-browser@3.0.0':
-    dependencies:
-      '@aws-crypto/ie11-detection': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-crypto/supports-web-crypto': 3.0.0
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      '@aws-sdk/util-locate-window': 3.208.0
-      '@aws-sdk/util-utf8-browser': 3.259.0
-      tslib: 1.14.1
-
-  '@aws-crypto/sha256-js@3.0.0':
-    dependencies:
-      '@aws-crypto/util': 3.0.0
-      '@aws-sdk/types': 3.413.0
-      tslib: 1.14.1
-
-  '@aws-crypto/supports-web-crypto@3.0.0':
-    dependencies:
-      tslib: 1.14.1
-
-  '@aws-crypto/util@3.0.0':
-    dependencies:
-      '@aws-sdk/types': 3.413.0
-      '@aws-sdk/util-utf8-browser': 3.259.0
-      tslib: 1.14.1
-
-  '@aws-sdk/client-s3@3.412.0':
-    dependencies:
-      '@aws-crypto/sha1-browser': 3.0.0
-      '@aws-crypto/sha256-browser': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/client-sts': 3.410.0
-      '@aws-sdk/credential-provider-node': 3.410.0
-      '@aws-sdk/middleware-bucket-endpoint': 3.410.0
-      '@aws-sdk/middleware-expect-continue': 3.410.0
-      '@aws-sdk/middleware-flexible-checksums': 3.410.0
-      '@aws-sdk/middleware-host-header': 3.410.0
-      '@aws-sdk/middleware-location-constraint': 3.410.0
-      '@aws-sdk/middleware-logger': 3.410.0
-      '@aws-sdk/middleware-recursion-detection': 3.410.0
-      '@aws-sdk/middleware-sdk-s3': 3.410.0
-      '@aws-sdk/middleware-signing': 3.410.0
-      '@aws-sdk/middleware-ssec': 3.410.0
-      '@aws-sdk/middleware-user-agent': 3.410.0
-      '@aws-sdk/signature-v4-multi-region': 3.412.0
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@aws-sdk/util-user-agent-browser': 3.410.0
-      '@aws-sdk/util-user-agent-node': 3.410.0
-      '@aws-sdk/xml-builder': 3.310.0
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/eventstream-serde-browser': 2.0.8
-      '@smithy/eventstream-serde-config-resolver': 2.0.8
-      '@smithy/eventstream-serde-node': 2.0.8
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/hash-blob-browser': 2.0.8
-      '@smithy/hash-node': 2.0.8
-      '@smithy/hash-stream-node': 2.0.8
-      '@smithy/invalid-dependency': 2.0.8
-      '@smithy/md5-js': 2.0.8
-      '@smithy/middleware-content-length': 2.0.10
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/middleware-retry': 2.0.11
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.5.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-body-length-browser': 2.0.0
-      '@smithy/util-body-length-node': 2.1.0
-      '@smithy/util-defaults-mode-browser': 2.0.9
-      '@smithy/util-defaults-mode-node': 2.0.11
-      '@smithy/util-retry': 2.0.1
-      '@smithy/util-stream': 2.0.11
       '@smithy/util-utf8': 2.0.0
-      '@smithy/util-waiter': 2.0.8
+      tslib: 2.6.2
+
+  '@aws-crypto/sha256-browser@5.2.0':
+    dependencies:
+      '@aws-crypto/sha256-js': 5.2.0
+      '@aws-crypto/supports-web-crypto': 5.2.0
+      '@aws-crypto/util': 5.2.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-locate-window': 3.208.0
+      '@smithy/util-utf8': 2.0.0
+      tslib: 2.6.2
+
+  '@aws-crypto/sha256-js@5.2.0':
+    dependencies:
+      '@aws-crypto/util': 5.2.0
+      '@aws-sdk/types': 3.598.0
+      tslib: 2.6.2
+
+  '@aws-crypto/supports-web-crypto@5.2.0':
+    dependencies:
+      tslib: 2.6.2
+
+  '@aws-crypto/util@5.2.0':
+    dependencies:
+      '@aws-sdk/types': 3.598.0
+      '@smithy/util-utf8': 2.0.0
+      tslib: 2.6.2
+
+  '@aws-sdk/client-s3@3.600.0':
+    dependencies:
+      '@aws-crypto/sha1-browser': 5.2.0
+      '@aws-crypto/sha256-browser': 5.2.0
+      '@aws-crypto/sha256-js': 5.2.0
+      '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/client-sts': 3.600.0
+      '@aws-sdk/core': 3.598.0
+      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/middleware-bucket-endpoint': 3.598.0
+      '@aws-sdk/middleware-expect-continue': 3.598.0
+      '@aws-sdk/middleware-flexible-checksums': 3.598.0
+      '@aws-sdk/middleware-host-header': 3.598.0
+      '@aws-sdk/middleware-location-constraint': 3.598.0
+      '@aws-sdk/middleware-logger': 3.598.0
+      '@aws-sdk/middleware-recursion-detection': 3.598.0
+      '@aws-sdk/middleware-sdk-s3': 3.598.0
+      '@aws-sdk/middleware-signing': 3.598.0
+      '@aws-sdk/middleware-ssec': 3.598.0
+      '@aws-sdk/middleware-user-agent': 3.598.0
+      '@aws-sdk/region-config-resolver': 3.598.0
+      '@aws-sdk/signature-v4-multi-region': 3.598.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-endpoints': 3.598.0
+      '@aws-sdk/util-user-agent-browser': 3.598.0
+      '@aws-sdk/util-user-agent-node': 3.598.0
+      '@aws-sdk/xml-builder': 3.598.0
+      '@smithy/config-resolver': 3.0.4
+      '@smithy/core': 2.2.4
+      '@smithy/eventstream-serde-browser': 3.0.4
+      '@smithy/eventstream-serde-config-resolver': 3.0.3
+      '@smithy/eventstream-serde-node': 3.0.4
+      '@smithy/fetch-http-handler': 3.2.0
+      '@smithy/hash-blob-browser': 3.1.2
+      '@smithy/hash-node': 3.0.3
+      '@smithy/hash-stream-node': 3.1.2
+      '@smithy/invalid-dependency': 3.0.3
+      '@smithy/md5-js': 3.0.3
+      '@smithy/middleware-content-length': 3.0.3
+      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-serde': 3.0.3
+      '@smithy/middleware-stack': 3.0.3
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/node-http-handler': 3.1.1
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/url-parser': 3.0.3
+      '@smithy/util-base64': 3.0.0
+      '@smithy/util-body-length-browser': 3.0.0
+      '@smithy/util-body-length-node': 3.0.0
+      '@smithy/util-defaults-mode-browser': 3.0.7
+      '@smithy/util-defaults-mode-node': 3.0.7
+      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-retry': 3.0.3
+      '@smithy/util-stream': 3.0.5
+      '@smithy/util-utf8': 3.0.0
+      '@smithy/util-waiter': 3.1.2
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - aws-crt
+
+  '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)':
+    dependencies:
+      '@aws-crypto/sha256-browser': 5.2.0
+      '@aws-crypto/sha256-js': 5.2.0
+      '@aws-sdk/client-sts': 3.600.0
+      '@aws-sdk/core': 3.598.0
+      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/middleware-host-header': 3.598.0
+      '@aws-sdk/middleware-logger': 3.598.0
+      '@aws-sdk/middleware-recursion-detection': 3.598.0
+      '@aws-sdk/middleware-user-agent': 3.598.0
+      '@aws-sdk/region-config-resolver': 3.598.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-endpoints': 3.598.0
+      '@aws-sdk/util-user-agent-browser': 3.598.0
+      '@aws-sdk/util-user-agent-node': 3.598.0
+      '@smithy/config-resolver': 3.0.4
+      '@smithy/core': 2.2.4
+      '@smithy/fetch-http-handler': 3.2.0
+      '@smithy/hash-node': 3.0.3
+      '@smithy/invalid-dependency': 3.0.3
+      '@smithy/middleware-content-length': 3.0.3
+      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-serde': 3.0.3
+      '@smithy/middleware-stack': 3.0.3
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/node-http-handler': 3.1.1
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/url-parser': 3.0.3
+      '@smithy/util-base64': 3.0.0
+      '@smithy/util-body-length-browser': 3.0.0
+      '@smithy/util-body-length-node': 3.0.0
+      '@smithy/util-defaults-mode-browser': 3.0.7
+      '@smithy/util-defaults-mode-node': 3.0.7
+      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-middleware': 3.0.3
+      '@smithy/util-retry': 3.0.3
+      '@smithy/util-utf8': 3.0.0
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - '@aws-sdk/client-sts'
+      - aws-crt
+
+  '@aws-sdk/client-sso@3.598.0':
+    dependencies:
+      '@aws-crypto/sha256-browser': 5.2.0
+      '@aws-crypto/sha256-js': 5.2.0
+      '@aws-sdk/core': 3.598.0
+      '@aws-sdk/middleware-host-header': 3.598.0
+      '@aws-sdk/middleware-logger': 3.598.0
+      '@aws-sdk/middleware-recursion-detection': 3.598.0
+      '@aws-sdk/middleware-user-agent': 3.598.0
+      '@aws-sdk/region-config-resolver': 3.598.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-endpoints': 3.598.0
+      '@aws-sdk/util-user-agent-browser': 3.598.0
+      '@aws-sdk/util-user-agent-node': 3.598.0
+      '@smithy/config-resolver': 3.0.4
+      '@smithy/core': 2.2.4
+      '@smithy/fetch-http-handler': 3.2.0
+      '@smithy/hash-node': 3.0.3
+      '@smithy/invalid-dependency': 3.0.3
+      '@smithy/middleware-content-length': 3.0.3
+      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-serde': 3.0.3
+      '@smithy/middleware-stack': 3.0.3
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/node-http-handler': 3.1.1
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/url-parser': 3.0.3
+      '@smithy/util-base64': 3.0.0
+      '@smithy/util-body-length-browser': 3.0.0
+      '@smithy/util-body-length-node': 3.0.0
+      '@smithy/util-defaults-mode-browser': 3.0.7
+      '@smithy/util-defaults-mode-node': 3.0.7
+      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-middleware': 3.0.3
+      '@smithy/util-retry': 3.0.3
+      '@smithy/util-utf8': 3.0.0
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - aws-crt
+
+  '@aws-sdk/client-sts@3.600.0':
+    dependencies:
+      '@aws-crypto/sha256-browser': 5.2.0
+      '@aws-crypto/sha256-js': 5.2.0
+      '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/core': 3.598.0
+      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/middleware-host-header': 3.598.0
+      '@aws-sdk/middleware-logger': 3.598.0
+      '@aws-sdk/middleware-recursion-detection': 3.598.0
+      '@aws-sdk/middleware-user-agent': 3.598.0
+      '@aws-sdk/region-config-resolver': 3.598.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-endpoints': 3.598.0
+      '@aws-sdk/util-user-agent-browser': 3.598.0
+      '@aws-sdk/util-user-agent-node': 3.598.0
+      '@smithy/config-resolver': 3.0.4
+      '@smithy/core': 2.2.4
+      '@smithy/fetch-http-handler': 3.2.0
+      '@smithy/hash-node': 3.0.3
+      '@smithy/invalid-dependency': 3.0.3
+      '@smithy/middleware-content-length': 3.0.3
+      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-serde': 3.0.3
+      '@smithy/middleware-stack': 3.0.3
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/node-http-handler': 3.1.1
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/url-parser': 3.0.3
+      '@smithy/util-base64': 3.0.0
+      '@smithy/util-body-length-browser': 3.0.0
+      '@smithy/util-body-length-node': 3.0.0
+      '@smithy/util-defaults-mode-browser': 3.0.7
+      '@smithy/util-defaults-mode-node': 3.0.7
+      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-middleware': 3.0.3
+      '@smithy/util-retry': 3.0.3
+      '@smithy/util-utf8': 3.0.0
+      tslib: 2.6.2
+    transitivePeerDependencies:
+      - aws-crt
+
+  '@aws-sdk/core@3.598.0':
+    dependencies:
+      '@smithy/core': 2.2.4
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/signature-v4': 3.1.2
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
       fast-xml-parser: 4.2.5
       tslib: 2.6.2
-    transitivePeerDependencies:
-      - aws-crt
 
-  '@aws-sdk/client-sso@3.410.0':
+  '@aws-sdk/credential-provider-env@3.598.0':
     dependencies:
-      '@aws-crypto/sha256-browser': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/middleware-host-header': 3.410.0
-      '@aws-sdk/middleware-logger': 3.410.0
-      '@aws-sdk/middleware-recursion-detection': 3.410.0
-      '@aws-sdk/middleware-user-agent': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@aws-sdk/util-user-agent-browser': 3.410.0
-      '@aws-sdk/util-user-agent-node': 3.410.0
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/hash-node': 2.0.8
-      '@smithy/invalid-dependency': 2.0.8
-      '@smithy/middleware-content-length': 2.0.10
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/middleware-retry': 2.0.11
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.5.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-body-length-browser': 2.0.0
-      '@smithy/util-body-length-node': 2.1.0
-      '@smithy/util-defaults-mode-browser': 2.0.9
-      '@smithy/util-defaults-mode-node': 2.0.11
-      '@smithy/util-retry': 2.0.1
-      '@smithy/util-utf8': 2.0.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/property-provider': 3.1.3
+      '@smithy/types': 3.3.0
+      tslib: 2.6.2
+
+  '@aws-sdk/credential-provider-http@3.598.0':
+    dependencies:
+      '@aws-sdk/types': 3.598.0
+      '@smithy/fetch-http-handler': 3.2.0
+      '@smithy/node-http-handler': 3.1.1
+      '@smithy/property-provider': 3.1.3
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/util-stream': 3.0.5
+      tslib: 2.6.2
+
+  '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)':
+    dependencies:
+      '@aws-sdk/client-sts': 3.600.0
+      '@aws-sdk/credential-provider-env': 3.598.0
+      '@aws-sdk/credential-provider-http': 3.598.0
+      '@aws-sdk/credential-provider-process': 3.598.0
+      '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
+      '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/types': 3.598.0
+      '@smithy/credential-provider-imds': 3.1.3
+      '@smithy/property-provider': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
     transitivePeerDependencies:
+      - '@aws-sdk/client-sso-oidc'
       - aws-crt
 
-  '@aws-sdk/client-sts@3.410.0':
+  '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)':
     dependencies:
-      '@aws-crypto/sha256-browser': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/credential-provider-node': 3.410.0
-      '@aws-sdk/middleware-host-header': 3.410.0
-      '@aws-sdk/middleware-logger': 3.410.0
-      '@aws-sdk/middleware-recursion-detection': 3.410.0
-      '@aws-sdk/middleware-sdk-sts': 3.410.0
-      '@aws-sdk/middleware-signing': 3.410.0
-      '@aws-sdk/middleware-user-agent': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@aws-sdk/util-user-agent-browser': 3.410.0
-      '@aws-sdk/util-user-agent-node': 3.410.0
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/hash-node': 2.0.8
-      '@smithy/invalid-dependency': 2.0.8
-      '@smithy/middleware-content-length': 2.0.10
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/middleware-retry': 2.0.11
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.5.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-body-length-browser': 2.0.0
-      '@smithy/util-body-length-node': 2.1.0
-      '@smithy/util-defaults-mode-browser': 2.0.9
-      '@smithy/util-defaults-mode-node': 2.0.11
-      '@smithy/util-retry': 2.0.1
-      '@smithy/util-utf8': 2.0.0
-      fast-xml-parser: 4.2.5
+      '@aws-sdk/credential-provider-env': 3.598.0
+      '@aws-sdk/credential-provider-http': 3.598.0
+      '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/credential-provider-process': 3.598.0
+      '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
+      '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/types': 3.598.0
+      '@smithy/credential-provider-imds': 3.1.3
+      '@smithy/property-provider': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
     transitivePeerDependencies:
+      - '@aws-sdk/client-sso-oidc'
+      - '@aws-sdk/client-sts'
       - aws-crt
 
-  '@aws-sdk/credential-provider-env@3.410.0':
+  '@aws-sdk/credential-provider-process@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/property-provider': 2.0.9
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/property-provider': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/credential-provider-ini@3.410.0':
+  '@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)':
     dependencies:
-      '@aws-sdk/credential-provider-env': 3.410.0
-      '@aws-sdk/credential-provider-process': 3.410.0
-      '@aws-sdk/credential-provider-sso': 3.410.0
-      '@aws-sdk/credential-provider-web-identity': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@smithy/credential-provider-imds': 2.0.11
-      '@smithy/property-provider': 2.0.9
-      '@smithy/shared-ini-file-loader': 2.0.10
-      '@smithy/types': 2.6.0
+      '@aws-sdk/client-sso': 3.598.0
+      '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
+      '@aws-sdk/types': 3.598.0
+      '@smithy/property-provider': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
     transitivePeerDependencies:
+      - '@aws-sdk/client-sso-oidc'
       - aws-crt
 
-  '@aws-sdk/credential-provider-node@3.410.0':
+  '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)':
     dependencies:
-      '@aws-sdk/credential-provider-env': 3.410.0
-      '@aws-sdk/credential-provider-ini': 3.410.0
-      '@aws-sdk/credential-provider-process': 3.410.0
-      '@aws-sdk/credential-provider-sso': 3.410.0
-      '@aws-sdk/credential-provider-web-identity': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@smithy/credential-provider-imds': 2.0.11
-      '@smithy/property-provider': 2.0.9
-      '@smithy/shared-ini-file-loader': 2.0.10
-      '@smithy/types': 2.6.0
-      tslib: 2.6.2
-    transitivePeerDependencies:
-      - aws-crt
-
-  '@aws-sdk/credential-provider-process@3.410.0':
-    dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/property-provider': 2.0.9
-      '@smithy/shared-ini-file-loader': 2.0.10
-      '@smithy/types': 2.6.0
+      '@aws-sdk/client-sts': 3.600.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/property-provider': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/credential-provider-sso@3.410.0':
+  '@aws-sdk/lib-storage@3.600.0(@aws-sdk/client-s3@3.600.0)':
     dependencies:
-      '@aws-sdk/client-sso': 3.410.0
-      '@aws-sdk/token-providers': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@smithy/property-provider': 2.0.9
-      '@smithy/shared-ini-file-loader': 2.0.10
-      '@smithy/types': 2.6.0
-      tslib: 2.6.2
-    transitivePeerDependencies:
-      - aws-crt
-
-  '@aws-sdk/credential-provider-web-identity@3.410.0':
-    dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/property-provider': 2.0.9
-      '@smithy/types': 2.6.0
-      tslib: 2.6.2
-
-  '@aws-sdk/lib-storage@3.412.0(@aws-sdk/client-s3@3.412.0)':
-    dependencies:
-      '@aws-sdk/client-s3': 3.412.0
-      '@smithy/abort-controller': 2.0.14
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/smithy-client': 2.1.5
+      '@aws-sdk/client-s3': 3.600.0
+      '@smithy/abort-controller': 3.1.1
+      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/smithy-client': 3.1.5
       buffer: 5.6.0
       events: 3.3.0
       stream-browserify: 3.0.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-bucket-endpoint@3.410.0':
+  '@aws-sdk/middleware-bucket-endpoint@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-arn-parser': 3.310.0
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
-      '@smithy/util-config-provider': 2.0.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-arn-parser': 3.568.0
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
+      '@smithy/util-config-provider': 3.0.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-expect-continue@3.410.0':
+  '@aws-sdk/middleware-expect-continue@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-flexible-checksums@3.410.0':
+  '@aws-sdk/middleware-flexible-checksums@3.598.0':
     dependencies:
-      '@aws-crypto/crc32': 3.0.0
-      '@aws-crypto/crc32c': 3.0.0
-      '@aws-sdk/types': 3.410.0
-      '@smithy/is-array-buffer': 2.0.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
-      '@smithy/util-utf8': 2.0.0
+      '@aws-crypto/crc32': 5.2.0
+      '@aws-crypto/crc32c': 5.2.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/is-array-buffer': 3.0.0
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
+      '@smithy/util-utf8': 3.0.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-host-header@3.410.0':
+  '@aws-sdk/middleware-host-header@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-location-constraint@3.410.0':
+  '@aws-sdk/middleware-location-constraint@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-logger@3.410.0':
+  '@aws-sdk/middleware-logger@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-recursion-detection@3.410.0':
+  '@aws-sdk/middleware-recursion-detection@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-sdk-s3@3.410.0':
+  '@aws-sdk/middleware-sdk-s3@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-arn-parser': 3.310.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-arn-parser': 3.568.0
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/signature-v4': 3.1.2
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/util-config-provider': 3.0.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-sdk-sts@3.410.0':
+  '@aws-sdk/middleware-signing@3.598.0':
     dependencies:
-      '@aws-sdk/middleware-signing': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/property-provider': 3.1.3
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/signature-v4': 3.1.2
+      '@smithy/types': 3.3.0
+      '@smithy/util-middleware': 3.0.3
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-signing@3.410.0':
+  '@aws-sdk/middleware-ssec@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/property-provider': 2.0.9
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/signature-v4': 2.0.5
-      '@smithy/types': 2.6.0
-      '@smithy/util-middleware': 2.0.1
+      '@aws-sdk/types': 3.598.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-ssec@3.410.0':
+  '@aws-sdk/middleware-user-agent@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/util-endpoints': 3.598.0
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/middleware-user-agent@3.410.0':
+  '@aws-sdk/region-config-resolver@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/types': 3.3.0
+      '@smithy/util-config-provider': 3.0.0
+      '@smithy/util-middleware': 3.0.3
       tslib: 2.6.2
 
-  '@aws-sdk/signature-v4-multi-region@3.412.0':
+  '@aws-sdk/signature-v4-multi-region@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/signature-v4': 2.0.5
-      '@smithy/types': 2.6.0
+      '@aws-sdk/middleware-sdk-s3': 3.598.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/signature-v4': 3.1.2
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/token-providers@3.410.0':
+  '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)':
     dependencies:
-      '@aws-crypto/sha256-browser': 3.0.0
-      '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/middleware-host-header': 3.410.0
-      '@aws-sdk/middleware-logger': 3.410.0
-      '@aws-sdk/middleware-recursion-detection': 3.410.0
-      '@aws-sdk/middleware-user-agent': 3.410.0
-      '@aws-sdk/types': 3.410.0
-      '@aws-sdk/util-endpoints': 3.410.0
-      '@aws-sdk/util-user-agent-browser': 3.410.0
-      '@aws-sdk/util-user-agent-node': 3.410.0
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/hash-node': 2.0.8
-      '@smithy/invalid-dependency': 2.0.8
-      '@smithy/middleware-content-length': 2.0.10
-      '@smithy/middleware-endpoint': 2.0.8
-      '@smithy/middleware-retry': 2.0.11
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.5.0
-      '@smithy/property-provider': 2.0.9
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/shared-ini-file-loader': 2.0.10
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-body-length-browser': 2.0.0
-      '@smithy/util-body-length-node': 2.1.0
-      '@smithy/util-defaults-mode-browser': 2.0.9
-      '@smithy/util-defaults-mode-node': 2.0.11
-      '@smithy/util-retry': 2.0.1
-      '@smithy/util-utf8': 2.0.0
-      tslib: 2.6.2
-    transitivePeerDependencies:
-      - aws-crt
-
-  '@aws-sdk/types@3.410.0':
-    dependencies:
-      '@smithy/types': 2.6.0
+      '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/types': 3.598.0
+      '@smithy/property-provider': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/types@3.413.0':
+  '@aws-sdk/types@3.598.0':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/util-arn-parser@3.310.0':
+  '@aws-sdk/util-arn-parser@3.568.0':
     dependencies:
       tslib: 2.6.2
 
-  '@aws-sdk/util-endpoints@3.410.0':
+  '@aws-sdk/util-endpoints@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/types': 3.3.0
+      '@smithy/util-endpoints': 2.0.4
       tslib: 2.6.2
 
   '@aws-sdk/util-locate-window@3.208.0':
     dependencies:
       tslib: 2.6.2
 
-  '@aws-sdk/util-user-agent-browser@3.410.0':
+  '@aws-sdk/util-user-agent-browser@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/types': 3.3.0
       bowser: 2.11.0
       tslib: 2.6.2
 
-  '@aws-sdk/util-user-agent-node@3.410.0':
+  '@aws-sdk/util-user-agent-node@3.598.0':
     dependencies:
-      '@aws-sdk/types': 3.410.0
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/types': 2.6.0
+      '@aws-sdk/types': 3.598.0
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@aws-sdk/util-utf8-browser@3.259.0':
-    dependencies:
-      tslib: 2.6.2
-
-  '@aws-sdk/xml-builder@3.310.0':
+  '@aws-sdk/xml-builder@3.598.0':
     dependencies:
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
   '@babel/code-frame@7.23.5':
@@ -11901,8 +12542,15 @@ snapshots:
       '@babel/highlight': 7.23.4
       chalk: 2.4.2
 
+  '@babel/code-frame@7.24.7':
+    dependencies:
+      '@babel/highlight': 7.24.7
+      picocolors: 1.0.0
+
   '@babel/compat-data@7.23.5': {}
 
+  '@babel/compat-data@7.24.7': {}
+
   '@babel/core@7.23.5':
     dependencies:
       '@ampproject/remapping': 2.2.1
@@ -11916,27 +12564,27 @@ snapshots:
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/core@7.24.0':
+  '@babel/core@7.24.7':
     dependencies:
       '@ampproject/remapping': 2.2.1
-      '@babel/code-frame': 7.23.5
-      '@babel/generator': 7.23.6
-      '@babel/helper-compilation-targets': 7.23.6
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
-      '@babel/helpers': 7.24.0
-      '@babel/parser': 7.24.0
-      '@babel/template': 7.24.0
-      '@babel/traverse': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/code-frame': 7.24.7
+      '@babel/generator': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helpers': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/template': 7.24.7
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -11950,20 +12598,23 @@ snapshots:
       '@jridgewell/trace-mapping': 0.3.18
       jsesc: 2.5.2
 
-  '@babel/generator@7.23.6':
+  '@babel/generator@7.24.7':
     dependencies:
-      '@babel/types': 7.24.0
-      '@jridgewell/gen-mapping': 0.3.2
-      '@jridgewell/trace-mapping': 0.3.18
+      '@babel/types': 7.24.7
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
       jsesc: 2.5.2
 
-  '@babel/helper-annotate-as-pure@7.22.5':
+  '@babel/helper-annotate-as-pure@7.24.7':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.7
 
-  '@babel/helper-builder-binary-assignment-operator-visitor@7.22.15':
+  '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
   '@babel/helper-compilation-targets@7.22.15':
     dependencies:
@@ -11973,40 +12624,42 @@ snapshots:
       lru-cache: 5.1.1
       semver: 6.3.1
 
-  '@babel/helper-compilation-targets@7.23.6':
+  '@babel/helper-compilation-targets@7.24.7':
     dependencies:
-      '@babel/compat-data': 7.23.5
-      '@babel/helper-validator-option': 7.23.5
+      '@babel/compat-data': 7.24.7
+      '@babel/helper-validator-option': 7.24.7
       browserslist: 4.23.0
       lru-cache: 5.1.1
       semver: 6.3.1
 
-  '@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.24.0)':
+  '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-function-name': 7.23.0
-      '@babel/helper-member-expression-to-functions': 7.23.0
-      '@babel/helper-optimise-call-expression': 7.22.5
-      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
-      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-member-expression-to-functions': 7.24.7
+      '@babel/helper-optimise-call-expression': 7.24.7
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+      '@babel/helper-split-export-declaration': 7.24.7
       semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.0)':
+  '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
       regexpu-core: 5.3.2
       semver: 6.3.1
 
-  '@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.24.0)':
+  '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-compilation-targets': 7.23.6
-      '@babel/helper-plugin-utils': 7.22.5
-      debug: 4.3.4(supports-color@8.1.1)
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      debug: 4.3.5(supports-color@8.1.1)
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -12014,23 +12667,46 @@ snapshots:
 
   '@babel/helper-environment-visitor@7.22.20': {}
 
+  '@babel/helper-environment-visitor@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
   '@babel/helper-function-name@7.23.0':
     dependencies:
-      '@babel/template': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/template': 7.24.7
+      '@babel/types': 7.24.7
+
+  '@babel/helper-function-name@7.24.7':
+    dependencies:
+      '@babel/template': 7.24.7
+      '@babel/types': 7.24.7
 
   '@babel/helper-hoist-variables@7.22.5':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.7
 
-  '@babel/helper-member-expression-to-functions@7.23.0':
+  '@babel/helper-hoist-variables@7.24.7':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.7
+
+  '@babel/helper-member-expression-to-functions@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
   '@babel/helper-module-imports@7.22.15':
     dependencies:
       '@babel/types': 7.23.5
 
+  '@babel/helper-module-imports@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
   '@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
@@ -12040,58 +12716,89 @@ snapshots:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/helper-validator-identifier': 7.22.20
 
-  '@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0)':
+  '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-module-imports': 7.22.15
-      '@babel/helper-simple-access': 7.22.5
-      '@babel/helper-split-export-declaration': 7.22.6
-      '@babel/helper-validator-identifier': 7.22.20
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-module-imports': 7.24.7
+      '@babel/helper-simple-access': 7.24.7
+      '@babel/helper-split-export-declaration': 7.24.7
+      '@babel/helper-validator-identifier': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/helper-optimise-call-expression@7.22.5':
+  '@babel/helper-optimise-call-expression@7.24.7':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.7
 
   '@babel/helper-plugin-utils@7.22.5': {}
 
-  '@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.0)':
-    dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-wrap-function': 7.22.20
+  '@babel/helper-plugin-utils@7.24.7': {}
 
-  '@babel/helper-replace-supers@7.22.20(@babel/core@7.24.0)':
+  '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-member-expression-to-functions': 7.23.0
-      '@babel/helper-optimise-call-expression': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-wrap-function': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-member-expression-to-functions': 7.24.7
+      '@babel/helper-optimise-call-expression': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
   '@babel/helper-simple-access@7.22.5':
     dependencies:
       '@babel/types': 7.23.5
 
-  '@babel/helper-skip-transparent-expression-wrappers@7.22.5':
+  '@babel/helper-simple-access@7.24.7':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
+    dependencies:
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
   '@babel/helper-split-export-declaration@7.22.6':
     dependencies:
       '@babel/types': 7.23.5
 
+  '@babel/helper-split-export-declaration@7.24.7':
+    dependencies:
+      '@babel/types': 7.24.7
+
   '@babel/helper-string-parser@7.23.4': {}
 
+  '@babel/helper-string-parser@7.24.7': {}
+
   '@babel/helper-validator-identifier@7.22.20': {}
 
+  '@babel/helper-validator-identifier@7.24.7': {}
+
   '@babel/helper-validator-option@7.23.5': {}
 
-  '@babel/helper-wrap-function@7.22.20':
+  '@babel/helper-validator-option@7.24.7': {}
+
+  '@babel/helper-wrap-function@7.24.7':
     dependencies:
-      '@babel/helper-function-name': 7.23.0
-      '@babel/template': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/helper-function-name': 7.24.7
+      '@babel/template': 7.24.7
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
   '@babel/helpers@7.23.5':
     dependencies:
@@ -12101,13 +12808,10 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/helpers@7.24.0':
+  '@babel/helpers@7.24.7':
     dependencies:
-      '@babel/template': 7.24.0
-      '@babel/traverse': 7.24.0
-      '@babel/types': 7.24.0
-    transitivePeerDependencies:
-      - supports-color
+      '@babel/template': 7.24.7
+      '@babel/types': 7.24.7
 
   '@babel/highlight@7.23.4':
     dependencies:
@@ -12115,48 +12819,63 @@ snapshots:
       chalk: 2.4.2
       js-tokens: 4.0.0
 
+  '@babel/highlight@7.24.7':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.24.7
+      chalk: 2.4.2
+      js-tokens: 4.0.0
+      picocolors: 1.0.0
+
   '@babel/parser@7.23.9':
     dependencies:
       '@babel/types': 7.23.5
 
-  '@babel/parser@7.24.0':
-    dependencies:
-      '@babel/types': 7.24.0
-
   '@babel/parser@7.24.5':
     dependencies:
       '@babel/types': 7.24.0
 
-  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.24.0)':
+  '@babel/parser@7.24.7':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/types': 7.24.7
 
-  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0)':
+  '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+
+  '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)':
+    dependencies:
+      '@babel/core': 7.24.7
 
   '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.5)':
@@ -12169,49 +12888,49 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-flow@7.23.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
   '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5)':
@@ -12219,9 +12938,9 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5)':
@@ -12229,9 +12948,9 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.5)':
@@ -12239,9 +12958,9 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5)':
@@ -12249,9 +12968,9 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5)':
@@ -12259,9 +12978,9 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5)':
@@ -12269,9 +12988,9 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5)':
@@ -12279,9 +12998,9 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5)':
@@ -12289,24 +13008,24 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
   '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5)':
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
   '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5)':
@@ -12314,435 +13033,471 @@ snapshots:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@babel/helper-plugin-utils': 7.22.5
 
-  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.0)':
+  '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-module-imports': 7.22.15
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-module-imports': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-classes@7.23.5(@babel/core@7.24.0)':
+  '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-compilation-targets': 7.23.6
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-function-name': 7.23.0
-      '@babel/helper-optimise-call-expression': 7.22.5
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
-      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-split-export-declaration': 7.24.7
       globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/template': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/template': 7.24.7
 
-  '@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-for-of@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-function-name@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-compilation-targets': 7.23.6
-      '@babel/helper-function-name': 7.23.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-literals@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-simple-access': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-simple-access': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-hoist-variables': 7.22.5
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-identifier': 7.22.20
+      '@babel/core': 7.24.7
+      '@babel/helper-hoist-variables': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-validator-identifier': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.0)':
+  '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-new-target@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/compat-data': 7.23.5
-      '@babel/core': 7.24.0
-      '@babel/helper-compilation-targets': 7.23.6
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-object-super@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
 
-  '@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-parameters@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.24.0)':
+  '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
       regenerator-transform: 0.15.2
 
-  '@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-spread@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-typescript@7.23.5(@babel/core@7.24.0)':
+  '@babel/plugin-transform-typescript@7.23.5(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-annotate-as-pure': 7.24.7
+      '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.24.0)':
+  '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.0)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.24.7
+      '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7)
+      '@babel/helper-plugin-utils': 7.24.7
 
-  '@babel/preset-env@7.23.5(@babel/core@7.24.0)':
+  '@babel/preset-env@7.24.7(@babel/core@7.24.7)':
     dependencies:
-      '@babel/compat-data': 7.23.5
-      '@babel/core': 7.24.0
-      '@babel/helper-compilation-targets': 7.23.6
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-option': 7.23.5
-      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.0)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0)
-      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.0)
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.0)
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.0)
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0)
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0)
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0)
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.0)
-      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0)
-      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.0)
-      '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.24.0)
-      '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.24.0)
-      '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.24.0)
-      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.0)
-      babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.24.0)
-      babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.24.0)
-      babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.24.0)
-      core-js-compat: 3.33.3
+      '@babel/compat-data': 7.24.7
+      '@babel/core': 7.24.7
+      '@babel/helper-compilation-targets': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-validator-option': 7.24.7
+      '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7)
+      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7)
+      '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7)
+      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7)
+      babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7)
+      babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7)
+      babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7)
+      core-js-compat: 3.37.1
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/preset-flow@7.23.3(@babel/core@7.24.0)':
+  '@babel/preset-flow@7.23.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-option': 7.23.5
-      '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-validator-option': 7.24.7
+      '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.24.7)
 
-  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.0)':
+  '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/types': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/types': 7.24.7
       esutils: 2.0.3
 
-  '@babel/preset-typescript@7.23.3(@babel/core@7.24.0)':
+  '@babel/preset-typescript@7.23.3(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-option': 7.23.5
-      '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-plugin-utils': 7.24.7
+      '@babel/helper-validator-option': 7.24.7
+      '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.24.7)
+    transitivePeerDependencies:
+      - supports-color
 
-  '@babel/register@7.22.15(@babel/core@7.24.0)':
+  '@babel/register@7.22.15(@babel/core@7.24.7)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       clone-deep: 4.0.1
       find-cache-dir: 2.1.0
       make-dir: 2.1.0
@@ -12763,9 +13518,15 @@ snapshots:
 
   '@babel/template@7.24.0':
     dependencies:
-      '@babel/code-frame': 7.23.5
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/code-frame': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+
+  '@babel/template@7.24.7':
+    dependencies:
+      '@babel/code-frame': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
 
   '@babel/traverse@7.23.5':
     dependencies:
@@ -12777,22 +13538,22 @@ snapshots:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.9
       '@babel/types': 7.23.5
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
 
-  '@babel/traverse@7.24.0':
+  '@babel/traverse@7.24.7':
     dependencies:
-      '@babel/code-frame': 7.23.5
-      '@babel/generator': 7.23.6
-      '@babel/helper-environment-visitor': 7.22.20
-      '@babel/helper-function-name': 7.23.0
-      '@babel/helper-hoist-variables': 7.22.5
-      '@babel/helper-split-export-declaration': 7.22.6
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
-      debug: 4.3.4(supports-color@8.1.1)
+      '@babel/code-frame': 7.24.7
+      '@babel/generator': 7.24.7
+      '@babel/helper-environment-visitor': 7.24.7
+      '@babel/helper-function-name': 7.24.7
+      '@babel/helper-hoist-variables': 7.24.7
+      '@babel/helper-split-export-declaration': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+      debug: 4.3.5(supports-color@8.1.1)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -12809,26 +13570,32 @@ snapshots:
       '@babel/helper-validator-identifier': 7.22.20
       to-fast-properties: 2.0.0
 
+  '@babel/types@7.24.7':
+    dependencies:
+      '@babel/helper-string-parser': 7.24.7
+      '@babel/helper-validator-identifier': 7.24.7
+      to-fast-properties: 2.0.0
+
   '@base2/pretty-print-object@1.0.1': {}
 
   '@bcoe/v8-coverage@0.2.3': {}
 
-  '@bull-board/api@5.17.0(@bull-board/ui@5.17.0)':
+  '@bull-board/api@5.20.5(@bull-board/ui@5.20.5)':
     dependencies:
-      '@bull-board/ui': 5.17.0
+      '@bull-board/ui': 5.20.5
       redis-info: 3.1.0
 
-  '@bull-board/fastify@5.17.0':
+  '@bull-board/fastify@5.20.5':
     dependencies:
-      '@bull-board/api': 5.17.0(@bull-board/ui@5.17.0)
-      '@bull-board/ui': 5.17.0
+      '@bull-board/api': 5.20.5(@bull-board/ui@5.20.5)
+      '@bull-board/ui': 5.20.5
       '@fastify/static': 6.12.0
       '@fastify/view': 8.2.0
-      ejs: 3.1.9
+      ejs: 3.1.10
 
-  '@bull-board/ui@5.17.0':
+  '@bull-board/ui@5.20.5':
     dependencies:
-      '@bull-board/api': 5.17.0(@bull-board/ui@5.17.0)
+      '@bull-board/api': 5.20.5(@bull-board/ui@5.20.5)
 
   '@bundled-es-modules/cookie@2.0.0':
     dependencies:
@@ -12926,7 +13693,7 @@ snapshots:
       performance-now: 2.1.0
       qs: 6.10.4
       safe-buffer: 5.2.1
-      tough-cookie: 4.1.3
+      tough-cookie: 4.1.4
       tunnel-agent: 0.6.0
       uuid: 8.3.2
 
@@ -12966,7 +13733,10 @@ snapshots:
   '@esbuild/aix-ppc64@0.19.11':
     optional: true
 
-  '@esbuild/aix-ppc64@0.20.2':
+  '@esbuild/aix-ppc64@0.21.5':
+    optional: true
+
+  '@esbuild/aix-ppc64@0.22.0':
     optional: true
 
   '@esbuild/android-arm64@0.18.20':
@@ -12975,7 +13745,10 @@ snapshots:
   '@esbuild/android-arm64@0.19.11':
     optional: true
 
-  '@esbuild/android-arm64@0.20.2':
+  '@esbuild/android-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/android-arm64@0.22.0':
     optional: true
 
   '@esbuild/android-arm@0.18.20':
@@ -12984,7 +13757,10 @@ snapshots:
   '@esbuild/android-arm@0.19.11':
     optional: true
 
-  '@esbuild/android-arm@0.20.2':
+  '@esbuild/android-arm@0.21.5':
+    optional: true
+
+  '@esbuild/android-arm@0.22.0':
     optional: true
 
   '@esbuild/android-x64@0.18.20':
@@ -12993,7 +13769,10 @@ snapshots:
   '@esbuild/android-x64@0.19.11':
     optional: true
 
-  '@esbuild/android-x64@0.20.2':
+  '@esbuild/android-x64@0.21.5':
+    optional: true
+
+  '@esbuild/android-x64@0.22.0':
     optional: true
 
   '@esbuild/darwin-arm64@0.18.20':
@@ -13002,7 +13781,10 @@ snapshots:
   '@esbuild/darwin-arm64@0.19.11':
     optional: true
 
-  '@esbuild/darwin-arm64@0.20.2':
+  '@esbuild/darwin-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.22.0':
     optional: true
 
   '@esbuild/darwin-x64@0.18.20':
@@ -13011,7 +13793,10 @@ snapshots:
   '@esbuild/darwin-x64@0.19.11':
     optional: true
 
-  '@esbuild/darwin-x64@0.20.2':
+  '@esbuild/darwin-x64@0.21.5':
+    optional: true
+
+  '@esbuild/darwin-x64@0.22.0':
     optional: true
 
   '@esbuild/freebsd-arm64@0.18.20':
@@ -13020,7 +13805,10 @@ snapshots:
   '@esbuild/freebsd-arm64@0.19.11':
     optional: true
 
-  '@esbuild/freebsd-arm64@0.20.2':
+  '@esbuild/freebsd-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.22.0':
     optional: true
 
   '@esbuild/freebsd-x64@0.18.20':
@@ -13029,7 +13817,10 @@ snapshots:
   '@esbuild/freebsd-x64@0.19.11':
     optional: true
 
-  '@esbuild/freebsd-x64@0.20.2':
+  '@esbuild/freebsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.22.0':
     optional: true
 
   '@esbuild/linux-arm64@0.18.20':
@@ -13038,7 +13829,10 @@ snapshots:
   '@esbuild/linux-arm64@0.19.11':
     optional: true
 
-  '@esbuild/linux-arm64@0.20.2':
+  '@esbuild/linux-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-arm64@0.22.0':
     optional: true
 
   '@esbuild/linux-arm@0.18.20':
@@ -13047,7 +13841,10 @@ snapshots:
   '@esbuild/linux-arm@0.19.11':
     optional: true
 
-  '@esbuild/linux-arm@0.20.2':
+  '@esbuild/linux-arm@0.21.5':
+    optional: true
+
+  '@esbuild/linux-arm@0.22.0':
     optional: true
 
   '@esbuild/linux-ia32@0.18.20':
@@ -13056,7 +13853,10 @@ snapshots:
   '@esbuild/linux-ia32@0.19.11':
     optional: true
 
-  '@esbuild/linux-ia32@0.20.2':
+  '@esbuild/linux-ia32@0.21.5':
+    optional: true
+
+  '@esbuild/linux-ia32@0.22.0':
     optional: true
 
   '@esbuild/linux-loong64@0.18.20':
@@ -13065,7 +13865,10 @@ snapshots:
   '@esbuild/linux-loong64@0.19.11':
     optional: true
 
-  '@esbuild/linux-loong64@0.20.2':
+  '@esbuild/linux-loong64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-loong64@0.22.0':
     optional: true
 
   '@esbuild/linux-mips64el@0.18.20':
@@ -13074,7 +13877,10 @@ snapshots:
   '@esbuild/linux-mips64el@0.19.11':
     optional: true
 
-  '@esbuild/linux-mips64el@0.20.2':
+  '@esbuild/linux-mips64el@0.21.5':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.22.0':
     optional: true
 
   '@esbuild/linux-ppc64@0.18.20':
@@ -13083,7 +13889,10 @@ snapshots:
   '@esbuild/linux-ppc64@0.19.11':
     optional: true
 
-  '@esbuild/linux-ppc64@0.20.2':
+  '@esbuild/linux-ppc64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.22.0':
     optional: true
 
   '@esbuild/linux-riscv64@0.18.20':
@@ -13092,7 +13901,10 @@ snapshots:
   '@esbuild/linux-riscv64@0.19.11':
     optional: true
 
-  '@esbuild/linux-riscv64@0.20.2':
+  '@esbuild/linux-riscv64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.22.0':
     optional: true
 
   '@esbuild/linux-s390x@0.18.20':
@@ -13101,7 +13913,10 @@ snapshots:
   '@esbuild/linux-s390x@0.19.11':
     optional: true
 
-  '@esbuild/linux-s390x@0.20.2':
+  '@esbuild/linux-s390x@0.21.5':
+    optional: true
+
+  '@esbuild/linux-s390x@0.22.0':
     optional: true
 
   '@esbuild/linux-x64@0.18.20':
@@ -13110,7 +13925,10 @@ snapshots:
   '@esbuild/linux-x64@0.19.11':
     optional: true
 
-  '@esbuild/linux-x64@0.20.2':
+  '@esbuild/linux-x64@0.21.5':
+    optional: true
+
+  '@esbuild/linux-x64@0.22.0':
     optional: true
 
   '@esbuild/netbsd-x64@0.18.20':
@@ -13119,7 +13937,13 @@ snapshots:
   '@esbuild/netbsd-x64@0.19.11':
     optional: true
 
-  '@esbuild/netbsd-x64@0.20.2':
+  '@esbuild/netbsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.22.0':
+    optional: true
+
+  '@esbuild/openbsd-arm64@0.22.0':
     optional: true
 
   '@esbuild/openbsd-x64@0.18.20':
@@ -13128,7 +13952,10 @@ snapshots:
   '@esbuild/openbsd-x64@0.19.11':
     optional: true
 
-  '@esbuild/openbsd-x64@0.20.2':
+  '@esbuild/openbsd-x64@0.21.5':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.22.0':
     optional: true
 
   '@esbuild/sunos-x64@0.18.20':
@@ -13137,7 +13964,10 @@ snapshots:
   '@esbuild/sunos-x64@0.19.11':
     optional: true
 
-  '@esbuild/sunos-x64@0.20.2':
+  '@esbuild/sunos-x64@0.21.5':
+    optional: true
+
+  '@esbuild/sunos-x64@0.22.0':
     optional: true
 
   '@esbuild/win32-arm64@0.18.20':
@@ -13146,7 +13976,10 @@ snapshots:
   '@esbuild/win32-arm64@0.19.11':
     optional: true
 
-  '@esbuild/win32-arm64@0.20.2':
+  '@esbuild/win32-arm64@0.21.5':
+    optional: true
+
+  '@esbuild/win32-arm64@0.22.0':
     optional: true
 
   '@esbuild/win32-ia32@0.18.20':
@@ -13155,7 +13988,10 @@ snapshots:
   '@esbuild/win32-ia32@0.19.11':
     optional: true
 
-  '@esbuild/win32-ia32@0.20.2':
+  '@esbuild/win32-ia32@0.21.5':
+    optional: true
+
+  '@esbuild/win32-ia32@0.22.0':
     optional: true
 
   '@esbuild/win32-x64@0.18.20':
@@ -13164,30 +14000,38 @@ snapshots:
   '@esbuild/win32-x64@0.19.11':
     optional: true
 
-  '@esbuild/win32-x64@0.20.2':
+  '@esbuild/win32-x64@0.21.5':
     optional: true
 
-  '@eslint-community/eslint-utils@4.4.0(eslint@8.53.0)':
-    dependencies:
-      eslint: 8.53.0
-      eslint-visitor-keys: 3.4.3
+  '@esbuild/win32-x64@0.22.0':
+    optional: true
 
-  '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
+  '@eslint-community/eslint-utils@4.4.0(eslint@9.6.0)':
     dependencies:
-      eslint: 8.57.0
+      eslint: 9.6.0
       eslint-visitor-keys: 3.4.3
 
   '@eslint-community/regexpp@4.10.0': {}
 
   '@eslint-community/regexpp@4.6.2': {}
 
-  '@eslint/eslintrc@2.1.4':
+  '@eslint/compat@1.1.0': {}
+
+  '@eslint/config-array@0.17.0':
+    dependencies:
+      '@eslint/object-schema': 2.1.4
+      debug: 4.3.5(supports-color@8.1.1)
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/eslintrc@3.1.0':
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@8.1.1)
-      espree: 9.6.1
-      globals: 13.19.0
-      ignore: 5.2.4
+      debug: 4.3.5(supports-color@8.1.1)
+      espree: 10.1.0
+      globals: 14.0.0
+      ignore: 5.3.1
       import-fresh: 3.3.0
       js-yaml: 4.1.0
       minimatch: 3.1.2
@@ -13195,9 +14039,9 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@eslint/js@8.53.0': {}
+  '@eslint/js@9.6.0': {}
 
-  '@eslint/js@8.57.0': {}
+  '@eslint/object-schema@2.1.4': {}
 
   '@fal-works/esbuild-plugin-global-externals@2.1.2': {}
 
@@ -13210,8 +14054,8 @@ snapshots:
 
   '@fastify/ajv-compiler@3.5.0':
     dependencies:
-      ajv: 8.13.0
-      ajv-formats: 2.1.1(ajv@8.13.0)
+      ajv: 8.16.0
+      ajv-formats: 2.1.1(ajv@8.16.0)
       fast-uri: 2.2.0
 
   '@fastify/busboy@2.1.0': {}
@@ -13246,12 +14090,12 @@ snapshots:
       '@fastify/reply-from': 9.0.1
       fast-querystring: 1.1.2
       fastify-plugin: 4.5.0
-      ws: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - bufferutil
       - utf-8-validate
 
-  '@fastify/multipart@8.2.0':
+  '@fastify/multipart@8.3.0':
     dependencies:
       '@fastify/busboy': 2.1.0
       '@fastify/deepmerge': 1.3.0
@@ -13287,14 +14131,14 @@ snapshots:
       glob: 8.1.0
       p-limit: 3.1.0
 
-  '@fastify/static@7.0.3':
+  '@fastify/static@7.0.4':
     dependencies:
       '@fastify/accept-negotiator': 1.0.0
       '@fastify/send': 2.0.1
       content-disposition: 0.5.4
       fastify-plugin: 4.5.0
       fastq: 1.17.1
-      glob: 10.3.12
+      glob: 10.4.2
 
   '@fastify/view@8.2.0':
     dependencies:
@@ -13310,13 +14154,11 @@ snapshots:
 
   '@hapi/boom@10.0.1':
     dependencies:
-      '@hapi/hoek': 11.0.2
+      '@hapi/hoek': 11.0.4
 
   '@hapi/bourne@3.0.0': {}
 
-  '@hapi/hoek@10.0.1': {}
-
-  '@hapi/hoek@11.0.2': {}
+  '@hapi/hoek@11.0.4': {}
 
   '@hapi/hoek@9.3.0': {}
 
@@ -13328,40 +14170,22 @@ snapshots:
     dependencies:
       '@hapi/boom': 10.0.1
       '@hapi/bourne': 3.0.0
-      '@hapi/hoek': 11.0.2
+      '@hapi/hoek': 11.0.4
 
   '@hexagon/base64@1.1.27': {}
 
-  '@humanwhocodes/config-array@0.11.13':
-    dependencies:
-      '@humanwhocodes/object-schema': 2.0.1
-      debug: 4.3.4(supports-color@8.1.1)
-      minimatch: 3.1.2
-    transitivePeerDependencies:
-      - supports-color
-
-  '@humanwhocodes/config-array@0.11.14':
-    dependencies:
-      '@humanwhocodes/object-schema': 2.0.2
-      debug: 4.3.4(supports-color@8.1.1)
-      minimatch: 3.1.2
-    transitivePeerDependencies:
-      - supports-color
-
   '@humanwhocodes/module-importer@1.0.1': {}
 
   '@humanwhocodes/momoa@2.0.4': {}
 
-  '@humanwhocodes/object-schema@2.0.1': {}
+  '@humanwhocodes/retry@0.3.0': {}
 
-  '@humanwhocodes/object-schema@2.0.2': {}
-
-  '@img/sharp-darwin-arm64@0.33.3':
+  '@img/sharp-darwin-arm64@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-darwin-arm64': 1.0.2
     optional: true
 
-  '@img/sharp-darwin-x64@0.33.3':
+  '@img/sharp-darwin-x64@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-darwin-x64': 1.0.2
     optional: true
@@ -13390,45 +14214,45 @@ snapshots:
   '@img/sharp-libvips-linuxmusl-x64@1.0.2':
     optional: true
 
-  '@img/sharp-linux-arm64@0.33.3':
+  '@img/sharp-linux-arm64@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-linux-arm64': 1.0.2
     optional: true
 
-  '@img/sharp-linux-arm@0.33.3':
+  '@img/sharp-linux-arm@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-linux-arm': 1.0.2
     optional: true
 
-  '@img/sharp-linux-s390x@0.33.3':
+  '@img/sharp-linux-s390x@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-linux-s390x': 1.0.2
     optional: true
 
-  '@img/sharp-linux-x64@0.33.3':
+  '@img/sharp-linux-x64@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-linux-x64': 1.0.2
     optional: true
 
-  '@img/sharp-linuxmusl-arm64@0.33.3':
+  '@img/sharp-linuxmusl-arm64@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-linuxmusl-arm64': 1.0.2
     optional: true
 
-  '@img/sharp-linuxmusl-x64@0.33.3':
+  '@img/sharp-linuxmusl-x64@0.33.4':
     optionalDependencies:
       '@img/sharp-libvips-linuxmusl-x64': 1.0.2
     optional: true
 
-  '@img/sharp-wasm32@0.33.3':
+  '@img/sharp-wasm32@0.33.4':
     dependencies:
       '@emnapi/runtime': 1.1.1
     optional: true
 
-  '@img/sharp-win32-ia32@0.33.3':
+  '@img/sharp-win32-ia32@0.33.4':
     optional: true
 
-  '@img/sharp-win32-x64@0.33.3':
+  '@img/sharp-win32-x64@0.33.4':
     optional: true
 
   '@inquirer/confirm@3.1.6':
@@ -13441,7 +14265,7 @@ snapshots:
       '@inquirer/figures': 1.0.1
       '@inquirer/type': 1.3.1
       '@types/mute-stream': 0.0.4
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       '@types/wrap-ansi': 3.0.0
       ansi-escapes: 4.3.2
       chalk: 4.1.2
@@ -13464,7 +14288,7 @@ snapshots:
   '@intlify/message-compiler@9.13.1':
     dependencies:
       '@intlify/shared': 9.13.1
-      source-map-js: 1.0.2
+      source-map-js: 1.2.0
 
   '@intlify/shared@9.13.1': {}
 
@@ -13492,7 +14316,7 @@ snapshots:
   '@jest/console@29.7.0':
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -13505,14 +14329,14 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.7.1
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.12.7)
+      jest-config: 29.7.0(@types/node@20.14.9)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -13524,7 +14348,7 @@ snapshots:
       jest-util: 29.7.0
       jest-validate: 29.7.0
       jest-watcher: 29.7.0
-      micromatch: 4.0.5
+      micromatch: 4.0.7
       pretty-format: 29.7.0
       slash: 3.0.0
       strip-ansi: 6.0.1
@@ -13541,7 +14365,7 @@ snapshots:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       jest-mock: 29.7.0
 
   '@jest/expect-utils@29.7.0':
@@ -13559,7 +14383,7 @@ snapshots:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -13581,7 +14405,7 @@ snapshots:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -13639,7 +14463,7 @@ snapshots:
       jest-haste-map: 29.7.0
       jest-regex-util: 29.6.3
       jest-util: 29.7.0
-      micromatch: 4.0.5
+      micromatch: 4.0.7
       pirates: 4.0.5
       slash: 3.0.0
       write-file-atomic: 4.0.2
@@ -13651,19 +14475,19 @@ snapshots:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       '@types/yargs': 17.0.19
       chalk: 4.1.2
 
-  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
+  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))':
     dependencies:
       glob: 7.2.3
       glob-promise: 4.2.2(glob@7.2.3)
       magic-string: 0.27.0
-      react-docgen-typescript: 2.2.2(typescript@5.5.2)
-      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      react-docgen-typescript: 2.2.2(typescript@5.5.3)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
 
   '@jridgewell/gen-mapping@0.3.2':
     dependencies:
@@ -13671,14 +14495,22 @@ snapshots:
       '@jridgewell/sourcemap-codec': 1.4.15
       '@jridgewell/trace-mapping': 0.3.18
 
+  '@jridgewell/gen-mapping@0.3.5':
+    dependencies:
+      '@jridgewell/set-array': 1.2.1
+      '@jridgewell/sourcemap-codec': 1.4.15
+      '@jridgewell/trace-mapping': 0.3.25
+
   '@jridgewell/resolve-uri@3.1.0': {}
 
   '@jridgewell/set-array@1.1.2': {}
 
+  '@jridgewell/set-array@1.2.1': {}
+
   '@jridgewell/source-map@0.3.5':
     dependencies:
-      '@jridgewell/gen-mapping': 0.3.2
-      '@jridgewell/trace-mapping': 0.3.18
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
 
   '@jridgewell/sourcemap-codec@1.4.14': {}
 
@@ -13689,6 +14521,11 @@ snapshots:
       '@jridgewell/resolve-uri': 3.1.0
       '@jridgewell/sourcemap-codec': 1.4.14
 
+  '@jridgewell/trace-mapping@0.3.25':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.0
+      '@jridgewell/sourcemap-codec': 1.4.15
+
   '@jsdevtools/ono@7.1.3': {}
 
   '@kurkle/color@0.3.2': {}
@@ -13727,23 +14564,23 @@ snapshots:
       '@types/react': 18.0.28
       react: 18.3.1
 
-  '@microsoft/api-extractor-model@7.28.14(@types/node@20.12.7)':
+  '@microsoft/api-extractor-model@7.29.2(@types/node@20.14.9)':
     dependencies:
-      '@microsoft/tsdoc': 0.14.2
-      '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 4.1.0(@types/node@20.12.7)
+      '@microsoft/tsdoc': 0.15.0
+      '@microsoft/tsdoc-config': 0.17.0
+      '@rushstack/node-core-library': 5.4.1(@types/node@20.14.9)
     transitivePeerDependencies:
       - '@types/node'
 
-  '@microsoft/api-extractor@7.43.1(@types/node@20.12.7)':
+  '@microsoft/api-extractor@7.47.0(@types/node@20.14.9)':
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.14(@types/node@20.12.7)
-      '@microsoft/tsdoc': 0.14.2
-      '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 4.1.0(@types/node@20.12.7)
+      '@microsoft/api-extractor-model': 7.29.2(@types/node@20.14.9)
+      '@microsoft/tsdoc': 0.15.0
+      '@microsoft/tsdoc-config': 0.17.0
+      '@rushstack/node-core-library': 5.4.1(@types/node@20.14.9)
       '@rushstack/rig-package': 0.5.2
-      '@rushstack/terminal': 0.10.1(@types/node@20.12.7)
-      '@rushstack/ts-command-line': 4.19.2(@types/node@20.12.7)
+      '@rushstack/terminal': 0.13.0(@types/node@20.14.9)
+      '@rushstack/ts-command-line': 4.22.0(@types/node@20.14.9)
       lodash: 4.17.21
       minimatch: 3.0.8
       resolve: 1.22.8
@@ -13753,43 +14590,31 @@ snapshots:
     transitivePeerDependencies:
       - '@types/node'
 
-  '@microsoft/tsdoc-config@0.16.2':
+  '@microsoft/tsdoc-config@0.17.0':
     dependencies:
-      '@microsoft/tsdoc': 0.14.2
-      ajv: 6.12.6
+      '@microsoft/tsdoc': 0.15.0
+      ajv: 8.12.0
       jju: 1.4.0
-      resolve: 1.19.0
+      resolve: 1.22.8
 
-  '@microsoft/tsdoc@0.14.2': {}
+  '@microsoft/tsdoc@0.15.0': {}
 
   '@misskey-dev/browser-image-resizer@2024.1.0': {}
 
-  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3))(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0))(eslint@8.53.0)':
+  '@misskey-dev/eslint-plugin@2.0.2(@eslint/compat@1.1.0)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)':
     dependencies:
-      '@typescript-eslint/eslint-plugin': 6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      eslint: 8.53.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)
-
-  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0))(eslint@8.57.0)':
-    dependencies:
-      '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      eslint: 8.57.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)
-
-  '@misskey-dev/eslint-plugin@1.0.0(@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)':
-    dependencies:
-      '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
-      eslint: 8.57.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)
+      '@eslint/compat': 1.1.0
+      '@typescript-eslint/eslint-plugin': 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
+      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      eslint: 9.6.0
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
+      globals: 15.7.0
 
   '@misskey-dev/sharp-read-bmp@1.2.0':
     dependencies:
       decode-bmp: 0.2.1
       decode-ico: 0.4.1
-      sharp: 0.33.3
+      sharp: 0.33.4
 
   '@misskey-dev/summaly@5.1.0':
     dependencies:
@@ -13833,7 +14658,7 @@ snapshots:
 
   '@mswjs/cookies@1.1.0': {}
 
-  '@mswjs/interceptors@0.26.15':
+  '@mswjs/interceptors@0.29.1':
     dependencies:
       '@open-draft/deferred-promise': 2.2.0
       '@open-draft/logger': 0.3.0
@@ -13842,44 +14667,44 @@ snapshots:
       outvariant: 1.4.2
       strict-event-emitter: 0.5.1
 
-  '@napi-rs/canvas-android-arm64@0.1.52':
+  '@napi-rs/canvas-android-arm64@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-darwin-arm64@0.1.52':
+  '@napi-rs/canvas-darwin-arm64@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-darwin-x64@0.1.52':
+  '@napi-rs/canvas-darwin-x64@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-linux-arm-gnueabihf@0.1.52':
+  '@napi-rs/canvas-linux-arm-gnueabihf@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-linux-arm64-gnu@0.1.52':
+  '@napi-rs/canvas-linux-arm64-gnu@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-linux-arm64-musl@0.1.52':
+  '@napi-rs/canvas-linux-arm64-musl@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-linux-x64-gnu@0.1.52':
+  '@napi-rs/canvas-linux-x64-gnu@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-linux-x64-musl@0.1.52':
+  '@napi-rs/canvas-linux-x64-musl@0.1.53':
     optional: true
 
-  '@napi-rs/canvas-win32-x64-msvc@0.1.52':
+  '@napi-rs/canvas-win32-x64-msvc@0.1.53':
     optional: true
 
-  '@napi-rs/canvas@0.1.52':
+  '@napi-rs/canvas@0.1.53':
     optionalDependencies:
-      '@napi-rs/canvas-android-arm64': 0.1.52
-      '@napi-rs/canvas-darwin-arm64': 0.1.52
-      '@napi-rs/canvas-darwin-x64': 0.1.52
-      '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.52
-      '@napi-rs/canvas-linux-arm64-gnu': 0.1.52
-      '@napi-rs/canvas-linux-arm64-musl': 0.1.52
-      '@napi-rs/canvas-linux-x64-gnu': 0.1.52
-      '@napi-rs/canvas-linux-x64-musl': 0.1.52
-      '@napi-rs/canvas-win32-x64-msvc': 0.1.52
+      '@napi-rs/canvas-android-arm64': 0.1.53
+      '@napi-rs/canvas-darwin-arm64': 0.1.53
+      '@napi-rs/canvas-darwin-x64': 0.1.53
+      '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.53
+      '@napi-rs/canvas-linux-arm64-gnu': 0.1.53
+      '@napi-rs/canvas-linux-arm64-musl': 0.1.53
+      '@napi-rs/canvas-linux-x64-gnu': 0.1.53
+      '@napi-rs/canvas-linux-x64-musl': 0.1.53
+      '@napi-rs/canvas-win32-x64-msvc': 0.1.53
 
   '@ndelangen/get-tarball@3.0.7':
     dependencies:
@@ -13887,49 +14712,51 @@ snapshots:
       pump: 3.0.0
       tar-fs: 2.1.1
 
-  '@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)':
+  '@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1)':
     dependencies:
       iterare: 1.2.1
       reflect-metadata: 0.2.2
       rxjs: 7.8.1
-      tslib: 2.6.2
+      tslib: 2.6.3
       uid: 2.0.2
 
-  '@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)':
+  '@nestjs/core@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)':
     dependencies:
-      '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/common': 10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1)
       '@nuxtjs/opencollective': 0.3.2(encoding@0.1.13)
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
       path-to-regexp: 3.2.0
       reflect-metadata: 0.2.2
       rxjs: 7.8.1
-      tslib: 2.6.2
+      tslib: 2.6.3
       uid: 2.0.2
     optionalDependencies:
-      '@nestjs/platform-express': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)
+      '@nestjs/platform-express': 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10)
     transitivePeerDependencies:
       - encoding
 
-  '@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)':
+  '@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10)':
     dependencies:
-      '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
-      '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/common': 10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/core': 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
       body-parser: 1.20.2
       cors: 2.8.5
       express: 4.19.2
       multer: 1.4.4-lts.1
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
       - supports-color
 
-  '@nestjs/testing@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8))':
+  '@nestjs/testing@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10))':
     dependencies:
-      '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1)
-      '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
-      tslib: 2.6.2
+      '@nestjs/common': 10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      '@nestjs/core': 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)
+      tslib: 2.6.3
     optionalDependencies:
-      '@nestjs/platform-express': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.8)
+      '@nestjs/platform-express': 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10)
+
+  '@noble/hashes@1.4.0': {}
 
   '@nodelib/fs.scandir@2.1.5':
     dependencies:
@@ -13941,7 +14768,7 @@ snapshots:
   '@nodelib/fs.walk@1.2.8':
     dependencies:
       '@nodelib/fs.scandir': 2.1.5
-      fastq: 1.15.0
+      fastq: 1.17.1
 
   '@npmcli/agent@2.2.0':
     dependencies:
@@ -13990,153 +14817,167 @@ snapshots:
 
   '@open-draft/until@2.1.0': {}
 
-  '@opentelemetry/api-logs@0.51.1':
+  '@opentelemetry/api-logs@0.52.1':
     dependencies:
-      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/api': 1.9.0
 
-  '@opentelemetry/api@1.8.0': {}
+  '@opentelemetry/api@1.9.0': {}
 
-  '@opentelemetry/context-async-hooks@1.24.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/api': 1.9.0
 
-  '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/api': 1.9.0
       '@opentelemetry/semantic-conventions': 1.24.1
 
-  '@opentelemetry/instrumentation-connect@0.36.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/semantic-conventions': 1.25.1
+
+  '@opentelemetry/instrumentation-connect@0.37.0(@opentelemetry/api@1.9.0)':
+    dependencies:
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
       '@types/connect': 3.4.36
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-express@0.39.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-express@0.40.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-fastify@0.36.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-fastify@0.37.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-graphql@0.40.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-graphql@0.41.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-hapi@0.38.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-hapi@0.39.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-http@0.51.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
       semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-ioredis@0.40.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-ioredis@0.41.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/redis-common': 0.36.2
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-koa@0.40.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-koa@0.41.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
       '@types/koa': 2.14.0
       '@types/koa__router': 12.0.3
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mongodb@0.43.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-mongodb@0.45.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mongoose@0.38.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-mongoose@0.39.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mysql2@0.38.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-mysql2@0.39.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
-      '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
+      '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mysql@0.38.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-mysql@0.39.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
       '@types/mysql': 2.15.22
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-nestjs-core@0.37.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-nestjs-core@0.38.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-pg@0.41.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-pg@0.42.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
-      '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
+      '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0)
       '@types/pg': 8.6.1
       '@types/pg-pool': 2.0.4
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation-redis-4@0.40.0(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/redis-common': 0.36.2
+      '@opentelemetry/semantic-conventions': 1.25.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.9.0)':
+    dependencies:
+      '@opentelemetry/api': 1.9.0
       '@types/shimmer': 1.0.5
       import-in-the-middle: 1.4.2
       require-in-the-middle: 7.3.0
@@ -14146,12 +14987,12 @@ snapshots:
       - supports-color
     optional: true
 
-  '@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/api-logs': 0.51.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/api-logs': 0.52.1
       '@types/shimmer': 1.0.5
-      import-in-the-middle: 1.7.4
+      import-in-the-middle: 1.8.1
       require-in-the-middle: 7.3.0
       semver: 7.6.0
       shimmer: 1.2.1
@@ -14160,32 +15001,40 @@ snapshots:
 
   '@opentelemetry/redis-common@0.36.2': {}
 
-  '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/semantic-conventions': 1.24.1
 
-  '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
+
+  '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0)':
+    dependencies:
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.9.0)
       lodash.merge: 4.6.2
 
-  '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
 
   '@opentelemetry/semantic-conventions@1.24.1': {}
 
-  '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.8.0)':
+  '@opentelemetry/semantic-conventions@1.25.1': {}
+
+  '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
 
   '@peculiar/asn1-android@2.3.10':
     dependencies:
@@ -14230,35 +15079,149 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
-  '@prisma/instrumentation@5.14.0':
+  '@prisma/instrumentation@5.16.0':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.28)(react@18.3.1)':
+  '@radix-ui/primitive@1.1.0': {}
+
+  '@radix-ui/react-compose-refs@1.1.0(@types/react@18.0.28)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.23.4
       react: 18.3.1
     optionalDependencies:
       '@types/react': 18.0.28
 
-  '@radix-ui/react-slot@1.0.2(@types/react@18.0.28)(react@18.3.1)':
+  '@radix-ui/react-context@1.1.0(@types/react@18.0.28)(react@18.3.1)':
     dependencies:
-      '@babel/runtime': 7.23.4
-      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.0.28)(react@18.3.1)
       react: 18.3.1
     optionalDependencies:
       '@types/react': 18.0.28
 
-  '@readme/better-ajv-errors@1.6.0(ajv@8.13.0)':
+  '@radix-ui/react-dialog@1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+    dependencies:
+      '@radix-ui/primitive': 1.1.0
+      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-context': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-dismissable-layer': 1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-focus-scope': 1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-id': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-portal': 1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-presence': 1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-slot': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      aria-hidden: 1.2.4
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+      react-remove-scroll: 2.5.7(@types/react@18.0.28)(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-dismissable-layer@1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+    dependencies:
+      '@radix-ui/primitive': 1.1.0
+      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-focus-guards@1.1.0(@types/react@18.0.28)(react@18.3.1)':
+    dependencies:
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-focus-scope@1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-id@1.1.0(@types/react@18.0.28)(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-portal@1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-presence@1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-primitive@2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-slot': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-slot@1.1.0(@types/react@18.0.28)(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.0.28)(react@18.3.1)':
+    dependencies:
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.0.28)(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.0.28)(react@18.3.1)':
+    dependencies:
+      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.0.28)(react@18.3.1)':
+    dependencies:
+      react: 18.3.1
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  '@readme/better-ajv-errors@1.6.0(ajv@8.16.0)':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/runtime': 7.23.4
       '@humanwhocodes/momoa': 2.0.4
-      ajv: 8.13.0
+      ajv: 8.16.0
       chalk: 4.1.2
       json-to-ast: 2.1.0
       jsonpointer: 5.0.1
@@ -14276,181 +15239,186 @@ snapshots:
       '@apidevtools/openapi-schemas': 2.1.0
       '@apidevtools/swagger-methods': 3.0.2
       '@jsdevtools/ono': 7.1.3
-      '@readme/better-ajv-errors': 1.6.0(ajv@8.13.0)
+      '@readme/better-ajv-errors': 1.6.0(ajv@8.16.0)
       '@readme/json-schema-ref-parser': 1.2.0
-      ajv: 8.13.0
-      ajv-draft-04: 1.0.0(ajv@8.13.0)
+      ajv: 8.16.0
+      ajv-draft-04: 1.0.0(ajv@8.16.0)
       call-me-maybe: 1.0.2
       openapi-types: 12.1.3
 
-  '@rollup/plugin-json@6.1.0(rollup@4.17.2)':
+  '@rollup/plugin-json@6.1.0(rollup@4.18.0)':
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
+      '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
     optionalDependencies:
-      rollup: 4.17.2
+      rollup: 4.18.0
 
-  '@rollup/plugin-replace@5.0.5(rollup@4.17.2)':
+  '@rollup/plugin-replace@5.0.7(rollup@4.18.0)':
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
-      magic-string: 0.30.7
+      '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
+      magic-string: 0.30.10
     optionalDependencies:
-      rollup: 4.17.2
+      rollup: 4.18.0
 
-  '@rollup/pluginutils@5.1.0(rollup@4.17.2)':
+  '@rollup/pluginutils@5.1.0(rollup@4.18.0)':
     dependencies:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
     optionalDependencies:
-      rollup: 4.17.2
+      rollup: 4.18.0
 
-  '@rollup/rollup-android-arm-eabi@4.17.2':
+  '@rollup/rollup-android-arm-eabi@4.18.0':
     optional: true
 
-  '@rollup/rollup-android-arm64@4.17.2':
+  '@rollup/rollup-android-arm64@4.18.0':
     optional: true
 
-  '@rollup/rollup-darwin-arm64@4.17.2':
+  '@rollup/rollup-darwin-arm64@4.18.0':
     optional: true
 
-  '@rollup/rollup-darwin-x64@4.17.2':
+  '@rollup/rollup-darwin-x64@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.17.2':
+  '@rollup/rollup-linux-arm-gnueabihf@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-arm-musleabihf@4.17.2':
+  '@rollup/rollup-linux-arm-musleabihf@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-arm64-gnu@4.17.2':
+  '@rollup/rollup-linux-arm64-gnu@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-arm64-musl@4.17.2':
+  '@rollup/rollup-linux-arm64-musl@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.17.2':
+  '@rollup/rollup-linux-powerpc64le-gnu@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-riscv64-gnu@4.17.2':
+  '@rollup/rollup-linux-riscv64-gnu@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-s390x-gnu@4.17.2':
+  '@rollup/rollup-linux-s390x-gnu@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-x64-gnu@4.17.2':
+  '@rollup/rollup-linux-x64-gnu@4.18.0':
     optional: true
 
-  '@rollup/rollup-linux-x64-musl@4.17.2':
+  '@rollup/rollup-linux-x64-musl@4.18.0':
     optional: true
 
-  '@rollup/rollup-win32-arm64-msvc@4.17.2':
+  '@rollup/rollup-win32-arm64-msvc@4.18.0':
     optional: true
 
-  '@rollup/rollup-win32-ia32-msvc@4.17.2':
+  '@rollup/rollup-win32-ia32-msvc@4.18.0':
     optional: true
 
-  '@rollup/rollup-win32-x64-msvc@4.17.2':
+  '@rollup/rollup-win32-x64-msvc@4.18.0':
     optional: true
 
-  '@rushstack/node-core-library@4.1.0(@types/node@20.12.7)':
+  '@rushstack/node-core-library@5.4.1(@types/node@20.14.9)':
     dependencies:
+      ajv: 8.13.0
+      ajv-draft-04: 1.0.0(ajv@8.13.0)
+      ajv-formats: 3.0.1(ajv@8.13.0)
       fs-extra: 7.0.1
       import-lazy: 4.0.0
       jju: 1.4.0
       resolve: 1.22.8
       semver: 7.5.4
-      z-schema: 5.0.5
     optionalDependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@rushstack/rig-package@0.5.2':
     dependencies:
       resolve: 1.22.8
       strip-json-comments: 3.1.1
 
-  '@rushstack/terminal@0.10.1(@types/node@20.12.7)':
+  '@rushstack/terminal@0.13.0(@types/node@20.14.9)':
     dependencies:
-      '@rushstack/node-core-library': 4.1.0(@types/node@20.12.7)
+      '@rushstack/node-core-library': 5.4.1(@types/node@20.14.9)
       supports-color: 8.1.1
     optionalDependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
-  '@rushstack/ts-command-line@4.19.2(@types/node@20.12.7)':
+  '@rushstack/ts-command-line@4.22.0(@types/node@20.14.9)':
     dependencies:
-      '@rushstack/terminal': 0.10.1(@types/node@20.12.7)
+      '@rushstack/terminal': 0.13.0(@types/node@20.14.9)
       '@types/argparse': 1.0.38
       argparse: 1.0.10
       string-argv: 0.3.1
     transitivePeerDependencies:
       - '@types/node'
 
-  '@sentry/core@8.5.0':
-    dependencies:
-      '@sentry/types': 8.5.0
-      '@sentry/utils': 8.5.0
+  '@sec-ant/readable-stream@0.4.1': {}
 
-  '@sentry/node@8.5.0':
+  '@sentry/core@8.13.0':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/context-async-hooks': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-connect': 0.36.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-express': 0.39.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-fastify': 0.36.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-graphql': 0.40.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-hapi': 0.38.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-http': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-ioredis': 0.40.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-koa': 0.40.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-mongodb': 0.43.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-mongoose': 0.38.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-mysql': 0.38.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-mysql2': 0.38.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-nestjs-core': 0.37.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation-pg': 0.41.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
-      '@prisma/instrumentation': 5.14.0
-      '@sentry/core': 8.5.0
-      '@sentry/opentelemetry': 8.5.0(@opentelemetry/api@1.8.0)(@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.24.1)
-      '@sentry/types': 8.5.0
-      '@sentry/utils': 8.5.0
+      '@sentry/types': 8.13.0
+      '@sentry/utils': 8.13.0
+
+  '@sentry/node@8.13.0':
+    dependencies:
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-connect': 0.37.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-express': 0.40.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-fastify': 0.37.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-graphql': 0.41.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-hapi': 0.39.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-ioredis': 0.41.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-koa': 0.41.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mongodb': 0.45.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mongoose': 0.39.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mysql': 0.39.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mysql2': 0.39.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-nestjs-core': 0.38.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-pg': 0.42.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-redis-4': 0.40.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
+      '@prisma/instrumentation': 5.16.0
+      '@sentry/core': 8.13.0
+      '@sentry/opentelemetry': 8.13.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)
+      '@sentry/types': 8.13.0
+      '@sentry/utils': 8.13.0
     optionalDependencies:
       opentelemetry-instrumentation-fetch-node: 1.2.0
     transitivePeerDependencies:
       - supports-color
 
-  '@sentry/opentelemetry@8.5.0(@opentelemetry/api@1.8.0)(@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.24.1)':
+  '@sentry/opentelemetry@8.13.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)':
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
-      '@sentry/core': 8.5.0
-      '@sentry/types': 8.5.0
-      '@sentry/utils': 8.5.0
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
+      '@sentry/core': 8.13.0
+      '@sentry/types': 8.13.0
+      '@sentry/utils': 8.13.0
 
-  '@sentry/profiling-node@8.5.0':
+  '@sentry/profiling-node@8.13.0':
     dependencies:
-      '@sentry/core': 8.5.0
-      '@sentry/node': 8.5.0
-      '@sentry/types': 8.5.0
-      '@sentry/utils': 8.5.0
+      '@sentry/core': 8.13.0
+      '@sentry/node': 8.13.0
+      '@sentry/types': 8.13.0
+      '@sentry/utils': 8.13.0
       detect-libc: 2.0.3
       node-abi: 3.62.0
     transitivePeerDependencies:
       - supports-color
 
-  '@sentry/types@8.5.0': {}
+  '@sentry/types@8.13.0': {}
 
-  '@sentry/utils@8.5.0':
+  '@sentry/utils@8.13.0':
     dependencies:
-      '@sentry/types': 8.5.0
+      '@sentry/types': 8.13.0
 
-  '@shikijs/core@1.4.0': {}
+  '@shikijs/core@1.10.0': {}
 
   '@sideway/address@4.1.4':
     dependencies:
@@ -14482,7 +15450,11 @@ snapshots:
 
   '@sindresorhus/is@5.3.0': {}
 
-  '@sindresorhus/is@6.1.0': {}
+  '@sindresorhus/is@6.3.1': {}
+
+  '@sindresorhus/merge-streams@2.3.0': {}
+
+  '@sindresorhus/merge-streams@4.0.0': {}
 
   '@sinonjs/commons@2.0.0':
     dependencies:
@@ -14508,154 +15480,172 @@ snapshots:
 
   '@sinonjs/text-encoding@0.7.2': {}
 
-  '@smithy/abort-controller@2.0.14':
-    dependencies:
-      '@smithy/types': 2.6.0
-      tslib: 2.6.2
-
   '@smithy/abort-controller@2.2.0':
     dependencies:
       '@smithy/types': 2.12.0
       tslib: 2.6.2
 
-  '@smithy/chunked-blob-reader-native@2.0.0':
+  '@smithy/abort-controller@3.1.1':
     dependencies:
-      '@smithy/util-base64': 2.0.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/chunked-blob-reader@2.0.0':
+  '@smithy/chunked-blob-reader-native@3.0.0':
+    dependencies:
+      '@smithy/util-base64': 3.0.0
+      tslib: 2.6.2
+
+  '@smithy/chunked-blob-reader@3.0.0':
     dependencies:
       tslib: 2.6.2
 
-  '@smithy/config-resolver@2.0.9':
+  '@smithy/config-resolver@3.0.4':
     dependencies:
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/types': 2.6.0
-      '@smithy/util-config-provider': 2.0.0
-      '@smithy/util-middleware': 2.0.1
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/types': 3.3.0
+      '@smithy/util-config-provider': 3.0.0
+      '@smithy/util-middleware': 3.0.3
       tslib: 2.6.2
 
-  '@smithy/credential-provider-imds@2.0.11':
+  '@smithy/core@2.2.4':
     dependencies:
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/property-provider': 2.0.9
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
+      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-serde': 3.0.3
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/util-middleware': 3.0.3
       tslib: 2.6.2
 
-  '@smithy/eventstream-codec@2.0.8':
+  '@smithy/credential-provider-imds@3.1.3':
     dependencies:
-      '@aws-crypto/crc32': 3.0.0
-      '@smithy/types': 2.6.0
-      '@smithy/util-hex-encoding': 2.0.0
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/property-provider': 3.1.3
+      '@smithy/types': 3.3.0
+      '@smithy/url-parser': 3.0.3
       tslib: 2.6.2
 
-  '@smithy/eventstream-serde-browser@2.0.8':
+  '@smithy/eventstream-codec@3.1.2':
     dependencies:
-      '@smithy/eventstream-serde-universal': 2.0.8
-      '@smithy/types': 2.6.0
+      '@aws-crypto/crc32': 5.2.0
+      '@smithy/types': 3.3.0
+      '@smithy/util-hex-encoding': 3.0.0
       tslib: 2.6.2
 
-  '@smithy/eventstream-serde-config-resolver@2.0.8':
+  '@smithy/eventstream-serde-browser@3.0.4':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/eventstream-serde-universal': 3.0.4
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/eventstream-serde-node@2.0.8':
+  '@smithy/eventstream-serde-config-resolver@3.0.3':
     dependencies:
-      '@smithy/eventstream-serde-universal': 2.0.8
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/eventstream-serde-universal@2.0.8':
+  '@smithy/eventstream-serde-node@3.0.4':
     dependencies:
-      '@smithy/eventstream-codec': 2.0.8
-      '@smithy/types': 2.6.0
+      '@smithy/eventstream-serde-universal': 3.0.4
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/fetch-http-handler@2.1.4':
+  '@smithy/eventstream-serde-universal@3.0.4':
     dependencies:
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/querystring-builder': 2.0.14
-      '@smithy/types': 2.6.0
-      '@smithy/util-base64': 2.0.0
+      '@smithy/eventstream-codec': 3.1.2
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/hash-blob-browser@2.0.8':
+  '@smithy/fetch-http-handler@3.2.0':
     dependencies:
-      '@smithy/chunked-blob-reader': 2.0.0
-      '@smithy/chunked-blob-reader-native': 2.0.0
-      '@smithy/types': 2.6.0
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/querystring-builder': 3.0.3
+      '@smithy/types': 3.3.0
+      '@smithy/util-base64': 3.0.0
       tslib: 2.6.2
 
-  '@smithy/hash-node@2.0.8':
+  '@smithy/hash-blob-browser@3.1.2':
     dependencies:
-      '@smithy/types': 2.6.0
-      '@smithy/util-buffer-from': 2.0.0
-      '@smithy/util-utf8': 2.0.0
+      '@smithy/chunked-blob-reader': 3.0.0
+      '@smithy/chunked-blob-reader-native': 3.0.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/hash-stream-node@2.0.8':
+  '@smithy/hash-node@3.0.3':
     dependencies:
-      '@smithy/types': 2.6.0
-      '@smithy/util-utf8': 2.0.0
+      '@smithy/types': 3.3.0
+      '@smithy/util-buffer-from': 3.0.0
+      '@smithy/util-utf8': 3.0.0
       tslib: 2.6.2
 
-  '@smithy/invalid-dependency@2.0.8':
+  '@smithy/hash-stream-node@3.1.2':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
+      '@smithy/util-utf8': 3.0.0
+      tslib: 2.6.2
+
+  '@smithy/invalid-dependency@3.0.3':
+    dependencies:
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
   '@smithy/is-array-buffer@2.0.0':
     dependencies:
       tslib: 2.6.2
 
-  '@smithy/md5-js@2.0.8':
+  '@smithy/is-array-buffer@3.0.0':
     dependencies:
-      '@smithy/types': 2.6.0
-      '@smithy/util-utf8': 2.0.0
       tslib: 2.6.2
 
-  '@smithy/middleware-content-length@2.0.10':
+  '@smithy/md5-js@3.0.3':
     dependencies:
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
+      '@smithy/util-utf8': 3.0.0
       tslib: 2.6.2
 
-  '@smithy/middleware-endpoint@2.0.8':
+  '@smithy/middleware-content-length@3.0.3':
     dependencies:
-      '@smithy/middleware-serde': 2.0.8
-      '@smithy/types': 2.6.0
-      '@smithy/url-parser': 2.0.8
-      '@smithy/util-middleware': 2.0.1
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/middleware-retry@2.0.11':
+  '@smithy/middleware-endpoint@3.0.4':
     dependencies:
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/protocol-http': 3.0.10
-      '@smithy/service-error-classification': 2.0.1
-      '@smithy/types': 2.6.0
-      '@smithy/util-middleware': 2.0.1
-      '@smithy/util-retry': 2.0.1
-      tslib: 2.6.2
-      uuid: 8.3.2
-
-  '@smithy/middleware-serde@2.0.8':
-    dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/middleware-serde': 3.0.3
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/types': 3.3.0
+      '@smithy/url-parser': 3.0.3
+      '@smithy/util-middleware': 3.0.3
       tslib: 2.6.2
 
-  '@smithy/middleware-stack@2.0.1':
+  '@smithy/middleware-retry@3.0.7':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/service-error-classification': 3.0.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
+      '@smithy/util-middleware': 3.0.3
+      '@smithy/util-retry': 3.0.3
+      tslib: 2.6.2
+      uuid: 9.0.1
+
+  '@smithy/middleware-serde@3.0.3':
+    dependencies:
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/node-config-provider@2.0.11':
+  '@smithy/middleware-stack@3.0.3':
     dependencies:
-      '@smithy/property-provider': 2.0.9
-      '@smithy/shared-ini-file-loader': 2.0.10
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
+      tslib: 2.6.2
+
+  '@smithy/node-config-provider@3.1.3':
+    dependencies:
+      '@smithy/property-provider': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
   '@smithy/node-http-handler@2.5.0':
@@ -14666,14 +15656,17 @@ snapshots:
       '@smithy/types': 2.12.0
       tslib: 2.6.2
 
-  '@smithy/property-provider@2.0.9':
+  '@smithy/node-http-handler@3.1.1':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/abort-controller': 3.1.1
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/querystring-builder': 3.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/protocol-http@3.0.10':
+  '@smithy/property-provider@3.1.3':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
   '@smithy/protocol-http@3.3.0':
@@ -14681,10 +15674,9 @@ snapshots:
       '@smithy/types': 2.12.0
       tslib: 2.6.2
 
-  '@smithy/querystring-builder@2.0.14':
+  '@smithy/protocol-http@4.0.3':
     dependencies:
-      '@smithy/types': 2.6.0
-      '@smithy/util-uri-escape': 2.0.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
   '@smithy/querystring-builder@2.2.0':
@@ -14693,62 +15685,70 @@ snapshots:
       '@smithy/util-uri-escape': 2.2.0
       tslib: 2.6.2
 
-  '@smithy/querystring-parser@2.0.8':
+  '@smithy/querystring-builder@3.0.3':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
+      '@smithy/util-uri-escape': 3.0.0
       tslib: 2.6.2
 
-  '@smithy/service-error-classification@2.0.1':
+  '@smithy/querystring-parser@3.0.3':
     dependencies:
-      '@smithy/types': 2.6.0
-
-  '@smithy/shared-ini-file-loader@2.0.10':
-    dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/signature-v4@2.0.5':
+  '@smithy/service-error-classification@3.0.3':
     dependencies:
-      '@smithy/eventstream-codec': 2.0.8
-      '@smithy/is-array-buffer': 2.0.0
-      '@smithy/types': 2.6.0
-      '@smithy/util-hex-encoding': 2.0.0
-      '@smithy/util-middleware': 2.0.1
-      '@smithy/util-uri-escape': 2.0.0
-      '@smithy/util-utf8': 2.0.0
+      '@smithy/types': 3.3.0
+
+  '@smithy/shared-ini-file-loader@3.1.3':
+    dependencies:
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/smithy-client@2.1.5':
+  '@smithy/signature-v4@3.1.2':
     dependencies:
-      '@smithy/middleware-stack': 2.0.1
-      '@smithy/types': 2.6.0
-      '@smithy/util-stream': 2.0.11
+      '@smithy/is-array-buffer': 3.0.0
+      '@smithy/types': 3.3.0
+      '@smithy/util-hex-encoding': 3.0.0
+      '@smithy/util-middleware': 3.0.3
+      '@smithy/util-uri-escape': 3.0.0
+      '@smithy/util-utf8': 3.0.0
+      tslib: 2.6.2
+
+  '@smithy/smithy-client@3.1.5':
+    dependencies:
+      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/middleware-stack': 3.0.3
+      '@smithy/protocol-http': 4.0.3
+      '@smithy/types': 3.3.0
+      '@smithy/util-stream': 3.0.5
       tslib: 2.6.2
 
   '@smithy/types@2.12.0':
     dependencies:
       tslib: 2.6.2
 
-  '@smithy/types@2.6.0':
+  '@smithy/types@3.3.0':
     dependencies:
       tslib: 2.6.2
 
-  '@smithy/url-parser@2.0.8':
+  '@smithy/url-parser@3.0.3':
     dependencies:
-      '@smithy/querystring-parser': 2.0.8
-      '@smithy/types': 2.6.0
+      '@smithy/querystring-parser': 3.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/util-base64@2.0.0':
+  '@smithy/util-base64@3.0.0':
     dependencies:
-      '@smithy/util-buffer-from': 2.0.0
+      '@smithy/util-buffer-from': 3.0.0
+      '@smithy/util-utf8': 3.0.0
       tslib: 2.6.2
 
-  '@smithy/util-body-length-browser@2.0.0':
+  '@smithy/util-body-length-browser@3.0.0':
     dependencies:
       tslib: 2.6.2
 
-  '@smithy/util-body-length-node@2.1.0':
+  '@smithy/util-body-length-node@3.0.0':
     dependencies:
       tslib: 2.6.2
 
@@ -14757,117 +15757,136 @@ snapshots:
       '@smithy/is-array-buffer': 2.0.0
       tslib: 2.6.2
 
-  '@smithy/util-config-provider@2.0.0':
+  '@smithy/util-buffer-from@3.0.0':
+    dependencies:
+      '@smithy/is-array-buffer': 3.0.0
+      tslib: 2.6.2
+
+  '@smithy/util-config-provider@3.0.0':
     dependencies:
       tslib: 2.6.2
 
-  '@smithy/util-defaults-mode-browser@2.0.9':
+  '@smithy/util-defaults-mode-browser@3.0.7':
     dependencies:
-      '@smithy/property-provider': 2.0.9
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
+      '@smithy/property-provider': 3.1.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
       bowser: 2.11.0
       tslib: 2.6.2
 
-  '@smithy/util-defaults-mode-node@2.0.11':
+  '@smithy/util-defaults-mode-node@3.0.7':
     dependencies:
-      '@smithy/config-resolver': 2.0.9
-      '@smithy/credential-provider-imds': 2.0.11
-      '@smithy/node-config-provider': 2.0.11
-      '@smithy/property-provider': 2.0.9
-      '@smithy/smithy-client': 2.1.5
-      '@smithy/types': 2.6.0
+      '@smithy/config-resolver': 3.0.4
+      '@smithy/credential-provider-imds': 3.1.3
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/property-provider': 3.1.3
+      '@smithy/smithy-client': 3.1.5
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/util-hex-encoding@2.0.0':
+  '@smithy/util-endpoints@2.0.4':
+    dependencies:
+      '@smithy/node-config-provider': 3.1.3
+      '@smithy/types': 3.3.0
+      tslib: 2.6.2
+
+  '@smithy/util-hex-encoding@3.0.0':
     dependencies:
       tslib: 2.6.2
 
-  '@smithy/util-middleware@2.0.1':
+  '@smithy/util-middleware@3.0.3':
     dependencies:
-      '@smithy/types': 2.6.0
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/util-retry@2.0.1':
+  '@smithy/util-retry@3.0.3':
     dependencies:
-      '@smithy/service-error-classification': 2.0.1
-      '@smithy/types': 2.6.0
+      '@smithy/service-error-classification': 3.0.3
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
-  '@smithy/util-stream@2.0.11':
-    dependencies:
-      '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/node-http-handler': 2.5.0
-      '@smithy/types': 2.6.0
-      '@smithy/util-base64': 2.0.0
-      '@smithy/util-buffer-from': 2.0.0
-      '@smithy/util-hex-encoding': 2.0.0
-      '@smithy/util-utf8': 2.0.0
-      tslib: 2.6.2
-
-  '@smithy/util-uri-escape@2.0.0':
+  '@smithy/util-stream@3.0.5':
     dependencies:
+      '@smithy/fetch-http-handler': 3.2.0
+      '@smithy/node-http-handler': 3.1.1
+      '@smithy/types': 3.3.0
+      '@smithy/util-base64': 3.0.0
+      '@smithy/util-buffer-from': 3.0.0
+      '@smithy/util-hex-encoding': 3.0.0
+      '@smithy/util-utf8': 3.0.0
       tslib: 2.6.2
 
   '@smithy/util-uri-escape@2.2.0':
     dependencies:
       tslib: 2.6.2
 
+  '@smithy/util-uri-escape@3.0.0':
+    dependencies:
+      tslib: 2.6.2
+
   '@smithy/util-utf8@2.0.0':
     dependencies:
       '@smithy/util-buffer-from': 2.0.0
       tslib: 2.6.2
 
-  '@smithy/util-waiter@2.0.8':
+  '@smithy/util-utf8@3.0.0':
     dependencies:
-      '@smithy/abort-controller': 2.0.14
-      '@smithy/types': 2.6.0
+      '@smithy/util-buffer-from': 3.0.0
+      tslib: 2.6.2
+
+  '@smithy/util-waiter@3.1.2':
+    dependencies:
+      '@smithy/abort-controller': 3.1.1
+      '@smithy/types': 3.3.0
       tslib: 2.6.2
 
   '@sqltools/formatter@1.2.5': {}
 
-  '@storybook/addon-actions@8.0.9':
+  '@storybook/addon-actions@8.1.11':
     dependencies:
-      '@storybook/core-events': 8.0.9
+      '@storybook/core-events': 8.1.11
       '@storybook/global': 5.0.0
       '@types/uuid': 9.0.8
       dequal: 2.0.3
       polished: 4.2.2
       uuid: 9.0.1
 
-  '@storybook/addon-backgrounds@8.0.9':
+  '@storybook/addon-backgrounds@8.1.11':
     dependencies:
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       ts-dedent: 2.2.0
 
-  '@storybook/addon-controls@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/addon-controls@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/blocks': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      dequal: 2.0.3
       lodash: 4.17.21
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@types/react'
+      - '@types/react-dom'
       - encoding
+      - prettier
       - react
       - react-dom
       - supports-color
 
-  '@storybook/addon-docs@8.0.9(encoding@0.1.13)':
+  '@storybook/addon-docs@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
       '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.3.1)
-      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/client-logger': 8.0.9
-      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/csf-plugin': 8.0.9
-      '@storybook/csf-tools': 8.0.9
+      '@storybook/blocks': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/client-logger': 8.1.11
+      '@storybook/components': 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/csf-plugin': 8.1.11
+      '@storybook/csf-tools': 8.1.11
       '@storybook/global': 5.0.0
-      '@storybook/node-logger': 8.0.9
-      '@storybook/preview-api': 8.0.9
-      '@storybook/react-dom-shim': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.0.9
+      '@storybook/node-logger': 8.1.11
+      '@storybook/preview-api': 8.1.11
+      '@storybook/react-dom-shim': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/types': 8.1.11
       '@types/react': 18.0.28
       fs-extra: 11.1.1
       react: 18.3.1
@@ -14876,42 +15895,46 @@ snapshots:
       rehype-slug: 6.0.0
       ts-dedent: 2.2.0
     transitivePeerDependencies:
+      - '@types/react-dom'
       - encoding
+      - prettier
       - supports-color
 
-  '@storybook/addon-essentials@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/addon-essentials@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@storybook/addon-actions': 8.0.9
-      '@storybook/addon-backgrounds': 8.0.9
-      '@storybook/addon-controls': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/addon-docs': 8.0.9(encoding@0.1.13)
-      '@storybook/addon-highlight': 8.0.9
-      '@storybook/addon-measure': 8.0.9
-      '@storybook/addon-outline': 8.0.9
-      '@storybook/addon-toolbars': 8.0.9
-      '@storybook/addon-viewport': 8.0.9
-      '@storybook/core-common': 8.0.9(encoding@0.1.13)
-      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/node-logger': 8.0.9
-      '@storybook/preview-api': 8.0.9
+      '@storybook/addon-actions': 8.1.11
+      '@storybook/addon-backgrounds': 8.1.11
+      '@storybook/addon-controls': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/addon-docs': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/addon-highlight': 8.1.11
+      '@storybook/addon-measure': 8.1.11
+      '@storybook/addon-outline': 8.1.11
+      '@storybook/addon-toolbars': 8.1.11
+      '@storybook/addon-viewport': 8.1.11
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/node-logger': 8.1.11
+      '@storybook/preview-api': 8.1.11
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@types/react'
+      - '@types/react-dom'
       - encoding
+      - prettier
       - react
       - react-dom
       - supports-color
 
-  '@storybook/addon-highlight@8.0.9':
+  '@storybook/addon-highlight@8.1.11':
     dependencies:
       '@storybook/global': 5.0.0
 
-  '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/addon-interactions@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
     dependencies:
       '@storybook/global': 5.0.0
-      '@storybook/instrumenter': 8.0.9
-      '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
-      '@storybook/types': 8.0.9
+      '@storybook/instrumenter': 8.1.11
+      '@storybook/test': 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+      '@storybook/types': 8.1.11
       polished: 4.2.2
       ts-dedent: 2.2.0
     transitivePeerDependencies:
@@ -14921,58 +15944,58 @@ snapshots:
       - jest
       - vitest
 
-  '@storybook/addon-links@8.0.9(react@18.3.1)':
+  '@storybook/addon-links@8.1.11(react@18.3.1)':
     dependencies:
-      '@storybook/csf': 0.1.6
+      '@storybook/csf': 0.1.9
       '@storybook/global': 5.0.0
       ts-dedent: 2.2.0
     optionalDependencies:
       react: 18.3.1
 
-  '@storybook/addon-mdx-gfm@8.0.9':
+  '@storybook/addon-mdx-gfm@8.1.11':
     dependencies:
-      '@storybook/node-logger': 8.0.9
+      '@storybook/node-logger': 8.1.11
       remark-gfm: 4.0.0
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
 
-  '@storybook/addon-measure@8.0.9':
+  '@storybook/addon-measure@8.1.11':
     dependencies:
       '@storybook/global': 5.0.0
-      tiny-invariant: 1.3.1
+      tiny-invariant: 1.3.3
 
-  '@storybook/addon-outline@8.0.9':
+  '@storybook/addon-outline@8.1.11':
     dependencies:
       '@storybook/global': 5.0.0
       ts-dedent: 2.2.0
 
-  '@storybook/addon-storysource@8.0.9':
+  '@storybook/addon-storysource@8.1.11':
     dependencies:
-      '@storybook/source-loader': 8.0.9
+      '@storybook/source-loader': 8.1.11
       estraverse: 5.3.0
-      tiny-invariant: 1.3.1
+      tiny-invariant: 1.3.3
 
-  '@storybook/addon-toolbars@8.0.9': {}
+  '@storybook/addon-toolbars@8.1.11': {}
 
-  '@storybook/addon-viewport@8.0.9':
+  '@storybook/addon-viewport@8.1.11':
     dependencies:
       memoizerific: 1.11.3
 
-  '@storybook/blocks@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/blocks@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@storybook/channels': 8.0.9
-      '@storybook/client-logger': 8.0.9
-      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/core-events': 8.0.9
-      '@storybook/csf': 0.1.6
-      '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
+      '@storybook/channels': 8.1.11
+      '@storybook/client-logger': 8.1.11
+      '@storybook/components': 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/core-events': 8.1.11
+      '@storybook/csf': 0.1.9
+      '@storybook/docs-tools': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/preview-api': 8.0.9
-      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.0.9
+      '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/preview-api': 8.1.11
+      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/types': 8.1.11
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
@@ -14990,20 +16013,22 @@ snapshots:
       react-dom: 18.3.1(react@18.3.1)
     transitivePeerDependencies:
       - '@types/react'
+      - '@types/react-dom'
       - encoding
+      - prettier
       - supports-color
 
-  '@storybook/builder-manager@8.0.9(encoding@0.1.13)':
+  '@storybook/builder-manager@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 8.0.9(encoding@0.1.13)
-      '@storybook/manager': 8.0.9
-      '@storybook/node-logger': 8.0.9
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/manager': 8.1.11
+      '@storybook/node-logger': 8.1.11
       '@types/ejs': 3.1.2
-      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.20.2)
+      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.19.11)
       browser-assert: 1.2.1
-      ejs: 3.1.9
-      esbuild: 0.20.2
+      ejs: 3.1.10
+      esbuild: 0.19.11
       esbuild-plugin-alias: 0.2.1
       express: 4.19.2
       fs-extra: 11.1.1
@@ -15011,55 +16036,57 @@ snapshots:
       util: 0.12.5
     transitivePeerDependencies:
       - encoding
+      - prettier
       - supports-color
 
-  '@storybook/builder-vite@8.0.9(encoding@0.1.13)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/builder-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))':
     dependencies:
-      '@storybook/channels': 8.0.9
-      '@storybook/client-logger': 8.0.9
-      '@storybook/core-common': 8.0.9(encoding@0.1.13)
-      '@storybook/core-events': 8.0.9
-      '@storybook/csf-plugin': 8.0.9
-      '@storybook/node-logger': 8.0.9
-      '@storybook/preview': 8.0.9
-      '@storybook/preview-api': 8.0.9
-      '@storybook/types': 8.0.9
+      '@storybook/channels': 8.1.11
+      '@storybook/client-logger': 8.1.11
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-events': 8.1.11
+      '@storybook/csf-plugin': 8.1.11
+      '@storybook/node-logger': 8.1.11
+      '@storybook/preview': 8.1.11
+      '@storybook/preview-api': 8.1.11
+      '@storybook/types': 8.1.11
       '@types/find-cache-dir': 3.2.1
       browser-assert: 1.2.1
-      es-module-lexer: 0.9.3
-      express: 4.18.2
+      es-module-lexer: 1.5.4
+      express: 4.19.2
       find-cache-dir: 3.3.2
       fs-extra: 11.1.1
-      magic-string: 0.30.7
+      magic-string: 0.30.10
       ts-dedent: 2.2.0
-      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
     transitivePeerDependencies:
       - encoding
+      - prettier
       - supports-color
 
-  '@storybook/channels@8.0.9':
+  '@storybook/channels@8.1.11':
     dependencies:
-      '@storybook/client-logger': 8.0.9
-      '@storybook/core-events': 8.0.9
+      '@storybook/client-logger': 8.1.11
+      '@storybook/core-events': 8.1.11
       '@storybook/global': 5.0.0
       telejson: 7.2.0
-      tiny-invariant: 1.3.1
+      tiny-invariant: 1.3.3
 
-  '@storybook/cli@8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
+  '@storybook/cli@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/types': 7.24.7
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 8.0.9
-      '@storybook/core-common': 8.0.9(encoding@0.1.13)
-      '@storybook/core-events': 8.0.9
-      '@storybook/core-server': 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
-      '@storybook/csf-tools': 8.0.9
-      '@storybook/node-logger': 8.0.9
-      '@storybook/telemetry': 8.0.9(encoding@0.1.13)
-      '@storybook/types': 8.0.9
+      '@storybook/codemod': 8.1.11
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-events': 8.1.11
+      '@storybook/core-server': 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+      '@storybook/csf-tools': 8.1.11
+      '@storybook/node-logger': 8.1.11
+      '@storybook/telemetry': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/types': 8.1.11
       '@types/semver': 7.5.8
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
@@ -15073,17 +16100,17 @@ snapshots:
       fs-extra: 11.1.1
       get-npm-tarball-url: 2.0.3
       giget: 1.1.2
-      globby: 11.1.0
-      jscodeshift: 0.15.1(@babel/preset-env@7.23.5(@babel/core@7.24.0))
+      globby: 14.0.1
+      jscodeshift: 0.15.1(@babel/preset-env@7.24.7(@babel/core@7.24.7))
       leven: 3.1.0
       ora: 5.4.1
-      prettier: 3.2.5
+      prettier: 3.3.2
       prompts: 2.4.2
       read-pkg-up: 7.0.1
       semver: 7.6.0
       strip-json-comments: 3.1.1
-      tempy: 1.0.1
-      tiny-invariant: 1.3.1
+      tempy: 3.1.0
+      tiny-invariant: 1.3.3
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@babel/preset-env'
@@ -15094,104 +16121,112 @@ snapshots:
       - supports-color
       - utf-8-validate
 
-  '@storybook/client-logger@8.0.9':
+  '@storybook/client-logger@8.1.11':
     dependencies:
       '@storybook/global': 5.0.0
 
-  '@storybook/codemod@8.0.9':
+  '@storybook/codemod@8.1.11':
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/preset-env': 7.23.5(@babel/core@7.24.0)
-      '@babel/types': 7.24.0
-      '@storybook/csf': 0.1.6
-      '@storybook/csf-tools': 8.0.9
-      '@storybook/node-logger': 8.0.9
-      '@storybook/types': 8.0.9
+      '@babel/core': 7.24.7
+      '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
+      '@babel/types': 7.24.7
+      '@storybook/csf': 0.1.9
+      '@storybook/csf-tools': 8.1.11
+      '@storybook/node-logger': 8.1.11
+      '@storybook/types': 8.1.11
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
-      globby: 11.1.0
-      jscodeshift: 0.15.1(@babel/preset-env@7.23.5(@babel/core@7.24.0))
+      globby: 14.0.1
+      jscodeshift: 0.15.1(@babel/preset-env@7.24.7(@babel/core@7.24.7))
       lodash: 4.17.21
-      prettier: 3.2.5
+      prettier: 3.3.2
       recast: 0.23.6
-      tiny-invariant: 1.3.1
+      tiny-invariant: 1.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/components@8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@radix-ui/react-slot': 1.0.2(@types/react@18.0.28)(react@18.3.1)
-      '@storybook/client-logger': 8.0.9
-      '@storybook/csf': 0.1.6
+      '@radix-ui/react-dialog': 1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@radix-ui/react-slot': 1.1.0(@types/react@18.0.28)(react@18.3.1)
+      '@storybook/client-logger': 8.1.11
+      '@storybook/csf': 0.1.9
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.0.9
+      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/types': 8.1.11
       memoizerific: 1.11.3
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
       util-deprecate: 1.0.2
     transitivePeerDependencies:
       - '@types/react'
+      - '@types/react-dom'
 
-  '@storybook/core-common@8.0.9(encoding@0.1.13)':
+  '@storybook/core-common@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
     dependencies:
-      '@storybook/core-events': 8.0.9
-      '@storybook/csf-tools': 8.0.9
-      '@storybook/node-logger': 8.0.9
-      '@storybook/types': 8.0.9
+      '@storybook/core-events': 8.1.11
+      '@storybook/csf-tools': 8.1.11
+      '@storybook/node-logger': 8.1.11
+      '@storybook/types': 8.1.11
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      esbuild: 0.20.2
-      esbuild-register: 3.5.0(esbuild@0.20.2)
+      esbuild: 0.19.11
+      esbuild-register: 3.5.0(esbuild@0.19.11)
       execa: 5.1.1
       file-system-cache: 2.3.0
       find-cache-dir: 3.3.2
       find-up: 5.0.0
       fs-extra: 11.1.1
-      glob: 10.3.12
+      glob: 10.4.2
       handlebars: 4.7.7
       lazy-universal-dotenv: 4.0.0
       node-fetch: 2.7.0(encoding@0.1.13)
       picomatch: 2.3.1
       pkg-dir: 5.0.0
+      prettier-fallback: prettier@3.3.2
       pretty-hrtime: 1.0.3
       resolve-from: 5.0.0
       semver: 7.6.0
-      tempy: 1.0.1
-      tiny-invariant: 1.3.1
+      tempy: 3.1.0
+      tiny-invariant: 1.3.3
       ts-dedent: 2.2.0
       util: 0.12.5
+    optionalDependencies:
+      prettier: 3.3.2
     transitivePeerDependencies:
       - encoding
       - supports-color
 
-  '@storybook/core-events@8.0.9':
+  '@storybook/core-events@8.1.11':
     dependencies:
+      '@storybook/csf': 0.1.9
       ts-dedent: 2.2.0
 
-  '@storybook/core-server@8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
+  '@storybook/core-server@8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/parser': 7.24.7
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 8.0.9(encoding@0.1.13)
-      '@storybook/channels': 8.0.9
-      '@storybook/core-common': 8.0.9(encoding@0.1.13)
-      '@storybook/core-events': 8.0.9
-      '@storybook/csf': 0.1.6
-      '@storybook/csf-tools': 8.0.9
-      '@storybook/docs-mdx': 3.0.0
+      '@storybook/builder-manager': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/channels': 8.1.11
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-events': 8.1.11
+      '@storybook/csf': 0.1.9
+      '@storybook/csf-tools': 8.1.11
+      '@storybook/docs-mdx': 3.1.0-next.0
       '@storybook/global': 5.0.0
-      '@storybook/manager': 8.0.9
-      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/node-logger': 8.0.9
-      '@storybook/preview-api': 8.0.9
-      '@storybook/telemetry': 8.0.9(encoding@0.1.13)
-      '@storybook/types': 8.0.9
+      '@storybook/manager': 8.1.11
+      '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/node-logger': 8.1.11
+      '@storybook/preview-api': 8.1.11
+      '@storybook/telemetry': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/types': 8.1.11
       '@types/detect-port': 1.3.2
+      '@types/diff': 5.2.1
       '@types/node': 18.17.15
       '@types/pretty-hrtime': 1.0.1
       '@types/semver': 7.5.8
@@ -15200,10 +16235,10 @@ snapshots:
       cli-table3: 0.6.3
       compression: 1.7.4
       detect-port: 1.5.1
-      express: 4.18.2
+      diff: 5.2.0
+      express: 4.19.2
       fs-extra: 11.1.1
-      globby: 11.1.0
-      ip: 2.0.1
+      globby: 14.0.1
       lodash: 4.17.21
       open: 8.4.2
       pretty-hrtime: 1.0.3
@@ -15211,59 +16246,61 @@ snapshots:
       read-pkg-up: 7.0.1
       semver: 7.6.0
       telejson: 7.2.0
-      tiny-invariant: 1.3.1
+      tiny-invariant: 1.3.3
       ts-dedent: 2.2.0
       util: 0.12.5
       util-deprecate: 1.0.2
       watchpack: 2.4.0
-      ws: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - bufferutil
       - encoding
+      - prettier
       - react
       - react-dom
       - supports-color
       - utf-8-validate
 
-  '@storybook/csf-plugin@8.0.9':
+  '@storybook/csf-plugin@8.1.11':
     dependencies:
-      '@storybook/csf-tools': 8.0.9
+      '@storybook/csf-tools': 8.1.11
       unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
 
-  '@storybook/csf-tools@8.0.9':
+  '@storybook/csf-tools@8.1.11':
     dependencies:
-      '@babel/generator': 7.23.6
-      '@babel/parser': 7.24.0
-      '@babel/traverse': 7.24.0
-      '@babel/types': 7.24.0
-      '@storybook/csf': 0.1.6
-      '@storybook/types': 8.0.9
+      '@babel/generator': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
+      '@storybook/csf': 0.1.9
+      '@storybook/types': 8.1.11
       fs-extra: 11.1.1
       recast: 0.23.6
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
 
-  '@storybook/csf@0.1.6':
+  '@storybook/csf@0.1.9':
     dependencies:
       type-fest: 2.19.0
 
-  '@storybook/docs-mdx@3.0.0': {}
+  '@storybook/docs-mdx@3.1.0-next.0': {}
 
-  '@storybook/docs-tools@8.0.9(encoding@0.1.13)':
+  '@storybook/docs-tools@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
     dependencies:
-      '@storybook/core-common': 8.0.9(encoding@0.1.13)
-      '@storybook/core-events': 8.0.9
-      '@storybook/preview-api': 8.0.9
-      '@storybook/types': 8.0.9
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-events': 8.1.11
+      '@storybook/preview-api': 8.1.11
+      '@storybook/types': 8.1.11
       '@types/doctrine': 0.0.3
       assert: 2.1.0
       doctrine: 3.0.0
       lodash: 4.17.21
     transitivePeerDependencies:
       - encoding
+      - prettier
       - supports-color
 
   '@storybook/global@5.0.0': {}
@@ -15273,27 +16310,27 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  '@storybook/instrumenter@8.0.9':
+  '@storybook/instrumenter@8.1.11':
     dependencies:
-      '@storybook/channels': 8.0.9
-      '@storybook/client-logger': 8.0.9
-      '@storybook/core-events': 8.0.9
+      '@storybook/channels': 8.1.11
+      '@storybook/client-logger': 8.1.11
+      '@storybook/core-events': 8.1.11
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.9
+      '@storybook/preview-api': 8.1.11
       '@vitest/utils': 1.6.0
       util: 0.12.5
 
-  '@storybook/manager-api@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/manager-api@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
-      '@storybook/channels': 8.0.9
-      '@storybook/client-logger': 8.0.9
-      '@storybook/core-events': 8.0.9
-      '@storybook/csf': 0.1.6
+      '@storybook/channels': 8.1.11
+      '@storybook/client-logger': 8.1.11
+      '@storybook/core-events': 8.1.11
+      '@storybook/csf': 0.1.9
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/router': 8.0.9
-      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.0.9
+      '@storybook/router': 8.1.11
+      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/types': 8.1.11
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
@@ -15304,65 +16341,67 @@ snapshots:
       - react
       - react-dom
 
-  '@storybook/manager@8.0.9': {}
+  '@storybook/manager@8.1.11': {}
 
-  '@storybook/node-logger@8.0.9': {}
+  '@storybook/node-logger@8.1.11': {}
 
-  '@storybook/preview-api@8.0.9':
+  '@storybook/preview-api@8.1.11':
     dependencies:
-      '@storybook/channels': 8.0.9
-      '@storybook/client-logger': 8.0.9
-      '@storybook/core-events': 8.0.9
-      '@storybook/csf': 0.1.6
+      '@storybook/channels': 8.1.11
+      '@storybook/client-logger': 8.1.11
+      '@storybook/core-events': 8.1.11
+      '@storybook/csf': 0.1.9
       '@storybook/global': 5.0.0
-      '@storybook/types': 8.0.9
+      '@storybook/types': 8.1.11
       '@types/qs': 6.9.7
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
       qs: 6.11.1
-      tiny-invariant: 1.3.1
+      tiny-invariant: 1.3.3
       ts-dedent: 2.2.0
       util-deprecate: 1.0.2
 
-  '@storybook/preview@8.0.9': {}
+  '@storybook/preview@8.1.11': {}
 
-  '@storybook/react-dom-shim@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/react-dom-shim@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  '@storybook/react-vite@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/react-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))':
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
-      '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
-      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
-      '@storybook/node-logger': 8.0.9
-      '@storybook/react': 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+      '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
+      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+      '@storybook/node-logger': 8.1.11
+      '@storybook/react': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)
+      '@storybook/types': 8.1.11
       find-up: 5.0.0
-      magic-string: 0.30.7
+      magic-string: 0.30.10
       react: 18.3.1
       react-docgen: 7.0.1
       react-dom: 18.3.1(react@18.3.1)
       resolve: 1.22.8
       tsconfig-paths: 4.2.0
-      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
+      - prettier
       - rollup
       - supports-color
       - typescript
       - vite-plugin-glimmerx
 
-  '@storybook/react@8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.2)':
+  '@storybook/react@8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)':
     dependencies:
-      '@storybook/client-logger': 8.0.9
-      '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
+      '@storybook/client-logger': 8.1.11
+      '@storybook/docs-tools': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.9
-      '@storybook/react-dom-shim': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.0.9
+      '@storybook/preview-api': 8.1.11
+      '@storybook/react-dom-shim': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/types': 8.1.11
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 18.17.15
@@ -15381,30 +16420,31 @@ snapshots:
       type-fest: 2.19.0
       util-deprecate: 1.0.2
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
     transitivePeerDependencies:
       - encoding
+      - prettier
       - supports-color
 
-  '@storybook/router@8.0.9':
+  '@storybook/router@8.1.11':
     dependencies:
-      '@storybook/client-logger': 8.0.9
+      '@storybook/client-logger': 8.1.11
       memoizerific: 1.11.3
       qs: 6.11.1
 
-  '@storybook/source-loader@8.0.9':
+  '@storybook/source-loader@8.1.11':
     dependencies:
-      '@storybook/csf': 0.1.6
-      '@storybook/types': 8.0.9
+      '@storybook/csf': 0.1.9
+      '@storybook/types': 8.1.11
       estraverse: 5.3.0
       lodash: 4.17.21
-      prettier: 3.2.5
+      prettier: 3.3.2
 
-  '@storybook/telemetry@8.0.9(encoding@0.1.13)':
+  '@storybook/telemetry@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
     dependencies:
-      '@storybook/client-logger': 8.0.9
-      '@storybook/core-common': 8.0.9(encoding@0.1.13)
-      '@storybook/csf-tools': 8.0.9
+      '@storybook/client-logger': 8.1.11
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/csf-tools': 8.1.11
       chalk: 4.1.2
       detect-package-manager: 2.0.1
       fetch-retry: 5.0.4
@@ -15412,18 +16452,19 @@ snapshots:
       read-pkg-up: 7.0.1
     transitivePeerDependencies:
       - encoding
+      - prettier
       - supports-color
 
-  '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@storybook/test@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
     dependencies:
-      '@storybook/client-logger': 8.0.9
-      '@storybook/core-events': 8.0.9
-      '@storybook/instrumenter': 8.0.9
-      '@storybook/preview-api': 8.0.9
-      '@testing-library/dom': 9.3.4
-      '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
-      '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4)
-      '@vitest/expect': 1.3.1
+      '@storybook/client-logger': 8.1.11
+      '@storybook/core-events': 8.1.11
+      '@storybook/instrumenter': 8.1.11
+      '@storybook/preview-api': 8.1.11
+      '@testing-library/dom': 10.1.0
+      '@testing-library/jest-dom': 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+      '@testing-library/user-event': 14.5.2(@testing-library/dom@10.1.0)
+      '@vitest/expect': 1.6.0
       '@vitest/spy': 1.6.0
       util: 0.12.5
     transitivePeerDependencies:
@@ -15433,37 +16474,39 @@ snapshots:
       - jest
       - vitest
 
-  '@storybook/theming@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/theming@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
     dependencies:
       '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.3.1)
-      '@storybook/client-logger': 8.0.9
+      '@storybook/client-logger': 8.1.11
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
     optionalDependencies:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  '@storybook/types@8.0.9':
+  '@storybook/types@8.1.11':
     dependencies:
-      '@storybook/channels': 8.0.9
+      '@storybook/channels': 8.1.11
       '@types/express': 4.17.17
       file-system-cache: 2.3.0
 
-  '@storybook/vue3-vite@8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))':
+  '@storybook/vue3-vite@8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
-      '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.5.2)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
-      '@storybook/core-server': 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
-      '@storybook/vue3': 8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.5.2))
+      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+      '@storybook/core-server': 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+      '@storybook/types': 8.1.11
+      '@storybook/vue3': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))
       find-package-json: 1.2.0
-      magic-string: 0.30.7
-      typescript: 5.5.2
-      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
-      vue-component-meta: 2.0.16(typescript@5.5.2)
-      vue-docgen-api: 4.75.1(vue@3.4.26(typescript@5.5.2))
+      magic-string: 0.30.10
+      typescript: 5.5.3
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vue-component-meta: 2.0.16(typescript@5.5.3)
+      vue-docgen-api: 4.75.1(vue@3.4.31(typescript@5.5.3))
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - bufferutil
       - encoding
+      - prettier
       - react
       - react-dom
       - supports-color
@@ -15471,26 +16514,27 @@ snapshots:
       - vite-plugin-glimmerx
       - vue
 
-  '@storybook/vue3@8.0.9(encoding@0.1.13)(vue@3.4.26(typescript@5.5.2))':
+  '@storybook/vue3@8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))':
     dependencies:
-      '@storybook/docs-tools': 8.0.9(encoding@0.1.13)
+      '@storybook/docs-tools': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.0.9
-      '@storybook/types': 8.0.9
-      '@vue/compiler-core': 3.4.21
+      '@storybook/preview-api': 8.1.11
+      '@storybook/types': 8.1.11
+      '@vue/compiler-core': 3.4.29
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.4.26(typescript@5.5.2)
-      vue-component-type-helpers: 2.0.21
+      vue: 3.4.31(typescript@5.5.3)
+      vue-component-type-helpers: 2.0.24
     transitivePeerDependencies:
       - encoding
+      - prettier
       - supports-color
 
-  '@swc/cli@0.3.12(@swc/core@1.4.17)(chokidar@3.5.3)':
+  '@swc/cli@0.3.12(@swc/core@1.6.6)(chokidar@3.5.3)':
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
-      '@swc/core': 1.4.17
+      '@swc/core': 1.6.6
       '@swc/counter': 0.1.3
       commander: 8.3.0
       fast-glob: 3.3.2
@@ -15510,13 +16554,13 @@ snapshots:
   '@swc/core-darwin-arm64@1.3.56':
     optional: true
 
-  '@swc/core-darwin-arm64@1.4.17':
+  '@swc/core-darwin-arm64@1.6.6':
     optional: true
 
   '@swc/core-darwin-x64@1.3.56':
     optional: true
 
-  '@swc/core-darwin-x64@1.4.17':
+  '@swc/core-darwin-x64@1.6.6':
     optional: true
 
   '@swc/core-freebsd-x64@1.3.11':
@@ -15527,77 +16571,79 @@ snapshots:
   '@swc/core-linux-arm-gnueabihf@1.3.56':
     optional: true
 
-  '@swc/core-linux-arm-gnueabihf@1.4.17':
+  '@swc/core-linux-arm-gnueabihf@1.6.6':
     optional: true
 
   '@swc/core-linux-arm64-gnu@1.3.56':
     optional: true
 
-  '@swc/core-linux-arm64-gnu@1.4.17':
+  '@swc/core-linux-arm64-gnu@1.6.6':
     optional: true
 
   '@swc/core-linux-arm64-musl@1.3.56':
     optional: true
 
-  '@swc/core-linux-arm64-musl@1.4.17':
+  '@swc/core-linux-arm64-musl@1.6.6':
     optional: true
 
   '@swc/core-linux-x64-gnu@1.3.56':
     optional: true
 
-  '@swc/core-linux-x64-gnu@1.4.17':
+  '@swc/core-linux-x64-gnu@1.6.6':
     optional: true
 
   '@swc/core-linux-x64-musl@1.3.56':
     optional: true
 
-  '@swc/core-linux-x64-musl@1.4.17':
+  '@swc/core-linux-x64-musl@1.6.6':
     optional: true
 
   '@swc/core-win32-arm64-msvc@1.3.56':
     optional: true
 
-  '@swc/core-win32-arm64-msvc@1.4.17':
+  '@swc/core-win32-arm64-msvc@1.6.6':
     optional: true
 
   '@swc/core-win32-ia32-msvc@1.3.56':
     optional: true
 
-  '@swc/core-win32-ia32-msvc@1.4.17':
+  '@swc/core-win32-ia32-msvc@1.6.6':
     optional: true
 
   '@swc/core-win32-x64-msvc@1.3.56':
     optional: true
 
-  '@swc/core-win32-x64-msvc@1.4.17':
+  '@swc/core-win32-x64-msvc@1.6.6':
     optional: true
 
-  '@swc/core@1.4.17':
+  '@swc/core@1.6.6':
     dependencies:
       '@swc/counter': 0.1.3
-      '@swc/types': 0.1.5
+      '@swc/types': 0.1.9
     optionalDependencies:
-      '@swc/core-darwin-arm64': 1.4.17
-      '@swc/core-darwin-x64': 1.4.17
-      '@swc/core-linux-arm-gnueabihf': 1.4.17
-      '@swc/core-linux-arm64-gnu': 1.4.17
-      '@swc/core-linux-arm64-musl': 1.4.17
-      '@swc/core-linux-x64-gnu': 1.4.17
-      '@swc/core-linux-x64-musl': 1.4.17
-      '@swc/core-win32-arm64-msvc': 1.4.17
-      '@swc/core-win32-ia32-msvc': 1.4.17
-      '@swc/core-win32-x64-msvc': 1.4.17
+      '@swc/core-darwin-arm64': 1.6.6
+      '@swc/core-darwin-x64': 1.6.6
+      '@swc/core-linux-arm-gnueabihf': 1.6.6
+      '@swc/core-linux-arm64-gnu': 1.6.6
+      '@swc/core-linux-arm64-musl': 1.6.6
+      '@swc/core-linux-x64-gnu': 1.6.6
+      '@swc/core-linux-x64-musl': 1.6.6
+      '@swc/core-win32-arm64-msvc': 1.6.6
+      '@swc/core-win32-ia32-msvc': 1.6.6
+      '@swc/core-win32-x64-msvc': 1.6.6
 
   '@swc/counter@0.1.3': {}
 
-  '@swc/jest@0.2.36(@swc/core@1.4.17)':
+  '@swc/jest@0.2.36(@swc/core@1.6.6)':
     dependencies:
       '@jest/create-cache-key-function': 29.7.0
-      '@swc/core': 1.4.17
+      '@swc/core': 1.6.6
       '@swc/counter': 0.1.3
       jsonc-parser: 3.2.0
 
-  '@swc/types@0.1.5': {}
+  '@swc/types@0.1.9':
+    dependencies:
+      '@swc/counter': 0.1.3
 
   '@swc/wasm@1.2.130':
     optional: true
@@ -15701,12 +16747,12 @@ snapshots:
       - encoding
       - seedrandom
 
-  '@testing-library/dom@9.3.3':
+  '@testing-library/dom@10.1.0':
     dependencies:
-      '@babel/code-frame': 7.23.5
+      '@babel/code-frame': 7.24.7
       '@babel/runtime': 7.23.4
       '@types/aria-query': 5.0.1
-      aria-query: 5.1.3
+      aria-query: 5.3.0
       chalk: 4.1.2
       dom-accessibility-api: 0.5.16
       lz-string: 1.5.0
@@ -15723,7 +16769,7 @@ snapshots:
       lz-string: 1.5.0
       pretty-format: 27.5.1
 
-  '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
     dependencies:
       '@adobe/css-tools': 4.3.3
       '@babel/runtime': 7.23.4
@@ -15736,21 +16782,21 @@ snapshots:
     optionalDependencies:
       '@jest/globals': 29.7.0
       '@types/jest': 29.5.12
-      jest: 29.7.0(@types/node@20.12.7)
-      vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+      jest: 29.7.0(@types/node@20.14.9)
+      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
 
-  '@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4)':
+  '@testing-library/user-event@14.5.2(@testing-library/dom@10.1.0)':
     dependencies:
-      '@testing-library/dom': 9.3.4
+      '@testing-library/dom': 10.1.0
 
-  '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))':
+  '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
       '@babel/runtime': 7.23.4
-      '@testing-library/dom': 9.3.3
-      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))
-      vue: 3.4.26(typescript@5.5.2)
+      '@testing-library/dom': 9.3.4
+      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
+      vue: 3.4.31(typescript@5.5.3)
     optionalDependencies:
-      '@vue/compiler-sfc': 3.4.26
+      '@vue/compiler-sfc': 3.4.31
     transitivePeerDependencies:
       - '@vue/server-renderer'
 
@@ -15758,7 +16804,7 @@ snapshots:
 
   '@trysound/sax@0.2.0': {}
 
-  '@tsd/typescript@5.3.3': {}
+  '@tsd/typescript@5.4.5': {}
 
   '@twemoji/parser@15.0.0': {}
 
@@ -15766,7 +16812,7 @@ snapshots:
 
   '@types/accepts@1.3.7':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/archiver@6.0.2':
     dependencies:
@@ -15778,31 +16824,31 @@ snapshots:
 
   '@types/babel__core@7.20.0':
     dependencies:
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
       '@types/babel__generator': 7.6.4
       '@types/babel__template': 7.4.1
       '@types/babel__traverse': 7.20.0
 
   '@types/babel__generator@7.6.4':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.7
 
   '@types/babel__template@7.4.1':
     dependencies:
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
 
   '@types/babel__traverse@7.20.0':
     dependencies:
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.7
 
   '@types/bcryptjs@2.4.6': {}
 
   '@types/body-parser@1.19.5':
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/braces@3.0.1': {}
 
@@ -15810,15 +16856,9 @@ snapshots:
     dependencies:
       '@types/http-cache-semantics': 4.0.4
       '@types/keyv': 3.1.4
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       '@types/responselike': 1.0.0
 
-  '@types/chai-subset@1.3.5':
-    dependencies:
-      '@types/chai': 4.3.11
-
-  '@types/chai@4.3.11': {}
-
   '@types/color-convert@2.0.3':
     dependencies:
       '@types/color-name': 1.1.1
@@ -15827,11 +16867,11 @@ snapshots:
 
   '@types/connect@3.4.35':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/connect@3.4.36':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/content-disposition@0.5.8': {}
 
@@ -15839,14 +16879,14 @@ snapshots:
 
   '@types/cookies@0.9.0':
     dependencies:
-      '@types/connect': 3.4.35
+      '@types/connect': 3.4.36
       '@types/express': 4.17.17
       '@types/keygrip': 1.0.6
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/cross-spawn@6.0.2':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/debug@4.1.12':
     dependencies:
@@ -15854,6 +16894,8 @@ snapshots:
 
   '@types/detect-port@1.3.2': {}
 
+  '@types/diff@5.2.1': {}
+
   '@types/disposable-email-domains@1.0.2': {}
 
   '@types/doctrine@0.0.3': {}
@@ -15871,7 +16913,7 @@ snapshots:
   '@types/eslint@7.29.0':
     dependencies:
       '@types/estree': 1.0.5
-      '@types/json-schema': 7.0.12
+      '@types/json-schema': 7.0.15
 
   '@types/estree@0.0.51': {}
 
@@ -15879,7 +16921,7 @@ snapshots:
 
   '@types/express-serve-static-core@4.17.33':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
 
@@ -15894,16 +16936,16 @@ snapshots:
 
   '@types/fluent-ffmpeg@2.1.24':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/glob@7.2.0':
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/graceful-fs@4.1.6':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/hast@3.0.4':
     dependencies:
@@ -15917,9 +16959,9 @@ snapshots:
 
   '@types/http-errors@2.0.4': {}
 
-  '@types/http-link-header@1.0.5':
+  '@types/http-link-header@1.0.7':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/istanbul-lib-coverage@2.0.4': {}
 
@@ -15938,9 +16980,9 @@ snapshots:
 
   '@types/js-yaml@4.0.9': {}
 
-  '@types/jsdom@21.1.6':
+  '@types/jsdom@21.1.7':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
 
@@ -15950,7 +16992,7 @@ snapshots:
 
   '@types/json5@0.0.29': {}
 
-  '@types/jsonld@1.5.13': {}
+  '@types/jsonld@1.5.14': {}
 
   '@types/jsrsasign@10.5.14': {}
 
@@ -15958,7 +17000,7 @@ snapshots:
 
   '@types/keyv@3.1.4':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/koa-compose@3.2.8':
     dependencies:
@@ -15973,7 +17015,7 @@ snapshots:
       '@types/http-errors': 2.0.4
       '@types/keygrip': 1.0.6
       '@types/koa-compose': 3.2.8
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/koa__router@12.0.3':
     dependencies:
@@ -15991,7 +17033,7 @@ snapshots:
 
   '@types/mdx@2.0.3': {}
 
-  '@types/micromatch@4.0.7':
+  '@types/micromatch@4.0.9':
     dependencies:
       '@types/braces': 3.0.1
 
@@ -16007,15 +17049,15 @@ snapshots:
 
   '@types/mute-stream@0.0.4':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/mysql@2.15.22':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/node-fetch@2.6.4':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       form-data: 3.0.1
 
   '@types/node@18.17.15': {}
@@ -16024,7 +17066,7 @@ snapshots:
     dependencies:
       undici-types: 5.26.5
 
-  '@types/node@20.12.7':
+  '@types/node@20.14.9':
     dependencies:
       undici-types: 5.26.5
 
@@ -16034,7 +17076,7 @@ snapshots:
 
   '@types/nodemailer@6.4.15':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/normalize-package-data@2.4.1': {}
 
@@ -16045,11 +17087,11 @@ snapshots:
   '@types/oauth2orize@1.11.5':
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
-  '@types/oauth@0.9.4':
+  '@types/oauth@0.9.5':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/offscreencanvas@2019.3.0': {}
 
@@ -16057,17 +17099,17 @@ snapshots:
 
   '@types/pg-pool@2.0.4':
     dependencies:
-      '@types/pg': 8.11.5
+      '@types/pg': 8.11.6
 
-  '@types/pg@8.11.5':
+  '@types/pg@8.11.6':
     dependencies:
-      '@types/node': 20.12.7
-      pg-protocol: 1.6.0
+      '@types/node': 20.14.9
+      pg-protocol: 1.6.1
       pg-types: 4.0.1
 
   '@types/pg@8.6.1':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       pg-protocol: 1.6.1
       pg-types: 2.2.0
 
@@ -16081,7 +17123,7 @@ snapshots:
 
   '@types/qrcode@1.5.5':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/qs@6.9.7': {}
 
@@ -16099,7 +17141,7 @@ snapshots:
 
   '@types/readdir-glob@1.1.1':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/rename@1.0.7': {}
 
@@ -16107,7 +17149,7 @@ snapshots:
 
   '@types/responselike@1.0.0':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/sanitize-html@2.11.0':
     dependencies:
@@ -16124,7 +17166,7 @@ snapshots:
   '@types/serve-static@1.15.1':
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/serviceworker@0.0.67': {}
 
@@ -16156,15 +17198,17 @@ snapshots:
 
   '@types/unist@3.0.2': {}
 
+  '@types/uuid@10.0.0': {}
+
   '@types/uuid@9.0.8': {}
 
   '@types/vary@1.1.3':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/web-push@3.6.3':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/webgl-ext@0.0.30': {}
 
@@ -16172,7 +17216,7 @@ snapshots:
 
   '@types/ws@8.5.10':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
 
   '@types/yargs-parser@21.0.0': {}
 
@@ -16182,19 +17226,19 @@ snapshots:
 
   '@types/yauzl@2.10.0':
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
     optional: true
 
-  '@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0)(typescript@5.3.3)':
+  '@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.53.0
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 9.6.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -16205,16 +17249,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)':
+  '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.57.0
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 9.6.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -16225,62 +17269,60 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@7.7.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)':
+  '@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)':
     dependencies:
       '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
-      '@typescript-eslint/scope-manager': 7.7.1
-      '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
-      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
-      '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.57.0
+      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      '@typescript-eslint/scope-manager': 7.15.0
+      '@typescript-eslint/type-utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      '@typescript-eslint/visitor-keys': 7.15.0
+      eslint: 9.6.0
       graphemer: 1.4.0
       ignore: 5.3.1
       natural-compare: 1.4.0
-      semver: 7.6.0
-      ts-api-utils: 1.3.0(typescript@5.5.2)
+      ts-api-utils: 1.3.0(typescript@5.5.3)
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3)':
+  '@typescript-eslint/parser@6.11.0(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.53.0
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 9.6.0
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3)':
+  '@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 7.1.0
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.57.0
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 9.6.0
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2)':
+  '@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3)':
     dependencies:
-      '@typescript-eslint/scope-manager': 7.7.1
-      '@typescript-eslint/types': 7.7.1
-      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.2)
-      '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.57.0
+      '@typescript-eslint/scope-manager': 7.15.0
+      '@typescript-eslint/types': 7.15.0
+      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+      '@typescript-eslint/visitor-keys': 7.15.0
+      debug: 4.3.5(supports-color@8.1.1)
+      eslint: 9.6.0
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
     transitivePeerDependencies:
       - supports-color
 
@@ -16294,44 +17336,44 @@ snapshots:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/visitor-keys': 7.1.0
 
-  '@typescript-eslint/scope-manager@7.7.1':
+  '@typescript-eslint/scope-manager@7.15.0':
     dependencies:
-      '@typescript-eslint/types': 7.7.1
-      '@typescript-eslint/visitor-keys': 7.7.1
+      '@typescript-eslint/types': 7.15.0
+      '@typescript-eslint/visitor-keys': 7.15.0
 
-  '@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3)':
+  '@typescript-eslint/type-utils@6.11.0(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.53.0
+      '@typescript-eslint/utils': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
+      debug: 4.3.5(supports-color@8.1.1)
+      eslint: 9.6.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3)':
+  '@typescript-eslint/type-utils@7.1.0(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.57.0
+      '@typescript-eslint/utils': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 9.6.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/type-utils@7.7.1(eslint@8.57.0)(typescript@5.5.2)':
+  '@typescript-eslint/type-utils@7.15.0(eslint@9.6.0)(typescript@5.5.3)':
     dependencies:
-      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.2)
-      '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.57.0
-      ts-api-utils: 1.3.0(typescript@5.5.2)
+      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+      '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      debug: 4.3.5(supports-color@8.1.1)
+      eslint: 9.6.0
+      ts-api-utils: 1.3.0(typescript@5.5.3)
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
     transitivePeerDependencies:
       - supports-color
 
@@ -16339,13 +17381,13 @@ snapshots:
 
   '@typescript-eslint/types@7.1.0': {}
 
-  '@typescript-eslint/types@7.7.1': {}
+  '@typescript-eslint/types@7.15.0': {}
 
   '@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -16359,7 +17401,7 @@ snapshots:
     dependencies:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/visitor-keys': 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.3
@@ -16370,59 +17412,56 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/typescript-estree@7.7.1(typescript@5.5.2)':
+  '@typescript-eslint/typescript-estree@7.15.0(typescript@5.5.3)':
     dependencies:
-      '@typescript-eslint/types': 7.7.1
-      '@typescript-eslint/visitor-keys': 7.7.1
-      debug: 4.3.4(supports-color@8.1.1)
+      '@typescript-eslint/types': 7.15.0
+      '@typescript-eslint/visitor-keys': 7.15.0
+      debug: 4.3.5(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.4
       semver: 7.6.0
-      ts-api-utils: 1.3.0(typescript@5.5.2)
+      ts-api-utils: 1.3.0(typescript@5.5.3)
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.3)':
+  '@typescript-eslint/utils@6.11.0(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
-      eslint: 8.53.0
+      eslint: 9.6.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3)':
+  '@typescript-eslint/utils@7.1.0(eslint@9.6.0)(typescript@5.3.3)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 7.1.0
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      eslint: 8.57.0
+      eslint: 9.6.0
       semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@7.7.1(eslint@8.57.0)(typescript@5.5.2)':
+  '@typescript-eslint/utils@7.15.0(eslint@9.6.0)(typescript@5.5.3)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      '@types/json-schema': 7.0.15
-      '@types/semver': 7.5.8
-      '@typescript-eslint/scope-manager': 7.7.1
-      '@typescript-eslint/types': 7.7.1
-      '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.2)
-      eslint: 8.57.0
-      semver: 7.6.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
+      '@typescript-eslint/scope-manager': 7.15.0
+      '@typescript-eslint/types': 7.15.0
+      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+      eslint: 9.6.0
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -16437,84 +17476,59 @@ snapshots:
       '@typescript-eslint/types': 7.1.0
       eslint-visitor-keys: 3.4.3
 
-  '@typescript-eslint/visitor-keys@7.7.1':
+  '@typescript-eslint/visitor-keys@7.15.0':
     dependencies:
-      '@typescript-eslint/types': 7.7.1
+      '@typescript-eslint/types': 7.15.0
       eslint-visitor-keys: 3.4.3
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-vue@5.0.4(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.5.2))':
+  '@vitejs/plugin-vue@5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
-      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
-      vue: 3.4.26(typescript@5.5.2)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vue: 3.4.31(typescript@5.5.3)
 
-  '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@bcoe/v8-coverage': 0.2.3
+      debug: 4.3.4(supports-color@5.5.0)
       istanbul-lib-coverage: 3.2.2
       istanbul-lib-report: 3.0.1
-      istanbul-lib-source-maps: 4.0.1
+      istanbul-lib-source-maps: 5.0.4
       istanbul-reports: 3.1.6
-      magic-string: 0.30.7
+      magic-string: 0.30.10
+      magicast: 0.3.4
       picocolors: 1.0.0
       std-env: 3.7.0
+      strip-literal: 2.1.0
       test-exclude: 6.0.0
-      v8-to-istanbul: 9.2.0
-      vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitest/expect@0.34.6':
+  '@vitest/expect@1.6.0':
     dependencies:
-      '@vitest/spy': 0.34.6
-      '@vitest/utils': 0.34.6
+      '@vitest/spy': 1.6.0
+      '@vitest/utils': 1.6.0
       chai: 4.3.10
 
-  '@vitest/expect@1.3.1':
+  '@vitest/runner@1.6.0':
     dependencies:
-      '@vitest/spy': 1.3.1
-      '@vitest/utils': 1.3.1
-      chai: 4.3.10
-
-  '@vitest/runner@0.34.6':
-    dependencies:
-      '@vitest/utils': 0.34.6
-      p-limit: 4.0.0
+      '@vitest/utils': 1.6.0
+      p-limit: 5.0.0
       pathe: 1.1.2
 
-  '@vitest/snapshot@0.34.6':
+  '@vitest/snapshot@1.6.0':
     dependencies:
-      magic-string: 0.30.7
+      magic-string: 0.30.10
       pathe: 1.1.2
       pretty-format: 29.7.0
 
-  '@vitest/spy@0.34.6':
-    dependencies:
-      tinyspy: 2.2.0
-
-  '@vitest/spy@1.3.1':
-    dependencies:
-      tinyspy: 2.2.0
-
   '@vitest/spy@1.6.0':
     dependencies:
       tinyspy: 2.2.0
 
-  '@vitest/utils@0.34.6':
-    dependencies:
-      diff-sequences: 29.6.3
-      loupe: 2.3.7
-      pretty-format: 29.7.0
-
-  '@vitest/utils@1.3.1':
-    dependencies:
-      diff-sequences: 29.6.3
-      estree-walker: 3.0.3
-      loupe: 2.3.7
-      pretty-format: 29.7.0
-
   '@vitest/utils@1.6.0':
     dependencies:
       diff-sequences: 29.6.3
@@ -16526,125 +17540,149 @@ snapshots:
     dependencies:
       '@volar/source-map': 2.2.0
 
+  '@volar/language-core@2.4.0-alpha.11':
+    dependencies:
+      '@volar/source-map': 2.4.0-alpha.11
+
   '@volar/source-map@2.2.0':
     dependencies:
       muggle-string: 0.4.1
 
+  '@volar/source-map@2.4.0-alpha.11': {}
+
   '@volar/typescript@2.2.0':
     dependencies:
       '@volar/language-core': 2.2.0
       path-browserify: 1.0.1
 
-  '@vue/compiler-core@3.4.21':
+  '@volar/typescript@2.4.0-alpha.11':
     dependencies:
-      '@babel/parser': 7.24.0
-      '@vue/shared': 3.4.21
-      entities: 4.5.0
-      estree-walker: 2.0.2
-      source-map-js: 1.0.2
+      '@volar/language-core': 2.4.0-alpha.11
+      path-browserify: 1.0.1
+      vscode-uri: 3.0.8
 
-  '@vue/compiler-core@3.4.25':
+  '@vue/compiler-core@3.4.29':
     dependencies:
-      '@babel/parser': 7.24.5
-      '@vue/shared': 3.4.25
+      '@babel/parser': 7.24.7
+      '@vue/shared': 3.4.29
       entities: 4.5.0
       estree-walker: 2.0.2
       source-map-js: 1.2.0
 
-  '@vue/compiler-core@3.4.26':
+  '@vue/compiler-core@3.4.31':
     dependencies:
-      '@babel/parser': 7.24.5
-      '@vue/shared': 3.4.26
+      '@babel/parser': 7.24.7
+      '@vue/shared': 3.4.31
       entities: 4.5.0
       estree-walker: 2.0.2
       source-map-js: 1.2.0
 
-  '@vue/compiler-dom@3.4.21':
+  '@vue/compiler-dom@3.4.29':
     dependencies:
-      '@vue/compiler-core': 3.4.21
-      '@vue/shared': 3.4.21
+      '@vue/compiler-core': 3.4.29
+      '@vue/shared': 3.4.29
 
-  '@vue/compiler-dom@3.4.25':
+  '@vue/compiler-dom@3.4.31':
     dependencies:
-      '@vue/compiler-core': 3.4.25
-      '@vue/shared': 3.4.25
+      '@vue/compiler-core': 3.4.31
+      '@vue/shared': 3.4.31
 
-  '@vue/compiler-dom@3.4.26':
+  '@vue/compiler-sfc@3.4.31':
     dependencies:
-      '@vue/compiler-core': 3.4.26
-      '@vue/shared': 3.4.26
-
-  '@vue/compiler-sfc@3.4.26':
-    dependencies:
-      '@babel/parser': 7.24.5
-      '@vue/compiler-core': 3.4.26
-      '@vue/compiler-dom': 3.4.26
-      '@vue/compiler-ssr': 3.4.26
-      '@vue/shared': 3.4.26
+      '@babel/parser': 7.24.7
+      '@vue/compiler-core': 3.4.31
+      '@vue/compiler-dom': 3.4.31
+      '@vue/compiler-ssr': 3.4.31
+      '@vue/shared': 3.4.31
       estree-walker: 2.0.2
       magic-string: 0.30.10
       postcss: 8.4.38
       source-map-js: 1.2.0
 
-  '@vue/compiler-ssr@3.4.26':
+  '@vue/compiler-ssr@3.4.29':
     dependencies:
-      '@vue/compiler-dom': 3.4.26
-      '@vue/shared': 3.4.26
+      '@vue/compiler-dom': 3.4.29
+      '@vue/shared': 3.4.29
+    optional: true
+
+  '@vue/compiler-ssr@3.4.31':
+    dependencies:
+      '@vue/compiler-dom': 3.4.31
+      '@vue/shared': 3.4.31
 
   '@vue/devtools-api@6.6.1': {}
 
-  '@vue/language-core@2.0.16(typescript@5.5.2)':
+  '@vue/language-core@2.0.16(typescript@5.5.3)':
     dependencies:
       '@volar/language-core': 2.2.0
-      '@vue/compiler-dom': 3.4.25
-      '@vue/shared': 3.4.25
+      '@vue/compiler-dom': 3.4.29
+      '@vue/shared': 3.4.29
       computeds: 0.0.1
       minimatch: 9.0.4
       path-browserify: 1.0.1
       vue-template-compiler: 2.7.14
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
 
-  '@vue/reactivity@3.4.26':
+  '@vue/language-core@2.0.24(typescript@5.5.3)':
     dependencies:
-      '@vue/shared': 3.4.26
+      '@volar/language-core': 2.4.0-alpha.11
+      '@vue/compiler-dom': 3.4.29
+      '@vue/shared': 3.4.29
+      computeds: 0.0.1
+      minimatch: 9.0.4
+      muggle-string: 0.4.1
+      path-browserify: 1.0.1
+      vue-template-compiler: 2.7.14
+    optionalDependencies:
+      typescript: 5.5.3
 
-  '@vue/runtime-core@3.4.26':
+  '@vue/reactivity@3.4.31':
     dependencies:
-      '@vue/reactivity': 3.4.26
-      '@vue/shared': 3.4.26
+      '@vue/shared': 3.4.31
 
-  '@vue/runtime-dom@3.4.26':
+  '@vue/runtime-core@3.4.31':
     dependencies:
-      '@vue/runtime-core': 3.4.26
-      '@vue/shared': 3.4.26
+      '@vue/reactivity': 3.4.31
+      '@vue/shared': 3.4.31
+
+  '@vue/runtime-dom@3.4.31':
+    dependencies:
+      '@vue/reactivity': 3.4.31
+      '@vue/runtime-core': 3.4.31
+      '@vue/shared': 3.4.31
       csstype: 3.1.3
 
-  '@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2))':
+  '@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3))':
     dependencies:
-      '@vue/compiler-ssr': 3.4.26
-      '@vue/shared': 3.4.26
-      vue: 3.4.26(typescript@5.5.2)
+      '@vue/compiler-ssr': 3.4.29
+      '@vue/shared': 3.4.29
+      vue: 3.4.31(typescript@5.5.3)
+    optional: true
 
-  '@vue/shared@3.4.21': {}
+  '@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3))':
+    dependencies:
+      '@vue/compiler-ssr': 3.4.31
+      '@vue/shared': 3.4.31
+      vue: 3.4.31(typescript@5.5.3)
 
-  '@vue/shared@3.4.25': {}
+  '@vue/shared@3.4.29': {}
 
-  '@vue/shared@3.4.26': {}
+  '@vue/shared@3.4.31': {}
 
-  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.5.2)))(vue@3.4.26(typescript@5.5.2))':
+  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.4.26(typescript@5.5.2)
+      vue: 3.4.31(typescript@5.5.3)
       vue-component-type-helpers: 1.8.4
     optionalDependencies:
-      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.5.2))
+      '@vue/server-renderer': 3.4.29(vue@3.4.31(typescript@5.5.3))
 
   '@webgpu/types@0.1.30': {}
 
-  '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.20.2)':
+  '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.19.11)':
     dependencies:
-      esbuild: 0.20.2
+      esbuild: 0.19.11
       tslib: 2.6.2
 
   '@yarnpkg/fslib@2.10.3':
@@ -16672,22 +17710,22 @@ snapshots:
       mime-types: 2.1.35
       negotiator: 0.6.3
 
-  acorn-import-assertions@1.9.0(acorn@8.11.3):
+  acorn-import-assertions@1.9.0(acorn@8.12.0):
     dependencies:
-      acorn: 8.11.3
+      acorn: 8.12.0
     optional: true
 
-  acorn-import-attributes@1.9.5(acorn@8.11.3):
+  acorn-import-attributes@1.9.5(acorn@8.12.0):
     dependencies:
-      acorn: 8.11.3
+      acorn: 8.12.0
 
   acorn-jsx@5.3.2(acorn@7.4.1):
     dependencies:
       acorn: 7.4.1
 
-  acorn-jsx@5.3.2(acorn@8.11.3):
+  acorn-jsx@5.3.2(acorn@8.12.0):
     dependencies:
-      acorn: 8.11.3
+      acorn: 8.12.0
 
   acorn-walk@7.2.0: {}
 
@@ -16695,7 +17733,7 @@ snapshots:
 
   acorn@7.4.1: {}
 
-  acorn@8.11.3: {}
+  acorn@8.12.0: {}
 
   address@1.2.2: {}
 
@@ -16709,13 +17747,13 @@ snapshots:
 
   agent-base@6.0.2:
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
   agent-base@7.1.0:
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -16738,7 +17776,15 @@ snapshots:
     optionalDependencies:
       ajv: 8.13.0
 
-  ajv-formats@2.1.1(ajv@8.13.0):
+  ajv-draft-04@1.0.0(ajv@8.16.0):
+    optionalDependencies:
+      ajv: 8.16.0
+
+  ajv-formats@2.1.1(ajv@8.16.0):
+    optionalDependencies:
+      ajv: 8.16.0
+
+  ajv-formats@3.0.1(ajv@8.13.0):
     optionalDependencies:
       ajv: 8.13.0
 
@@ -16749,6 +17795,13 @@ snapshots:
       json-schema-traverse: 0.4.1
       uri-js: 4.4.1
 
+  ajv@8.12.0:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      json-schema-traverse: 1.0.0
+      require-from-string: 2.0.2
+      uri-js: 4.4.1
+
   ajv@8.13.0:
     dependencies:
       fast-deep-equal: 3.1.3
@@ -16756,6 +17809,13 @@ snapshots:
       require-from-string: 2.0.2
       uri-js: 4.4.1
 
+  ajv@8.16.0:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      json-schema-traverse: 1.0.0
+      require-from-string: 2.0.2
+      uri-js: 4.4.1
+
   ansi-colors@4.1.3: {}
 
   ansi-escapes@4.3.2:
@@ -16798,7 +17858,7 @@ snapshots:
 
   archiver-utils@5.0.2:
     dependencies:
-      glob: 10.3.12
+      glob: 10.4.2
       graceful-fs: 4.2.11
       is-stream: 2.0.1
       lazystream: 1.0.1
@@ -16832,10 +17892,18 @@ snapshots:
 
   argparse@2.0.1: {}
 
+  aria-hidden@1.2.4:
+    dependencies:
+      tslib: 2.6.2
+
   aria-query@5.1.3:
     dependencies:
       deep-equal: 2.2.0
 
+  aria-query@5.3.0:
+    dependencies:
+      dequal: 2.0.3
+
   array-buffer-byte-length@1.0.0:
     dependencies:
       call-bind: 1.0.2
@@ -16931,6 +17999,8 @@ snapshots:
     dependencies:
       tslib: 2.6.2
 
+  async@0.2.10: {}
+
   async@3.2.4: {}
 
   asynckit@0.4.0: {}
@@ -16945,12 +18015,12 @@ snapshots:
     dependencies:
       '@fastify/error': 3.4.0
       archy: 1.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       fastq: 1.17.1
     transitivePeerDependencies:
       - supports-color
 
-  aws-sdk-client-mock@3.0.1:
+  aws-sdk-client-mock@4.0.1:
     dependencies:
       '@types/sinon': 10.0.13
       sinon: 16.1.3
@@ -16962,13 +18032,13 @@ snapshots:
 
   axios@0.24.0:
     dependencies:
-      follow-redirects: 1.15.2(debug@4.3.4)
+      follow-redirects: 1.15.2(debug@4.3.5)
     transitivePeerDependencies:
       - debug
 
-  axios@1.6.2(debug@4.3.4):
+  axios@1.6.2(debug@4.3.5):
     dependencies:
-      follow-redirects: 1.15.2(debug@4.3.4)
+      follow-redirects: 1.15.2(debug@4.3.5)
       form-data: 4.0.0
       proxy-from-env: 1.1.0
     transitivePeerDependencies:
@@ -16976,9 +18046,9 @@ snapshots:
 
   b4a@1.6.4: {}
 
-  babel-core@7.0.0-bridge.0(@babel/core@7.24.0):
+  babel-core@7.0.0-bridge.0(@babel/core@7.24.7):
     dependencies:
-      '@babel/core': 7.24.0
+      '@babel/core': 7.24.7
 
   babel-jest@29.7.0(@babel/core@7.23.5):
     dependencies:
@@ -17006,31 +18076,31 @@ snapshots:
   babel-plugin-jest-hoist@29.6.3:
     dependencies:
       '@babel/template': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/types': 7.24.7
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
 
-  babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.24.0):
+  babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7):
     dependencies:
-      '@babel/compat-data': 7.23.5
-      '@babel/core': 7.24.0
-      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
+      '@babel/compat-data': 7.24.7
+      '@babel/core': 7.24.7
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
 
-  babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.24.0):
+  babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7):
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
-      core-js-compat: 3.33.3
+      '@babel/core': 7.24.7
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
+      core-js-compat: 3.37.1
     transitivePeerDependencies:
       - supports-color
 
-  babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.24.0):
+  babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7):
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7)
     transitivePeerDependencies:
       - supports-color
 
@@ -17058,7 +18128,7 @@ snapshots:
 
   babel-walk@3.0.0-canary-5:
     dependencies:
-      '@babel/types': 7.23.5
+      '@babel/types': 7.24.0
 
   bail@2.0.2: {}
 
@@ -17110,23 +18180,6 @@ snapshots:
 
   bn.js@4.12.0: {}
 
-  body-parser@1.20.1:
-    dependencies:
-      bytes: 3.1.2
-      content-type: 1.0.5
-      debug: 2.6.9
-      depd: 2.0.0
-      destroy: 1.2.0
-      http-errors: 2.0.0
-      iconv-lite: 0.4.24
-      on-finished: 2.4.1
-      qs: 6.11.0
-      raw-body: 2.5.1
-      type-is: 1.6.18
-      unpipe: 1.0.0
-    transitivePeerDependencies:
-      - supports-color
-
   body-parser@1.20.2:
     dependencies:
       bytes: 3.1.2
@@ -17165,6 +18218,10 @@ snapshots:
     dependencies:
       fill-range: 7.0.1
 
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+
   broadcast-channel@7.0.0:
     dependencies:
       '@babel/runtime': 7.23.4
@@ -17224,7 +18281,7 @@ snapshots:
       node-gyp-build: 4.6.0
     optional: true
 
-  bullmq@5.7.8:
+  bullmq@5.8.3:
     dependencies:
       cron-parser: 4.8.1
       ioredis: 5.4.1
@@ -17252,7 +18309,7 @@ snapshots:
     dependencies:
       '@npmcli/fs': 3.1.0
       fs-minipass: 3.0.2
-      glob: 10.3.12
+      glob: 10.4.2
       lru-cache: 10.2.2
       minipass: 7.0.4
       minipass-collect: 1.0.2
@@ -17277,6 +18334,16 @@ snapshots:
       normalize-url: 8.0.0
       responselike: 3.0.0
 
+  cacheable-request@12.0.1:
+    dependencies:
+      '@types/http-cache-semantics': 4.0.4
+      get-stream: 9.0.1
+      http-cache-semantics: 4.1.1
+      keyv: 4.5.4
+      mimic-response: 4.0.0
+      normalize-url: 8.0.1
+      responselike: 3.0.0
+
   cacheable-request@7.0.2:
     dependencies:
       clone-response: 1.0.3
@@ -17371,26 +18438,26 @@ snapshots:
     dependencies:
       is-regex: 1.1.4
 
-  chart.js@4.4.2:
+  chart.js@4.4.3:
     dependencies:
       '@kurkle/color': 0.3.2
 
-  chartjs-adapter-date-fns@3.0.0(chart.js@4.4.2)(date-fns@2.30.0):
+  chartjs-adapter-date-fns@3.0.0(chart.js@4.4.3)(date-fns@2.30.0):
     dependencies:
-      chart.js: 4.4.2
+      chart.js: 4.4.3
       date-fns: 2.30.0
 
-  chartjs-chart-matrix@2.0.1(chart.js@4.4.2):
+  chartjs-chart-matrix@2.0.1(chart.js@4.4.3):
     dependencies:
-      chart.js: 4.4.2
+      chart.js: 4.4.3
 
-  chartjs-plugin-gradient@0.6.1(chart.js@4.4.2):
+  chartjs-plugin-gradient@0.6.1(chart.js@4.4.3):
     dependencies:
-      chart.js: 4.4.2
+      chart.js: 4.4.3
 
-  chartjs-plugin-zoom@2.0.1(chart.js@4.4.2):
+  chartjs-plugin-zoom@2.0.1(chart.js@4.4.3):
     dependencies:
-      chart.js: 4.4.2
+      chart.js: 4.4.3
       hammerjs: 2.0.8
 
   check-error@1.0.3:
@@ -17434,7 +18501,7 @@ snapshots:
 
   chownr@2.0.0: {}
 
-  chromatic@11.3.0: {}
+  chromatic@11.5.4: {}
 
   ci-info@3.7.1: {}
 
@@ -17459,8 +18526,6 @@ snapshots:
       parse5-htmlparser2-tree-adapter: 6.0.1
       yargs: 16.2.0
 
-  cli-spinners@2.7.0: {}
-
   cli-spinners@2.9.2: {}
 
   cli-table3@0.6.3:
@@ -17612,8 +18677,8 @@ snapshots:
 
   constantinople@4.0.1:
     dependencies:
-      '@babel/parser': 7.23.9
-      '@babel/types': 7.23.5
+      '@babel/parser': 7.24.5
+      '@babel/types': 7.24.0
 
   content-disposition@0.5.4:
     dependencies:
@@ -17631,7 +18696,7 @@ snapshots:
 
   cookie@0.6.0: {}
 
-  core-js-compat@3.33.3:
+  core-js-compat@3.37.1:
     dependencies:
       browserslist: 4.23.0
 
@@ -17653,13 +18718,13 @@ snapshots:
       crc-32: 1.2.2
       readable-stream: 4.3.0
 
-  create-jest@29.7.0(@types/node@20.12.7):
+  create-jest@29.7.0(@types/node@20.14.9):
     dependencies:
       '@jest/types': 29.6.3
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.12.7)
+      jest-config: 29.7.0(@types/node@20.14.9)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -17705,7 +18770,9 @@ snapshots:
       shebang-command: 2.0.0
       which: 2.0.2
 
-  crypto-random-string@2.0.0: {}
+  crypto-random-string@4.0.0:
+    dependencies:
+      type-fest: 1.4.0
 
   css-declaration-sorter@7.2.0(postcss@8.4.38):
     dependencies:
@@ -17793,7 +18860,7 @@ snapshots:
     dependencies:
       uniq: 1.0.1
 
-  cypress@13.7.3:
+  cypress@13.13.0:
     dependencies:
       '@cypress/request': 3.0.0
       '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
@@ -17811,52 +18878,7 @@ snapshots:
       commander: 6.2.1
       common-tags: 1.8.2
       dayjs: 1.11.10
-      debug: 4.3.4(supports-color@8.1.1)
-      enquirer: 2.3.6
-      eventemitter2: 6.4.7
-      execa: 4.1.0
-      executable: 4.1.1
-      extract-zip: 2.0.1(supports-color@8.1.1)
-      figures: 3.2.0
-      fs-extra: 9.1.0
-      getos: 3.2.1
-      is-ci: 3.0.1
-      is-installed-globally: 0.4.0
-      lazy-ass: 1.6.0
-      listr2: 3.14.0(enquirer@2.3.6)
-      lodash: 4.17.21
-      log-symbols: 4.1.0
-      minimist: 1.2.8
-      ospath: 1.2.2
-      pretty-bytes: 5.6.0
-      process: 0.11.10
-      proxy-from-env: 1.0.0
-      request-progress: 3.0.0
-      semver: 7.6.0
-      supports-color: 8.1.1
-      tmp: 0.2.3
-      untildify: 4.0.0
-      yauzl: 2.10.0
-
-  cypress@13.8.1:
-    dependencies:
-      '@cypress/request': 3.0.0
-      '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
-      '@types/sinonjs__fake-timers': 8.1.1
-      '@types/sizzle': 2.3.3
-      arch: 2.2.0
-      blob-util: 2.0.2
-      bluebird: 3.7.2
-      buffer: 5.7.1
-      cachedir: 2.3.0
-      chalk: 4.1.2
-      check-more-types: 2.24.0
-      cli-cursor: 3.1.0
-      cli-table3: 0.6.3
-      commander: 6.2.1
-      common-tags: 1.8.2
-      dayjs: 1.11.10
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       enquirer: 2.3.6
       eventemitter2: 6.4.7
       execa: 4.1.0
@@ -17920,7 +18942,7 @@ snapshots:
     optionalDependencies:
       supports-color: 5.5.0
 
-  debug@4.3.4(supports-color@8.1.1):
+  debug@4.3.5(supports-color@8.1.1):
     dependencies:
       ms: 2.1.2
     optionalDependencies:
@@ -18013,17 +19035,6 @@ snapshots:
 
   defu@6.1.4: {}
 
-  del@6.1.1:
-    dependencies:
-      globby: 11.1.0
-      graceful-fs: 4.2.11
-      is-glob: 4.0.3
-      is-path-cwd: 2.2.0
-      is-path-inside: 3.0.3
-      p-map: 4.0.0
-      rimraf: 3.0.2
-      slash: 3.0.0
-
   delayed-stream@1.0.0: {}
 
   delegates@1.0.0:
@@ -18046,6 +19057,8 @@ snapshots:
 
   detect-newline@3.1.0: {}
 
+  detect-node-es@1.1.0: {}
+
   detect-package-manager@2.0.1:
     dependencies:
       execa: 5.1.1
@@ -18053,7 +19066,7 @@ snapshots:
   detect-port@1.5.1:
     dependencies:
       address: 1.2.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -18067,6 +19080,8 @@ snapshots:
 
   diff@5.1.0: {}
 
+  diff@5.2.0: {}
+
   dijkstrajs@1.0.2: {}
 
   dir-glob@3.0.1:
@@ -18140,7 +19155,7 @@ snapshots:
 
   ee-first@1.1.1: {}
 
-  ejs@3.1.9:
+  ejs@3.1.10:
     dependencies:
       jake: 10.8.5
 
@@ -18239,7 +19254,7 @@ snapshots:
       isarray: 2.0.5
       stop-iteration-iterator: 1.0.0
 
-  es-module-lexer@0.9.3: {}
+  es-module-lexer@1.5.4: {}
 
   es-set-tostringtag@2.0.1:
     dependencies:
@@ -18267,10 +19282,10 @@ snapshots:
 
   esbuild-plugin-alias@0.2.1: {}
 
-  esbuild-register@3.5.0(esbuild@0.20.2):
+  esbuild-register@3.5.0(esbuild@0.19.11):
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
-      esbuild: 0.20.2
+      debug: 4.3.5(supports-color@8.1.1)
+      esbuild: 0.19.11
     transitivePeerDependencies:
       - supports-color
 
@@ -18325,31 +19340,58 @@ snapshots:
       '@esbuild/win32-ia32': 0.19.11
       '@esbuild/win32-x64': 0.19.11
 
-  esbuild@0.20.2:
+  esbuild@0.21.5:
     optionalDependencies:
-      '@esbuild/aix-ppc64': 0.20.2
-      '@esbuild/android-arm': 0.20.2
-      '@esbuild/android-arm64': 0.20.2
-      '@esbuild/android-x64': 0.20.2
-      '@esbuild/darwin-arm64': 0.20.2
-      '@esbuild/darwin-x64': 0.20.2
-      '@esbuild/freebsd-arm64': 0.20.2
-      '@esbuild/freebsd-x64': 0.20.2
-      '@esbuild/linux-arm': 0.20.2
-      '@esbuild/linux-arm64': 0.20.2
-      '@esbuild/linux-ia32': 0.20.2
-      '@esbuild/linux-loong64': 0.20.2
-      '@esbuild/linux-mips64el': 0.20.2
-      '@esbuild/linux-ppc64': 0.20.2
-      '@esbuild/linux-riscv64': 0.20.2
-      '@esbuild/linux-s390x': 0.20.2
-      '@esbuild/linux-x64': 0.20.2
-      '@esbuild/netbsd-x64': 0.20.2
-      '@esbuild/openbsd-x64': 0.20.2
-      '@esbuild/sunos-x64': 0.20.2
-      '@esbuild/win32-arm64': 0.20.2
-      '@esbuild/win32-ia32': 0.20.2
-      '@esbuild/win32-x64': 0.20.2
+      '@esbuild/aix-ppc64': 0.21.5
+      '@esbuild/android-arm': 0.21.5
+      '@esbuild/android-arm64': 0.21.5
+      '@esbuild/android-x64': 0.21.5
+      '@esbuild/darwin-arm64': 0.21.5
+      '@esbuild/darwin-x64': 0.21.5
+      '@esbuild/freebsd-arm64': 0.21.5
+      '@esbuild/freebsd-x64': 0.21.5
+      '@esbuild/linux-arm': 0.21.5
+      '@esbuild/linux-arm64': 0.21.5
+      '@esbuild/linux-ia32': 0.21.5
+      '@esbuild/linux-loong64': 0.21.5
+      '@esbuild/linux-mips64el': 0.21.5
+      '@esbuild/linux-ppc64': 0.21.5
+      '@esbuild/linux-riscv64': 0.21.5
+      '@esbuild/linux-s390x': 0.21.5
+      '@esbuild/linux-x64': 0.21.5
+      '@esbuild/netbsd-x64': 0.21.5
+      '@esbuild/openbsd-x64': 0.21.5
+      '@esbuild/sunos-x64': 0.21.5
+      '@esbuild/win32-arm64': 0.21.5
+      '@esbuild/win32-ia32': 0.21.5
+      '@esbuild/win32-x64': 0.21.5
+
+  esbuild@0.22.0:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.22.0
+      '@esbuild/android-arm': 0.22.0
+      '@esbuild/android-arm64': 0.22.0
+      '@esbuild/android-x64': 0.22.0
+      '@esbuild/darwin-arm64': 0.22.0
+      '@esbuild/darwin-x64': 0.22.0
+      '@esbuild/freebsd-arm64': 0.22.0
+      '@esbuild/freebsd-x64': 0.22.0
+      '@esbuild/linux-arm': 0.22.0
+      '@esbuild/linux-arm64': 0.22.0
+      '@esbuild/linux-ia32': 0.22.0
+      '@esbuild/linux-loong64': 0.22.0
+      '@esbuild/linux-mips64el': 0.22.0
+      '@esbuild/linux-ppc64': 0.22.0
+      '@esbuild/linux-riscv64': 0.22.0
+      '@esbuild/linux-s390x': 0.22.0
+      '@esbuild/linux-x64': 0.22.0
+      '@esbuild/netbsd-x64': 0.22.0
+      '@esbuild/openbsd-arm64': 0.22.0
+      '@esbuild/openbsd-x64': 0.22.0
+      '@esbuild/sunos-x64': 0.22.0
+      '@esbuild/win32-arm64': 0.22.0
+      '@esbuild/win32-ia32': 0.22.0
+      '@esbuild/win32-x64': 0.22.0
 
   escalade@3.1.1: {}
 
@@ -18392,37 +19434,17 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.53.0):
+  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0):
     dependencies:
       debug: 3.2.7(supports-color@8.1.1)
     optionalDependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      eslint: 8.53.0
+      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      eslint: 9.6.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
-    dependencies:
-      debug: 3.2.7(supports-color@8.1.1)
-    optionalDependencies:
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-      eslint: 8.57.0
-      eslint-import-resolver-node: 0.3.9
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
-    dependencies:
-      debug: 3.2.7(supports-color@8.1.1)
-    optionalDependencies:
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
-      eslint: 8.57.0
-      eslint-import-resolver-node: 0.3.9
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint@8.53.0):
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0):
     dependencies:
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
@@ -18430,9 +19452,9 @@ snapshots:
       array.prototype.flatmap: 1.3.2
       debug: 3.2.7(supports-color@8.1.1)
       doctrine: 2.1.0
-      eslint: 8.53.0
+      eslint: 9.6.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.53.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -18443,76 +19465,22 @@ snapshots:
       semver: 6.3.1
       tsconfig-paths: 3.15.0
     optionalDependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0):
+  eslint-plugin-vue@9.26.0(eslint@9.6.0):
     dependencies:
-      array-includes: 3.1.7
-      array.prototype.findlastindex: 1.2.3
-      array.prototype.flat: 1.3.2
-      array.prototype.flatmap: 1.3.2
-      debug: 3.2.7(supports-color@8.1.1)
-      doctrine: 2.1.0
-      eslint: 8.57.0
-      eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
-      hasown: 2.0.0
-      is-core-module: 2.13.1
-      is-glob: 4.0.3
-      minimatch: 3.1.2
-      object.fromentries: 2.0.7
-      object.groupby: 1.0.1
-      object.values: 1.1.7
-      semver: 6.3.1
-      tsconfig-paths: 3.15.0
-    optionalDependencies:
-      '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
-    transitivePeerDependencies:
-      - eslint-import-resolver-typescript
-      - eslint-import-resolver-webpack
-      - supports-color
-
-  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0):
-    dependencies:
-      array-includes: 3.1.7
-      array.prototype.findlastindex: 1.2.3
-      array.prototype.flat: 1.3.2
-      array.prototype.flatmap: 1.3.2
-      debug: 3.2.7(supports-color@8.1.1)
-      doctrine: 2.1.0
-      eslint: 8.57.0
-      eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
-      hasown: 2.0.0
-      is-core-module: 2.13.1
-      is-glob: 4.0.3
-      minimatch: 3.1.2
-      object.fromentries: 2.0.7
-      object.groupby: 1.0.1
-      object.values: 1.1.7
-      semver: 6.3.1
-      tsconfig-paths: 3.15.0
-    optionalDependencies:
-      '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.2)
-    transitivePeerDependencies:
-      - eslint-import-resolver-typescript
-      - eslint-import-resolver-webpack
-      - supports-color
-
-  eslint-plugin-vue@9.25.0(eslint@8.57.0):
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      eslint: 8.57.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
+      eslint: 9.6.0
       globals: 13.24.0
       natural-compare: 1.4.0
       nth-check: 2.1.1
-      postcss-selector-parser: 6.0.15
+      postcss-selector-parser: 6.0.16
       semver: 7.6.0
-      vue-eslint-parser: 9.4.2(eslint@8.57.0)
+      vue-eslint-parser: 9.4.3(eslint@9.6.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -18524,40 +19492,43 @@ snapshots:
       esrecurse: 4.3.0
       estraverse: 5.3.0
 
+  eslint-scope@8.0.1:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
   eslint-visitor-keys@3.4.3: {}
 
-  eslint@8.53.0:
+  eslint-visitor-keys@4.0.0: {}
+
+  eslint@9.6.0:
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
-      '@eslint-community/regexpp': 4.6.2
-      '@eslint/eslintrc': 2.1.4
-      '@eslint/js': 8.53.0
-      '@humanwhocodes/config-array': 0.11.13
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
+      '@eslint-community/regexpp': 4.10.0
+      '@eslint/config-array': 0.17.0
+      '@eslint/eslintrc': 3.1.0
+      '@eslint/js': 9.6.0
       '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.3.0
       '@nodelib/fs.walk': 1.2.8
-      '@ungap/structured-clone': 1.2.0
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
-      doctrine: 3.0.0
+      debug: 4.3.5(supports-color@8.1.1)
       escape-string-regexp: 4.0.0
-      eslint-scope: 7.2.2
-      eslint-visitor-keys: 3.4.3
-      espree: 9.6.1
-      esquery: 1.4.2
+      eslint-scope: 8.0.1
+      eslint-visitor-keys: 4.0.0
+      espree: 10.1.0
+      esquery: 1.5.0
       esutils: 2.0.3
       fast-deep-equal: 3.1.3
-      file-entry-cache: 6.0.1
+      file-entry-cache: 8.0.0
       find-up: 5.0.0
       glob-parent: 6.0.2
-      globals: 13.19.0
-      graphemer: 1.4.0
-      ignore: 5.2.4
+      ignore: 5.3.1
       imurmurhash: 0.1.4
       is-glob: 4.0.3
       is-path-inside: 3.0.3
-      js-yaml: 4.1.0
       json-stable-stringify-without-jsonify: 1.0.1
       levn: 0.4.1
       lodash.merge: 4.6.2
@@ -18569,53 +19540,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint@8.57.0:
+  espree@10.1.0:
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      '@eslint-community/regexpp': 4.6.2
-      '@eslint/eslintrc': 2.1.4
-      '@eslint/js': 8.57.0
-      '@humanwhocodes/config-array': 0.11.14
-      '@humanwhocodes/module-importer': 1.0.1
-      '@nodelib/fs.walk': 1.2.8
-      '@ungap/structured-clone': 1.2.0
-      ajv: 6.12.6
-      chalk: 4.1.2
-      cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
-      doctrine: 3.0.0
-      escape-string-regexp: 4.0.0
-      eslint-scope: 7.2.2
-      eslint-visitor-keys: 3.4.3
-      espree: 9.6.1
-      esquery: 1.4.2
-      esutils: 2.0.3
-      fast-deep-equal: 3.1.3
-      file-entry-cache: 6.0.1
-      find-up: 5.0.0
-      glob-parent: 6.0.2
-      globals: 13.19.0
-      graphemer: 1.4.0
-      ignore: 5.2.4
-      imurmurhash: 0.1.4
-      is-glob: 4.0.3
-      is-path-inside: 3.0.3
-      js-yaml: 4.1.0
-      json-stable-stringify-without-jsonify: 1.0.1
-      levn: 0.4.1
-      lodash.merge: 4.6.2
-      minimatch: 3.1.2
-      natural-compare: 1.4.0
-      optionator: 0.9.3
-      strip-ansi: 6.0.1
-      text-table: 0.2.0
-    transitivePeerDependencies:
-      - supports-color
+      acorn: 8.12.0
+      acorn-jsx: 5.3.2(acorn@8.12.0)
+      eslint-visitor-keys: 4.0.0
 
   espree@9.6.1:
     dependencies:
-      acorn: 8.11.3
-      acorn-jsx: 5.3.2(acorn@8.11.3)
+      acorn: 8.12.0
+      acorn-jsx: 5.3.2(acorn@8.12.0)
       eslint-visitor-keys: 3.4.3
 
   esprima@4.0.1: {}
@@ -18624,6 +19558,10 @@ snapshots:
     dependencies:
       estraverse: 5.3.0
 
+  esquery@1.5.0:
+    dependencies:
+      estraverse: 5.3.0
+
   esrecurse@4.3.0:
     dependencies:
       estraverse: 5.3.0
@@ -18718,6 +19656,21 @@ snapshots:
       signal-exit: 4.1.0
       strip-final-newline: 3.0.0
 
+  execa@9.2.0:
+    dependencies:
+      '@sindresorhus/merge-streams': 4.0.0
+      cross-spawn: 7.0.3
+      figures: 6.1.0
+      get-stream: 9.0.1
+      human-signals: 7.0.0
+      is-plain-obj: 4.1.0
+      is-stream: 4.0.1
+      npm-run-path: 5.3.0
+      pretty-ms: 9.0.0
+      signal-exit: 4.1.0
+      strip-final-newline: 4.0.0
+      yoctocolors: 2.0.2
+
   executable@4.1.1:
     dependencies:
       pify: 2.3.0
@@ -18734,42 +19687,6 @@ snapshots:
 
   exponential-backoff@3.1.1: {}
 
-  express@4.18.2:
-    dependencies:
-      accepts: 1.3.8
-      array-flatten: 1.1.1
-      body-parser: 1.20.1
-      content-disposition: 0.5.4
-      content-type: 1.0.5
-      cookie: 0.5.0
-      cookie-signature: 1.0.6
-      debug: 2.6.9
-      depd: 2.0.0
-      encodeurl: 1.0.2
-      escape-html: 1.0.3
-      etag: 1.8.1
-      finalhandler: 1.2.0
-      fresh: 0.5.2
-      http-errors: 2.0.0
-      merge-descriptors: 1.0.1
-      methods: 1.1.2
-      on-finished: 2.4.1
-      parseurl: 1.3.3
-      path-to-regexp: 0.1.7
-      proxy-addr: 2.0.7
-      qs: 6.11.0
-      range-parser: 1.2.1
-      safe-buffer: 5.2.1
-      send: 0.18.0
-      serve-static: 1.15.0
-      setprototypeof: 1.2.0
-      statuses: 2.0.1
-      type-is: 1.6.18
-      utils-merge: 1.0.1
-      vary: 1.1.2
-    transitivePeerDependencies:
-      - supports-color
-
   express@4.19.2:
     dependencies:
       accepts: 1.3.8
@@ -18819,7 +19736,7 @@ snapshots:
 
   extract-zip@2.0.1(supports-color@8.1.1):
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       get-stream: 5.2.0
       yauzl: 2.10.0
     optionalDependencies:
@@ -18843,15 +19760,15 @@ snapshots:
       '@nodelib/fs.walk': 1.2.8
       glob-parent: 5.1.2
       merge2: 1.4.1
-      micromatch: 4.0.5
+      micromatch: 4.0.7
 
   fast-json-stable-stringify@2.1.0: {}
 
   fast-json-stringify@5.8.0:
     dependencies:
       '@fastify/deepmerge': 1.3.0
-      ajv: 8.13.0
-      ajv-formats: 2.1.1(ajv@8.13.0)
+      ajv: 8.16.0
+      ajv-formats: 2.1.1(ajv@8.16.0)
       fast-deep-equal: 3.1.3
       fast-uri: 2.2.0
       rfdc: 1.3.0
@@ -18880,7 +19797,7 @@ snapshots:
       raw-body: 2.5.2
       secure-json-parse: 2.7.0
 
-  fastify@4.26.2:
+  fastify@4.28.1:
     dependencies:
       '@fastify/ajv-compiler': 3.5.0
       '@fastify/error': 3.4.0
@@ -18891,20 +19808,16 @@ snapshots:
       fast-json-stringify: 5.8.0
       find-my-way: 8.2.0
       light-my-request: 5.11.0
-      pino: 8.17.0
+      pino: 9.2.0
       process-warning: 3.0.0
       proxy-addr: 2.0.7
       rfdc: 1.3.0
       secure-json-parse: 2.7.0
       semver: 7.6.0
-      toad-cache: 3.3.0
+      toad-cache: 3.7.0
     transitivePeerDependencies:
       - supports-color
 
-  fastq@1.15.0:
-    dependencies:
-      reusify: 1.0.4
-
   fastq@1.17.1:
     dependencies:
       reusify: 1.0.4
@@ -18932,9 +19845,13 @@ snapshots:
     dependencies:
       escape-string-regexp: 1.0.5
 
-  file-entry-cache@6.0.1:
+  figures@6.1.0:
     dependencies:
-      flat-cache: 3.0.4
+      is-unicode-supported: 2.0.0
+
+  file-entry-cache@8.0.0:
+    dependencies:
+      flat-cache: 4.0.1
 
   file-system-cache@2.3.0:
     dependencies:
@@ -18969,6 +19886,10 @@ snapshots:
     dependencies:
       to-regex-range: 5.0.1
 
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+
   finalhandler@1.2.0:
     dependencies:
       debug: 2.6.9
@@ -19028,23 +19949,23 @@ snapshots:
       ps-list: 8.1.1
       taskkill: 5.0.0
 
-  flat-cache@3.0.4:
+  flat-cache@4.0.1:
     dependencies:
-      flatted: 3.2.7
-      rimraf: 3.0.2
+      flatted: 3.3.1
+      keyv: 4.5.4
 
-  flatted@3.2.7: {}
+  flatted@3.3.1: {}
 
   flow-parser@0.202.0: {}
 
-  fluent-ffmpeg@2.1.2:
+  fluent-ffmpeg@2.1.3:
     dependencies:
-      async: 3.2.4
+      async: 0.2.10
       which: 1.3.1
 
-  follow-redirects@1.15.2(debug@4.3.4):
+  follow-redirects@1.15.2(debug@4.3.5):
     optionalDependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
 
   for-each@0.3.3:
     dependencies:
@@ -19171,6 +20092,8 @@ snapshots:
       has-proto: 1.0.1
       has-symbols: 1.0.3
 
+  get-nonce@1.0.1: {}
+
   get-npm-tarball-url@2.0.3: {}
 
   get-package-type@0.1.0: {}
@@ -19199,6 +20122,11 @@ snapshots:
 
   get-stream@8.0.1: {}
 
+  get-stream@9.0.1:
+    dependencies:
+      '@sec-ant/readable-stream': 0.4.1
+      is-stream: 4.0.1
+
   get-symbol-description@1.0.0:
     dependencies:
       call-bind: 1.0.2
@@ -19265,6 +20193,15 @@ snapshots:
       minipass: 7.0.4
       path-scurry: 1.10.2
 
+  glob@10.4.2:
+    dependencies:
+      foreground-child: 3.1.1
+      jackspeak: 3.4.0
+      minimatch: 9.0.4
+      minipass: 7.1.2
+      package-json-from-dist: 1.0.0
+      path-scurry: 1.11.1
+
   glob@7.2.3:
     dependencies:
       fs.realpath: 1.0.0
@@ -19288,14 +20225,14 @@ snapshots:
 
   globals@11.12.0: {}
 
-  globals@13.19.0:
-    dependencies:
-      type-fest: 0.20.2
-
   globals@13.24.0:
     dependencies:
       type-fest: 0.20.2
 
+  globals@14.0.0: {}
+
+  globals@15.7.0: {}
+
   globalthis@1.0.3:
     dependencies:
       define-properties: 1.2.0
@@ -19309,6 +20246,15 @@ snapshots:
       merge2: 1.4.1
       slash: 3.0.0
 
+  globby@14.0.1:
+    dependencies:
+      '@sindresorhus/merge-streams': 2.3.0
+      fast-glob: 3.3.2
+      ignore: 5.3.1
+      path-type: 5.0.0
+      slash: 5.1.0
+      unicorn-magic: 0.1.0
+
   google-protobuf@3.21.2:
     optional: true
 
@@ -19344,12 +20290,12 @@ snapshots:
       p-cancelable: 3.0.0
       responselike: 3.0.0
 
-  got@14.2.1:
+  got@14.4.1:
     dependencies:
-      '@sindresorhus/is': 6.1.0
+      '@sindresorhus/is': 6.3.1
       '@szmarczak/http-timer': 5.0.1
       cacheable-lookup: 7.0.0
-      cacheable-request: 10.2.14
+      cacheable-request: 12.0.1
       decompress-response: 6.0.0
       form-data-encoder: 4.0.2
       get-stream: 8.0.1
@@ -19357,6 +20303,7 @@ snapshots:
       lowercase-keys: 3.0.0
       p-cancelable: 4.0.1
       responselike: 3.0.0
+      type-fest: 4.20.1
 
   graceful-fs@4.2.11: {}
 
@@ -19499,7 +20446,14 @@ snapshots:
   http-proxy-agent@7.0.0:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  http-proxy-agent@7.0.2:
+    dependencies:
+      agent-base: 7.1.0
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -19538,14 +20492,21 @@ snapshots:
   https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
   https-proxy-agent@7.0.2:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  https-proxy-agent@7.0.4:
+    dependencies:
+      agent-base: 7.1.0
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -19557,6 +20518,8 @@ snapshots:
 
   human-signals@5.0.0: {}
 
+  human-signals@7.0.0: {}
+
   iconv-lite@0.4.24:
     dependencies:
       safer-buffer: 2.1.2
@@ -19588,16 +20551,16 @@ snapshots:
 
   import-in-the-middle@1.4.2:
     dependencies:
-      acorn: 8.11.3
-      acorn-import-assertions: 1.9.0(acorn@8.11.3)
+      acorn: 8.12.0
+      acorn-import-assertions: 1.9.0(acorn@8.12.0)
       cjs-module-lexer: 1.2.2
       module-details-from-path: 1.0.3
     optional: true
 
-  import-in-the-middle@1.7.4:
+  import-in-the-middle@1.8.1:
     dependencies:
-      acorn: 8.11.3
-      acorn-import-attributes: 1.9.5(acorn@8.11.3)
+      acorn: 8.12.0
+      acorn-import-attributes: 1.9.5(acorn@8.12.0)
       cjs-module-lexer: 1.2.2
       module-details-from-path: 1.0.3
 
@@ -19637,11 +20600,15 @@ snapshots:
 
   intersection-observer@0.12.2: {}
 
+  invariant@2.2.4:
+    dependencies:
+      loose-envify: 1.4.0
+
   ioredis@5.4.1:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       denque: 2.1.0
       lodash.defaults: 4.2.0
       lodash.isarguments: 3.1.0
@@ -19653,15 +20620,14 @@ snapshots:
 
   iota-array@1.0.0: {}
 
-  ip-address@7.1.0:
+  ip-address@9.0.5:
     dependencies:
       jsbn: 1.1.0
-      sprintf-js: 1.1.2
+      sprintf-js: 1.1.3
 
-  ip-cidr@3.1.0:
+  ip-cidr@4.0.1:
     dependencies:
-      ip-address: 7.1.0
-      jsbn: 1.1.0
+      ip-address: 9.0.5
 
   ip-regex@4.3.0: {}
 
@@ -19776,8 +20742,6 @@ snapshots:
 
   is-number@7.0.0: {}
 
-  is-path-cwd@2.2.0: {}
-
   is-path-inside@3.0.3: {}
 
   is-plain-obj@1.1.0: {}
@@ -19811,11 +20775,13 @@ snapshots:
 
   is-stream@3.0.0: {}
 
+  is-stream@4.0.1: {}
+
   is-string@1.0.7:
     dependencies:
       has-tostringtag: 1.0.0
 
-  is-svg@5.0.0:
+  is-svg@5.0.1:
     dependencies:
       fast-xml-parser: 4.2.5
 
@@ -19835,6 +20801,8 @@ snapshots:
 
   is-unicode-supported@0.1.0: {}
 
+  is-unicode-supported@2.0.0: {}
+
   is-weakmap@2.0.1: {}
 
   is-weakref@1.0.2:
@@ -19868,8 +20836,8 @@ snapshots:
 
   istanbul-lib-instrument@5.2.1:
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/parser': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/parser': 7.24.7
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
       semver: 6.3.1
@@ -19878,8 +20846,8 @@ snapshots:
 
   istanbul-lib-instrument@6.0.0:
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/parser': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/parser': 7.24.7
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.2
       semver: 7.6.0
@@ -19894,12 +20862,20 @@ snapshots:
 
   istanbul-lib-source-maps@4.0.1:
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       istanbul-lib-coverage: 3.2.2
       source-map: 0.6.1
     transitivePeerDependencies:
       - supports-color
 
+  istanbul-lib-source-maps@5.0.4:
+    dependencies:
+      '@jridgewell/trace-mapping': 0.3.25
+      debug: 4.3.5(supports-color@8.1.1)
+      istanbul-lib-coverage: 3.2.2
+    transitivePeerDependencies:
+      - supports-color
+
   istanbul-reports@3.1.6:
     dependencies:
       html-escaper: 2.0.2
@@ -19913,6 +20889,12 @@ snapshots:
     optionalDependencies:
       '@pkgjs/parseargs': 0.11.0
 
+  jackspeak@3.4.0:
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+
   jake@10.8.5:
     dependencies:
       async: 3.2.4
@@ -19932,7 +20914,7 @@ snapshots:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -19952,16 +20934,16 @@ snapshots:
       - babel-plugin-macros
       - supports-color
 
-  jest-cli@29.7.0(@types/node@20.12.7):
+  jest-cli@29.7.0(@types/node@20.14.9):
     dependencies:
       '@jest/core': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
       chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.12.7)
+      create-jest: 29.7.0(@types/node@20.14.9)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.12.7)
+      jest-config: 29.7.0(@types/node@20.14.9)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -19971,7 +20953,7 @@ snapshots:
       - supports-color
       - ts-node
 
-  jest-config@29.7.0(@types/node@20.12.7):
+  jest-config@29.7.0(@types/node@20.14.9):
     dependencies:
       '@babel/core': 7.23.5
       '@jest/test-sequencer': 29.7.0
@@ -19990,13 +20972,13 @@ snapshots:
       jest-runner: 29.7.0
       jest-util: 29.7.0
       jest-validate: 29.7.0
-      micromatch: 4.0.5
+      micromatch: 4.0.7
       parse-json: 5.2.0
       pretty-format: 29.7.0
       slash: 3.0.0
       strip-json-comments: 3.1.1
     optionalDependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
     transitivePeerDependencies:
       - babel-plugin-macros
       - supports-color
@@ -20025,7 +21007,7 @@ snapshots:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       jest-mock: 29.7.0
       jest-util: 29.7.0
 
@@ -20042,14 +21024,14 @@ snapshots:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
       jest-regex-util: 29.6.3
       jest-util: 29.7.0
       jest-worker: 29.7.0
-      micromatch: 4.0.5
+      micromatch: 4.0.7
       walker: 1.0.8
     optionalDependencies:
       fsevents: 2.3.3
@@ -20073,7 +21055,7 @@ snapshots:
       '@types/stack-utils': 2.0.1
       chalk: 4.1.2
       graceful-fs: 4.2.11
-      micromatch: 4.0.5
+      micromatch: 4.0.7
       pretty-format: 29.7.0
       slash: 3.0.0
       stack-utils: 2.0.6
@@ -20081,7 +21063,7 @@ snapshots:
   jest-mock@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       jest-util: 29.7.0
 
   jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -20116,7 +21098,7 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -20144,7 +21126,7 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -20190,7 +21172,7 @@ snapshots:
   jest-util@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -20209,7 +21191,7 @@ snapshots:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -20223,17 +21205,17 @@ snapshots:
 
   jest-worker@29.7.0:
     dependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
-  jest@29.7.0(@types/node@20.12.7):
+  jest@29.7.0(@types/node@20.14.9):
     dependencies:
       '@jest/core': 29.7.0
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.12.7)
+      jest-cli: 29.7.0(@types/node@20.14.9)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -20263,6 +21245,8 @@ snapshots:
 
   js-tokens@4.0.0: {}
 
+  js-tokens@9.0.0: {}
+
   js-yaml@3.14.1:
     dependencies:
       argparse: 1.0.10
@@ -20278,55 +21262,55 @@ snapshots:
 
   jschardet@3.0.0: {}
 
-  jscodeshift@0.15.1(@babel/preset-env@7.23.5(@babel/core@7.24.0)):
+  jscodeshift@0.15.1(@babel/preset-env@7.24.7(@babel/core@7.24.7)):
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/parser': 7.24.0
-      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0)
-      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.24.0)
-      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.24.0)
-      '@babel/preset-flow': 7.23.3(@babel/core@7.24.0)
-      '@babel/preset-typescript': 7.23.3(@babel/core@7.24.0)
-      '@babel/register': 7.22.15(@babel/core@7.24.0)
-      babel-core: 7.0.0-bridge.0(@babel/core@7.24.0)
+      '@babel/core': 7.24.7
+      '@babel/parser': 7.24.7
+      '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7)
+      '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7)
+      '@babel/preset-flow': 7.23.3(@babel/core@7.24.7)
+      '@babel/preset-typescript': 7.23.3(@babel/core@7.24.7)
+      '@babel/register': 7.22.15(@babel/core@7.24.7)
+      babel-core: 7.0.0-bridge.0(@babel/core@7.24.7)
       chalk: 4.1.2
       flow-parser: 0.202.0
       graceful-fs: 4.2.11
-      micromatch: 4.0.5
+      micromatch: 4.0.7
       neo-async: 2.6.2
       node-dir: 0.1.17
-      recast: 0.23.4
+      recast: 0.23.6
       temp: 0.8.4
       write-file-atomic: 2.4.3
     optionalDependencies:
-      '@babel/preset-env': 7.23.5(@babel/core@7.24.0)
+      '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
     transitivePeerDependencies:
       - supports-color
 
-  jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+  jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
     dependencies:
       cssstyle: 4.0.1
       data-urls: 5.0.0
       decimal.js: 10.4.3
       form-data: 4.0.0
       html-encoding-sniffer: 4.0.0
-      http-proxy-agent: 7.0.0
-      https-proxy-agent: 7.0.2
+      http-proxy-agent: 7.0.2
+      https-proxy-agent: 7.0.4
       is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.9
+      nwsapi: 2.2.10
       parse5: 7.1.2
-      rrweb-cssom: 0.6.0
+      rrweb-cssom: 0.7.1
       saxes: 6.0.0
       symbol-tree: 3.2.4
-      tough-cookie: 4.1.3
+      tough-cookie: 4.1.4
       w3c-xmlserializer: 5.0.0
       webidl-conversions: 7.0.0
       whatwg-encoding: 3.1.1
       whatwg-mimetype: 4.0.0
       whatwg-url: 14.0.0
-      ws: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xml-name-validator: 5.0.0
     transitivePeerDependencies:
       - bufferutil
@@ -20407,8 +21391,6 @@ snapshots:
 
   jsrsasign@11.1.0: {}
 
-  jssha@3.3.1: {}
-
   jstransformer@1.0.0:
     dependencies:
       is-promise: 2.2.2
@@ -20487,7 +21469,10 @@ snapshots:
     optionalDependencies:
       enquirer: 2.3.6
 
-  local-pkg@0.4.3: {}
+  local-pkg@0.5.0:
+    dependencies:
+      mlly: 1.5.0
+      pkg-types: 1.0.3
 
   locate-path@3.0.0:
     dependencies:
@@ -20510,8 +21495,6 @@ snapshots:
 
   lodash.isarguments@3.1.0: {}
 
-  lodash.isequal@4.5.0: {}
-
   lodash.memoize@4.1.2: {}
 
   lodash.merge@4.6.2: {}
@@ -20583,9 +21566,11 @@ snapshots:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.4.15
 
-  magic-string@0.30.7:
+  magicast@0.3.4:
     dependencies:
-      '@jridgewell/sourcemap-codec': 1.4.15
+      '@babel/parser': 7.24.5
+      '@babel/types': 7.24.0
+      source-map-js: 1.2.0
 
   mailcheck@1.1.1: {}
 
@@ -20745,7 +21730,7 @@ snapshots:
 
   media-typer@0.3.0: {}
 
-  meilisearch@0.38.0(encoding@0.1.13):
+  meilisearch@0.41.0(encoding@0.1.13):
     dependencies:
       cross-fetch: 3.1.6(encoding@0.1.13)
     transitivePeerDependencies:
@@ -20958,7 +21943,7 @@ snapshots:
   micromark@4.0.0:
     dependencies:
       '@types/debug': 4.1.12
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       decode-named-character-reference: 1.0.2
       devlop: 1.1.0
       micromark-core-commonmark: 2.0.0
@@ -20977,9 +21962,9 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  micromatch@4.0.5:
+  micromatch@4.0.7:
     dependencies:
-      braces: 3.0.2
+      braces: 3.0.3
       picomatch: 2.3.1
 
   mime-db@1.52.0: {}
@@ -21076,6 +22061,8 @@ snapshots:
 
   minipass@7.0.4: {}
 
+  minipass@7.1.2: {}
+
   minizlib@1.3.3:
     dependencies:
       minipass: 2.9.0
@@ -21098,7 +22085,7 @@ snapshots:
 
   mlly@1.5.0:
     dependencies:
-      acorn: 8.11.3
+      acorn: 8.12.0
       pathe: 1.1.2
       pkg-types: 1.0.3
       ufo: 1.3.2
@@ -21137,18 +22124,18 @@ snapshots:
     optionalDependencies:
       msgpackr-extract: 3.0.2
 
-  msw-storybook-addon@2.0.1(msw@2.2.14(typescript@5.5.2)):
+  msw-storybook-addon@2.0.2(msw@2.3.1(typescript@5.5.3)):
     dependencies:
       is-node-process: 1.2.0
-      msw: 2.2.14(typescript@5.5.2)
+      msw: 2.3.1(typescript@5.5.3)
 
-  msw@2.2.14(typescript@5.5.2):
+  msw@2.3.1(typescript@5.5.3):
     dependencies:
       '@bundled-es-modules/cookie': 2.0.0
       '@bundled-es-modules/statuses': 1.0.1
       '@inquirer/confirm': 3.1.6
       '@mswjs/cookies': 1.1.0
-      '@mswjs/interceptors': 0.26.15
+      '@mswjs/interceptors': 0.29.1
       '@open-draft/until': 2.1.0
       '@types/cookie': 0.6.0
       '@types/statuses': 2.0.4
@@ -21162,7 +22149,7 @@ snapshots:
       type-fest: 4.9.0
       yargs: 17.7.2
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
 
   muggle-string@0.4.1: {}
 
@@ -21294,7 +22281,7 @@ snapshots:
     dependencies:
       env-paths: 2.2.1
       exponential-backoff: 3.1.1
-      glob: 10.3.12
+      glob: 10.4.2
       graceful-fs: 4.2.11
       make-fetch-happen: 13.0.0
       nopt: 7.2.0
@@ -21309,7 +22296,7 @@ snapshots:
 
   node-releases@2.0.14: {}
 
-  nodemailer@6.9.13: {}
+  nodemailer@6.9.14: {}
 
   nodemon@3.0.2:
     dependencies:
@@ -21324,14 +22311,14 @@ snapshots:
       touch: 3.1.0
       undefsafe: 2.0.5
 
-  nodemon@3.1.0:
+  nodemon@3.1.4:
     dependencies:
       chokidar: 3.5.3
       debug: 4.3.4(supports-color@5.5.0)
       ignore-by-default: 1.0.1
       minimatch: 3.1.2
       pstree.remy: 1.1.8
-      semver: 7.5.4
+      semver: 7.6.0
       simple-update-notifier: 2.0.0
       supports-color: 5.5.0
       touch: 3.1.0
@@ -21376,6 +22363,8 @@ snapshots:
 
   normalize-url@8.0.0: {}
 
+  normalize-url@8.0.1: {}
+
   npm-run-path@2.0.2:
     dependencies:
       path-key: 2.0.1
@@ -21388,6 +22377,10 @@ snapshots:
     dependencies:
       path-key: 4.0.0
 
+  npm-run-path@5.3.0:
+    dependencies:
+      path-key: 4.0.0
+
   npmlog@5.0.1:
     dependencies:
       are-we-there-yet: 2.0.0
@@ -21405,7 +22398,7 @@ snapshots:
     dependencies:
       boolbase: 1.0.0
 
-  nwsapi@2.2.9: {}
+  nwsapi@2.2.10: {}
 
   oauth-sign@0.9.0: {}
 
@@ -21505,9 +22498,9 @@ snapshots:
 
   opentelemetry-instrumentation-fetch-node@1.2.0:
     dependencies:
-      '@opentelemetry/api': 1.8.0
-      '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.8.0)
-      '@opentelemetry/semantic-conventions': 1.24.1
+      '@opentelemetry/api': 1.9.0
+      '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
     optional: true
@@ -21526,7 +22519,7 @@ snapshots:
       bl: 4.1.0
       chalk: 4.1.2
       cli-cursor: 3.1.0
-      cli-spinners: 2.7.0
+      cli-spinners: 2.9.2
       is-interactive: 1.0.0
       is-unicode-supported: 0.1.0
       log-symbols: 4.1.0
@@ -21541,9 +22534,9 @@ snapshots:
 
   ospath@1.2.2: {}
 
-  otpauth@9.2.3:
+  otpauth@9.3.1:
     dependencies:
-      jssha: 3.3.1
+      '@noble/hashes': 1.4.0
 
   outvariant@1.4.2: {}
 
@@ -21563,7 +22556,7 @@ snapshots:
     dependencies:
       yocto-queue: 0.1.0
 
-  p-limit@4.0.0:
+  p-limit@5.0.0:
     dependencies:
       yocto-queue: 1.0.0
 
@@ -21594,6 +22587,8 @@ snapshots:
 
   p-try@2.2.0: {}
 
+  package-json-from-dist@1.0.0: {}
+
   pako@0.2.9: {}
 
   parent-module@1.0.1:
@@ -21611,6 +22606,8 @@ snapshots:
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
 
+  parse-ms@4.0.0: {}
+
   parse-srcset@1.0.2: {}
 
   parse5-htmlparser2-tree-adapter@6.0.1:
@@ -21658,6 +22655,11 @@ snapshots:
       lru-cache: 10.2.2
       minipass: 7.0.4
 
+  path-scurry@1.11.1:
+    dependencies:
+      lru-cache: 10.2.2
+      minipass: 7.1.2
+
   path-to-regexp@0.1.7: {}
 
   path-to-regexp@1.8.0:
@@ -21670,6 +22672,8 @@ snapshots:
 
   path-type@4.0.0: {}
 
+  path-type@5.0.0: {}
+
   pathe@1.1.2: {}
 
   pathval@1.1.1: {}
@@ -21699,11 +22703,9 @@ snapshots:
 
   pg-numeric@1.0.2: {}
 
-  pg-pool@3.6.2(pg@8.11.5):
+  pg-pool@3.6.2(pg@8.12.0):
     dependencies:
-      pg: 8.11.5
-
-  pg-protocol@1.6.0: {}
+      pg: 8.12.0
 
   pg-protocol@1.6.1: {}
 
@@ -21725,10 +22727,10 @@ snapshots:
       postgres-interval: 3.0.0
       postgres-range: 1.1.3
 
-  pg@8.11.5:
+  pg@8.12.0:
     dependencies:
       pg-connection-string: 2.6.4
-      pg-pool: 3.6.2(pg@8.11.5)
+      pg-pool: 3.6.2(pg@8.12.0)
       pg-protocol: 1.6.1
       pg-types: 2.2.0
       pgpass: 1.0.5
@@ -21739,7 +22741,7 @@ snapshots:
     dependencies:
       split2: 4.1.0
 
-  photoswipe@5.4.3: {}
+  photoswipe@5.4.4: {}
 
   picocolors@1.0.0: {}
 
@@ -21753,26 +22755,26 @@ snapshots:
 
   pify@4.0.1: {}
 
-  pino-abstract-transport@1.1.0:
+  pino-abstract-transport@1.2.0:
     dependencies:
       readable-stream: 4.3.0
       split2: 4.1.0
 
-  pino-std-serializers@6.1.0: {}
+  pino-std-serializers@7.0.0: {}
 
-  pino@8.17.0:
+  pino@9.2.0:
     dependencies:
       atomic-sleep: 1.0.0
       fast-redact: 3.1.2
       on-exit-leak-free: 2.1.0
-      pino-abstract-transport: 1.1.0
-      pino-std-serializers: 6.1.0
-      process-warning: 2.2.0
+      pino-abstract-transport: 1.2.0
+      pino-std-serializers: 7.0.0
+      process-warning: 3.0.0
       quick-format-unescaped: 4.0.4
       real-require: 0.2.0
       safe-stable-stringify: 2.4.2
-      sonic-boom: 3.7.0
-      thread-stream: 2.3.0
+      sonic-boom: 4.0.1
+      thread-stream: 3.1.0
 
   pirates@4.0.5: {}
 
@@ -22007,7 +23009,7 @@ snapshots:
 
   prelude-ls@1.2.1: {}
 
-  prettier@3.2.5: {}
+  prettier@3.3.2: {}
 
   pretty-bytes@5.6.0: {}
 
@@ -22025,6 +23027,10 @@ snapshots:
 
   pretty-hrtime@1.0.3: {}
 
+  pretty-ms@9.0.0:
+    dependencies:
+      parse-ms: 4.0.0
+
   private-ip@2.3.3:
     dependencies:
       ip-regex: 4.3.0
@@ -22110,19 +23116,21 @@ snapshots:
       js-stringify: 1.0.2
       pug-runtime: 3.0.1
 
-  pug-code-gen@3.0.2:
+  pug-code-gen@3.0.3:
     dependencies:
       constantinople: 4.0.1
       doctypes: 1.1.0
       js-stringify: 1.0.2
       pug-attrs: 3.0.0
-      pug-error: 2.0.0
+      pug-error: 2.1.0
       pug-runtime: 3.0.1
       void-elements: 3.1.0
       with: 7.0.2
 
   pug-error@2.0.0: {}
 
+  pug-error@2.1.0: {}
+
   pug-filters@4.0.0:
     dependencies:
       constantinople: 4.0.1
@@ -22160,9 +23168,9 @@ snapshots:
 
   pug-walk@2.0.0: {}
 
-  pug@3.0.2:
+  pug@3.0.3:
     dependencies:
-      pug-code-gen: 3.0.2
+      pug-code-gen: 3.0.3
       pug-filters: 4.0.0
       pug-lexer: 5.0.1
       pug-linker: 4.0.0
@@ -22242,13 +23250,6 @@ snapshots:
 
   ratelimiter@3.4.1: {}
 
-  raw-body@2.5.1:
-    dependencies:
-      bytes: 3.1.2
-      http-errors: 2.0.0
-      iconv-lite: 0.4.24
-      unpipe: 1.0.0
-
   raw-body@2.5.2:
     dependencies:
       bytes: 3.1.2
@@ -22260,7 +23261,7 @@ snapshots:
     dependencies:
       setimmediate: 1.0.5
 
-  re2@1.21.2:
+  re2@1.21.3:
     dependencies:
       install-artifact-from-github: 1.3.5
       nan: 2.20.0
@@ -22273,15 +23274,15 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  react-docgen-typescript@2.2.2(typescript@5.5.2):
+  react-docgen-typescript@2.2.2(typescript@5.5.3):
     dependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
 
   react-docgen@7.0.1:
     dependencies:
-      '@babel/core': 7.24.0
-      '@babel/traverse': 7.24.0
-      '@babel/types': 7.24.0
+      '@babel/core': 7.24.7
+      '@babel/traverse': 7.24.7
+      '@babel/types': 7.24.7
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
       '@types/doctrine': 0.0.9
@@ -22314,6 +23315,34 @@ snapshots:
 
   react-is@18.2.0: {}
 
+  react-remove-scroll-bar@2.3.6(@types/react@18.0.28)(react@18.3.1):
+    dependencies:
+      react: 18.3.1
+      react-style-singleton: 2.2.1(@types/react@18.0.28)(react@18.3.1)
+      tslib: 2.6.2
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  react-remove-scroll@2.5.7(@types/react@18.0.28)(react@18.3.1):
+    dependencies:
+      react: 18.3.1
+      react-remove-scroll-bar: 2.3.6(@types/react@18.0.28)(react@18.3.1)
+      react-style-singleton: 2.2.1(@types/react@18.0.28)(react@18.3.1)
+      tslib: 2.6.2
+      use-callback-ref: 1.3.2(@types/react@18.0.28)(react@18.3.1)
+      use-sidecar: 1.1.2(@types/react@18.0.28)(react@18.3.1)
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  react-style-singleton@2.2.1(@types/react@18.0.28)(react@18.3.1):
+    dependencies:
+      get-nonce: 1.0.1
+      invariant: 2.2.4
+      react: 18.3.1
+      tslib: 2.6.2
+    optionalDependencies:
+      '@types/react': 18.0.28
+
   react@18.3.1:
     dependencies:
       loose-envify: 1.4.0
@@ -22375,14 +23404,6 @@ snapshots:
 
   real-require@0.2.0: {}
 
-  recast@0.23.4:
-    dependencies:
-      assert: 2.1.0
-      ast-types: 0.16.1
-      esprima: 4.0.1
-      source-map: 0.6.1
-      tslib: 2.6.2
-
   recast@0.23.6:
     dependencies:
       ast-types: 0.16.1
@@ -22527,7 +23548,7 @@ snapshots:
 
   require-in-the-middle@7.3.0:
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       module-details-from-path: 1.0.3
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -22551,11 +23572,6 @@ snapshots:
 
   resolve.exports@2.0.0: {}
 
-  resolve@1.19.0:
-    dependencies:
-      is-core-module: 2.13.1
-      path-parse: 1.0.7
-
   resolve@1.22.8:
     dependencies:
       is-core-module: 2.13.1
@@ -22595,31 +23611,34 @@ snapshots:
   rimraf@3.0.2:
     dependencies:
       glob: 7.2.3
+    optional: true
 
-  rollup@4.17.2:
+  rollup@4.18.0:
     dependencies:
       '@types/estree': 1.0.5
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.17.2
-      '@rollup/rollup-android-arm64': 4.17.2
-      '@rollup/rollup-darwin-arm64': 4.17.2
-      '@rollup/rollup-darwin-x64': 4.17.2
-      '@rollup/rollup-linux-arm-gnueabihf': 4.17.2
-      '@rollup/rollup-linux-arm-musleabihf': 4.17.2
-      '@rollup/rollup-linux-arm64-gnu': 4.17.2
-      '@rollup/rollup-linux-arm64-musl': 4.17.2
-      '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2
-      '@rollup/rollup-linux-riscv64-gnu': 4.17.2
-      '@rollup/rollup-linux-s390x-gnu': 4.17.2
-      '@rollup/rollup-linux-x64-gnu': 4.17.2
-      '@rollup/rollup-linux-x64-musl': 4.17.2
-      '@rollup/rollup-win32-arm64-msvc': 4.17.2
-      '@rollup/rollup-win32-ia32-msvc': 4.17.2
-      '@rollup/rollup-win32-x64-msvc': 4.17.2
+      '@rollup/rollup-android-arm-eabi': 4.18.0
+      '@rollup/rollup-android-arm64': 4.18.0
+      '@rollup/rollup-darwin-arm64': 4.18.0
+      '@rollup/rollup-darwin-x64': 4.18.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.18.0
+      '@rollup/rollup-linux-arm-musleabihf': 4.18.0
+      '@rollup/rollup-linux-arm64-gnu': 4.18.0
+      '@rollup/rollup-linux-arm64-musl': 4.18.0
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.18.0
+      '@rollup/rollup-linux-s390x-gnu': 4.18.0
+      '@rollup/rollup-linux-x64-gnu': 4.18.0
+      '@rollup/rollup-linux-x64-musl': 4.18.0
+      '@rollup/rollup-win32-arm64-msvc': 4.18.0
+      '@rollup/rollup-win32-ia32-msvc': 4.18.0
+      '@rollup/rollup-win32-x64-msvc': 4.18.0
       fsevents: 2.3.3
 
   rrweb-cssom@0.6.0: {}
 
+  rrweb-cssom@0.7.1: {}
+
   rss-parser@3.13.0:
     dependencies:
       entities: 2.2.0
@@ -22667,7 +23686,7 @@ snapshots:
       parse-srcset: 1.0.2
       postcss: 8.4.38
 
-  sass@1.76.0:
+  sass@1.77.6:
     dependencies:
       chokidar: 3.5.3
       immutable: 4.2.2
@@ -22749,14 +23768,14 @@ snapshots:
     dependencies:
       kind-of: 6.0.3
 
-  sharp@0.33.3:
+  sharp@0.33.4:
     dependencies:
       color: 4.2.3
       detect-libc: 2.0.3
       semver: 7.6.0
     optionalDependencies:
-      '@img/sharp-darwin-arm64': 0.33.3
-      '@img/sharp-darwin-x64': 0.33.3
+      '@img/sharp-darwin-arm64': 0.33.4
+      '@img/sharp-darwin-x64': 0.33.4
       '@img/sharp-libvips-darwin-arm64': 1.0.2
       '@img/sharp-libvips-darwin-x64': 1.0.2
       '@img/sharp-libvips-linux-arm': 1.0.2
@@ -22765,15 +23784,15 @@ snapshots:
       '@img/sharp-libvips-linux-x64': 1.0.2
       '@img/sharp-libvips-linuxmusl-arm64': 1.0.2
       '@img/sharp-libvips-linuxmusl-x64': 1.0.2
-      '@img/sharp-linux-arm': 0.33.3
-      '@img/sharp-linux-arm64': 0.33.3
-      '@img/sharp-linux-s390x': 0.33.3
-      '@img/sharp-linux-x64': 0.33.3
-      '@img/sharp-linuxmusl-arm64': 0.33.3
-      '@img/sharp-linuxmusl-x64': 0.33.3
-      '@img/sharp-wasm32': 0.33.3
-      '@img/sharp-win32-ia32': 0.33.3
-      '@img/sharp-win32-x64': 0.33.3
+      '@img/sharp-linux-arm': 0.33.4
+      '@img/sharp-linux-arm64': 0.33.4
+      '@img/sharp-linux-s390x': 0.33.4
+      '@img/sharp-linux-x64': 0.33.4
+      '@img/sharp-linuxmusl-arm64': 0.33.4
+      '@img/sharp-linuxmusl-x64': 0.33.4
+      '@img/sharp-wasm32': 0.33.4
+      '@img/sharp-win32-ia32': 0.33.4
+      '@img/sharp-win32-x64': 0.33.4
 
   shebang-command@1.2.0:
     dependencies:
@@ -22787,9 +23806,9 @@ snapshots:
 
   shebang-regex@3.0.0: {}
 
-  shiki@1.4.0:
+  shiki@1.10.0:
     dependencies:
-      '@shikijs/core': 1.4.0
+      '@shikijs/core': 1.10.0
 
   shimmer@1.2.1: {}
 
@@ -22805,11 +23824,11 @@ snapshots:
 
   signal-exit@4.1.0: {}
 
-  simple-oauth2@5.0.0:
+  simple-oauth2@5.0.1:
     dependencies:
-      '@hapi/hoek': 10.0.1
+      '@hapi/hoek': 11.0.4
       '@hapi/wreck': 18.0.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       joi: 17.11.0
     transitivePeerDependencies:
       - supports-color
@@ -22890,6 +23909,8 @@ snapshots:
 
   slash@3.0.0: {}
 
+  slash@5.1.0: {}
+
   slice-ansi@3.0.0:
     dependencies:
       ansi-styles: 4.3.0
@@ -22907,7 +23928,7 @@ snapshots:
   socks-proxy-agent@8.0.2:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       socks: 2.7.1
     transitivePeerDependencies:
       - supports-color
@@ -22917,7 +23938,7 @@ snapshots:
       ip: 2.0.1
       smart-buffer: 4.2.0
 
-  sonic-boom@3.7.0:
+  sonic-boom@4.0.1:
     dependencies:
       atomic-sleep: 1.0.0
 
@@ -22973,7 +23994,7 @@ snapshots:
 
   sprintf-js@1.0.3: {}
 
-  sprintf-js@1.1.2: {}
+  sprintf-js@1.1.3: {}
 
   sshpk@1.17.0:
     dependencies:
@@ -22999,16 +24020,16 @@ snapshots:
 
   standard-as-callback@2.1.0: {}
 
-  start-server-and-test@2.0.3:
+  start-server-and-test@2.0.4:
     dependencies:
       arg: 5.0.2
       bluebird: 3.7.2
       check-more-types: 2.24.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.5(supports-color@8.1.1)
       execa: 5.1.1
       lazy-ass: 1.6.0
       ps-tree: 1.2.0
-      wait-on: 7.2.0(debug@4.3.4)
+      wait-on: 7.2.0(debug@4.3.5)
     transitivePeerDependencies:
       - supports-color
 
@@ -23022,22 +24043,22 @@ snapshots:
 
   store2@2.14.2: {}
 
-  storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.0.9)(@storybook/manager-api@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.0.9)(@storybook/theming@8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.1.11)(@storybook/manager-api@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.1.11)(@storybook/theming@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.1.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
     dependencies:
-      '@storybook/blocks': 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/components': 8.0.9(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/core-events': 8.0.9
-      '@storybook/manager-api': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/preview-api': 8.0.9
-      '@storybook/theming': 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.0.9
+      '@storybook/blocks': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/components': 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/core-events': 8.1.11
+      '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/preview-api': 8.1.11
+      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      '@storybook/types': 8.1.11
     optionalDependencies:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  storybook@8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3):
+  storybook@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3):
     dependencies:
-      '@storybook/cli': 8.0.9(@babel/preset-env@7.23.5(@babel/core@7.24.0))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+      '@storybook/cli': 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - '@babel/preset-env'
       - bufferutil
@@ -23146,6 +24167,8 @@ snapshots:
 
   strip-final-newline@3.0.0: {}
 
+  strip-final-newline@4.0.0: {}
+
   strip-indent@3.0.0:
     dependencies:
       min-indent: 1.0.1
@@ -23156,9 +24179,9 @@ snapshots:
 
   strip-json-comments@3.1.1: {}
 
-  strip-literal@1.3.0:
+  strip-literal@2.1.0:
     dependencies:
-      acorn: 8.11.3
+      js-tokens: 9.0.0
 
   strip-outer@2.0.0: {}
 
@@ -23208,7 +24231,7 @@ snapshots:
 
   symbol-tree@3.2.4: {}
 
-  systeminformation@5.22.7: {}
+  systeminformation@5.22.11: {}
 
   tar-fs@2.1.1:
     dependencies:
@@ -23259,24 +24282,23 @@ snapshots:
     dependencies:
       memoizerific: 1.11.3
 
-  temp-dir@2.0.0: {}
+  temp-dir@3.0.0: {}
 
   temp@0.8.4:
     dependencies:
       rimraf: 2.6.3
 
-  tempy@1.0.1:
+  tempy@3.1.0:
     dependencies:
-      del: 6.1.1
-      is-stream: 2.0.1
-      temp-dir: 2.0.0
-      type-fest: 0.16.0
-      unique-string: 2.0.0
+      is-stream: 3.0.0
+      temp-dir: 3.0.0
+      type-fest: 2.19.0
+      unique-string: 3.0.0
 
-  terser@5.30.3:
+  terser@5.31.1:
     dependencies:
       '@jridgewell/source-map': 0.3.5
-      acorn: 8.11.3
+      acorn: 8.12.0
       commander: 2.20.3
       source-map-support: 0.5.21
 
@@ -23298,13 +24320,13 @@ snapshots:
     dependencies:
       any-promise: 1.3.0
 
-  thread-stream@2.3.0:
+  thread-stream@3.1.0:
     dependencies:
       real-require: 0.2.0
 
-  three@0.164.1: {}
+  three@0.165.0: {}
 
-  throttle-debounce@5.0.0: {}
+  throttle-debounce@5.0.2: {}
 
   throttleit@1.0.0: {}
 
@@ -23317,8 +24339,6 @@ snapshots:
 
   through@2.3.8: {}
 
-  tiny-invariant@1.3.1: {}
-
   tiny-invariant@1.3.3: {}
 
   tiny-lru@10.0.1: {}
@@ -23327,7 +24347,7 @@ snapshots:
 
   tinycolor2@1.6.0: {}
 
-  tinypool@0.7.0: {}
+  tinypool@0.8.4: {}
 
   tinyspy@2.2.0: {}
 
@@ -23343,8 +24363,6 @@ snapshots:
     dependencies:
       is-number: 7.0.0
 
-  toad-cache@3.3.0: {}
-
   toad-cache@3.7.0: {}
 
   tocbot@4.21.1: {}
@@ -23367,7 +24385,7 @@ snapshots:
       psl: 1.9.0
       punycode: 2.3.1
 
-  tough-cookie@4.1.3:
+  tough-cookie@4.1.4:
     dependencies:
       psl: 1.9.0
       punycode: 2.3.1
@@ -23394,9 +24412,9 @@ snapshots:
     dependencies:
       typescript: 5.3.3
 
-  ts-api-utils@1.3.0(typescript@5.5.2):
+  ts-api-utils@1.3.0(typescript@5.5.3):
     dependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
 
   ts-case-convert@2.0.2: {}
 
@@ -23404,7 +24422,7 @@ snapshots:
 
   ts-map@1.0.3: {}
 
-  tsc-alias@1.8.8:
+  tsc-alias@1.8.10:
     dependencies:
       chokidar: 3.5.3
       commander: 9.5.0
@@ -23426,9 +24444,9 @@ snapshots:
       minimist: 1.2.8
       strip-bom: 3.0.0
 
-  tsd@0.30.7:
+  tsd@0.31.1:
     dependencies:
-      '@tsd/typescript': 5.3.3
+      '@tsd/typescript': 5.4.5
       eslint-formatter-pretty: 4.1.0
       globby: 11.1.0
       jest-diff: 29.7.0
@@ -23440,6 +24458,8 @@ snapshots:
 
   tslib@2.6.2: {}
 
+  tslib@2.6.3: {}
+
   tsx@4.4.0:
     dependencies:
       esbuild: 0.18.20
@@ -23459,8 +24479,6 @@ snapshots:
 
   type-detect@4.0.8: {}
 
-  type-fest@0.16.0: {}
-
   type-fest@0.18.1: {}
 
   type-fest@0.20.2: {}
@@ -23471,8 +24489,12 @@ snapshots:
 
   type-fest@0.8.1: {}
 
+  type-fest@1.4.0: {}
+
   type-fest@2.19.0: {}
 
+  type-fest@4.20.1: {}
+
   type-fest@4.9.0: {}
 
   type-is@1.6.18:
@@ -23509,7 +24531,7 @@ snapshots:
 
   typedarray@0.0.6: {}
 
-  typeorm@0.3.20(ioredis@5.4.1)(pg@8.11.5):
+  typeorm@0.3.20(ioredis@5.4.1)(pg@8.12.0):
     dependencies:
       '@sqltools/formatter': 1.2.5
       app-root-path: 3.1.0
@@ -23517,7 +24539,7 @@ snapshots:
       chalk: 4.1.2
       cli-highlight: 2.1.11
       dayjs: 1.11.10
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       dotenv: 16.0.3
       glob: 10.3.10
       mkdirp: 2.1.6
@@ -23528,7 +24550,7 @@ snapshots:
       yargs: 17.7.2
     optionalDependencies:
       ioredis: 5.4.1
-      pg: 8.11.5
+      pg: 8.12.0
     transitivePeerDependencies:
       - supports-color
 
@@ -23536,7 +24558,7 @@ snapshots:
 
   typescript@5.4.2: {}
 
-  typescript@5.5.2: {}
+  typescript@5.5.3: {}
 
   ufo@1.3.2: {}
 
@@ -23577,6 +24599,8 @@ snapshots:
 
   unicode-property-aliases-ecmascript@2.1.0: {}
 
+  unicorn-magic@0.1.0: {}
+
   unified@11.0.4:
     dependencies:
       '@types/unist': 3.0.2
@@ -23597,9 +24621,9 @@ snapshots:
     dependencies:
       imurmurhash: 0.1.4
 
-  unique-string@2.0.0:
+  unique-string@3.0.0:
     dependencies:
-      crypto-random-string: 2.0.0
+      crypto-random-string: 4.0.0
 
   unist-util-is@6.0.0:
     dependencies:
@@ -23632,7 +24656,7 @@ snapshots:
 
   unplugin@1.4.0:
     dependencies:
-      acorn: 8.11.3
+      acorn: 8.12.0
       chokidar: 3.5.3
       webpack-sources: 3.2.3
       webpack-virtual-modules: 0.5.0
@@ -23660,6 +24684,21 @@ snapshots:
       querystringify: 2.2.0
       requires-port: 1.0.0
 
+  use-callback-ref@1.3.2(@types/react@18.0.28)(react@18.3.1):
+    dependencies:
+      react: 18.3.1
+      tslib: 2.6.2
+    optionalDependencies:
+      '@types/react': 18.0.28
+
+  use-sidecar@1.1.2(@types/react@18.0.28)(react@18.3.1):
+    dependencies:
+      detect-node-es: 1.1.0
+      react: 18.3.1
+      tslib: 2.6.2
+    optionalDependencies:
+      '@types/react': 18.0.28
+
   utf-8-validate@6.0.3:
     dependencies:
       node-gyp-build: 4.6.0
@@ -23677,20 +24716,22 @@ snapshots:
 
   utils-merge@1.0.1: {}
 
+  uuid@10.0.0: {}
+
   uuid@3.4.0: {}
 
   uuid@8.3.2: {}
 
   uuid@9.0.1: {}
 
-  v-code-diff@1.11.0(vue@3.4.26(typescript@5.5.2)):
+  v-code-diff@1.12.0(vue@3.4.31(typescript@5.5.3)):
     dependencies:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.9.0
-      vue: 3.4.26(typescript@5.5.2)
-      vue-demi: 0.14.7(vue@3.4.26(typescript@5.5.2))
-      vue-i18n: 9.13.1(vue@3.4.26(typescript@5.5.2))
+      vue: 3.4.31(typescript@5.5.3)
+      vue-demi: 0.14.7(vue@3.4.31(typescript@5.5.3))
+      vue-i18n: 9.13.1(vue@3.4.31(typescript@5.5.3))
 
   v8-to-istanbul@9.2.0:
     dependencies:
@@ -23703,8 +24744,6 @@ snapshots:
       spdx-correct: 3.1.1
       spdx-expression-parse: 3.0.1
 
-  validator@13.9.0: {}
-
   vary@1.1.2: {}
 
   verror@1.10.0:
@@ -23724,14 +24763,13 @@ snapshots:
       unist-util-stringify-position: 4.0.0
       vfile-message: 4.0.2
 
-  vite-node@0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3):
+  vite-node@1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
     dependencies:
       cac: 6.7.14
-      debug: 4.3.4(supports-color@8.1.1)
-      mlly: 1.5.0
+      debug: 4.3.5(supports-color@8.1.1)
       pathe: 1.1.2
       picocolors: 1.0.0
-      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -23744,53 +24782,50 @@ snapshots:
 
   vite-plugin-turbosnap@1.0.3: {}
 
-  vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3):
+  vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
     dependencies:
-      esbuild: 0.20.2
+      esbuild: 0.21.5
       postcss: 8.4.38
-      rollup: 4.17.2
+      rollup: 4.18.0
     optionalDependencies:
-      '@types/node': 20.12.7
+      '@types/node': 20.14.9
       fsevents: 2.3.3
-      sass: 1.76.0
-      terser: 5.30.3
+      sass: 1.77.6
+      terser: 5.31.1
 
-  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)):
+  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)):
     dependencies:
       cross-fetch: 3.1.6(encoding@0.1.13)
-      vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
     transitivePeerDependencies:
       - encoding
 
-  vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3):
+  vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1):
     dependencies:
-      '@types/chai': 4.3.11
-      '@types/chai-subset': 1.3.5
-      '@types/node': 20.12.7
-      '@vitest/expect': 0.34.6
-      '@vitest/runner': 0.34.6
-      '@vitest/snapshot': 0.34.6
-      '@vitest/spy': 0.34.6
-      '@vitest/utils': 0.34.6
-      acorn: 8.11.3
+      '@vitest/expect': 1.6.0
+      '@vitest/runner': 1.6.0
+      '@vitest/snapshot': 1.6.0
+      '@vitest/spy': 1.6.0
+      '@vitest/utils': 1.6.0
       acorn-walk: 8.3.2
-      cac: 6.7.14
       chai: 4.3.10
-      debug: 4.3.4(supports-color@8.1.1)
-      local-pkg: 0.4.3
-      magic-string: 0.30.7
+      debug: 4.3.4(supports-color@5.5.0)
+      execa: 8.0.1
+      local-pkg: 0.5.0
+      magic-string: 0.30.10
       pathe: 1.1.2
       picocolors: 1.0.0
       std-env: 3.7.0
-      strip-literal: 1.3.0
+      strip-literal: 2.1.0
       tinybench: 2.6.0
-      tinypool: 0.7.0
-      vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
-      vite-node: 0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
+      tinypool: 0.8.4
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite-node: 1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
       why-is-node-running: 2.2.2
     optionalDependencies:
+      '@types/node': 20.14.9
       happy-dom: 10.0.3
-      jsdom: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      jsdom: 24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -23823,98 +24858,100 @@ snapshots:
     dependencies:
       vscode-languageserver-protocol: 3.17.5
 
-  vue-component-meta@2.0.16(typescript@5.5.2):
+  vscode-uri@3.0.8: {}
+
+  vue-component-meta@2.0.16(typescript@5.5.3):
     dependencies:
       '@volar/typescript': 2.2.0
-      '@vue/language-core': 2.0.16(typescript@5.5.2)
+      '@vue/language-core': 2.0.16(typescript@5.5.3)
       path-browserify: 1.0.1
       vue-component-type-helpers: 2.0.16
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
 
   vue-component-type-helpers@1.8.4: {}
 
   vue-component-type-helpers@2.0.16: {}
 
-  vue-component-type-helpers@2.0.21: {}
+  vue-component-type-helpers@2.0.24: {}
 
-  vue-demi@0.14.7(vue@3.4.26(typescript@5.5.2)):
+  vue-demi@0.14.7(vue@3.4.31(typescript@5.5.3)):
     dependencies:
-      vue: 3.4.26(typescript@5.5.2)
+      vue: 3.4.31(typescript@5.5.3)
 
-  vue-docgen-api@4.75.1(vue@3.4.26(typescript@5.5.2)):
+  vue-docgen-api@4.75.1(vue@3.4.31(typescript@5.5.3)):
     dependencies:
-      '@babel/parser': 7.24.0
-      '@babel/types': 7.24.0
-      '@vue/compiler-dom': 3.4.21
-      '@vue/compiler-sfc': 3.4.26
+      '@babel/parser': 7.24.7
+      '@babel/types': 7.24.7
+      '@vue/compiler-dom': 3.4.29
+      '@vue/compiler-sfc': 3.4.31
       ast-types: 0.16.1
       hash-sum: 2.0.0
       lru-cache: 8.0.4
-      pug: 3.0.2
-      recast: 0.23.4
+      pug: 3.0.3
+      recast: 0.23.6
       ts-map: 1.0.3
-      vue: 3.4.26(typescript@5.5.2)
-      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.26(typescript@5.5.2))
+      vue: 3.4.31(typescript@5.5.3)
+      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.31(typescript@5.5.3))
 
-  vue-eslint-parser@9.4.2(eslint@8.57.0):
+  vue-eslint-parser@9.4.3(eslint@9.6.0):
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.57.0
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 9.6.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1
       esquery: 1.4.2
       lodash: 4.17.21
-      semver: 7.5.4
+      semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
 
-  vue-i18n@9.13.1(vue@3.4.26(typescript@5.5.2)):
+  vue-i18n@9.13.1(vue@3.4.31(typescript@5.5.3)):
     dependencies:
       '@intlify/core-base': 9.13.1
       '@intlify/shared': 9.13.1
       '@vue/devtools-api': 6.6.1
-      vue: 3.4.26(typescript@5.5.2)
+      vue: 3.4.31(typescript@5.5.3)
 
-  vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.26(typescript@5.5.2)):
+  vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.31(typescript@5.5.3)):
     dependencies:
-      vue: 3.4.26(typescript@5.5.2)
+      vue: 3.4.31(typescript@5.5.3)
 
   vue-template-compiler@2.7.14:
     dependencies:
       de-indent: 1.0.2
       he: 1.2.0
 
-  vue-tsc@2.0.16(typescript@5.5.2):
+  vue-tsc@2.0.24(typescript@5.5.3):
     dependencies:
-      '@volar/typescript': 2.2.0
-      '@vue/language-core': 2.0.16(typescript@5.5.2)
+      '@volar/typescript': 2.4.0-alpha.11
+      '@vue/language-core': 2.0.24(typescript@5.5.3)
       semver: 7.6.0
-      typescript: 5.5.2
+      typescript: 5.5.3
 
-  vue@3.4.26(typescript@5.5.2):
+  vue@3.4.31(typescript@5.5.3):
     dependencies:
-      '@vue/compiler-dom': 3.4.26
-      '@vue/compiler-sfc': 3.4.26
-      '@vue/runtime-dom': 3.4.26
-      '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.5.2))
-      '@vue/shared': 3.4.26
+      '@vue/compiler-dom': 3.4.31
+      '@vue/compiler-sfc': 3.4.31
+      '@vue/runtime-dom': 3.4.31
+      '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3))
+      '@vue/shared': 3.4.31
     optionalDependencies:
-      typescript: 5.5.2
+      typescript: 5.5.3
 
-  vuedraggable@4.1.0(vue@3.4.26(typescript@5.5.2)):
+  vuedraggable@4.1.0(vue@3.4.31(typescript@5.5.3)):
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.4.26(typescript@5.5.2)
+      vue: 3.4.31(typescript@5.5.3)
 
   w3c-xmlserializer@5.0.0:
     dependencies:
       xml-name-validator: 5.0.0
 
-  wait-on@7.2.0(debug@4.3.4):
+  wait-on@7.2.0(debug@4.3.5):
     dependencies:
-      axios: 1.6.2(debug@4.3.4)
+      axios: 1.6.2(debug@4.3.5)
       joi: 17.11.0
       lodash: 4.17.21
       minimist: 1.2.8
@@ -24026,8 +25063,8 @@ snapshots:
 
   with@7.0.2:
     dependencies:
-      '@babel/parser': 7.23.9
-      '@babel/types': 7.23.5
+      '@babel/parser': 7.24.5
+      '@babel/types': 7.24.0
       assert-never: 1.2.1
       babel-walk: 3.0.0-canary-5
 
@@ -24064,7 +25101,7 @@ snapshots:
       imurmurhash: 0.1.4
       signal-exit: 3.0.7
 
-  ws@8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+  ws@8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3):
     optionalDependencies:
       bufferutil: 4.0.7
       utf-8-validate: 6.0.3
@@ -24152,13 +25189,7 @@ snapshots:
 
   yocto-queue@1.0.0: {}
 
-  z-schema@5.0.5:
-    dependencies:
-      lodash.get: 4.4.2
-      lodash.isequal: 4.5.0
-      validator: 13.9.0
-    optionalDependencies:
-      commander: 9.5.0
+  yoctocolors@2.0.2: {}
 
   zip-stream@6.0.1:
     dependencies:
diff --git a/scripts/changelog-checker/.eslintrc.cjs b/scripts/changelog-checker/.eslintrc.cjs
deleted file mode 100644
index 6acf8b3e6e..0000000000
--- a/scripts/changelog-checker/.eslintrc.cjs
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
-	parserOptions: {
-		tsconfigRootDir: __dirname,
-		project: ['./tsconfig.json'],
-	},
-	extends: [
-		'../../packages/shared/.eslintrc.js',
-	],
-};
diff --git a/scripts/changelog-checker/eslint.config.js b/scripts/changelog-checker/eslint.config.js
new file mode 100644
index 0000000000..813e96981a
--- /dev/null
+++ b/scripts/changelog-checker/eslint.config.js
@@ -0,0 +1,17 @@
+import tsParser from '@typescript-eslint/parser';
+import sharedConfig from '../../packages/shared/eslint.config.js';
+
+export default [
+	...sharedConfig,
+	{
+		files: ['src/**/*.ts', 'src/**/*.tsx'],
+		languageOptions: {
+			parserOptions: {
+				parser: tsParser,
+				project: ['./tsconfig.json'],
+				sourceType: 'module',
+				tsconfigRootDir: import.meta.dirname,
+			},
+		},
+	},
+];

From eafae79869204e6de4a3fd4835eda3eb23b53974 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Tue, 2 Jul 2024 14:29:44 +0900
Subject: [PATCH 066/206] test(backend): goodbye, Lenna (#14111)

---
 .../src/models/json-schema/drive-file.ts       |   2 +-
 .../api/endpoints/admin/drive/show-file.ts     |   2 +-
 packages/backend/test/e2e/drive.ts             |   6 +++---
 packages/backend/test/e2e/endpoints.ts         |   2 +-
 packages/backend/test/e2e/note.ts              |   4 ++--
 packages/backend/test/e2e/user-notes.ts        |   4 ++--
 packages/backend/test/resources/192.jpg        | Bin 0 -> 5131 bytes
 packages/backend/test/resources/192.png        | Bin 0 -> 26568 bytes
 packages/backend/test/resources/Lenna.jpg      | Bin 25360 -> 0 bytes
 packages/backend/test/resources/Lenna.png      | Bin 473831 -> 0 bytes
 packages/backend/test/unit/FileInfoService.ts  |  10 +++++-----
 packages/backend/test/utils.ts                 |   2 +-
 packages/misskey-js/src/autogen/types.ts       |   4 ++--
 13 files changed, 18 insertions(+), 18 deletions(-)
 create mode 100644 packages/backend/test/resources/192.jpg
 create mode 100644 packages/backend/test/resources/192.png
 delete mode 100644 packages/backend/test/resources/Lenna.jpg
 delete mode 100644 packages/backend/test/resources/Lenna.png

diff --git a/packages/backend/src/models/json-schema/drive-file.ts b/packages/backend/src/models/json-schema/drive-file.ts
index ca88cc0e39..5ee1561c50 100644
--- a/packages/backend/src/models/json-schema/drive-file.ts
+++ b/packages/backend/src/models/json-schema/drive-file.ts
@@ -20,7 +20,7 @@ export const packedDriveFileSchema = {
 		name: {
 			type: 'string',
 			optional: false, nullable: false,
-			example: 'lenna.jpg',
+			example: '192.jpg',
 		},
 		type: {
 			type: 'string',
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
index 459d8880fa..a7136d8c8c 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
@@ -61,7 +61,7 @@ export const meta = {
 			name: {
 				type: 'string',
 				optional: false, nullable: false,
-				example: 'lenna.jpg',
+				example: '192.jpg',
 			},
 			type: {
 				type: 'string',
diff --git a/packages/backend/test/e2e/drive.ts b/packages/backend/test/e2e/drive.ts
index 828c5200ef..43a73163eb 100644
--- a/packages/backend/test/e2e/drive.ts
+++ b/packages/backend/test/e2e/drive.ts
@@ -23,7 +23,7 @@ describe('Drive', () => {
 
 		const marker = Math.random().toString();
 
-		const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg';
+		const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg';
 
 		const catcher = makeStreamCatcher(
 			alice,
@@ -41,14 +41,14 @@ describe('Drive', () => {
 		const file = await catcher;
 
 		assert.strictEqual(res.status, 204);
-		assert.strictEqual(file.name, 'Lenna.jpg');
+		assert.strictEqual(file.name, '192.jpg');
 		assert.strictEqual(file.type, 'image/jpeg');
 	});
 
 	test('ローカルからアップロードできる', async () => {
 		// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
 
-		const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
+		const res = await uploadFile(alice, { path: '192.jpg', name: 'テスト画像' });
 
 		assert.strictEqual(res.body?.name, 'テスト画像.jpg');
 		assert.strictEqual(res.body.type, 'image/jpeg');
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index de5e8ba95e..d5583ea8bb 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -584,7 +584,7 @@ describe('Endpoints', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
-			assert.strictEqual(res.body!.name, 'Lenna.jpg');
+			assert.strictEqual(res.body!.name, '192.jpg');
 		});
 
 		test('ファイルに名前を付けられる', async () => {
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index bda31d9640..7ce9f47bc3 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -41,7 +41,7 @@ describe('Note', () => {
 	});
 
 	test('ファイルを添付できる', async () => {
-		const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
+		const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg');
 
 		const res = await api('notes/create', {
 			fileIds: [file.id],
@@ -53,7 +53,7 @@ describe('Note', () => {
 	}, 1000 * 10);
 
 	test('他人のファイルで怒られる', async () => {
-		const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
+		const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg');
 
 		const res = await api('notes/create', {
 			text: 'test',
diff --git a/packages/backend/test/e2e/user-notes.ts b/packages/backend/test/e2e/user-notes.ts
index 331e053935..cc07c5ae71 100644
--- a/packages/backend/test/e2e/user-notes.ts
+++ b/packages/backend/test/e2e/user-notes.ts
@@ -17,8 +17,8 @@ describe('users/notes', () => {
 
 	beforeAll(async () => {
 		alice = await signup({ username: 'alice' });
-		const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg');
-		const png = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.png');
+		const jpg = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.jpg');
+		const png = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/192.png');
 		jpgNote = await post(alice, {
 			fileIds: [jpg.id],
 		});
diff --git a/packages/backend/test/resources/192.jpg b/packages/backend/test/resources/192.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..76374628e0acf2e3b85152d55c11c9a763027810
GIT binary patch
literal 5131
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
XAeI42H(xV+Q@$1Zd~-g)z#sfC`(dk&

literal 0
HcmV?d00001

diff --git a/packages/backend/test/resources/192.png b/packages/backend/test/resources/192.png
new file mode 100644
index 0000000000000000000000000000000000000000..15fd1e3731210ad53f3948c1551a5be1f5ab1396
GIT binary patch
literal 26568
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
LNkvXXu0mjfqyVNd

literal 0
HcmV?d00001

diff --git a/packages/backend/test/resources/Lenna.jpg b/packages/backend/test/resources/Lenna.jpg
deleted file mode 100644
index 6b5b32281c1ffd5893d23392169cc368d72e0823..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 25360
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
Ho3H=b#ca3`

diff --git a/packages/backend/test/resources/Lenna.png b/packages/backend/test/resources/Lenna.png
deleted file mode 100644
index 59ef68aabd033341028ccd2b1f6c5871c02b9d7b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 473831
zcmV)CK*GO?P)<h;3K|Lk000e1NJLTq00IC200ICA0ssI2dm2Nn00001b5ch_0Itp)
z=>P{hp-DtRRCwC#;a!q#Taq2-jp6QduC*iLoO?5?APD4(S|Fp0Mp2~kPbH(7IwPY1
z>QxO;<J16Aq`?4^NFb}S^4@zQ_FijFcOOc%1NnXY*Z;5oCxF5UZMO@rw8x<&rgvVh
zqqIAJ_~X5jy%xqo4IU?W^K)Y)t7;robaSh1`<dgPLQnOZ$EW4$WBRH(eztzv`-i>$
z!H&NHZz`L^k6^>6rMeRnO;j%9d+4>g2Jct(qkjE&%Z@(&H|x*;`21Y@<@47#etfR|
zb(Qhz*nQag#Xf%j7<;d;aU9By2vQ*fl4axcvdwJC(7Oekv*EL=)oc?$R#+qoRrDwf
z=PezHrM+o3x@e=K1}5IzKbM91@CYI^ul;y`OH_NCAFOBg$HTr%LvJ3**WOVbW7L~R
zwrIA?&U3BBZrXcU+kWNy@2CCf<wmlu!ceArYJAv_A9);Ii*E;WvDj{&uN;5t`$xn4
zJWZeC-=JT(p7WT!bDs~Zn+VYyj(j4{xWD$>yJf4;4p;V=Tzkbibaij>(VY{!@rFQ$
zeEVYi?jG{pnn4=(r5J$?JdmGxeq$v%v%iI3l#IBFWy8)7KK~q5{r>x2pRBueo1KL-
z^Vf%2ISledsbSxXo4Nb#U0p*@*n93gwF+<Ki(TdKWB&kMWJS0I`c}iuyYG$%Bp;Ud
zHLI2%#!>e+R38r;gM<&}^DEl`*}k?B{r>Pp)0KkT!MTB`=S$|qYJc;Vale8Gj!_%a
z)_S&uUN#R~SC4Pz#^;YsxAPp?%B?s$HxA{?5N$SjzZ7(39zXN`^EJQo>!on&uzaC%
z9S2)WU7{+xx(s2KKc2glJihm*zmLhEIer`#^Wz-RHl}H4hMRa4{HN%bv+8jeHqjiX
z)}6-(^z!Emdhge7#?T?&UZ=Pbt$hw(7w6B}?)59jJ9T$EARXVlU(C<)Z>%5I?AmyI
z)5_Lz3)83Ut?D4Z2s1(5K;(z}-9SnyO}D$pckHcWQrQ~5?<T>+M>hyS8HT42+CMV>
zDgBQJ9_N2J#^3%;oaT>rx_msuO+6S!3lQF0R<NV?>UHn?`Ssg={jZ<D{$c&Q|LO04
z`I8!3tKb34<2dl_@gcV`OE*WU?GVyJ55+oNSI+(Q!n?;FF_6#N!w*ZH8f|{)^{kG+
z*!jzwEF9ZHt2E+me?9%<P4}<keD9mbd;a#rkEn2#w`1sTzGtS}MMWT=cS+~@-PUC>
zKo#YfXRbe+y^Xvo6CdxjGVEFSn|}S;bMDWYkFL8IXY<ykj*oY{e)|6Q?SJ<lj?w1j
zRw=#y@$>Kh)jt^i>aYIlum0-aONPJttH1iIzxwx*;jjMcum0+<{=H=QtH1iIzxu0x
zFZsXtzy6mr+5KzP%WN2{i-A|+%iDGTs+i2rzQgb|Es!DUx}3Xnv*kH?KYhFrn^hWW
z{cQEg@k#TJ;N>YQf02J!J%ip3c4`dw0hw7feVBO`%k+@d1N@V%liYm$w&}f9een6b
zfBo~iGQs=R6-*87u1@zx?^CVnpd68Q;?}5FJwE3CtP$+Al}5@Nt=uqcyH(Lg-*~MW
zK3BF>CKa@n`N8!SgSxWarY#-sxOT_rKwJxc;O!XerT~YHFpSbN(pc(qw8un)c-?Iy
z=q_cuvOD{*>@l-q)SZvBWYlGS8=K<Zjoo-mtAx2ZsweU?c#ly%J)8pNqf!`V0?o$h
z?Euke7Q<l=j5ZA$bTj9t@Nm3<0i*SeX=RU5zetC3+pOv_vPJMFXK4n$zM@+wmRx(F
z+uj@(dzX21snOh3-^TstcBy~DxNP5})}~PJKIN}pgY4n$g|E+^<T5X{_9#(RV@9Pb
zAi(I{z191sb*S?+-1)k9`NLGJ8F&Gq%p~*ij@OfnR&|{0-7T8i^Vy6ZM_hNIoLh0i
zY~6LN)uQC|&6;f$9?z=N+3Vsk%dUMD9I#`EE1RdetY}=xQGTkTUth5j5RE;5*7_p7
z?1<jli+%(XsyZKd$vmmjIH@fUuZ)dNV@ZVN)!fElu$`T&%A|Jmh*|fSdS;h=f;H~K
zh*j%*pnVu!^}_L!@ANUjG(V_BmGLCH^$5O8zuEW+YqOU5z`9|lyhe8(?dyYl=J?6i
zSM4O-y!pC1NP5*WChFq)q48ndlsRsgBQJn%M0Ug3lURXPykoOZ_xx%;e3a%8H;XVp
z?t2Rt^#*TYgM%^pUe+RRLkNvZD3nk1?0EC>v|~7fyv@IZfVo=%MuAq83EeDn113-q
zVT-TcjpvWgx^`!IoStd@!rSjmucaQxd*vE*X+Qb}IGM+K)_J&A`3Li}eNL@D-lXnu
zFEa14oX#sZ7@g?Dd|kUET-FzTa7s4O1lF{oAHB1B_n2W_wT1Fys4w+VO2@`{jQrB>
zjB$Fla*(!VvCS!)g^e=TE?`hD>s9O>$A?@a%nmc-IbhE}UCY0I`^R7Z-Phm$kAL|8
z{(t`b|NbA+|NH;uKWBA6z&7CYxU7ME!6tm5V3Sr~@-{+SRB8UL^z;oL@4db(4x-bH
z0!#Gr`>IBp+wq_bqT6Mh=I887hXtbNVP(d9z=TOH_6>h)->P@k7q*#FVs^8#@|hZS
zy|O~<>htmXd}g2d+%&7Jd(6v5%tx=qIeazh_P%^PsKPNh2kvXP<K8{ovUEmVOY^|J
zuT6|-kTYb~27kTm<XE>nQiNcc*_w}!x~_?~-UE>Ln50vS2xAsN$G}ibVz<Rs<??ZI
z;eI0AdRs4_4?fK3+$B66QDc~o@%3qr%H;fDePKMjo3#^%eg23^tZ8qWtf*y`#$F}#
z=HqSj#l2Z=xvgG_K7z`t=SQ_jv;A@?limpft+in^AT6A05o(+ET2$6J*<TTFx?aEo
z;zo?>HWEgozdm&w)****LamnkAYM8D#<WzoscB>Fq<^3?AI5qhZ^c8lalEUFSH}b5
zT#UGOPto787xg09VLBd1HO5i)9ankC9m{c4D|bh@ljc@893642q#JOp@e^8F+Xg`g
zx|_qS=6kHqs4ffRXjGNCiIuE6Dhbtgf>+s8w-58Ub*jkq!ZBJ^dKho5m(?BCHBT54
z9`*Tf(oSBVc$gPt)}1JBa)ARI{${1H7h27^YrCwFqd~NSYNj3Z2lrCL!OmX21|W6s
z#YQoT-FuJsITi2*BatD|<YEuX-u(JSadO^e|3u@@YEWi<aSm=~*FeiGa+Qtf#$Py0
zY7Pd4W&V@w$@$i=FB^sy&JhCaLK7<QJ9(m$F0=tMZz*jgJ)FU~0bAq2Bq}_A<vt+5
zrutL<7E@h-p0wFA3hEc!iBP{lGnDAAy)dkAA{=4gCSwq0@uD4)%R!USpurDXQ!;Ge
zCQ>k=xnH$wzu0lqy=dl5x%FCO9z<E2RYge0Pt1F~zIMC|6S{XEGd2mG!7UR`Wep<S
zS6Vf2cf9$W*=g|x+c=J1D+hfJR-0hD-H+wIvg`=URei88zt!`k;I;f<+FRG4ZJ*&g
z8y?*K+OCq+JhN%0(B%yCA!I2rri1gOY!2EOGV9lE&$a&a%j<vnWB<$l>5u>8zrO#6
zKiq#<m;c}X?!Ri#uFqsdvjkPrTl%Se$xqc*rYUQGw+4qXp9rA^ZI~e;yTL&g<*F19
zH_*~|$}U)RU7~{S_PNKqWlxlXhL*(RAOc=0dcnut-I|egTQ#ifPSpv2<nyb6tUEK(
zXj=XGsYYYh*E4$6{T11!?5c5?HC$!xBf9tYIGhP@D_Kf6&QSn4Pbr;dz57*+;93^e
z?nnVk{s`?o#(}*%0vi(yce-#P4cf*IB7$`2;Or)va|ADMvoLG?MYYFq-V0GTl_+rn
z+tc%0``*PkEXw3m0nz4u{n`Ado}pGwfA;{gd<Z-IO}g9+Ba~JQTE>*&v1^;REx_oD
z6rqF-^(<}++db~|*$YP4cV<%AB30}*5ZLqqHHJDf=r!M+cd@Agz0ZNQH6FTegjWZc
z(6AWDrz)+=HMADa4<LIz`}ir$bZ_5Z{PxbxBnR()ynDXd+ieg%R+klE8zXtlY3rQ!
zdbKWxxi-|pO8d5wylICK*=7E2vOu<_I9+M7>@=FAAn&!DP)ICTwD-a8Tp~@cFo)0)
z)xEt656uxws_XcM`keLU5vuko)YgGv1P;$?XE*iuCrAC}eKcXpjR(W5%`Jm8v$;Pp
zf+`u~y2{@Q=_os9m4-39l|wskHd~<772c(4$Aj;YE3MOx8P?ZwnBLAuUmXuCFldkO
zgC!rLxn3H>A8*Wd{7_p!;=wtXgl+igc`JVI5Y`hIs2kk0Z@}Vuxu4V$q&5R(h|J<U
zvQP)#PPWu=3&Jg|Y#BuWRBsAIilorCw>jmsd=<j50kA@Yye`-SP-hv2&BN?&{RD(#
zVV@XPZ46o)>c-WKHX{ym4}ipi8);OVNLy%!f)cGLqf!;<TfO$DUL4nNS0#ELiqi2h
zx;>ng=0rEGS-_pI@mMo_eFeJh5mMdNF*2JwWYrcUFV3U7`|-PncB6xDwLgd3mPXj&
ztjuH>;cn{OrFa~kg{{L9P^#VM*h}4tBU?n-qKGjx+gA*azeQN@HKNTw!c#bmRXt=L
zfVp|K9X=4kvewmjet!Moe}4VzKm7O~|M1(t`8@vBKYU)8&#&A6#ee<3XWxoxESQUL
zp|3LMdE&kuPhj)}tK7vgDUrhC5Dv3v*D(L^%sL;ImW|+}(Nc0g*e+J*bi6#y7$`f(
ze2~w498t;hC+^E2Wr@pX(;Clh*aEo=y*s_uT`5YPx9srey*v8)TIaWQ-wp5X?J&gK
zfcdb1Tw|Q%ZR5MFxnJJmdIlBqaAbkgdll*tuId(BJQU^`W8~)H+6iSpzQvp8v)z+?
z(_ZmyR8PiS%`ht4={g?hEd{Wxyv+N)vlwygjEU7|`SvqvNy#uC<Bs4ft-XQpZdz8x
zX-3Fq>*jc<%{&{Y-B0s`4_-Hr$-{b=NV6^qY*fTXIa+iUZ+?8#m54W3TVHenFjU4d
z_p^CKY&{N0+ArE}P0<R(F=fy=GWXP@%nPcd{rpzz;&`_^5yR?|gLPZHZ>IN<?Fksn
zkkWKgNW-c5*6S-`-0=9vKc43(x^-gTJO&KBxKMFD<)*7dz(efv=?7qDdExCl^KwSO
zY+sOqFn?$Su$z-r10_4M7WjyDv1EUNp<LvKvH2ELrb;`p(*$&KPRq(MTfVEKm&c51
z?8P|)Ro#wuRVT7s?+34|$MAi-y_IfMQmpxLJpbNC^!Dw5vfLd>n&qqES!E6*DCP{P
zZdhQWi#)WR2>J|FJ`RFV0uicIme?li>!$5IV^DstjU%ij;#D$lwRd6!dZ2FE?8k`v
zRpa-zo(3aAMyrN|LFl%GVCXy49U;;T;E8$x?8E&3FKMUsXU)?(+;6ZrCsB@9Y1n8)
z8-G%MVN7>tC&o~`dEPRC65qYE-B8QS5Z@F6h4^Mb+toZ}L8Ef|`i*!OTm2Nh<uF4v
zsuXUBt|(dncDNZVf~4X^0chG3f@bKaXl4)yoa|3oXC7+g=htfwC{5dsZzEsHTRyCI
zusvBWg7xtZbkwrPN9@-cKV!de!fj%2GnjFwwUh~u^XMzOGdJ$-<_4-U1%QQfn4)(V
zaNY1ZP`0f}8ZZDHgtG4u+BV<!j=9A5fqU7&pd)PkSYx28=Vb3C^{CJYWXXKQ@B>yy
zx$Lz^Jj=fR@$<JoU;p~=_aDFN-~IXZdab*No$%@Z-QWGkqG)WMv3^DFA{^6*+MlTJ
zVi65HWInE6YdC~niAo~OPvTcmoixHE43;%;8-nwyP449be*SE1vzI#D%B<CHT5Q6f
zeUQ6=2X$#~=34`GoP4d!7j+iL{(80PUaHsHZrN6^7w%g0P0`=Mdo;QLm5zt5A7irN
zc7JXd)ns6feqM5ms#kGLSPefW`c68X23oI%x^ir&+6L<DQszsD^MNu$3KM<xE*&4k
zoHjPqi78X<IF7wx%06eiXSTwJI?Kko+>vO=W#Kq<s_}3sw_EgE@6Kp?H;^wciDv!E
z@83N)&^u0aDMb(U3kkDeU^1%!!C<S~I4osI>L_2?p+TnHL~FAF*tG3?=W+T7rW!O-
zJ81`_`#kK~6*WY-9A?5CnD?c}d+HVrfBT)KEXz7~^?1Ja{5g8}j`Z8WK0~s+p2%)2
zGn?2pjlI>I*Q@b{O7Dis##?4u3~w~SLR*_p@6F3<-)yi=Ce_tztH&NdQdt#4-m0;y
zhRT?-8R|Ak>6Btr_Asx#EwkE;E^b=Lhjt=`h-r!~L)CaIwok@gjV^mfzA)Un&BF1d
zy_o}<s0{Sw?^KVzvBhw`?(UgmjO*6i?C<HjasfubdpFPgaSv{gKEJQNry2L`C}BGK
zw_UL54TTko@Rki`vmL&kHRl24CbY#ln&b3W;*j8E*f68k?S2L~45jYz7WJd!Tj-Ka
z+7M+GFxTFi-`D_e_+tm7I!Hc-deKiBtY5G*aB03-E*5sDt5rZHY?}!t@3dBlmg<;j
zl0*CEJYkzR&<<`O%qIE^K7iBmH;#|4HLTJkf3HsOONaCM1uM9>m!aF=P>GZx{WxvV
zyy$N*14VDq%r+2BcQpVr0NL>%iv7jw9liUR)jg`a%<4LfjfZ=p*y9+?ZPWM5TjpWP
zPF6T^o6a<ucEnk^+WL6^?$%hhY%g){SItBAnEJwaSTCT{ksh68P#p-)cH7LlOAZ@u
z8kQH2hm6Cg?U$NqgV8ps_ThWO%2>uCyU=VqYIm2-li|4*;t&$pyN`YS`1;4MU+(?K
zPyYQcU;mJKFKefX((c^;&;RcKNV1WLld926I+ZWPO?#u2_a#5EZu4lsDEf!`>Gr)w
zxISTSbvBloaFG>q9#QvA$-U+8q{uJ9t?7qB+kId+@q(LtI-(=|^=k=KU|oo%%jCtj
zdTrdVChPTN{6xbBdR=>Im+F?ON*Nl<eNOQ_@OF+nAc+KFFgqlk%uOZ48g{VD*mq}}
z)fLk{+_rk%P6{F1h$FhXtr=rZ4H)IdiWsXfbgWlL9R8(ovV`GtL~>#Txt%|0e+@I>
z#cZ*TFl<fuoG5zahNF%L_LJNbQ6*V3j>yj*-v-3arW#;|DFdZ(!0Z@1^`^XrGuI1a
zvI$R98s2R#WHD1ljPR8<kJ;5H`&H~Vv{zQ4+L;N46Djj!7~J|Rp9K<mV;4PorJ~fy
z1Pzf_nT_Xfdi%C_Mz3<|pli#}<BivI4(kmKrz&UABl0z1c|2IV9`BiO$!)*lW3Er|
zW8lexoK#ke=fi*e@J0d4r=hJytvVf4hr5O1pnI$aOUDQIOLVnqNP4fjh5`|H)q$3P
zII7F#K!fRWtcLDBPhVejHY`a%j)ER;J5bekFjAV`;!$-49=E2)iwMemcr#7lFwtmM
z?0};$<Ij!qfi`o(5g;J(7&Ksb#yhYKwawO&YH$0N*UQaEwYx>3a+w>(TN3+r9-+1C
zd}Ft(d);{)LyZ~EgOp8oQ;F`w{(^ro&4Or)lLk0!3p4zB61V2>R4=Y)`{5YAZ**`p
zb4EYi-Y^704#ESg<GZ*5r`eaqgKpBTei)eb4%^^ER;J{6cHPsxz3C<wc~SyzCU^|y
zQ}%QA-?JY<XY5_h`)LC$<J}#D*3Om>?U!u=O+zEh6z5I<iLtB=9yF&Cc!xF#*mC}b
z00$Bc_7q=8T<G-=Pj%J4t8FrO7t}&~ukyL?bp}i|2BdR$4Kyp5o1tN6^=7UJU2S%K
z0%MsG*&M1RZ|hRFT4Y{2ykF))(oHL8$?ba0_m7mZ>H88bnFfPQ>`jw5G(0ugCit|o
zcWz1EeF`$~@X64Oa3)DbEA9UL@yn%u{8fMYQ~sMj?;k(zLNG?RTFrA{4*&1|?mq*}
z*skGrbx+(<Egav#8{DAK=^{bSyK7Fbd%oHA#aNbZc$FrG#hvgiJ7^ut&0j51&Y*Bu
zBi?07^q`}y&CvFpQAI{2WIQs4p=OUnHtgt`T5o#&)(zZhRIjeR?yr7b_jCNDYo+b#
zXg}}1=gwXGEE~GCUpdYP7uMZ=B7mxqDeE4SpgL%7=QLY2y>}@Qo#G7Cp6?_~(eD$L
zmXNl0s%n%tZS>2-HD=^8F)6cB2>^QmnsMA*4|_|ikqLh!&D`P5t~?&tm$M^VF564z
z#9jOCchcQkW->Lhx(&{L`yKlB0R2F>*KVc780yV4!iK!|4!{J;6{Lu6nT5Y~ZAWF8
z-KqFK>K0gl+i-;fgqJR3fK659<?$UREa^PF#QBqurpbIs1spcRv6MS(SJ93+x9brs
zfEQ~h%KKuPMe&BuY2EFI;b<1K3_)A6^XR*Rbwo%o`e=>!=~nw@oB`689^XcOdN`An
zILvP8Z4O=SmQ-2@sTm;8h$5+PZk-vzs(yRhWr5Os^y{mKf!x(0)!VEBorSV-T4kT3
z*Tr%660w|;eMf)V7=)KC#ao8xVYrLmyzU&2A|}x}hu{79*>sT+{ep`+;2zn)5F2z^
z(tKfXuZe2-$jtdTbZLx@exTdb^N`T0+XO|%1Fzr0NLi1`*UMri;K#SDuG&Xi8*ogQ
zn74%)N*o^Q7STk+di6P2q+OKM5rnh@)<&z1^TY7!U=_)98f{leHvDe0qs+Y9uzR<S
z@7-ic^OUu>Ks9a=T&+o4f(cCxvjlwleS<BLGHC&Zn#$_>gwEu55N2#{%s24KG$^YN
z!>##-%%VdqwC;9)=dn19MtT{IGI1G5I$VUoz7boPWH%c1%r<--nL9tX%WO<l6c}jE
z_clTkNC&|aCc<dt=$^-otnu(luU!Y-zs368BIf~-VZ=wsgj+UJ*>-^PamMPBIV$z@
zQd&q6n|RY5k!i!grEu1`G7-iTZb4L+Da#9+>^8W<9d&s3`E#r0UT}vGMy=;{-Tw7?
z|M5@HKmPLimp}GrW8GO`?#&=YCBXFm$KU-I1_xz(fH(81_rJkd2uCG3S#(KV;0YAz
z-JA2M3a8A|@Yo}G|G;^o1sN-4sGRn21EKJD!#sTfMh>%MiZ`3|WFC%JUX%Ta3Hvqo
zaj^ff`}ZA}?#i~V&9%x(JiBh>s$IM6-Wx72d)>Ffr{pUC_OXrny#4TyWX7=Y&^V@6
z;cTN*<tMEBc(l?u0TVGfDz9c-dxsO<$i-#!77Zls#u(vc;WXFnJ?H2}w@Bm26K1s4
z)pnRljfBtqEH|5Pww}ibId!#U<hI;A+}F-BLbUexkMaCbex}XgNrsG^KDI1m`?g`s
zXvkVv-E7#Q+S^YX!n)aRGdtdUJ&EXc*WNa9h?v@!nLs0?c6qw8b)NQFK4Tj@Lu_b6
zM(H?7)bU~KnRX3ErqPc^M&8VA<9Ds4;l5VsO_|ZX=EIg;rC~m&R*&hxCXKFkS7j4*
znq?2;#`cQ7HTJXOt&wqEirH-XgmpK4Z*e#)0;?UW5x!m;ZhN}P;3Km8oX%bGjk%QN
zeJUmlBMC?}9%t1KhrxpCqCqXUTJnQ1?w4vD4|Ja$gf?ldzQ^#AV?}Pw_n8;TmTxLt
zd9OF`^uw_2PMzL+7cE;}NXhmsH?cRnmMq(iLfYF&o~Rd^uPr-IWQ_GCPI?+B)}?ti
zQC-Mzlt-G08r^-;vXv{FGPNBavulL~EMUiXu4Rt{Sso2Ic$*b_**WcQz~w^(-P_F8
zMmQz*?ze-x%><jG(Ff`aaTqVgurT1ZBgjA`^k_72V@%7J&50hSGB>n0XlG#yc49A^
zNDdiY8`C9LmpSVFC)Vw102U=2l$l8096z&v!^byXqxQ0JImGY^p3fMx;~Sl%!NS-?
zXuJ`Ey`z(MLuS^nY1)lYda<?E>y^1Z8w^R^_K<FqIYu+b>?-$62poqxX02s@l*upi
zqeah(AvmkT^W{#A)4u-f;3RLnzl~lf6Z+%#zCU3@{TkBIH&%OY!@lv|_AP1*M`dEd
zWtPLtxlBz+3+1)r_v(#^-n+&To<y6qj_>;VrF2(~A7A&MzUoi^c>UqkU$-ZZTrcxz
zmN}rIc?aeH{onl`h!g{sngjh2{To2<hjcL;$Lc=FMBh5Tn{B09Z)5%1r@`L(^9h4|
zT2FLogdsF6Jh?&lh_+3Jc_hu#Oo05TdPvLSyEtfWyt3cmWi_R(Whm3$g4-8!dSxty
zC_Vc}9=D|lB=XCO$%r_u8SKl}?dK!BJHpK1&0RKx9`ICbJAR_cYZc%lm!_R#*4nuu
zP()bR>?S2a7ckq}V-W2OPA5lFkGf^v1Kwqa+Zc@x?dRw=Gx_PgxmRnLJnAI`{-U!P
z2%9OwnnU-E<HIr)Zp%ik<}vgo`*1Y^lADd*pKjke)2%JI{V1=&@8}mDQOp)-JVG7-
zQS|$h-`}yb<BYtD6Z;iL<u;gx2Y0@`(W7!<4Bv}y*1gq%embUV`!Ts+E}$LIaW*7|
zYgaY8S%i%P;wyc;pYVz+yUPrScgv?T_T%01C6B1$uF*Gf+TA=HdkKMVpAXDieGcAG
zAUCZoDOk)wc=C#*=>aD1%h2IgF2ot@%ZYkFt;4#!HqT)@sla}5S{JRiVOW0|CS~On
zhWiLIXxkD^k5*<+w>m%g_3HD@8rave8HmX8+WqlUFO&`Li{_&Xxy&COhS#e(_&9KX
zVZN&tRq3Z$(P3js<_nzU`OaDv;T7z=(sUeyUe6U|&yczv-$sG=3te^|y=t#@R(&z1
z8KWG&?eN~EL^AylHJuoP$H&m}0|>T))n=RHgEpvw4JPV*(0}uu9#BI}+O`?KvWIcI
zc!0yX?0j@5A~a~4X0)%1BQl0pS`D)#+E*bA8{P}&?`wZqFge0nv6K(srj=FWzBnF0
zK_A{f)XfPj18<H8?;ogP4(k#Mm4yLqZSTG;2J%)HFoBIN`Xi2;c93ak8R5h92^P^b
z5FrEX19f*itY`*hS8nWl{+0`LcR`$Q^I?c%=bz(v9D7%h7WM6;KA#+>Vp(#Wo~!QV
z$Km${5{aG>XpQ$#FCuFkkk{i7#k}C>QTwh9>zS(uIUZnz!R%pb1>i>$c$LM1y~~>;
z3}IS{uyNSwY;)ZkZxem^g84XN>{j=>@cO!b`>KEVP5<WKJpb)y{(AnLe>?iOPbGU7
zhBFxxl!x5^#sB3$1qt&ijQn%O8~uy@7bC|S_oetAtu}X1wN*R(_gzmKxL(%S)2$J`
z>$sVtxg1cnA?UPG(A3)?4_cFHesrg|H7)o<^`QP-F~LN(_#OSrmchRHme!~2jas~(
zIev=QFMXqX^!vlE-0F4DRrh{;*naJlpN~O+ZkDijXKGCARJM6|tI@;5v&$;0EoeNu
zbG%;uadxY_l|xw-B!(YDwTgrHO{xS7AlWwj^<q3aY*w|Q(&+5WK{^u_&aOC|yN^)t
zVYbvQ?J7COaddsbXZJP=BXY>LsLU*&spNT7UhZ$|tK+D(6ybTbcKQ3LtrkQR4GTOB
z8?7TI*0VGeq6{b$7`K(9pMekrGs{axJl@BB^-cFyDcdWvC<=HS=-Km#`!egsDEQ$<
zE3el5N*l0m)U7y$jroRPAR(rA*sx)3X4kJc{)W-@`c!<kS_1NMIJN6`Ps;Nc+)qbB
zKAqA=94Xl_u{kEV>*7{phOfe3cx}xdK{8lUT4njE^*O{kN8U9A%oBUB$2Wd{BBpV%
zRpS`wqEw8%`tg0dvZI{_$mor%<)|AS{s`puJ~hp-!~-<$A7#NVRgM?{T4o&79fGHl
z0YLP@@cre-yQ|F#@U9i-H{@;D9A0<Y`KVl4rFF|);QOrAKXpIR9X09eW!CYYd7noR
zcI(}y_19O~u}NBt2B;Af;0j((Hw$-%k7x@(C)nOs$HSzKMGoTX^R!OXOa42$12UuV
zE>GIquq}I7cS8f);~_K3X}88f=OMsfU1+H3tVFP#e)W<Cm)QqgxRWCc$xGy*)9e!7
z&7Q~;Rw|dk4z<G^mTi(%_mrQYj1zs5yXOZSq%+7R)rz1!-ux^JqkjZ0+Ce*jK`R(3
zQG${dY!aWq34XR6eebnUpFd>p6J50N`l?+Mq`Xx4i^F|CyHoil)2bCl%N@sPHB<EA
z!;1^$U=^BKMG4UJHg=4MtDgSTv0rzYIStlsBx?`XtLKB>0%ur<Cv31l_v^~a<iWmP
zF|B;igR)}+*)!Nn5n8dYAD_?bPcQ!Q*ZU9u%a4EibwBU58sESD&3=6$!VU}=K7yc{
za`%7nfBr9lfG-MY!U7#uuNo)e95?LfUcy8tY|xWCJia=nIA}MEPOsIpQ?>Q>?yoP*
zw~p;mh@VX%zTEHfcUwhsx`0U&<Ga@nG@@6ppU3>s8eGqcM_<<bf~d;w=>3&4UO%#9
z-`3CPow^?%x_4EVSu3XnN~)WxYQ^Z7fyQA1vi+EKanfZNQ}?US5k+Q3RqriWRH8Wm
z1Z5U`RZMfl+Ju@u++@>cI!6Ueuj-g}$*j)z<Lk?Clu0gE6;=Fc+Dhl6eRD+&gQh$D
zfjL-NWLa>`t|Cv&bH=s$-C+4Py*F1cm)W7~Wp5{zPkZs4T2~d^2YbuATYy1^PHOjj
zcdAnw9wX#$qjweToF+54_0AZ6{9L!<`J*4dx4yO82g)DTyBORLaz!tJ=~GQ-;ymzm
zmpM9heCPhv;%qa{;Ops0k5<8xQmInz<k9((ilkul16e&L*X8rOU0)m%9ib8fG~kj3
z#-p`dDgUVZZX7<^ya~EDDtFm&w%cfI^aNNjqstl^4k31OqA+w!uVj<TLgD<`sl2{G
zGrT2}dtsba<s-1WMp@6$zTO_E@9M^0ub`VyfIHVEO=vMxIIUiEE9c&O_@Rg%6TW0W
z^>s&dKfc-hf;AaMGZK4sZj6J+w2hRLr%%UW>+#d>YbZ*W*G1xTpzeUVa;)C-VP^6B
zv;Y2w&uLD7-O;!MaI@@5mfFstIEKlz5MFl3Mv#hZg`*I&)7%kvF8k&XY?(WAGfLy2
zXuZ_S&4r?$z{c8?0EZb^OmyF!1}|#TTQgV-Wi&>(K+~6b?0Xv;>Fy>g%#?nzeu<Yp
zej>K@(cIK<K05_xmkW~=IYz!{Y!4_=4MKULU39JmY@cM9dm9NOFz7cp!6Xh?zyvv*
z21sXVp||wWda57$x6G{3HzHD(jdK)LnWYEZ-=osPZOIHzi}$d=1fG>;eU?fwgXZWO
zUAs8B5g$6-T*H_NH#<~`JbW6%dRa2ZThuch#_8Fu80#Xt$A?|ZV$x)8Z6l5m`8E8F
z$C8h*)#7nH;!%Cu=WBocxc~gwfB5zO;~&=FzwS?-*Ujp^?u)D0-Ic1%@y_R0yv^E|
z|BL_TKY_X`U?qNUei=q7t!X5etXlC61hCDwZs%BDrTB*4WpCC?VQs_qpBrx`LvPF(
z$M2e#b<r2?1mK4uQ~GH9#xk=)x33q>VC9VN5{G#LrUG`sXPRlDGcBXXw_AdLh^wFb
zb*n17lvR8!cT;VPK!fz7?w&J*B&fz$2|B&|5zS)D-R)ZTm?U*=jfWN_<6vpo`3?8Y
zU2UAr8bejAf?M1!-W&v-04J#Fdl4XSo6|`fIJR(3G$70M<>!a?K0Q=O^b~~-8=-=E
zv?POF!YS)gTQ1pOka2jvG>7jy=Jb9c2kAq*&eKz1wGX;m)_6R+u3qgjPEvG5>x%R2
zc(eWLa9XQdw0!^WuOB$h?K)I@hVQ~0un&{Fe|7vc1mW)UeXJ+g=C}8o3T8VkM&H1P
z>BWmw+7W(h`+!Oonboq6;Sxre(*U!HZVox4?+Z2uUl@POTAFhxK}U5bu+o~s8b@kO
zuQWFC23*<Z$H?Bg-{5Uo=As&77&eY?&_=7m{fx-wE*~%AXg`GXTxsP!d%S?(V=U%2
z0C9M}=JxU~rj;c#x{wrl_TBRg$SpSjzJ>16O?w*z?x*GHw#nu$TA}(JOMIA~=%C}v
z-j7{|oi>8OZsY}>VK@3kFuPbc#B}!8l|(o=K60-Z{+65A#ei*hTAY=aUJGwX4`J%=
zQCa4k<3Ov}!4YH(tCkhH@B*d-E(;FXx)=wlZ6>CLu~Q7=-P}pujNuGw(+zC-K)h9c
zv9k5fvypE-+wACTHQ(7!*zbuio$s)M4IfOn^`uL1^p?MImDLjDxv`I?$q(AW{iLhU
zkKQXP>63KoOBHR-od{ZQRzZd_YS8Z+ity_Au*Vy{EDqA)4LT6SMV^4mf-tJ#Mr_)o
z7E$)P*5_I{uF~!!UYC66@nEh1>-6EyYF3%bi&51a6FYp+S;nY#z!tMM7<cnHPxaj(
z_f8@nZ+%@v_VHHxE9b277N#zh2>GyAB6v<kiSwu@-=mq+j~egi`V8zne||tzPi@#3
z<4B-(y?)%U=jY%5vj6z&{cr#H`ukt6&nZ-FT`9f8Ry7X5f-byDSu}W@{(t|w|6DXk
zvi5l4I<cN+1`i9sotCyQSg?&K8c`~{rJsyx`K$S$0&JD{IIWk<>|om??lLRIH!?T@
zy&!M<l0C>veORIB>WlVWd5ar5d{r;d$S!i}@~Y)nYfF3ezWep{{MremRQJ{QjWG`}
z?`2L-v_InY#5p)m<n#UUUVHVRd+~VNck_Bevt)t@b-Y>3#5F@XMqOWh{C?1)Z&nc~
z6p|Qnw^5al4G-wiNx^%Do7KgSM|8*D-Ei3g!*}n2L3gwLqNDFs&GGKImm0=1Ej`&Z
zb`o6kZCZsk{QX#$=J3AgA)(v2E|TrDUB>9O%>f>YW*A94ziK?3ndTHiQfH&++%|Zx
z_QC6u<KZ~C{{;UX`$;-7CF{TaC!JtYe8Nlwp1;W-p=a-M8(_nZdLIc3rN!a8CFU?e
z{OQ|OUkn>;loZ>^pxKacgsKE*^pGqFAcjmT)_eH#vx_|@p?u!`;mq`KIYF<TM`oSh
zlnb5kqY#!a8^fv5X{zlXqn_i8#)JEY9ngT<x(eMbsT{^$<O!;}2BgQFpRb+~a<3;D
z*E|j@h6Eoeb%L(Lt$?qZkI^k;rymEl*=!TKMUXCvFa_*VPcsY1%pdHvz+quZ8R<tG
z9Wpq!_4mkv2hpyqiX*rV^Dzb_ech<+^h;SB(wx4Z{>IPO(aK@(G3t8B-UnXkqxU||
z)U+`?0*`Oe@Q*j;XWKU#0tsuxL;C`Rb2QArf@voh@KZKm15Bf%uEt@t5l)ye%ogHo
z$#g?926o~w&x`XLVZgS?=7akwfISR}a44*|e3K8kI=lU$naGWClEB_DH-ic_zOybj
zcDUr|-F`sJ+A+5JK)txl!mLU;?V+$Q#;{2eW=(s*2MG2C5;j<iE`U}8-C${_!Fydl
zUbcR{g4f%}OlVzQ#t2Q>Z33Ngnd2BfVvm@2izwZgZ^ypfdl}C$R<}(bRhm8=#7K_m
zb#pFb)Ux9Yt-&tiaQSq!q3VA7u(FX=mZLksmPc=>CyVPEaj=W5j%k6t%GUk0>iT;9
z<3Hv<{`UIUf9gN~xSk~J+nqn;hlllb;r;ypcJJAPb=z(kXSo0Kzx&TXV7ToY_-G%_
zB?^9cFD*&Xk;FHh?R6s@K6EtfU3sI~Q2Ri+f(CI$pC5*_9vn~22Z3?`ck$s`R<xMS
z+s3G-m1#sXI$uoV87#>T>s`*uWSzjRo9%DC7iAlKVOBEly<2$y{bE<%{q0-qmoqrV
zh;s9u`LKC^eVNRxVWamo%z6h#>HP5CyUluIZ*O=U*e?w_824wJt;E>1A8rjn<-_OU
z<Me(BV?RdZ+>`luSf<;z3nx9jJ)s@%g4wWft&7IU;73DUWz`t(G)ieo#P6LO${{z(
zu1j{>y80ZVf}t3;`aX`hUwr?W>$7Qqmp>*h%B)Etv%-w(9`D$x>Cvls3ZgKf?DkGV
zs^%QK+ijX|`RR3|y>s*W)z0B2ly~ib<X+jdqt9ut&1h+1W7yT%93R@Bka|r2y6sH^
zf1~>op;0jm)f>l$U9apShw_G0SK(+CV_!YTBTlVdkg6mCL*^xi2Ag;ch<F^U+Saq|
z@a|HMazTE-`g~_Mh6`4Hcp?WtlabmWnjs6y&@k^k4`nM(dDr$p7Qs;Yi7;&$8Cf?C
z26b^BepP1Ue4y^ejJMHvM?XmfC#-7=0k}uysx!C*5^Yx6?dG(OZq?>EVdeML`;};#
z`?_g!_1^x0HicDBmkPS=0W)YjJ1kTQz=Bnn*XJHqRmOwYof|Q#G7hmu_~`T#=kVum
z@X`77c9eL^;vpZ_pBRrZ0?C0}1jidHtwe!U76)vxL3H~GC69-2)6COznCs~`?DXbs
z;XQ`+Zk4s6Z`IxU?y+4D^Xd_NQGO_5=#q?04s?~z)BDEuJ;D&y3ov@fgS|}_T7`Gn
zW>^`mfd*`=jchfo?_Hs_ai+(~G2Iqyf+@QB01O87O*;Tln?4&abb;Uc^;@P;@%ru6
zJu;<kpOYNY1zLh`mg_YhKk@maV*s8B8H_$U$MnlTA}sIj^L(3RRh{ov$=#)brb7m{
zo4$X;^NSel<@a-$VVt<H!AdL~?<xyygEJzs{XD9&r#q;4cgu(XJMX-1K7al3_kUXd
zcJF`nhyD5TUv*ce+~;{B%|z=lN7pWoQMKGpoYRzG`#=A${v&j@s@(wCARw~IZU!e%
zbIjIjxeXa$M!9QtE{n1@*+-9)5_>m^F4wLU;{`vMw|qEXR*i<QTVuEZ_Y?h3qyG%&
z;G@?kY@jmvoeXpd-LDIv?7;=an|fn@U&rriJOIb{pEp@wJHz#Pbvx=BIB+;6GG*a+
zGjD!>k1Lz5!W=;#d<+xk{p_C}tV3|OTSE5HvAnI1_wHxlp>Ng#-hOJ>%$M2+yQmQZ
z<3K!YJ<mXujEUTMd#BUp?6}w>GWOlIjz?I#DUk`Iu`HBq=TU>YZ|CYk`H(qnS}*s3
zGFOoc4Z5@4jN2iU{3koC)f=aImJLLpn?M2vYMS88-b&xo2DLBw>1)U7oA0%^3p7!A
z$AgxDd0*n$uV<0H?`dv6vpSB6bpZi0?Pa?GWFr_CoCPyhzgujNhgQ*0Hc=PYj&biz
zpB`papbJiHrYcP%hI?t<Jspi0HBLNE>u@(R!CAv}cTdOb<%jvl@9O&WLZ2sVaiHTU
zb<`yd_-4(_1(wIj7+vzp9p(|L_O>>87N-mo6p@@Cem#4sx$N6Nvu~!)g(mhc8-%<Y
zM+cBGRoyzlr$NAbJx;GL0X!adUpPJl>FvXIH}=l9`K|E`|1jP;e#f11<~9UOV+P?=
z3k=q)vi$MJ{o9r+9%J8Gui@wI92T2UA2cb?ts8nb?i^EC9^Q9RRS|BZy}k2oa~v72
zh`~%hY4K+J*)h=5qP+@8PKSJGrM-c}m|!!UwJID}bD~FavHG-n>O9D@9`sH#Ygix1
zZjgJy2sZ?&4L(rGN;CFobz3JQ6BBT-e3teBj-uGF<~P=7gKV)k!yy2LV|H#|G<%i3
zaSXs^FtzFG<3qjLhi{BGx(|jBgPdkBwbKV>BB>??8km$51Nf8?T-Q_Ux!1iyEiBa#
zdvuqb(_p<TrZrV}cO3Sj+#K|r!`I&KabGLoQnM~nYi-Jl<c!eOisU{0;Pq?ct-9S<
zVLtzf?_WHI*?c%!$NT;blHx<gW=*4fL;!I>9qdxp>Rs#4zto?v*B}0&{`A}a!&N_C
zUol2~d~>tDZ=LY6nC{=-R68)WUzzO*ppW7H-~G4$iJVaQ@r|pc9c5jFpR|k~%xYTN
z15xxh-%pKiT2Bt@V)nyMQH3n-w%9zs8@e5ax4|FeNmN<56-4TRNcwTlgX<QET2-0j
zL1yQ+z6~YL#){q)4b1D4bnX|fd+X@+Yho_bOX#ZCzVZHMU!SlcKWsfmq><7deo7X#
z$Ki@-PjhQ}aNi>E5+A0QRj<bA-quVhChu)kTMahH^dG;#VZZ?P?$7|$e#X8XwrS+9
z-5#4^asAM4z|O2Tlr+LfV%PGSuYCq#c#ZetzPpNO8M>z-8(|Q#ERL2LFGLjHQOe$s
z`M%hQIjT>)f3cLsf_-D}*sqR&xreC*p>Yg-Za<(BhPkT~k5kInopbbtGZn+LnJaA!
z?DjXVPuU@zZeJ`%9GxXI(~=Ei$tMkv=i9+odss^hlQ~-U^&7dHZg9)Z0Pb{LI=`V^
z_1u2UVaO#>5x7B^c(YMLygiZ^dT4(t5Z#1;hAaAjsAR9p*<yL^q%eN+_2f7zM()L6
z&<&w1W%2yz$HU5$qKK~N_!f0hY(GXm`@Y8G`?_DE7u!`~qfq;P#rtGTuALQTGD~ZB
z^_nC0t8IqTeVQ(-bFZyVv>CR_?kMlWhOk$Vss@v7sZA2~0v)1zbE~<!=;J)J?`yk7
ziNjAd#EO6VAMX49sC$puDBP(*hV5)a^jfGf97&5^VsS7?M)b7P+dV|MnU7G*dYe%u
zk7~7v(xvfToY3tet*s%WZ&%!|YEER-<Bg8cch+T@HJg~V%?;gXF7SlWQY^qu38YvP
zV^%FZzO#b+SIQ9I=mDCk%HP!VuEYRn-J)xpQdvwPc^;@zSo6dB3u$gfwe2pSH0Svq
z8@*PuI1W1s^I;5-fYT?D;sgi}Q1C%}1Dg73g>_nU_q}V}_r9O~_;zr<opm)}XTRBF
zqmSBq)jVAD!K#W``<gy?lZ3jOBRtJiI=ySdd_b>4C5~?!^4D|YacFt1HblH<oRd%W
zFjqlLr!GYFJjpVgVYh5TbnKo#*<6*gtFODBzuf<D>rel9{p&CP<CVa9Ig<8%9OG}l
zb(b2-2AeS~m3eEtM+@I3wiv_z`~T|yWNl>k%CH7n)-Y2L&&!p3^R^Nnu|Cmv#u@vr
zc(=Uvm{E)M>NMEh^C#>A$v9ZMkHe0Dy@42>B{Mty`XS97@UPAJ#eVMza7hnl(xe6>
z)X$-BO|!oH_=)S6-3P0aJMZHCymzf%f7REONvw*fSDCcu;T3+mk<4XrI?%($4nr-}
z-2d$N<LkvVdHOBi)xHs@y4S<~2y5M6Uk+2S^LEn0_u9s(&T`O4jgwQr0p3oN3(0e+
z%YtEd?&%R@$2t3n$9ux8cf6gEU*oWX#n81MV_#C)4D&bBosbUKMumauE-1}T7`MrE
zY$X_R9NBCJ`bLXASYH_*wyzbZ?<-2|$Z1g<#OMVanr^y<+4ZZM%NgBn3xTTP{ps@&
zvQ}C*1~VJjgR2>cv}?B?wO5ayD#EUG+kKu{eP}I}#v4a@8@PRVbu+U=+G3M%gwtkx
z`^M{~F0H$dZ^JG%Ah`N2j*!eUhJXFCb06+KOadeTcCU;<t=^ntsEc3?^LAOQB&f?n
z3=uUHMj8jVPYbA$*Rs<(G_HiB_A(OV-S<`Petz3{jXmIApFiACN)@`C)EF~Y&ckJ^
zd+_zCX}YtOTDR46#iQaJz1ZDYAl$(~uP3&<-{=$EFt@zCuJCW}i@|C`c&kJylNyDl
zC>yp@)@2V4&O1x0XWCwSfS?M_){;$GNv9U42O4J4XX^rvd>+$4x5)|`(cgY=`Dez1
zmbHi3#t6kXvaK3HZ^6J}W`Vuh!QJ2ocv+^ax|V<f47-%3fn_#c;7`g=jX`S)xo(Y<
zt4ITHY4Ev;mu%2BYpAQ-x$b^@!@AJME~kS{!I)$tHwENs93~@v$ObZc6hLK@4l{eb
zvX?hX7M7gf+_#U33H}8riEzXs8VKta1l&Y0D3u#?qkr7jSLNE+MVcSlg<d--I2Hp)
zPA@a7WMm=AG{I{X+-rP9^opPH^=Hoae91!h$KMV<8{V8pqfgxaVKLg~o*u^=167P#
zKHePna1$vw#yjgpAA#n0_)>)p?_Ru`pMU!8-~Lwr>JPvDyI=C_bziG*E!WAs^}6;-
z-_krqVPExmE4wCQlVfT1<8fA%|BL_jzkqvdn~$pU{iO(UctW$03w)qXQRe8Fz+v%$
zx;xC!h#{M>t*gRlc1gx|#}zy*FV1cKYzF7-r12rW%7)qD{iXTluhwX!0VntaI(gm6
zfmW*kt5-s`&$qft?eyQL``TT{&tLbYwY8wGo$r`7XUw)HP&MCd-YpgyGSs^Jc#nVf
zv;FxO-*>_^yex2M-#yU{(I@lnhI#imXKoxP?+arD1QD*O!)G>|ReT$c##*N8dHNB1
zwN4+0?>l!JjH*JncE|7*z1uwas$#?C@krj|l6etrngc5%++lzWwyWHjYmPf>*ktZx
z$??e<-Sa*WDRfu4<Euwljfxj`W1PNjdT`J>k_Cu^C>pzB9;JdoLx+3S-ratXCtC%^
z!`UOleAmlin(T`)XMXZ1W_Nq^m)V=0(WScEkPh`u`Dusy&Rh#~0#Y#5`1+v**~&g?
z4dia;R8mcO5vaI#Z@QI*`zm9rQKD&geCK-Fu-bwN24ou{%hEe5?Z8Nq*?s_5Te;Z#
z?k>BoP5N5hB)jRu1R5)Q1Y5vb{yrej%Z^YX{dB`Jjzym+maUYzJm{fYW`&*&X1j&^
zZdtolzW+X;O-H-;iWtkv`Y>z*T-FU_S~lkaxwK43vr%Xd)owGZU9(I?-Fn{1a(0(H
zOVc~8L{<62c^ukVS!}MdwrqL(5xIkA=Mf+|LP?*_i~eYzE;oa$P4CjSAn%PhC_%J+
zH{Vjv*k<#9i)(`@104_=4`X+Hi~SqsLBEiVS**cO@}xiQHr0jkCfi0)ciB6TmW%cP
zU)(F*nCe{Az+{VrfPpSU_B@qE8QEwfjQiCNYbTaOnS{b~iM?&I5)GSApQle43e(kz
zILKRikPAA<Cp$oue>dH<ki2wzUby=6xBFx1s)aN%8AgQ95M^uiw|6{moR6cfmJRJv
zcfnJ*Uv3Hcn2xw*KS%x8bvXiS`}$g^+dBRJ?Xcz?!NPjkb=%up`yr=wT``Zmo2Oed
zqmRWt%t-r%$Jt-6d%b@7%zyXqe*MEAumAbS{!?*qyzbA`Ffaavb3P8z$NSIv`o&I<
z<I(#wI2hYy$HOzlA^+$9^?!;Mt$UmG;`|PjA-1K@cW5!*T<QI6ym{Sls=iR!faAb^
zX*$7<7Q;YtG2Y2v;ol=dVX{!XDVI0(zUo=<S#hxqDp6<_AFPYB(ZN`K{4Tuwz6uSS
z-C-En$)@h-?Q!nD*Au^8+r~Y3zg}Uo+PF6cToo>3+Xy6R>E{u10=NJ9Yj-CA$-9ic
z-{#Sk%%fesy~`iRoDW2)yVnN1&xduD*?^m6t!5M0*jXE9f=|zt;MgVD5kvB*Iu3aM
z1xBbm>fT}I>O6hmvB`s}`0(!fcvl&)bv$%y>V?&Yf;r4!hi(uZHZdQtOXqjfj*6(;
zS~<UaUxFm(Zkt1@ECaXG1eq2Q7T@jlYCF1RtTwY?NJI!NcKe6tu1@2!)o|z9*`P5Z
zv^Vs`+duW^Z#%EHSw2-2DVw3q$WQq+lU&+jU4|3fgq+sf%q#b{wi<|B5FGnN?AjAT
zBIeLuk(Z6pa6xq0Jas+kjzm5TaYTJOd@C$0y3soA=##<Wk3&n<cXQfy^xEd0UE|Sp
z!v=$+?=SzRl?h?D=(OILvwmx{VOF_hV7NsdWp%{fcW@%0hQ>^L#QH(+_Ce#&m7KD&
zOvLc@NfhDS&!7pL$0K_oA(olIN7rsr+VOZGpT$7f%uX-1t@C46Re}?}rZF8Wi~H>-
z?$1$es`RmiB~rcT9Cv2e7=y(a0Bt~$zcRP>K@)iT08k#I&oOGN-{St1L-Sx1`(_8m
zN!wJZDM!vXu5BuM!Mtl(+!-Gznzq`^md0c+<^(@G57`#qfn8;Ebyw(ae>W^!SNSk*
zMjH<Fsu*3$IJ_2YIzzbBp$TYL8L(>v(h<=z(i%wt^oE#JxZKp`@vYB+`%1bG>wJTA
zp!fUt@CL$+@P<1`P&9*DKEb91xtS(?b&hgiU2FSO5yDj&G3I09vg~EGHRs#j-rSbK
zc(on4Ykc>GIU9y)-CK>O)SZr+ax>Gu&iD@8gCw%#Eg$<Xi||-g$9lB}rGm^Kjy14-
zklog0Hh^rtuU}tZzuo`lm;PV>`1+SGeyZ{Iw>iD5{oD8Pw{P5r!MN@^T+h3;Z2Q=Y
z!J?Uromks?&-b7Fpa0!|40fAwvF$Go49I@xwy9<|txRkaY!TnUTZj4WpxO+Y=0?F*
zSb#pt+WnnVv}OQG<-t@R8}8Vv`7oo}*+Dya8IN-Pr0effkI{btuWFGfHcDlFDlg>9
z-hC}wc;;*GF5!~wUXl~t?qhGd05flUmEG{Dnuit&Ux?p(Slmw?=jffo1&#d|u4w!C
zU>AnPy1z2dwavxhxtpB^Bih{@ZO23-v^olJ|1_>IgNkF=^7Ak=^%dhJWXz73Tg@S$
zRz);}d3@u(j!^r@tgrAB`=zYBQ^(}1noO5|{@>#H#l787W|$V4d@ZZn%Ge7|!sxbA
zkX&Rprgh7pnw;{gG<vVk{rJZ9)x3sBq36C7A5mK~ye;s|@BhL0{KZgh!g0zBDux>w
zjmzF?a)Y^T4?BK}W+?`?hDC2%yO@}yhVNal2nV0&u>&Z&ZR9eA9wR)I7v~$VOFP32
zb(Npm-Sz78pHwFxcH-VuWQcHKi|4$a25uzAwB6P(V_b^4zjnOEswG)h9>=T~{JjdJ
zcAbyI(iUi-S?9KMh8w%Vn)Dh!d;gg8$bAuzpVqDGwrneyH>*I3=!luOeACe!c5jbr
zJ3=d@T`=>Ko1vig!7*{I(^L}Uz)IDs^8>i8se*XAWMWk|tc$00yLUEA(=44)#Wq{l
zj)(5O7_4PxC{|xos2%JEoRQPV<LsCRFb?rN9BwT*!{3c*VHAx99_FMb=VlEax8Y4n
zU?7)lSZvIbGW5i{JB@?T+3w6QlF)3s+3N9zwc%|bd4x(5*wx~AG~8zDo;^;}3$-u~
z_<#-?(3d4NMqjs`--AweH;hsx?64L+8G|Sn+Nbx`HlvmN@{AOxWz~uP4WONe``37U
zGbZgceh>+NfFu(B4lU5x070hY^jkQ(R=7U@^!$3JjW^2+M1el2aH`k&uE1kN9e!ro
z*~3oreJ9;8*tg6*kHD4ZK#8~2X1woSsWx=OI9$Cu&Cc(KezU4lc)v8pS>0}P-}v|^
zhHYgyf5QDsmtEKX?en*P{A2zv|M27AJX`<4KWpRt%V+KET-c4*%KUPV{Q6?H9o#Do
z{TJ^KUvLkOV{%T}Y5DSh{@?sZKvg=NR)tAV>wSfuef$lEP*0AdI-uJ)n&S3`nHA)1
z;hFHxaRN1181K{%@||&#FT*!@d$v&~s+Prrc=nQ!P{F*p@|CoP4m*k-FdzA&=6BWR
zenGeH(aNaYc(1#=U!BLkKX)c6?7a(h(~lfI?c1r<Y-zdx?bHa4fOD*6kE2)GaY_c9
zAtj7swuBLV?Xu})+M&u`sq@|4_`EorO`Dd*aX|AF&g1@#V}ihJ<n%#_IIXwSS6kRl
zscIm+DEUBP_{tqFnd?Tr6kHaA<yb|t-1+V2;PTHO7~wwP4S}@YUB^2-yOKQ8e6Wbo
z*ON!o`LpNc2&rsQa2HtVKpu~|yJi1BhVX6Mwrxodv;IFKYHh9eG3H!r@13dC!N!s}
zT2PFT3#Qm0BP4S$PlFCT2Cu-4Z@A)u2LW!uMW$G`)0w$XYpprQ=&jWn5kL8T!Ni`}
zEf-C2(n4vrUtZhdzPCltuMe-=i`dx&#%b5jG_auD(N#kK_-p_1<m;A=D%xTB__gng
zyCD*?cGk<{Fafy%u{OoLmer-Q4innxXkw`Bq5SNa=n8Cd*vg(Uk@rOoSeLfUUM%b5
zbSxyg7SH#c%P7_hjq27kzc!FPMlwt<(;lB!Js!5UYCGvrXUWVRPWXDktcpGzpWT$H
zWus9Vhp&}M8hqU$SuemvADrCZG2a?eOGTV#R3aWy(FKelcU;Z)LXO@^C!>2gNfQh_
zM@s}YiWWd6e#lVJLU_6S_&VN?tY+25w9akDRVK>Mz|9S50m+;?@o02ajK@KL44a=7
z=Yey&TBM<HPB8>TEHErwG+?tGdWjF;rAShJK3E1_-6wFtf93uS5OA3S80?E``8Gbm
z@9ODT&9*ARdpG^){pqlBO0?@GmqTq2e<kf@aQ7-J13gaGH!Hw^>1cBm0kfO!f|`>m
zxeKxlMA$0ep;5q)4f$<746re6l5x6ygH5=z7kCgGMqnTcBtRpry8u_hX7yFS)ct+s
z0(uc=0}kb8Juq?j{YjR|-Mp)s8d3xO_8xrgcpENXp2VBI_IOJhhHT7fO=E6@mW?m3
zV@`=(wF3H-haGf18`rUs8sN<cv%8vC$M@g<{Fi^!fANQpzxt{E`1J=r>=?(#=TA|N
z>&1E6deH+4?#*n`om(QSykM5maps4N#`Hn|=l}6Pg#OyQh5_Z~){CJwW8PdHk-)Yl
zd~|K%V5SJYO(xkdwi7qAY!3R%=UZ0?muVQlmia0iJ#NE<D2UzE5{VLX!ye)i9X3qm
zlId<d3Qx;lv`yn|H9OH=nftnX>E7S3EB4CzyrI$7dGF-x+B1S#aMI|5X3hv8?cRgH
zN7`f5D}4m_U1mm@sm<`Z-Ht@Z-l+peJO?{t<Z`NF%-V7*(9lv?-rcA<1M;eLb4JZ6
z$a9-og)zTmu7kA^?$O4vZ`G`5t5ayrfV*{wLT4U-GxjHP5&PhCm%;(<n{&YIezhM1
z&AsWTx^;EWkU!eYyNB%tJdg(ZfRb^_t$TSDY{a^^8?m|qqp`a0+C0Yo+;6|a>mtM5
zZC1f)v$}hRb-@hJuM^nMAB>W{`?uc)T!+`Il;17F&o_L(I1YLRy{LLEn5~s=2F})|
zf8iVyT4fTf7*-azuVY4QZ+{PfA+RxnN?iw{)nvmM{g^E)2<5F}^>(+2B<pTBH<0nf
zzI%ij1UQ)H>M*B_Tr>l#ji&HX#PCu#y${oFGqkE5YP2CTGt@l|Rc`P@<sHry;A*74
z{4KmboZhgBZyf=DGkjqCh~5Nl)dA;{zM7{8<ZzE1C-;Do1u=^c@Yqyft9I`REDR*f
z@9&tS3eUPFvz_&v)4KQa7FAYV0mBT!GJT$0pXI33h!}O#MyL%-dUP*_+=iiiICgTw
zgAlvR#_lo$BxAQ{O61xKH+)MFdC(lWY(BdHS=$r!A<-aut3-1#YdnMzvCXM;JDnH$
z5Q{K}vklr=n%><$TWN(B<Wp4*x3Q7K?P@-XVS15o+?R%t!|>V1v+BcM$#DuV<QMwF
z>lce@bMxJO9E8yY1`R+{f~D{V6un`fk#e9}Rp9>4yRP-~8bgC=4zk1KO-Ue3mDzS4
zHdsBxh$BoqK9j+MxBxfM_PVHGWzz$bb<ZD;<|EV<7$3iPjUAdXXk>HL=Xro;?gbDn
z!+yQK|Nf8pyKf)A|K9)RFF$|k^Yiz=tM99omR`AP@2c*d(bsTpg0>HeVgO+<a6Be^
zALk5@`&-xE{?GrD{|G*C7;5`?unQ*aGJB&{#4HPeXy;BFf<G~TV*emDYavI!HF5M9
zzAx~p-u4W$(T__xecqYFkBR*abvM5{Dre!<VaN?Tf1UZkhQdrkmFC~uf88B%E@TMa
z#<+heVdanG_50j<)!y*W+-Lh;i5b`VL)1MGN!Hrum}PpX>v$4UFF(d+xOUIc+*E|2
zqxj>Wq;{9VU2$N5_to<ae*3EQS*u~}FtMi%vco{3bekG7<Mg1y%)G$?K3EhMM(ka4
z#vB5s#Wml&n(b}Q;raHQ>oOIP(8<=ehuz;14-4g1$BEnxNTC0y{khJs%-wt?PgtuL
z&ogR!8qHgUI6PYxh)~feU|1yZAlj@jtgm)j9n;WcKwtonK!b3Y>Ok+kWrQ}(q006X
zc{#1K;#zrrC7!;%dzHOE;`w7<m%aXoI7y%v)~+&Om=OdV(ANBMeSC878fK{CP*roF
z+Ja@a{c+@~d5-+ljs;^^*`p3+HDE&v3gg;3O>9cXlR(pOzS(|R--E~H<f^1|b)m*{
zvP`Bfa+J}^*gQ1NYtv6vb&JD$rM-Q@=W9jV8fM|x1ZBstU8H-cSR+$M)cQQ$;!5_t
z&COJtcJCgsFl^lz4+}wD&RLs8?@hus{#uyYj%G)`UT{&a?z&nDi+z*+d15uczS~D_
zm_I+G>?)t*T8l8y44seBwS0K(p4e4`(<T{n*n|@YJ%;HBR>kw#O!9~TMMgD_XxKBd
zth*7@6J#OOrS>QAY57BstK$oHnpt<tx@}rrgj2sr)d-^)Nf~HmBT#&Oh_ZahP5!{=
z&lYBL8fi|UwntccA&udVy>+}}xaGwefff!R5kz$k8lCI1V^w{P`ra1SX2l8z+|AVA
zeG_K1^!de(n56juPBQ~<V2~$ZvcqtJZbl%0TRYm8`D0hMp7qnWPfcr~Y@R3*{)CH)
zbzf<HoL|daI7-`uWOYsfJ!hbF7`h#^Y>xh{FF)3|SAill+;Z1KP<BK$I_Av642W(G
zj1H%t*?Ye}K7P4=`-kh#|NP_M{Qmmsb!SYN+TfVAcEeIRMu4UH476dohtP3^*<;ks
zDhiL&U%PrkopAZT`e*;x7}lG5+VRPhtYwpWQHiPusRL+u0S*l^vR@t(wird6sx)Pf
zA7gyD9iw(?nw@UHY+B5>RoT6bvsA>uUMQ>k6Yi`9K0I%Mu=nV1m|&UVlXrK^`co}*
zX0}wlYRl71wX<TC-C1Db<5q-J8u@rTn&cF!jX}r^b{ED|4Xv^f>Jl9u{PwN8;bS_L
z3)hwBJ0kQ-JFL`!nZ0Aop}HXY6U;&cO7R5-pP-v6-3l#@U_jLtljzE2PBoexhTM(e
zGMO~%RR+;)ci~!h9#hgC2t5z*rcf<&x=I3Vc{SCI2?PAibOS*H4rgyUWN-NVY$xN;
z{;csF{n7Pq`DzWta6xZTeVRFI18!!@W#<=6haC6I5uy->mFS{*n+cC;x3!pN2hx)G
zER)3}Bb2w#38ZZqSQ4w3yCH#MoYGF7oi~rpw#ak}`^I>1w@_>c!&T*{_ZC<IZnL^F
z-a6N0me@R83%7>0+p}knOJ&>ah}R!<d>IyoMY_457Tr*~)5aHQF)#1f&o8A^V(4WS
zV#^0$K5ew}Uhcwt1J#flO~_p`Gmp|qZ6n#yw&7pMW&5M~blnph9?kH&h<!7>Zp)<}
z2Uh!AaA%#Hp}siWIn)R1cD4z0_o`^WIl|FCE8gwnTU7RZ^e%mT_~Xact;5FJ7(-Zd
z(b1h2Uh>(-bNH+0IT5t;81V2(_U3s)8fYC4MiGI&-5+h)nPkd`9Z$=Gx4K}#30oz;
zh$-DB*6}b~R3XWbtPz7Zh~z_lPt}GUv3Ie+IgJp6Pt`m4!7&Z&{6b`tF86l%L5D;W
ziM!J|-@yXB%fhy{HO(a!m%B6Fjvfc=1x#uQG*9fi37c8_p;z)uviy037tFv!ypv{1
z`CxBA#0dc~qjfk@k)I9b$9-??KYX9kJ|i>Qmn~y&9)8*gO1qni+S2kJs?BpJ27Ad|
z*D~=ki5Mokiore)Wgh6D*D=rDetKn11I+w<h!-_6NUV>V`Io=_{ThG#uYUTgKivQ3
zr~K{Jxm8+9#c`;1qupq&DYY{K)#HGQX?|*_b-S6~Y&>R`-+Svg?0BDl@89x&@lXB(
zNM^g{R4w0cv=`jXhOpZH#PZcKp_?{3my2*gFh8v~){xI>qQV|QZJXRj45-GEG05zS
zymY?NJ^C(8-d?k5VS&Q%wp-~?TTjm#2ir8CRyQ9{_RBPDU#w^|-6ik)l?~q8_tVeM
zy8MI_tKI!=x;Mf)&Nr^MR&sNEu?f)GsmSTBMuA|%8fF8wQE|U;jN36*8*&DnMlZp%
z1+NIR>hh=RI?UI#hBso^F1IGbS~DeGlwI?E)J@mmq=Z6|r|p))l!x11Z7{hUGzJZ0
z;=1wv-q$BLISp%ZM2n2n2G!DtbIHtN>Xiy(-@@Vh<KmkG^h;Z9W<mfcXf)$6l?Rcx
z;ALiA=G8rVEU#0;@clD5{o{qVSq-b|y+vK|)m5mA*OD0;QhFJW(<)_CuTN_%_Sjd(
zuLLlAa}=)WM0-t2*%g+1F}yd-$`GcqX;xO~8)@NO@~QE#+*UV0m<XcHtKxts8og;3
zkI;u|+nf`Q^<wjkN;u{97;OP-Rs%geR_9quQm0jI>w>+T?rup128W7fPQN;AT}i##
z^A&F5nKMyGWMxE5Ql0+hc1>MGSOiDMQu%cCK8G#H6Z-+*2)0I|yblzTQRB({PCMEN
zlz?~)Zxtkkg3Xw$;i1>!{juZOl_Tuc^{~0Q@R2v$I0iaBWb84mO$<Y`=X4}6N(=}#
zx(g$$eLNLY`62rn`4h%h)2&|C0m7<mC6VwcF81JE=vl`A*dx&LYROIL@aC^3UcejL
z5L0M3$C$CylIFp&SQBGaOsfys7ifD%YtbPnE{VnZCFfUfz$d4nHpiozO?b3z&wyjF
ziRqRfZtq>)X^m~&f>)ouq2q8PUX62<+U_;xa}N1o;9+nW3<XjQpy*B}<s?LzmPl2&
zuWY+F>Zf0ny3L=RlAlial=i_La?jPhtrOGxI1MSd4}Vl;ue2{QE7anMy>7buqxRKq
zZLXDrGPxedSa-Jkr0@2+D*hy5-@CKp+b=);<!`_Lw|~rk{fBjlALpqobdDH>h|<Cx
zJjUUAJnOpIidyqm?}a{ekNGf^#n8P+$JRm`;2W>G&Hwp7{hxu=JP9Kh?%h=}BOynK
zVm4)MI8AUw3h$0owE(_-Y0B(^J9+r{P`^>?EONxYr3n=ks9U*0UyR>n#BTIWyrFjU
zp-%G$^_cldM%x(J&O3WXF0=FgEYICpbxZTyFWzqZ+UwfAv5&);pjmC_fC)`UxrY;~
z9?_W|zTBs|VdplV?=wF4&SeVY&Um65t<je35PHjzVxU_+1lmYlW~U+B46<T6b+<(t
z-I{WGw0y8`I~}WHNCCX&e9vep!l&-lJ3~8sln$>O4LY(O7Eo>3F&>ks?uxL4YhU6a
zi^;p&?H29TK3r@xz?{>`zF|I+5&fb?wOn*AA6DzGa(R2w&^7{a+n?egg!bka@+zDM
zn4Y^s5}Q4rPiB_6b=UC*sLwC5#<Z;`dt0vd!cxx@dD}S}tk#VD7^hX$>t&eo9!gal
zs0?=i;d`YMh_T<F-RaxSllw+IU`3lcCedD@fso9%Af9j7DVy3BT;goCY?{+w-|P8n
z)Mv+=r3>w_4#nDioJH5Lrswc^K1DMZb}Br#c3I@BvetM;-^qj4a9{I_@8#Sph9TW-
z!yy%{1q*AFo0W(yt>xZ5hkY)doOIq<Ai_hQzGpu<jFt5~VG-AN4?{|3@HgXS=NTS-
zUC6G8D#2ITSFFXsM5yX;hHpr`vq#w7ivb$NOcd?mN_(2qsN)PX^BLY-_+b11ms`uE
z_F$W{9VVgB5r$Wfch;xyfIcx!u8+dBr1BHvbX(}77s77(o8|9~gnu_0P+Wwi7|Qa7
zNv#5ZfIh8Kaz;v|o(LA5_@td!8GCJjb~T4&b|!#$UHN=>y8$6-Dv-j1{Ygbc8v&GY
z^kCng^wE7;Mzab}^5xGl=8sbHFhhbt9%M=egLqhrR=^2^x7y9%z-8CHUikJ)^1coR
z-;b(<d#L+3L;4^lMLIWlm8JVJylP*&AHVX?U)JM|D`A4oZ3Fs#Jl?bRV;;V?TlVqQ
zu1_64<})1VO%Lm<_R7!iKmW3R{^R$bew+XLFF$`)zjlLkKX`bIF+{K8@vywswb^BZ
z=i_Z(%T}Rb8VFDg)rhgXD%&5X`8<7QrT?pc_8*dN{y=|N5tJ<1h6r+bzH|&8U!d%r
z<Sl!;h71}hOv{&#8{WV>;}boJ5>K;XOrB!>y5~>84+pmp+hDItMR>DDnG?cxL-r#^
zzMAk!OGJO{@oV27`I4eqT#&Sjk*{^%cVX7P7Yyc1>)j1^9{!Mf%OAu-jXH;Q79>0;
zi!)lIo^Ktq%lx3qy2UZuZF=>6QEsJn&Qsa5=VRu#j5mvtJ?ZVG@=bV;2n(Yh0fWQG
z*$an9ryf%_%`~*UWTvXM_73xfZDFB7`s$u%_maj*lbcI`k5ucvU=N~Z?&ZHReK-%z
zC)VrMfW!I@dlQ{yh3#d1Oj)`$lwRmDLtS>7t+qi6)Ex(9E_bip1Pb)ox^cYuy--~-
zt=H##3#}S&bGIOAyL#iqT=JvK&C)(U!>>w*7-;sUJz1adsp<}z<SA>}M77&EDtqVV
zF-;}r($iTS)9zQ(0|(<}ua)B=6m_?TeXjB_P}vS7!qMPy_a_9O2y}0ORmUT0u^cBN
zSr)!79bb?)Z77!ov$prJps$U(0K073&TSlij&Qs_+aW(q-C*08AzOvYYUrN91Pj<_
z3P{uq4fxQtf}+cWHiv&%oK7`Hb@p+>iIsK+yQvpsJ?yxCmOq@;d~5DXG|}yq$*#r_
zqOKJlv`*z@TW9((7)sUAWs~Yq7*)DS5AuAg)#HaZ&1LrGI7L(wveP@u%f`F+6&3<*
z-tsp~u##3n8;kZ7Hx_Ul>`%7KiS?<X6YXagWG}Z3N*3n3?};uH%+P4ghq@3Y9je80
z?z;jWPh`kN8Y#1aZs33l=iqz;<Z)Uq=CaG_Wk}l4-U=aT9%Vqb0L;~VG6VQ1G3sjn
zkp=|oYPysiF>U_hk2lytA9SY)D0u*m;Uqx_MH<ZrLP^8kS$15ngtRSer)AY@!&H5k
znjY<J<1u&{!g_IfY>r1+VsmKIR0Xfr0gAAe9o8k853k<l8}{zkMRpzG;|Hv7``W+%
zCI8|N-+%v?_kZ!r{nJ(D-fgh-a|{Tddj*JX9!U@0ZO3tHy*^_eV}_5SIucSUX5P0^
z-O_<bm*-mT|MH*wBdDuAGBGD0ZBVZvq0wIA6YHB7AixO>*R6Q4fCS)<lzrutZ}nr4
z1%vsGzJRag-)*rZ@77FP6Mln++!(1cS_9DZShq!zZhUloLeus~p3msd+=dv(zM(`D
zjxO%kC;Dh~E+)!r1Hw6^J?FEG%C0U`G6vj_{!9k9q?`s^i&oyoQHq&i*4;6CCDcbt
zRNiH7zH_e<lA5MZ>o&L`Yllq^>UdL@*Fxo<=9}m=7?HTukmS+b^GjrDFE5WcysuZd
z^MOuam=-*&tG8;~5j7&h3R^V+pBv6s+w9B&ye&e~y6X7B>wCg{G8$aldLbUDPmck4
z;8Q(W7j<A1apLuJ$8Y@pP6tBaC+lv1&V228sITIWp3BA<$D@1qVP5JgQw=oK?)3GY
zJhT+YD0=y}`7~?Zci;CkaKGa5urbSSRRBGzcp5j7sA*MC^9@ibn*wmx!u9{q<KML=
z?PZ)<w!`z@#&&iN7z~)ucc16jVYm~L%|_MOnQvB>@!EJE{GND^+`={&^tEHVOrotU
z$xb&JkC*Z{r#^curkY?5jbHr!(exqPTn}>5N*?j}zajpoRxUimwsg^xt36HgFjTkg
zY(IcM7*HkRxxfjP@T|P_^!1_n!1zV$VP&wf{Y3{ZqFr$l-_U=?eQ8pw#@ktBgQwmK
zOs&Rc_y7Gm|G|9yxaK$2JMCrn%wA4)+VlQ-!RbE_e$Ml%pGA++)A*rb9#>w=tc_3c
zWeISJMMs7k1B%AxBBwJq{e*4N+0=pN7g<`j1I2ez2G5oqV85$@N0v^j4c*{>*7hF;
z2|sF@_nYjOkc$t^?_&d_RcB)J@anMMO3*&~bj#qpX`T4LTK`kn%QXtS?Qja=MgtDJ
z9`$VDeV95dve-X#e_H=%iGPTDc4c1~^Dl+JwD?*2zeqnS{z(06@DJ9z@eAx?-;5>>
zbBF)h`^Wj^Z;!{neexe%`S;i7zpMJ&`~2_o_0~W9z>M|+_nV62*!?r=Z+QRgxPAFu
zm$UUqFZ5W#vAw33TlD;5AHOi7JPc`(K7LT?e!kw(qpzaY<#*0+IDh{AuYUXe?GGP+
zBmHml@mR0lpue(o{%~P5>XQGz+5e95d#9jceRZFJ;2L%sDHP1SuutEGBc?Qt_hesr
z(*MOj`A2{lQS)G4XvZ9WUv-|1;0F8?SbP44bGQ<murM1f3?xs$-pJdnU<fP9o!wTj
z)BI)i-uOjhLMN=4+4&Mu=3PcV94$XB@Ai!zurOcZ%i^KI_;wYD6js&x?3-HHul>E>
zP}uiXcYjp*j7`NE<GRQAnkcP=kKt>a?|v`t?1(U|L!(`_x`}SMGC{a&aZLZ|=Rrd>
z5TKH<T{<41>U<oy&p_iaB`k0pgKgLHF7MmjY??4kF#63AHZ1nWF&<xtt8KnIPnk{_
zZ1E9b#AoC5NI{VCVZ2HYc*9uNl25u&>CrFz_@jS(!${{u(1S)0cl%+z;Ip&MFpaFM
z`Q#Y0mJzT8e{T|bYh>Gd@KrIo=`fSa58NM1W|9k>Wfs=nRIZBknSPk)cH#I9b{qEg
z-vq9n?~JTHtS{hU*R>&0rUa<#?&i2(P`LIjr#k=-p+M3<c9qj^=jfiMMEnD9L!kO;
z)jqRz96__)$J^MSjh4i1&CTh3_l`&Ow~R0RydorLmmRLcLv`O#QKfi#l!{8wYl|FF
z8+w?p82n{hu-Gzcjd65_+m*)~*48-=T*-H}wEw*bF`P=vy^m;^Gt9@R&T>U{w}-*r
zWRt_&+oi}fKo6<pA!U{S5d9N4ExLhiUpKyIsp7{dfj2!&X(-+m#<L2x>fT|;efN%k
z-(L9S8t5^ww_hoOhj=*J!=J<OeB<dm#u(E!=WtCz&aXyyw&>19|H^I%gsC3xgl}%*
z9Mp|YkPX*uj9D(j%c^A6O-{iKx){OEvValm;nRxM{6YBlEw4twjcW;W*e;#SC4UnV
z`Dnj=-R`4ZRpLAir>~M=H3S;|SMr}5Ux-bOQ6*h$&xWFnm(>>U?hstTUcVQI_rIrp
z(ZS~U$~k|;+n@RIH#z@P_rGcOcfh|({tEtQh97BvDtoeBbgJ0D$iC<PukY9IuAly&
zuRs2qy#HT`zhM67vcGcu7WFZ>B97fwyN;fXOEAW`<T2spL2o~%!WdCY{%h1OteDfQ
zntpOC9%Ehpd_<1rd*l6=Z}stqpa1H2*MIe||Kk7i^B;eg-#$P7>)L;1^YXWEnZLKb
zd3=E$6@mD7e4fGLWf2@dM4Xy<MM^~PvPRclVX@KHG%G6Xux$3}|Kk7fj}fCBo_CR;
zv2WRdn7&^1_Kv>Aus*E6nY}r?5bR$NV|$w!JUvtK#t?6VHc?2Iger7GWL@%rr3ot7
zRXv`F!}_!OjQ(hMO7J82K|8t<Hc$&1oon;yd9SX%uN!CYy;t3}SM#pse7k@8zWju9
z&^3eCFQDwp5e46^Du$m^tl?E*(`=JQbc2kxtbH#?VZDor*=CMHcZcaS&k1dl3d;eg
z;pfwFqlL#4*XDW93{{<dT4$9J?zUd*7>fIw+h|FlkoHa-T;n;e@5{!SB@)$&2QxLl
zJQ_k8f`nDOv!UUEd`X|wurXpS!$J-rhApLOXK<TM)od;uzb;+jO{wDx?(eE}Z(b>b
zWs<Xaz>_xh@ePl|lhyK*ou>+5wFt^Xe&;$)xS`RwF%Iu<r3gC=Y<-TmBN?V7B51or
z0AjO>PN<cz;pl8?oY*VK%N!jsJ3-%99gp6ogXo!bbhm`*KHe6|`*WeYe9viXNq~1)
zuo5u`N@Mt5Z8lAaH=)YMOl=#mtqP1FA#;;R8chiw&$Ld2xi{oCHm@kPwZ#x)ygt(h
zyEwnlOIBWGjJwTth4(~;u!J4M_9cPCb`MInUhY=rytC=z3;nQy&N;YtyPqx!*Da2z
z`;KR*9n1ZReJzfmK+HC))@oVHju5yASjHH=t~f?w$B4MTYevU}<M03q=;4hrHy|sV
zW{}B;2;)(((=0m;$1oZ737d9()_`|t-99HH)DCZS2814nvTU#@LPbz(wTBT}!YET)
zCz`^C^}!IdYQ6)PG8#>XX-T1x*0<h&=mo2$9n4L=6xQufK}j=1GY=w#>KYvbl)46u
zO`3#f?N>;00}B$6bhW#BQ_ja>N5}azyBvpHFp6K<1tbkHLK{XB<V31<RKME(w7>n~
z`!BQhB@7mmn8h83z9k=KUD%!D7-roawxW)lhV)jt61yvCTMir3+os8hJWTEL5T`Eq
z!*}+_>z6z3Z@>IEzx(|2kNV|xUAbDrRLL=Rk(}_Z3?`5BZ9Mz>#V{U$<+sP-_sw6*
zu!tCMN3FXpY#dg}ppC~og0H)(r<ecpfBGLo-uaz85Y{d4tH#q>Vn$P^`4Lt?1aE66
z_9c7x@vv?)1iJi<?&|Mm6PB`&8S`njy`cch+)nmOS;ZeT<Xc<~|6+Xg<{oEIp|X^y
zethw|=nH#T-U!!g1Bn_i?b5#Yr?oyz%Ie*4j7K56+V=;?Bj&V{(Q-VFzSiR`bG)6X
zeOoI%oh<{=GE(%W$T-?F#<Nh~Rf9v0VV&svWwPVw0%Q=`+O%kf*@ZaR*D&k&YTV61
zm%I;q2-&;gL7G+`UYOzj;O93*n9O=Bpful?J)X8N@7EPyJHHK@+6QmMduOWTV-^Sc
z=J|tO3zAsg>LxPpm;+~wZKJvobx{E%KUD^diMoA-)e<Ls*1p`I3zu|sR7>GKO2S_K
zun=z<*0w<#o}Xe^r<SpA34Q#U`}?pkOD;pc?7sIHsD82SF#ru^+rdt@xfdI<(ed!!
z<q<-SE<T)XZH^ok*2}3nfJ|A1eYN!mKbiaXoBJHxFV}AW5}A6f+blY3=l%ZW%+GD7
zD`i&1*muQ=IU`f|J;HH#m&}dfDIcTIA*Ne5e<izYA-ha6V)KY@Jx&*>Et7SXChoG+
zN+YtYZ=<0An(}t{DBwI|-C~$>ScI0mdZ!)6m3Er#&x+}HH!X5#m>W0F2uG%hu9f5}
zw_4bry03m5?5wVg)AwRFq#$h4K0ffP->h=qcZ5f`5zUhZb8y>mYqyvI6TmXK11j-w
zZEvK~Qm{lkyVJs3-h%|I2J)5_+PVQM`nz=0D9dVlkl(BzhM6Oi1Xf9*h8W0Sc)rUC
zJ32**_RjT<^Bo}IhGGvf(Jq?ZuZU=N;{>TJJ17(R0G26hd{N(4yYb{O+PhufFn-Yb
z6i6EK^6^FM?w}!P4iPCkUU7V-hsUHj?X9_JQ^(UVVN?e^s3rzz#)<qsw5Yf1{M4si
zfBacRJ7C{G`TiAo**Uz6R@BT8YlVUg9=UpqXd5yKx#eR^ij()t-R*pKYs|^qWA;A(
zX65hhWxr(p@%JD9<$rzu`R_jec<aJ2G>SYPWbL~m8q+m=XZr{<^|eRz+Yf)c?rp1@
z(ZeCy5qf0@C}(5Ke1==rYSo3peVPA@fBH`Vcid)<+>8f?y9>QxVz(XOO6mbt`_G(z
z%TO3#eS!`zgagSc5-hUObY^R&!?QM~gaDh@N8^YR^LrJ0jNY4sEI7gnx<7fGtYo?A
zh5dw^klLfU>VoB7NzCZG@R`^1hbxiyrrWAIR*%Qgnc?l{$z226VhGKwO2kU6wDTw!
z)~*1$*d~=QP~DaC(`sX5b(_Db3WGc*#?z0n+rt$rIR{8%0NLk5D`VA~hZ{32vH)wz
zcz9P?7KR!qgI8LdwYGpbbC>0!JJ{y4VWtI=k_#%uoV$SIOZQK9yn7aL#qH`FZE^~`
z=MT9XpbsL{cH-lwhLykd=Qj(_S|EaM9*iz8#DiKW?S$5==jm&WK;Nk<9Y1uZ#=~#{
z!@AY*A&QEViGp}I)}2~bX{|M#+19Nwv!KnWHh?i>sY95$JM=WyYFu+n^$kTvM$1Ec
zp2L;-$#L-gSLgn@M1}c&(X&e~RdlTF!)hE^*S0s}9>sL{JgKd6-XEKXxjC9NxchoB
z6cM{CL|TSr?s1^fBpaR^ooNrgz8lgormym&Q=PBg?NqnVy4sFI%X*8hed{pmSTUuI
zaMH%U2zUymv{X?l;~2vXyY)UIu`^mV!s<>$pgk{QG&J}Kq_4cBVbfl(^P~!!C!=CP
z-R?WjA3SfuaxlW%sCymv`c5^>7-e1X5hTNG%9fq)O0xqV(H%^dA7%}drgP3V8XG-{
zWs|se!%aY4I38Bj_HOtDJN+Pv_tJ4tH~B!zBa$$R!nQG##h`6g!GGj#T2r?YX!Hn<
zH{+#V>_@YShM+i6tKD2h4Dq6GUXYnd%9c;^Mk{EUqFJvy#O!2*+-URxWm$5c)3Q0t
zq|V}UKW;yk&mU;ZV}b_TKv6{lloCV&FaTEA#@?9JTI)8fwO+el>o|jJ_ucS{Fk|<a
z$8C*WZRW$e9C0@u*p%)0Hjf}N9F=~A9wC2y=>6?s-5uA*AM?6@_lMUX{_68Det-SN
zXZ`M5E;AvyV~>{P2i<+o8RwJE!0-si7$FiXFLVQq`voHA(f73?cB?8!s9XJ*)+oZS
z?ijt>|JDEIKcKP$K$*#Ms6X4x{mc6C-@@DQJ8HS+LLTxn)-OGObbN=o9dD)&V@OZ%
zC&t4&^B8P1{4&zp+H7+*SE?OxE4`}C2bQd<X|zd{b<60}wM)ZHE=_t{sdHlgqLoXg
zYNG3^o7^|o%381F-meA_nZ0g*jNbCcfuFxuff3N2^C&;adk%G}tE|;gyvssrC8ftH
zZ8vlos3_G4|6DLP_{mjOina#h?BS>e?ik0+ornwvn<G%w8wxw8fwArn%rM4%eH(|j
z4KV9*GP$#l=3ws`aS+F^>hpugW96s&san+zzd6rsVSDw}o;RUB0>!Qh>l}xq%u+Pl
zsaG$8Njr$`d`JLDDx%e~^VKC-_u7kNT5%jwIH}S8_@U$Uh_EAE`NMfYh!)ZyU?De#
z?DX+jq1(sT^c|TuhbirNAh%RL+fs6ASG#pMHiYnou66|aB~rO<jd%n<t>3@$mk*3w
z{wuqG-cZ^YgsFw0o%i-GV4irbJq{3JbhlYpb5D4wj!DJz1$#E8*w!ezYQx^MZ;IQW
z+~|nbq8-vKre#O8jsY}h<l5yAR=In7lJ6Y`ip?HyYRxzL0;d;RFX9wNQ8a27ro*k*
z_P~})PS8&8{uT3sQesqCn4=S5&AAY)?`E<p_M*Axy+<(~4L5dH3s&NJzIASUJi0f^
zTpP7V*NGwtf7V*VS*i1gz7eN}ol!5&)38l2BfMIz0i(KLDM}#eg*M6?Y(U}8gdA&|
zKGD$t#)&&+AfM1}4KOp_S(}mFqz-h`2Jk|^lZ%6z#$LqJ@`lJt>s!;UmRq9@R!nhh
z!eNIzJ(D(tqvB1wn(f2X?#NyZ&<Y)GfV-5Dds}bVk=0}pu64@~Ti?X@szt#3J*I3V
zBS3>C><cJh3x`|=Cr%iJ!Z@W;-v%PN?(g+o*Z2Fg#&FRZ=bPP^Bd&amhoa;IwMsBP
z+`<LJuC}+%56mC1lIIv~zz|HhC+^n$dHvz1`m2xozx?y-FFvk6eA{<xdz-zriSEai
zS*YO>v!c^-uj^)Cm1ey_07b&5<z4n=c9%aMYM9Z>EZeQnNTn{_T+;CW(|_`hMWZEp
z<2YGc6ylcNM*e}{U-VC;vtRP3#|Lbn35h{pjhWU@_G<oU{HA?az`NVnJ4x#d*(W)Q
zVYS+vZsb|mH3o8;y=;uOfHZd6bdx(+)^Js>&VqTZ>du^Bv@&<)ZULWbuXKH8m4-T>
zgV{y*(f--}1YtED=;5aQi@RZ--WLQ8&)U?0qt(UnMA<4da)9j`O^`zxQ@yR!yLNLR
znD+)FT--#k+s+PXG{${#(9L|ePFBTXyD(V0$J6sR9$wWrgX!mcXYsvp9fjPhCK}A;
z-b=u?IGLMGSf-rAthdNryKR*AfNoATbb|4N<#nDy?Tj}Vhc3kz>BD3&s~FU9-x~q2
z5i}26w1@U5#_76HjO)AKt7qu?BVXi7bZ7ivS+-Z6U*hv~*<q|kU_{XhuDWS6?oE_g
zFuNVmUG8A92Dp{BdCpl2qjB`c*ms^!ceFk8{)xpvHDW@v0i-kb3gER9Hr#tN&&f6G
zu5D47IC$Xl(B4C8kn^Bcf~<u$7BOKPHRwCrAFZx5dQE$1Smeugkq!gyJt1SOmwLEo
z?;RtABw)GB4$C%`VR|miqi+nn(w{SLKo%|u;PzsCg*VI7IH+E$nvT?=Fs}&aRkxfn
zgis^AaZo54IpxR3(0$85_QnR!dV8!nv~E9sMWsxOhw>uk=Qf#VW;C~RR8vjMF;VWL
z*Ls{20o=o3W^!}>HEEs%*zLAd6c6fq#&nS?jQ|D*RF3&o+NLH!?($z5lU2r)WI4Dl
zLt&oSOS<I0a$PdAXbmLjR9@&HOFV4d7HT_qy04NG8t}yh8C8`3;QMD)ld=#DSP53)
z(N?0|hnDQ{s)jyooY<eBdsd<yX=-J|U0RvvF+TbJ#)&Y>2N2{<bMPTIb`pa~qT$2T
z<PE`Hv`ZI0KKFWE&`{o+eq!Gm?nc~IU@s8u2eLxdS(Qhs&R&Yy`m*)QvsLHUUH6<}
z^4uS<U-*Z0{q#%z<xlk&-}3wCdf6Q3q2pP7Da)G1ZbA9t#5^5)jA>@D2@dOEJRU}6
zQ0EuG0E2X^J<ZN(LRYO_#GBQU51wy!(Erc>$N!}TRE;>Of<Oo?-XK9M_R{gA_3XUN
z59EvU33t*MA}B6&^1iG$HEmb*?EM|f>D@|W(+97D(n+PEt0`|YU&O9DF`PXWZD7@{
z7-ac+RUYmr9ID;p4fm_<=)G4V@BF+vuZ=SfYB#FG)dR$s@tknua92*Am12;k+%d4O
z_g~?7_;m#xV@RMhQVB;ui+(C3N}aGUGj^-8=w<|r-o=N8ja9NhmyK`|MqTzcRK+p0
zI=<|_RfkPut`|-}s*|~=k9&#k7Z>DHf(@ZHJW#t`lBGx3L4y?=-uJD?o9*s-tc;_W
zg~L{iXRjKMNWN&li59~fMmIY=Htd+S7kC(606bZJcMm)0^I&d${o!@v(My6@eLPJI
zA=r~g>t147t$Likvq=~o?UT28G_4o2$@Cdg8qAdBgRaO{Nkio(#Qf6zon@;bE0}kf
z**xv~y_5TRJM$y6XkF#R%S+ADooYK)*&})hZriNK>FXot8E$#o_yV*C{gqZ-F+&?_
zO`C#>T`%sy+YwSuN3N<epO#IR47)^aOYd4HZ3}dAjMf+*YtI7(ffj&~jLz+%)glfH
zCsl__GADNz-dtPWVb+aSqX+OEgV88^X7-_l<AEfmS$4Bg-m2L5rnxzRqjeibuAL9F
z!gcXh3ASEcRp2mqCZIltT|I;D<J4`BhmAK4z@I~HM|dY>Vd^;BiVts|-Y>TY^P4>6
z1e!h|V}sT0?wwvK1bA^Av>P-OgofWG7zq?QfRan&AP?>r;t{#(DD9@Du&F~;ZcP#U
zYO9W?akn4i6ZaXcz##U<c<^q#IZB4a<15!9(65aMs}CLTyl<dvePXm_X~7L*sB5D8
zIIv0$2sEV`tF0S`@d$sw`GCVI*lCo^3BpK`1U-NRnmmA}1ZE-n^|7wGzg<m}`FQ`K
z!)5GhplJZ6HGGDdZ+~2CaW>4&hx6O<co*FBnr`UK;Pw6b!+rhnr~SL%Uw{7FZ-2b<
z+VVK+ev$GqD2`)jMj=!YKKbQqeEmUR-sYeF4b10I#CQZ4esq;b>9ajW%Jxm`+4n`a
zyfII$Yg27)TgUV8fBwJw#{zYj3hoOC=H~bT@o5Z(9J0#tAi;6M;aR4pNgl0;oehC!
zds%x4%uld1-xymQ9M+mxFAbxgxNgh`lx>HL?T7g(rDK`ev5iJHMreg&wYqhnF1w|j
z`T5H1tnBY9%AC3FqnKr`vLQd7_V&={=X~9I%&|+)V>&j5p&6drysAHL(2r5E8b?^I
z&9g$RcC*^WQk+lZjh%E)7T=E8J-h)pF(f@~WO#rsC$Iro90@bY!o2bqJAN3pwvmJ5
zG~cgnUo|#csPZ^`x<0-O8!5uVmvw{(t6B<K$!;H3!V$i!0;t9@k@px;Eg-y8_l>o9
zyjy-iBks#^4-D*-w`7C8ZTR)sk2h<$Gv-ivFUjht?=(|YM~wCA_8t>p-`OS`rz5M)
zdz+u8Qay<!#!*X$V-o~x(vV}Ith}?$55jC?KK8A7rhg%1pCA3>54IQN?2RE)Qy9^!
z^w)A<ZbzxNJ*<-h3if<YK~Wg0C9=&u^ts1lW}{bk0yFgLhVidiOl6N2Ta=k*1>$BL
z^)cm|?~Yfo(JaAg(RKTq)@2c}FOgRSdS6g4dp6r>2%=JG6=X$^>Nee=>*IyYjQ7Jq
z(mLS|gn1Og?Nj3c_r8|Q*mqSplRif+POogTTPk~csB5JLD_M7sBQvdS$I;%yqFf{Q
z&V?}>Hc;;H^XL_AHNiFQaj$t6?3n#>xT)IP?2Sq@X3?v{osfamK$G!cVocX=c%dje
zbe!5RX!hQir3^|iU~NARGGX+Eev&mCHa>ppsRWH*xPRh!!l)$}wHF!+11FK-U~j_V
zv<mrmD@bN6U{vm4Vg&CGV6?WB8t;y)<sG!Fj*}fSx2f)FwVAi<=w9q?h6<Afdl@JD
z)jaH=&1r`igL8rnRx@Cv3?#K_kOo{R2O9lNdD!q;mp<41>MptY);{hFk1zH5B<rcT
zV6j_Ny0R|KUmaX+567$4^I+*g@20_Uet!Su<CQ=C_S<j&xc~B}k3V))+ua?ZbH1%k
zdlivGx5pRDt9zT3uNPnU{QlGY_LDw-x?kT+JHLIqYgvA3apR>%tC&<9Dtk^ARWHC`
zp@N&2+W*x*`;REGe(9vE5kpw&uhLC4>`VDjTS9g`#=5|%NzH*MAicaf;2$h&bIa~p
zvfKDYxBx?5y_5S%E9@oy1|jygaI`zWlLuj5e`JTx7X+AN10wnsjGd~2F5K7V#QoZ3
zSED|=sa4|>#VSbdX5%p4{<`u0wLf0*iTbl&+kK<U-v1=(7ghP=<h@!>Y_-w`6Rn`r
zi|S+#JfBE-eeOE#Y5fUCW4p0>ZMs{k8}qKTIV)F(BZ=aeHlpAiv+KM3%{q;Rv{|p7
zkCB9Ky*=&oMvtSv-x^+`TSe^j=*DrV<eoi#sNO6IY%Tco`<raqUb8Ap^elXJv}MK^
z;PAXK9*w;Ln}oCxye>W7u!{__+=@o*F5@5~T%zZIcgL{f=xg<Sc$Q^FeL9CKHDGkl
z>&6p%arWlJD_v`iU)$^QLkV*ZrpXrsU#sRhNU$VxrC*DB!B8=WwtRY!l^WA_buaNi
zG;Q3<$Z83mv$+$@iymDP+kHeqwvD!uH1IAxCTde5hpO8t5AU53wt603vccvEnUQAK
zM^}S`(jF(VV$ES?y+WDePe%VNpQZ_>878A4UKjic0x`ul&2kZ)YG*6PWLI_1VWz4=
zm#xjWk!Z&~YHJ*u<}39!*dC9E_Puxd@pfSnjrF;YXEcg85FG`1w-DOB0z9tQj);P)
z9^)alU4^op9a?rCyA{|K<2>Fw1&VWMuIPu@bm@3lzudL$J?cd+A!ueGSgpc&Fq1Lc
zFuE*J0TSf2ot!2cDCfRiO$p=l>m?l8FCJg3)6B{qz|AT+-38m~K}(jwLEZ8bxtlNq
z0^oxEsn<=bQj!~RfhvHI1r?1AgsQEY07%y_Er^X9p!$(^-Go4wnf10xluVQ$_0Y>6
z5fAqxXhn0-$b(I}kp(~;<c1lwO+*2@*<E*Ouj|G8V|DdcTkCmhe}+9gg@_(z$3WHA
z9P2I>d%C^CCeTfJz$x7yYwypG{o7yi`s3&S?=L@o9_QMf$YBpi33Rm&ue7EN9mfgM
z&T&2-RJQYy@tEow$8(|8yKn2GIexIw*&xB)S6GxUY?A{>sK=>mZOH${KmA8gA{}w?
zujR$w<kY?$%?2!?ZDkBod|*Dw5dUC2xDPB-|E~9mez6{y)puJZXe=}S-1V?7pu28z
z;PBu9d>4)`R=><ndJCsnsKe@Ro(MBrjZlXbP_}Nhx*NE2dF1C_;6khvE{lk(%{tk%
zi11mT*ZAS7$B)B*9*F|-^*wk%EBEH(OS2DC4NAtCvL&DH^&DegcjxZ+r@t1g>rQ`z
zt%0GbbfcT#ZtOZDyjw-@_DRyVVqjFlUTz1ydvBLuL9Fg~z74C53O_CC_GpfW@8s}N
zC2M;OepZznCV!8-{26L_3E<h6<`0HX96!1>b~C2qUh(URmrNRmABSEm9uIc$UOi@Z
zt~v%@X1DxLbnnhj9B;!^@Uge{cSI;Ubf5|lM|jpcA`CgqbI@B?@$1v>rNi3WX|Og<
z^ov!u#pp<V;+&l~%K8{{UH$%;-D%-$_6iYH7~Br-k20c~>fL4&==4fbZKJ!*X_e-N
zNr*K(l-k)Pp^05~s#osZgKC@Tgjrk60IM-*=#)=`WaWjFVJvM#VBcd#_c#n$RE{ng
zvsKNa+Fi(H=AH2OV%7B;=CZ@gHDqmie_E4E)=ZCR>vM!XEYz=UY>_?Quy*L89kvM*
z6G13E`cAsC^8B&aOKk7VV|FGbKr~pVs(mHTx1q9HW!fAzO|;zXN0>5O`*={-PQcqN
z2D$0ptB?73y;#D-jKuoH7=|#C^_lUc#AB#wT<kVNB^gjjn5~bRCrUbC-F}BgxdQb;
z0tTY3)eLMRgx(e`7;2&yr+?gYB3a#X1RYBx+|h8FH)K>^u!rB@r3Qz=;LpY|Y*tef
zcmbO!?E<0UK=gjWzdF43*7>8=r|w06$W8gV?TgiAnH&x*{xy_1<;NJ!Ie?%C{eTH(
z;9?Kb076NLZ2ABSq&iEPSM2Na{wS=Xv6AMe-JfE#vG$ft^qmp86^E<EiKsB=e8cFy
z#r3_e>;C=kfBD__^`HMVf4X*VR;6rorrNxc<_eg$W7y-d`?4-Qj@W60#xdvYVV3zy
zH}px~?#F!7M?ysll&{foj9I;^CSzN-S(KQXNBF<|r~eB8c0_5@1_M36dTky@TR|o}
z8WHYr1P(`Av+axTPtJGsFU*vNQK({GM57E$`0jGUR=*3?+rtj%RzJ{JAAjccN6?Ws
ztW*Q6>g}}j$==X#8V5UX?9p~Nef}`l_uBI$qISMkw<?n=t-ks7%h3(J@c4}du<qy$
z%<LBX3+kRznS+E)(~3~|Q=ZL0S6BLzouewlhRtnmYOC{EcT(FP2OuWaZ1a;rPgYpA
zg?I7|-SCR}HEbzmK4#u9x1FpK0A=L}hp_MDoW_#*6rswpVI{Z9t%zxnr8#=pFq8WA
z0aIzPKk3I_ZCY4v51;Nt%16!v+gQoO=JaZgO^mR1&aMQIMfjaED|ownRUgEbnPk|n
zqO&{pf*;Ycl|H;Bx8kb0<=Q%j-vCr3pk6!QPh`3GK#9S=g|Hx*7!Tx4G$4`^Fa!4z
zJZcS6gH@5WiLiLe-T1bhHR7<**Q<~7wFectUO5js_v#UDQ?EUpMTDNGb-I!3cEe}(
z@Kd#vmZ0@khhW(E+$DFl3}}EftRxTL*E)YQ_LZPW#3KN0K$5@k@;c-=g!Ex3Ymd<&
z6$evo(A_rJyWtfe{RmL7R(HXwa0WpIxn;^~jBpW3H*YS$53g4dcFb=1_m`>{eRT4i
z?4=rsHXrpcZM2XPV>{KQ7MOQ!D#>Gi2WPeis`_xdtGnZQ)P*_AW%0CVi~-0tG<NHB
z_m(?8&=4URiek}Raj-!ode?Xh6I2=oKe)bAgc|h1lXbNW?v%lZ_=@YLG0D&3X?{j$
zp~*YysQGlF{a{sf1kmQ#cdz8zJ6P;zvv6PNUtmvmmj?MAHNtl{)y+k;5-Xa$U|8La
zx-|~(f_2wBbCo+F9S0X-XdGMZsd#@_95$yJ%s2Ri33dXTG+;`b3|OFx_B4G{7Hn*<
z{NAq%`R#s{(=Aka3`Vf3)o^ub%*TPv5#fg5=3wY(<nvGK55K!ozx~7OzxnO;$8Ysl
zfBdF#X0J?<f`x1KoZ*0L+EC<Jh_~lyvVHo4COf~rb;}@!+&kBHxW>5qCd%iVsyf=X
z_13O!9K}7%BA#BKTWBl)7ytYJH7wAZ>sA1<tfN*ESK9!ZlF>Cb17ztu+2txc-t_t%
z(zF3@EfP0vu(BbmgWav-9^K!`DJ$xRJM0^o0k2Mu?RIqDX@_H#H78&Pv{^+Tb(6I!
z9@5@8^t;ch=Gyz#=gK>k<~{IP+c!Llr5Fbx<et-fs!dOeR|(B=PSSDj76y=G<mYO2
zSSZV7A=KkZ>?)y~-4_qbJI(>BH%F#8Jj@JAMPs|3sx`iZRveG1s+BZ$%Ag(RM7KrF
z6COD2kn()1sIlc%HX%KB-#$&|hV+8HqxU9S8qG$&q@W}GAvHLXMSsAS&L?+vyQ-~s
zAtFC4ep9y4h`!;6C|k%_X|xi&v2TvU-DzvZadyE>GtiTiD>TfGAQ~GyA)B=`YAH^)
zXcp^We_CgG_%Up}t6p9jy>kTIe06t$fwf|IDF(0YPk3((BGFC=xt4t)R_D}?<!AY$
z{e4&J@#FYA|HJ(9Cu!_>=HL47*4IBRZnkRvs!!LAmnNrG7U8uuvVyh7G>L)Ew!;}_
zd)>vx;%>Mvy8Uphz1_y%9i7z{y{qxsH6D_}1_HY<$r|Vu*#;DAa}JjZlwICW*d05K
z&S}bRwqbGf_d9t6o5q`FN^m}oh_qf*v=39GyMe+qH&kgi<}7RHX~4n0=mU};Slhw2
zc-<P=-B?FV*)`7|M%{yldZqTPA+zD~fEBin$GT$#2Gp$>u4Nuk1+0dL?S>8gB!$sx
z10K$RwbIzITC!%*hN;C&wn4YVI;L9#P8mDPhvy|1{ekPJ#?UeBz91u={Yiu<&G)#!
z$quiV4-zd-%iV+ptknw^S0_M$gXP-5VYRo}+YNa?vOXF!hpeoNj4n>!S&%VY3lKOF
zgu#1|n{a4jJgtjp-)UdnA4I_60%^e#%;{Sk6vz!YX+%SmHe6<(SdaSt@$u2C>Wld<
zgw0k*ZH_4)4>JL+HjW&pqfPSXAnf(=@$Hw_w_mQ`{_*~A|FHkt&CJ-{Tjg}05$>U=
zTydDeL^pRt5r!-Ck?sS+{ryEV+xK309#%N&F~*&M&}O=VLNDy?yM#$^vQ5^6$y%I4
z{xAQx|A<JHK&QEuYb`vM`lKMeJKNZcm~@VJ^v{~En*I3)?Ewjyxrw~cQT>E&DZuE_
z=w0L2#HH~Y);FCq?6g^J5A59*P`6xs&?jp%PMBM@K|J5NZyK;a*;8m=sjiJK_2;F#
zt#=l!U%k3~S+l*3Uytqu`)jn+=+1_Yrfeq<?skuHbYJODvak!|$=8kWwHGQz+{Uw1
zXpGZsXEYqtafA{K4vl%T3vy=|+YUSYfXceUyS&ILbG)u%hdq^_;TD0h8~HKsQW$sj
zy_d_zTih$SP;Uev*qoaa$06iiW>A}iIQNZ5I2*T3Q<*7FnZpxi_WZT&Z$jzopZWdU
zMqrrjTK-<F!bGD9llb`4u7&f>AsA5Fy$403Luku+#`o{*n8C;__U<l*!O#n6nwE}d
z8%9SXqpd;gBGPu%E*z&Qsq7P7FEQ9wuNU-I9@cZ2G=8`dLF)0Vq|dis+rRy{YEXaj
zr}H2F!}D*?as37DtN%~_=Wl=i&*uC&<469T|Iz$=e+%)Y-`@DAe`oyW-)Z2rUFVbY
zS=CELi=dS%sL{E@8I#$rr3mQitGqhvVmdt0GRxj{`))7#IW4!P_8_iB;{cP83pUtt
zl@Xf{;syynM6NU{!3Xoo^pi+S9q-)VI39|Lx>qymmfS`Yxy~<z%bSegbd%QA8a4-`
zp=YS2J)|ySk2mYB#5&`MiR*hE$HBYyIP#-QpxbBN<2<1(fzCN%CnI{8j>oZYpBB^5
zW**ILqkDYi5!{W4KHiX*1;D0-CfY+yDmkb$sW=W`%TFRLpN$W67_RiE)d&1bn@3$W
zPLgO@jMkdtfNz!|%=b6Nw6Vd#RXq^)S=}bXRQq8h`#UgUWu0KlVf)oIARR0ZgKfZa
zAYaDf6#QhbR=8RXuyeSrvRW1-#rwwNOJVwcHD`~ufIIDU^LOj-@Bs(i34@A!z-}S{
zkqNRv)@IMVVg2p?d0nr_$~n1rIx;+xJvsWz!>*6+W8#Wg_V#Xk&Ft$lzyH<me|q7s
zeyM-+hx-pN+?6^fLz9o>7A>orErr6S#?EVESl0k7Y$b-dH7qc~mYswCx(oH!R#e}O
z_c+^&&tvaRc*^L_Haa<jmEFOUz2*P%KmEtBK(@vKKP_K2jr$G!&OB^LQ8F7W*x~z|
z_ePrXleg-0`}`%_?U*by+Rdn}p4dO3H}f7{=mYzcv53RQ>?}oVd@(KyTDE)uLZ{}h
z48K^DfRU^Z+t=n{c!=M*vwhlL*E(AFeIM<v&(F(Zpzri!SOS{wv6@-Pa*k^6N1aRi
z?d@^D%IA?EINpjKot$4nU8k9KO+1$N^UbR(rtO!JRWc#tl(Mu>8)}`VeSYam&5*X9
zzn)bKsG6Z<w2$YgR~F--433z%#X%cdpQQDm6+(N+>497FRaJUCM=6f7W!r|o)-(WK
z@uu7s!#i!T@fJ$?oGjPv*0n{TUNwDh9l+%!5YtTNvVANWt4kxmIM|y5@u$C*o$>jh
zailxiAnFWg+VhbOe*X^4+28AY6U^$i1m6z{s#fHb>?u3X+rO%ByOK)Tmwvb{@4iQS
zKAyC>{EfQp?b-j<zl)=LOnv>c`^)=!3_X8+nd$wD|GmGV?|)p6AKw1K|M>jjJO9i7
z@5Z0R|KRU_{mp0m{?BWj{&)X-&mZ!<|0>>pi2u&t{`z<S<amDT_g@`<|L@1&`+N1{
zS5>0J=MQCdukG;FrmR>SwJT0Xl{%X_r^~1pEBg5snT#EXoM%+BTWwyF!>?i><!<u4
zDyGiUx;(6_ScE@R9~i?@!;o%9d@}l8A)_*3B(Xop!#UWhhA-0$+h*pL+qGYXpj)`5
zLHAOii`iRD;1E{Pb(sUN1Y2{;PUViQCK+blC3&tf;+~P`C_EmvUFMPg|K|u#du`jY
zbTR9FTWh_KG3H#w-uqNhX5O>`3xp6L7sv?9#Wj`@e+KToz#pTqz@M-z3oK#T0tDAL
zlZ=cw5hu<$yI5<@ImYO{6$CtwkYd7<r4R^f>XZ6GPp44^sxwZFgROg-j5|vVSu{qt
zU5#E@FqJvWD?1?tDCZmW$||WTK1wu$6-Dv%i^jcVVz409g#u1vMj^qqGAITtSt2zE
zAOdrWwJ4%G<6fm?A*^zQt@JZ_VJ`(qFCjgl7mOFm5)29ojE4t#eXG?VmQvNOnWyCL
zSR|*85Hz`%YBSqP24yKEo3cWnL_&-#5`YLq5QQR`dY}1Je(Yc7I7?aii?8e4Q6k!w
z1qfpk)rsY6EP)V7`0LBhZ~W=y_NUMvKA)dnZwHy#y#^h+E>u9MUJ^mVOlP`KN<dq|
z3Tf-swLEjGaC$<QG2To;ZA}7?S8*P%pO?p{^M1-KT$VXQX(0lpXP8~nXQWfR{?mW&
zA2S>3p6j*MEbZ{fNY)CQs7KKhgyiI=(2HUgf5Gv+W21Y55|eH<PD<3d3&D9S(V7+{
zQ7A1nC`Cx=ERiVSinudp$+tNVR<BFzEyWw-UUCHrIf+Plf_t4Az-(XF?c-eE7wi~E
zMG}~)!<1fSY=K%$qVU|BcEhwToTJvYd775iI4|$#gpFy7K&e{`ubgK%t1WgO>64KK
zwq=Qk5*14s!z4p3!@ES7@fcJErNXsl;kLvQ>oBBdD&mw4l}f25W3)wUmY7sPoNBBp
z*18G^NfA{Lv|tGr4;T(#J`lUE3q4z%6dIb(*U~FErF<Y{X^d21da#@&5EQ0jiKZp3
z&Y|Vim6@!S6-L$_z{q3hc$-sEXadO;wGw1|;On7_&stZN*-YoCl!^cpoRx~UwDUew
zxKrd#ud#eh849o3SQ$bO#M4#v63@Tg_j|4lfA!zj?RLD}>fw@YNzG+z6Hy<&<A+C$
zn?L_@`)~fugZy3o^WUAzZ`$Adrq936KOQ^3wQrw$e8eBW=ku@DUwybP(Leoj*z@{}
zUp{}mT^?iQ$@jm(U;h<;`&She@JcCYMvXkdPM+M4u#42p+YCC|Y8gRgx{wiC6)jkZ
zX-!)b9{?S#niiv`gszq$=vj?gQlyxo6h3SPtJt$7#}K`M4w9mXNr+dOPnszuQqHGU
zU*Dh_kSq#ADs@gvKlm);l{pj)?MujMSp2NMRP_nyqCFY&)PQ0JT04&eTzkuNgvvsg
zyR=#|L#xSxwt$D(R`zVM7n93-#?G~8EzGQ>%$d@WRA%s+AQW(v077&v7l{cul$|If
z2x<pONJUhs#nj$1A=L~t<^eu}2g;D7WaIQQiAff@2={WPfU`1FL8pUXYktXOCTUOQ
zv-}i8WG6ymFf}8oC92Gk@_}`x6s)r@QYXiVen~%47O1l>>K}ov5YR@pq^hmj7cr$O
zb)f(_i7SB;mI%UuMgUYGD4OA2Sz11%-u5b`LZ83U;nbd*2oXcS?PTd{^Bm2L=k4<?
zKYhA=|L6UO-+lSKm#bs}x-b%AOMz8VM51__UeVo3HZ}6dT5LhW)kVs~Z0$?5G7nWv
zh~mUKW28i8W%}V~dX=JNf=ty?HxD6CYBMtG(0}qD{A=JWxXu7()@f0=XjM=b(VJFa
z{W>iKswL43`E%A)I5JCTNLpaBMnNkiBv~_Mjn-tMDwxM;@6=|~o~?slJlABe+_PEj
zg|z_WF<AvQWu;o-EH;S&N7WnJC0<^1Bxjxks^Y!}xbLyBkIX326Z#h-bu3W<)Jf#f
zszuGC5|*N(ov@;jnUU@b&0?OxMI~BBU7lpTEz}Wisccq0MM)}A7lIIPCG1T?WKkce
zToOKlfrZU6LKU)TF9v61b(CqZ1Sk~ky;4we61}Q8SX!{EV^NJ9G?1!wR9ofCEmf%r
zX^dIg4J8t=rl#gDXN6kn#&u0oc(vUV=Yg&<sfE&#CZ4{ERxz`BrOEUx+vL1MJ#?i<
zF_|HXWZ<|%FXA^^*kr6U4bd$#tVmXgxRj`*Gl#c&t@XR&L&h;L@BIB==l#_lKjc69
zZ{_}nozKfJ{<@Z*$EP}$uU#Y(WyJPi&+nFtFC}>Y?(!7d?eE4v-1cAnx9wN&+uJ|P
zA8Y>NUy|!xKL0phGJgHr_U%=V-}(2S&dXohx8JU^%jdt1FJ0e1FTeci-E*$`M@6si
z{w4p_f205Uw_{u2(IkgH**U^0*}Xv{X8_0$3!}_PS$l(LWM49fn6oC=1?S<7j8r1l
z=1oE{Rdhz8hoYoAMG!?5K~@^7x*A*cCik~y4mCAZ5K>hec~V5093@r<v#Drp4;B@|
zic^irmAONh3P?+Z1a%yek%@3O?VKl?QR#*%VE|epOJq<)v)Jg`TByW}Rn6_OpJ!dI
zq-*X1zy+Yp;k|)-nWC?lAsE?u)gpPavE-zum}PZoCw&1DJ;Yn3YI@owGGs9NrkKc`
z?W*$>4GlmW<S4ypNFhoV&Z31>WZNoEz!F37pm|Sh$b+iIdvTMxqa4lL0T|}YzUE?8
zAc!%;7S=48oTWzii&cb*a^ZZ_b(Q;oBr+XMli;joGE&YLxjfqTCjA}pngC@XQ7Tea
z6hSeQ3Y0@s(gZ*aCNf_u>-H)9?!4cIL+@|ZF6$CDX6o7>*;Bx#ub*FMd^-5km-ELR
zuXh|iV`)Cz$53J6V92D0Pp@3N8Yxh!1F*~T7qK5zwJdg4Ws#OXm<sU%JO*7PRRTv;
zs}U$d0i@7OnapFlb|4@UMZI*@fBKLAEtaH_HK3d3J#DEmYlH<gwR#oRD5);B!;~02
zw#vOc3qbjlh5La7;Y{)^TpFUN4L+(rnhGM=P$g1cq0L{Q&&*;bN;QktN>sZdPLzRQ
zfHQ(ZR7I6fW<{}l)b4k1?mn4ENiWIRTTr?nPLWp7Ra^vod~yaIp_D>a(MQHfP8R8r
z;t7cF+?MoM;FK%?Ca5A3`+y1qk`cyU>@mVt%Iq{os*Nfw2sNS1rE8_8NluhVy3EoD
zk(g1XtJDcosUe(7<*JB8gf$Tq7-jwNrnDi6BbUoUb%-*OoUAV8l8QMct5VA}os<-9
znupoyD3OpRLS$gfsiDa!?II`>x^<c9i8ft2i=8S)&PIx~`|hQ}Xf^!0;Pf!COj{tT
z_K~IF5k7^zmg}76eWN_t*B|C(sqH<#et*Qv`SoA5uNRM(oBoyj>dN@&H_qNjm`z<S
zT(6hKB~JDWe)Dbn{I~KC-_Ogx;_ttD+}`Zv$APc%H^1rmL;dlSKm1m|dw$>_<@2Ze
z=WYJxA3c11(bxale>ji!j1TYhV*T|G`1y4n&+^T;+ppTwFV^ir`t_PmAL6@znH$GB
zSrc5-OPjK(Pi8tweMV{NvU!Yf>4;g=%p7VUVw66LphBQ+&132X;7nJ!Gn(36DidWj
z^O(vCKdp7C$aGo@WR?i88E+^pVWceZGdD~3rmRdq%Zf1JJlDs%jV_aElt?rp3R$Xl
z-q^Mf7QigmD{_QuLQoYY)rgd^HJGw3+c~W{ukV$U6-|p#5SPko&9q7qeu{L#S*f~c
zo{=&olFO!;<y5$0NuNp-3^fW$_p}4LBCFC9tsMt=stD1`i%h9Z6jCaRycdiuSP*q4
z3#v5`LiZvCfo8&1IUHSBa7%`W&5E+M>aOWo#@!16oMf@eOmm{@;O<jb(Wu(IEmrrM
z4qeC=tuaM&$6^!-snBk`$$FJGAq)FT8f0S*V1R%Gq=5zrh@v0>nGDZT%FvhN<u**k
zeby!y*3xXZzGzi%hQsG^zl|SX{HLFe-+lk~>E^fa?4|*BwDlR`Di#=AF03#n9AYFy
zY;mNpGR;|9?ug4b-?^WnSI>c}$!yyyORu6h&v7h?J*Wzhn8++`-rA*CHPyDZOlagj
zOaI9~{x^UGnpSeo!XuIp5ngJMT7}8Q22@n9db~+irj_3^&lKhKjK=ee(~=g!@_;Hc
zDJudJN9hG*E+ns=Ub6C1&W^sx`BtX1P0C4S&EjMsRmD*ReJ``D8s1o}eR{eVBJ_5i
zWB9RGt#P~@C4D}~R;TGwGF2((o$E95)D);|I4<v7+?us86OU{)mu2zbag<p_0FseK
zwOSw+=>$?#35b}8B(=99SB_aNWR>s^iOw3}s!HpU%4IAM#91a}qb;H#h$SLy=`y{V
z_?aSYyG|F$QE?_U10<ss<Y7!*Y|cQQSu9xQR9%25C6+5CM|;fG+DM8>%Zji;INH;~
zD29tPP*sdIi;qpe;Fi%Aj1kIKhuB)nW<EJ<S?+dS_;y0IESguS$?D~eGfX^=Kvs2g
zgq#+(5Q{(kdMf6(e<L3r(?84g5)T^lW~=ne=KE_sKR;{-k9o``jhn1>-bya*@nN-3
zTps(c|B<eve)oTm_$&YVH|x8}FMsRD`@CG2^>rD0?GO0yU3*OV`FnrsNBhRUTA%Xg
z{Q2{-U*`3_T<^VptWO{JpH{zI@FWXgJ9-O>b$jaD2YvY9wzPWq#qsnTJgy+)nCih1
z0C=&w%@7uxMfypk7$%k~mQF;gtkh;Q#8&Y$F^Y>wE!C=q%%*Ugz-8rRa?sve-3n3F
z-~(xrXF`Ffa<|q`FCJP?DrZ)et(vvONHNSKy)jQm(yS(p1T)r$s(E;}R8Mcps?gXi
zEesKuSY@Hb$kv1^Etd4Qb;YD!i8m9Omk$_kQ08Jfr-%WQ0>%k!pjPa~Csu&a0y#jD
zF+*0#Atn*R4io`s3LsTU3PaGvOtNNJQ<*`L=2a|ffMOo9e#hgBh6j2z60_K|r1F$n
z(o!d)ZJdLXStq<HK8HR)op%ST(4<%uB3G+DR53eROD4}1t;noeQNcPfGh|ULS$B(B
zP{1`SYYQVQV{{e6wy22`0#J9Nmt0T+DM(`_$WoF<DO3YzX)E`DV#axMD-f++8nx>3
z_!xICyuHQim*eLhfBy014==cd52sfcnFvOwA0`Tb5iQEK35Y4Fy~#|2Ee-q7c9qOB
zS<>R|%_?)B&CK1aQttOrP09JTS6@>+5<rzwI29~Z9s?#mhm^`ay+iw=|I2^;ZvaJi
zuoZ8}wdM_7OMB&1I@M?scqkTuYMP^QBBb%SCpMgsa|g6yz@^j(nd8(fkCZ-=7i+EA
zoxMj3Wig!YHILjb79rA@dr6ipJ<bxLKsY28<-jzAmcPYpY+h7{&e!uiGpaJd`z&3f
zCc||UMN!ZKTO%>v4pJ7-rK_I{7;CFh5UHRtJrb=I!7-toECZ)iLnLh7x^|`$D9vGu
zu)Ir~B_e()CpXVxQr4w|R8)AjMjVUJIUyLrM!(NcVKAIxDXE#TYPDAGCQ3kxdg&F}
z@=PwQGbmy~pu~h|heI<<?fO{DRmPXh*7mpRT5OU#C{E(^lz?^_(-g68{dlVtA}vQ0
zJwr>9>mr%88cx@kB}ikK$Fa2iCG>$fnT^M!LswviOTw<GDt2!V=i>)`c&Lr@t9N4U
zyub1C>@s;Qq0Pnt9KL<vAAVnX8Bbr~_S%+Kx!d&@;7eOgAHH55)8g;_4<qy&`Hi;u
z`~6Ep|Hj@q?{_&Go}cCGrg_BA-{*(loR_CIzn2%^Z{iOZJU;OJd;b{sFEX~@=$4%y
zfoIC<J%ND#toK8>6}mh=F2A^JzrL>TAJML3eRAF8vdm+zk%$u^&391+C96`<kdbaP
zKyeAGau&uAFsh-g5)Mx=XHlu)&ZvTmduU0rw6mJSAuvKjAiWuC+@x)#W^;BO5v&L>
z;>eogUQ)7Uh_CAsk&q+C9t+ZYITT7|oWX}+hR`8Z86`#37^`xO=u%bE6T|1i!sVUF
zL^JY2zKUKa&AOwse3m?voyA3@#56TPLJpe)(uiC12cTH9kk*Wqk%Ll|MDIjO9S210
zuC$<vUFFzG6far_DS#8GP{E`GqE&%WWd=FKDb`afQ(Nwtvq-?CD~5AOmR7%Nym5Ni
zLf^}JWfrpLPA^YLjf$P^LGoT8)MXZEG+-V;$JnK}iej-v7D8DP*)KpVv5sCuK{};^
zsZff5Mk?(D8mWRLT<adL^qIH04+1Jg?^=6F%_iqe{qocO<A|Ss@<0Ch{4&lnwIqir
zsZ>L`(AhRtQW<O-J4JJ8%#+F1mzcA1s5H)_yn+tZh|2PqE^K6qb(}uyEE$8$+hNuX
zX)IKB>rKe40gomn%Q<J_T$B1w|K0x)3y>3gf@Qtf4Y|}j7L;s@Wu>i%0~{$Q_hg)r
zckT$0x`*U+)hZwh!AfVhOv-qx-o!{-6tBe(kk49=sGY*lYq02GWXZ_WR7~|uQ_<&|
zZv>*jP%$g>u2q?c;W!QqkCK@)W+!7mSegzbmMAy96vP%0FN{+|FOMxupe}%>4lP9j
z%pN+XYZn#ILMk7%*GxB^t|LPvLtL9XgvGP4H6Bjtq}mZc$)d@#Qwr295z!0*3Z{9~
z<yq|z>2u6z&C~+PN@j&#3<(kALOrvLi}KjT18TFP#}qM4iW4Ta6oLgW?LBX`@1zuk
zi?#^O!*i*=7EWE3o)e44j5M)KP%|P=*i)-<<m0#Eca5{a8Lj6Go@Tuc;S51l&!80^
zIL_>;(HvQ|03r_7>X!%q@T+`ya8|7Al5g1_+cW2<AKTL}>M=OZJs<PscXK|<!xmOO
zXU><JrYyzHQu{By>-J-Qyt({x{`y<?X+QlO{B1t9rQYH#<Ga7n2WXx8!;iT>$MxNc
zm;Ul&9Gc(!g6~?7*ZddX&o?>W{aTi4_wQ3LWgqOR;rKy5e~h>LTz*^4(p+&{+gP=)
zNNrpHYFmH#*nah>o3cG;Bm~rJGQzUT(od!yp2j%W4|d$kKoLNjCa0g;B@do^K{%%s
zp=p(+YK)p1R0@-zdX~+l^<XWF`krH?ZW^O1hp9v(97{`YqpdjuT`GvJS?o-Bp|-T;
zsXSE*v(8HEI<PoXnJ2(9%uuU}P{6EP4pQdj+G@1bw5r)^^_B+HPCu;}Pk*iBg&{>`
zHuH!i>ug*jN}|};vTKf+-b@7j%2J6@)lCzd$>FJwRXf6^b^2ERYGI69No6#}8GU6I
z!V?XEU@u&%dAWk86_XmXm$k@9DECnynGlgNlPtAbSLDfAl`SEX6X_yKST>4BiHcZh
zNw(UA<6da7y@%adOEuF<>6-4fKJ$F@wxJjVSt=$MimE6{5uKoc262Xv0#<P$1xjjT
z5#?eR*aVs%G42E5J&Ub+jm-7t{^hh!KfL|<)A_l0I3^`swS*K?Pur5kqe=&K6>&7?
zX)04Zx%Mc(E<L%>Jt|8US*_wMX_`JIF=m?KJP$%^RIFXZ*_2s}DnmrktIiCCG^>f}
zcYS!MqSVlT^6&rKpz$vd>{_W(F1%oMUYs={s+g!VQ{g9bivsqBvZ_O;PpNd}d7IPd
zR*0ICaE!qA2tQjxwTAB0_ma1g42Y$&SLH>VXrp=q3c83)lo2NhJ+kTyNVqa*c#3j5
z!((_Iw-M-b)|q>SL`LO&ymZ7&rm7X9w8yfp>eCr*vANH_YB$C-Q;$igHlY^Yw4N72
z?ATpXo0_z&JEttN3q=&MEzML+=tv}G8HCs-l@JkYh&vQ&*)=QKDJsNBsZ6#46)si0
zyl;63V;PASIA6lv^PEyg=v9T43IxOyDph&ebWACr)L6K@8=k7H$eN=u3z^0ANzq1O
zPNBsyiYBqMVj8_|Wh>7w<uEw}>isHyYvP4#Sqq=^gGyEuy3HfI0*>L;H?I_lk{<mU
z?>?Llo5F(;coV&NLfTC~pL}>;<;2HR=i4#e&t{L8IFFtuUtWb>HpTwdzW%j5vEBY5
zzx4eV|B7ut@`pd@?KLjHvZp`DEqQq-Pw$uc2YdY(+c#58`PScl8lQ4J{EE-(>YwV<
zr{l-B@%U?fkTrkMq29mN$G+4TeERcz&GX&g)bq8sL;VN*^p^S+^~rl<9Se8E*cvXa
z*>$xKAN=d@s$G-E@#9^l3lp^pL0Jt<Q=IJYE^@vxaf(f8Dr`8WZp*kgVsw*ox|At=
zp4&T6%Ta`Jx?9B5Ibh8_a#*Pl?$TuLC*PSI-X7)cb@au8C@KihVk%~!``tN4Aq?{*
zyOR)#7aF9R*^$!oZ~%plY^d0-GNamD+GD#kV1{nyJ3HwM#o4-yp)z1=q?c@5Ed%5x
zF=|Qen}%09*eR2AB!Q~PQCe(-ms`&p3()uUn=Fmzy_RnxUNRaLD5_7SD=t|@7NfEh
z7_N)VDi&t13h@IHeVoY!nPHug8K4sTDL$yoN}$2q%P3iP(Qzw~S(+Y%<OCPQ!N3Vh
zC2AxV;ecq(U7?-<Tk5#8_qY#KAVS5tbu)+-_yDe;mo-W&1sH{5CaI{P5CmXEdbK=u
ze><yMZ*((Myw&n@fBWf=Z@>HKpYD}8Ls~|qmt+bQnkG__u(c{hibWO6%3iPqCRbZp
z>}OwF;-C|2kC!i+WfZBBl2DOCqQsU|%`pWkakS-X8lh~7WVS`Bb>BxeH5XKBRXO#a
z{X73X)|o8mqCQwtl_iVlQSz(-(SS%1@I`vhV=q`G!;aVy^@i$|qPMrm&U1$kQc%dg
zD16Zd4(Sad;y1Pj)D)>eml!4279Sx`0wJa#+Dz3mGpfB~-eityAKYJM4gq2oMUH!T
z)eaf^IjVBfqiCY*1tsRC)|aKU)={`<pli+D+dGRx5j9no8Bt{CIsG2Ct<IC>1`pAU
zN@*AedXu$xAJfHsDvjr)Bw44Ktz9KWE^;A&iE1&bR#SjkT$rQgtjl|yFR4qk5?(d$
zI7=*<mo&~3(pJJtQOhIpU<qqbRVeOh6xPv2g2vWML~6#nXTE+ZN_iEHSW|^^Q8_b1
zWCe})FSMp?F2zG+yg<rg!qp?Qg+UZ~W>-IvCz`52iW2b5oTf|<WP2Fa&AYdCEbA#B
zu${g>;ql2N<{fO}5uA!<&F^^n=E!q@Iq>w=9;1Ocemd>BUAl;l*!0p`eqVq5oVD<~
zcPoC6pX>DB`tsTCXWbs;`o7_3dHXca_qe`mi{tJ4k>Bj?yZZ2cSw?;Q`#IkaeXyrT
z<dD64t@`2G{bT*~QNrfCzt-n>i++;x$Ncm*^_zJ7tzKtu&f^O{ojyC3$GkjbjmxI%
zR?qK!dDmzk0+kUJ%nTD&-PNN)ATuF6jutw5tD4A$Im{jgL6lJ|v^X2}NLw0Qd&SxF
zbS@&wJj+wsL-J(*VBAlV5G790I%dm}O_^*mBDL9lQX)&HPg`Rg9mCs|v>XwVXG&7i
z*krQGOJ-84XM~7WZ!x`f^aX`&ouAPjA-knh7h|#&Rgpu%lHz%4RcIMP$>OZ3lp>ns
zVUj7mAS?jM<*IRq3e#W})v$IQftHK|O?XZuoZ8US2%Kn7?ne<)BhV0$A#!@ELUb;|
z2uYU|Dv5zaQ8-U#rkF58Sp7*P*h4}plc9y3ZPkd>7SSc%K&5z*nXKhOWVEUDA}pZP
z(i3ThC}{;<I1vMJ&XR1GwQVd_O3)@`qX?-)fM5#HArMMQmvrGlK-vbGdBof66nboZ
ze0lx!x&Hja{->Atbek_D-4#uir8}{<jy$uqqOEHOr4yFmdJ#hl$m1#|ur+gnQhy-~
zcP<k1w9AE>J^;lyBinM`&pM8T^rh!w9YQr9$0lunHY1`}Whbk*W)?oo7F<>T$-nzQ
z1d6tZL;=-J`=x|RE9_!j=aKS(ek4VXPgM<hD<;l^amFazJ?Sw~Nv{-LQx-5mk*<p3
z(yOT~T_kC2MOF(9xJaGRjANDtixOHZQY?K{B=k&sMw*QytEZ0;t67p~&WhqVW6bc(
z^Ua5fW{8AR6&3Rw9-?I)<)NX&y-B#PhRI10R8-8Uwg^C(48+I=%_a!#61%QTTYOYW
zl}M@C@Cn!D(ja{d2z-5P6&fCB3Rp8{ij@zRcJ~JCn2Kgr4qY{8=psglGRT(THRleI
z$jQi_nYIYvqr?_yN=a?4ln|u~r`yMmSXR+)kuGj|vXCw{-^!khC&uB{OOlibOwsOX
zy=dz=opwsRmv{*()^|<L@$kM!Mk+z*Lln5ICD)@avAt8}Sgs@5ak)%}Uf*wz)cs3f
zJB}UL?D1hriah3HHy?Glk+r?M;Lcf*+uGh(Vc-0^Z@2#P!^mHaZ+=y4w=cK&6!rCU
z;RpHt=ltrg^!>%+_v44NE??J&O~&v2hquUI;un|oqRaj-;`VUh7q-mCn|}Gyu3Np|
zOkecpzmM_FeE(s2|Fmwe_4)Vb566_R;^Ehq%c=8Excyjj8S!pjukz4nhhXX$;zO%*
zdDhEwJwL5qKg#p_e0dw^9#I6U87^(i(0M0&<S-EmMYOb7B1Ob3Z@PfGoso@^fJo0c
zl#zU}`4%9bA9YM=Yh~hmJLg4h6)E)-))Ex4sIKD{P+y;OB%rb|`qz5Am6?;pCM4Qw
zQPjES8TSh7cAO%d4WLkz-(B!BtaYm>YaOC}8n3l&5<^57UfaS9a=<QD!HNKt2EU_k
zjG^<)Mpi3YrInJ1v<_=1qV{SRn=X`!dm>3onB1zaYlNsUeW<a#t5#Z+ZxpW_h=hWg
zbZG6E!4jaLC}d#?OcG!Rp^}vfrerBI&aLWAM5`8@N70B$K9V=lD>`eSWD%1BEN4)}
zI`fdq!ligu3ahedMWCG2;8ajAi(H^~5izo$Rcxf8gfuX~CUQr1ASjImA{u5^xtHJW
z*k!)#FE8`;tUvte_Hy$BzFa>X`&q^jcerHj{OY^jdhK^bCUY@KO#yArLM_nDV5LBq
zUPegIaE)q~Sr^73c0CWL@bD8@(ToPj0?N`f`_}e1M<+&1lrDtN;fUdx6ey|@`oI3i
z|CX?&0sAd*wUnw!HA=|kAOIC&pa!F|&X}9dz}y4$armhYLq(k#KFQ{l<zCjMEz(?U
zradW#Bn4OU=C%R>4Nax488a*zrP?DKrGhy{z*;MlV5#LiYn)RTo*p$P=(C;2dF+02
znSrS$)zc|8MWy0Q>8-MB<)RDgfCz!!q!pY&uvC^7S!k}oOi5$g3VBILV>;7@kdq;x
zQiP1sE)mvx_i?7AP;$z$@i+;W3R)>Lii#8!CqvGuPoi!B_PaXc`lM2GFB%0&X(*r~
z!ew3HLn6yM+F7bRM{Qdga6b^Dek)luX3DA}UPBz%9$bpsYAyW;whqzBMysS7MTk6+
z(rWI!KI^!T?MicaN*pIvWY2Vg)$-wN57Abxn_sTeuBM+}roQA>`Gt?#9=2rz!_QoK
z-aJNo_h{qCWz6-l>h@>7-TiQRdeHgjl1<jqP2*TZd%q(8{^NOf`TE;Fzwe)ZJ}w`k
zLGC~K{O0)ZtL<{~4}Y6)Ydn9a*J(d|fBtZn=U>`4+5OI2J3m4{T$lEW&%eXtul=j{
z#?UYSkT0vtQ~&kDwJE>+m;PnB^Vj+AeSgf0yvWBt`wPy?ud%G_<8Inkf1PiinU|AK
zp_@EBsJfai^0M{OAB`V=?e_lkv&O_siO@=IlJ@919Fg*vP^D@f$s}gH|BZ}WfTa=;
zoHywYg~;#@93ZPKSrXy{nUi9g-qLnKovI2-q1<;_6q$q7pc?m6t%mz?t97$V$r&mf
zZ&0eXkxlcI%(*^D+&!Bdhg~0JA?_!Qx;!*F8=(=kK3ET?=F>MdcOIvRG6o1Kcq`4q
z^s+8FQxv_CQql$YQfQ$-nP*^QKWlVBCN^R)N!f#o<TaPw_C2i86QXiVvVvJtQtvV~
z7gaWlH;z$#$zC!eC{RTX1~5JC0f~$guoR|bu8aDt$YRwDE|xW!s_LN~tjg4I#@XwP
zX3WMMuriwDpd`?M1Cfd|d66noGSWO~nR8Khxu&*7#I#+Y3H=rv%0@=<{|`=WV6hUm
z5FrqeQCaaaU%njk)BR7M@^>HiKYcu3=0KR2q8yDaM2MK@YAJ|uc%&;N%BzV8R8oo%
zWzMRiBU;1MJl>==V2E^jCG>~^U^=w4t<|pAZXtz=S@ZPsOeB2dh*?}8`u%OE7*-=>
zedsng-1VRQ+y8?q&zPz2RZpy<scaLGrOzTnD&(Hy!qSSt8g;;P))5(W2anyo@&toX
zlg%<k+e$rEH!NyOl~xN(uULlo02XkTY*IrZ49R99Q`|~h`XYT+Cg7M>b*o5lp)$Vg
z+15PX?s>gW_hTL@n#uhvEtJEwfua&4?7AVGA<a@`NtPUrdAet%PeDd09Y-x%tp&U=
zG-60*l-7(gRAwoQSr1*<G4IG$8Nrs;Dq~T4g;t6O;HSOUc|;kR!QN|MX~}86hi*d0
zx@jHUM+i}SXAe5YWTeW9F|#ig;b}}QidBRowRixP{q(d3$(TrpX=Vt=NtHYXNkeh>
z@%${~%|~|6vZdTp7;p-wQ=O%OX|jD7T)jW4K14J9>NohsFGF14J(Ps!ah{Q{pWpbv
z?zTN{dS1k`_nu#3#(>JQ%*U+`r##-!<I5?RZzb-2erciBkRN|+U;QI{)@A;5JHe-K
zWDPm?aXzQ4$|GM6-9FUw){mF^!}lk@Zr^>~_3z?S?e@$1>KASOBtQOfjuva;sp)x}
zJ7TPMvE}vm`SYL0@@xF+oAzk^_P6o<ZNGoZ@1C~zw(8zr{%(HZxcoKWJ!{j(7d>A%
zFwbSScX55_HkVGt8%N-Hn{#D*w)I_m-`e}C%ZJ>L+D}JTWbWxpn?4kcm$gaJ6Cj*P
z?4m8_?3<h;RC-jw^9&KXmn>IfXg4jP&k-$~$vm4~O2f*h8@o-0mKW{xiJ9fIW{5Jz
zVO<IsCD04wu!?~u;kiAI<A_GF)k%sz<g6(?XV=LrYue9&X4R}}wk#+)cEbs4wODB{
zUBsI3A?px<XoM`9g;E<y28k*|D~1GBSb<h7u*k@qZk_HDfjsJ>7=`rECKya&Db54X
zHh~O{Gs?g$ZIFf1#e-~#N`Rwkc|a!T$qvgviDHp?PhK$J(4Grur{}VeTF1R)<v2to
z3BmNDm?E5#<#*u<q-3UuAR%2Us`X3<MdHT(;75uu#%Zf-6Kkuq1{~0h+)5fT0S6i&
zM3DtaKp{ZxfKlZ>=k0f2e*dTa&wo09xXl~d&@x-jaZgs6)_KrY$rP@2pWFnkZPs~N
zIz^?LDuhduI6@Ti*rwMa44!4pcAYj$yH$(y?2B4vxmnkJs4m$?-=wqqsDhMvd;TWf
z=i9wHR9Y~FsY%ZyQ~&9||F1z%U7PG-q9D`J2rOm`d3QgFB~C*nCNi=Hqj~O1>2aKM
z93EDlGd*A!siJnJXm2g0t*C2Csof*5Qa3@PJxU&uv$~X`l0t)rzzbK-pGu@gmpqGF
zW7pvn^}(8ZvBix2KF1sqxQ9pR$Y3zkWb4307Ta<f)~*26wJgR+TL`w+v}LBP4QCeQ
z`Vh*P9w%$lnz}5-@+`EGF}xA2_Yjdh(EAqdnw4NTil-JMNyVx%M`CfC*cR~-Kxjc5
zS&%g<B{bHn?58hFN-I$^Pp^AqESJ=>IL_^xaZblRdWo4;OzD`D%@QsPq1S{|&D4d%
zLBYCah3QsQvRaLi-c>RZY|MxwCqzm~J@L%Ado=y<*K&D3*LV7hZ)|b9Uh3UCj!*lj
zdAyu&ueUd^+bkcUTdU;z$9&!Ir9$_g=cy_ay@!1HxISO0C+?rx^JC|2?(R)dHuAbW
zZ?>8K@Gl}vzx}Iz{0sRs>fQT#fQ&s{{8zuJ?()08jm!J}-8Yx7yT!-&e(|rrULTI-
z_*329jk4@*TNfEO#HiY<5Bc=-ly~{~)x*Q+_)<UoY5pwZ*T3bHY}d2zALl2z%@6qY
zH<u@GCKx~DE5g3Q!dezvURO@dPjv*U9kUmgTsjw8?UrxLCfoZSkG~mn#r}e16n&nn
zsgl)N{zVk4oMkGgnYHM(OHeTB^FB{4&2XaCYH(-e2xb-Zz_7kLCV|qJvu&LB0r2RA
zPc^b@s%Ywr3QDmmFdcQSk~wQY2c(^6Ri*1OqWdlQP_@fKVYgOHW_h=Z8q-q%=@bVp
zYuLu7gfvK{b7qWDCJbh4<suwRDNI|RvF}MrC9Fp(lAyP-N7t6KT9a&2dsgbDVU|!S
zS$WsmP|nB{17?+TU3nC`6intQtQc?UC9NllPBUuL8fQvLHqQVq>bEdSQ_P|?iJ6_@
zCNiLpki%qArnim9TPz!WsP#ftTrxvdMJx{2@?rr^st}%p;8NpFOp+myL|N~)t+F&&
zCr+{vP7Fu`!M`XY3RRFqC8&Z#jL7Z&r$7GfKfnL&PvZ+A=#fOSl`tlkb-g^s{S|%H
zIogwqkT?s|S0#O>sFaLz)OqT*6(uWjFOO5~vTkj}zN}x#QgzZ*Ayc<2k28|WV)L#K
z^ENX>eHNyuVUFPil(AJa&1lI|r4L<9{}=z$f2X7qiXgd2OI@zSF55$D@;1dPhRFG)
zxa2u--b=UG_jI54;kUzsd5g0z;!>V4+Vv_M@a`8PpcOmO?yyH)l5~*b9vXAG$QT+5
z9T`!X*-8;?!?7tx23RLdF+HniO}4f6(<}Wr&sfjYnh`lM)3qf|9!gJJjPHMA%f)ZE
zzP0QZR7?@Dr90Ba3_^lgteMcY>fRt=x@1gEx}>E+Rj84o$gDF$MZ-Ws)p7tL?V`Dl
zkS2(tZoTDz1Ch|xX6l%vgf#d_(Fi9nb0^m&_K6Y^s$gB3pE)7tA>|M$rxJwA_D*XC
zut)-eipq-Xx|}zGgi4F#O!VX=<z8Irqar1Oa-|<tt+uN_J>$c_3|A`c!;}8iU*p>+
zy*Q3LtRIzm-rru2{q)>-bB^O=jMlB|E%n+OV*hOG+QvPVxm@-9Bs0Pu#{D+;(%Weh
z{wcAP%)37QR=(jfj`6A79%|`YugAxCc{Rx?AHUdjS$=bE<7fTD+qitwKiE2du!Hsd
zkK)_aF#X3L(%<o2xB6WASi*>zCwM#R(^Aim^3}D;IbXi`mxrTY{rPcw*w*v!>(6$^
z#ou4ouOBZxW&An6+>iYkU%j`B_RdzjocGu<=hfR|u1)$v98H^y7aVWti?sK8>HYo2
zr!CeeKX1n|SyVp&3uy-;rOA1qG6NN<b<5k)&bv25mH;Y|l9qA=6&|K4y^haPcJ{T;
z)RwFmdM$N$6fMI~P%~y0RyKI{wS*wWE?%-IPiU16BeV%z{0O!HwwdPwn<>)E!cVu3
zUgn-A!XDZeWu>xmp}+7iddE0GcyBb87S56-G&jUa5w%|9pfoo_L6)^*h)+l($TY2;
zYP1rztc`R=M@(c>MT`jq8zUt>>D1mFB{+di>h2KZEK_C!-_SSAKvqf8dRa&@MM{+8
z4RR4#P_K|yIZ>gI5*R&WLe|PmnsSO;N~?0s8HEyP11Yie3J42ibm00GVh`Dbj&gFX
z7_yjMr>^gWFQ{kg30^=cg4#uee}M^fP#_8^s6a7a{`B$xJ^uOs>*N3D2uXWOW*@E0
z!=X)C6f&c=)gJ7;k5!k9*_y>1Y|To%W*1YHd3azzHq&9bw7kU>itv2fUwq^#nO)nt
zAE3z6MB*H?rigouOewVHVSIk99P7IW1u<?&#F$OAOq;p>vw!E`WCDvzkk=<gl4tZT
zvxB?j;qgp=Nj}xQqXzIiAslxfIu9Si$GFTGbHs5QSh7oNk^*hV=X!pp^CqN_To#T~
zmxof6Z1gu^6T31-$O;ELN$8036l=;rD(e7xDZ}@AIaFBFomGz4X|><Z5Rc}tsngm;
z*CqPeu1$e){A_|8k<5(h9atW9TNATD#5vpA;Z=3`sYPjYT`@ffg~752RB<WQYmQ;P
z`_e#1T0vBO4LTL{r6eJ>pSeD-`>me0d3VE6WwCTWPEta;HZEougQ}c0+ge9R^tH}0
zliLUNx0H>WrlM-m3K>z58Ub77Oh+MM%T<q&)B;qIUSUqrNK_5X21%5NYGleJ?7F^N
zpZ@h<*FX9jz{Q$9Z~CzC_7Abg37jzxw|TqU?M-A@T{2&nT9!+YkEbV6E2huvR`ZlD
zAm2RR=|%JS;$-DvtsglPIghig)*sY<_+t&u4~veU=)tF7LJvqt_PA_qNq+t~U)S-l
z_NSZu^rc>&`?D>}&+_Rf;=2)vcdKegigKn#e)>6IJwJR~Pi5~PZ2!Xha^f3(u66Z3
zUis;7b9`67c+U0t(z*H%_3`J!V=ljxcfH4c!5hvu?B#ejwg+9MFS^R)_@c)#$4oxX
zdXI~0OGB>m+B&y)9oKK)QsZ_KmFc=}P?@0Zw<+w_kj^4tC(r6_7Hqm=a#{DLdLOZt
z&Y&`9(oB4S6<jK2sOXrn_9AI4XLZ^fR+*tSj#kJ@Y0N4VW+2Sw37tWTBgN?BP^h0?
z-2jD0Q<_LGECyLNS=VL0!7f;6OGkPwrbDEi$W<dL(___n#InKf1x1aNi`0>8S-Par
zC&MXKc|xC<FS&iC@q+eLaGiIGq>4m9GW#PDY&`(XJBol6g*Y*<f=Ey%M1fgUN>!F<
zl<OwUvW{a2R0&bxJO#akm0o%=M5rcIv}=40Jmh$zP+CD!D3D}K+t|nv1VC9O4@gsS
znQ(h(`(5)u2^c~P5~ZBgP4&7KN|i#C6=4JcVFq=fv|>^XOymw^jr;wNKYah6{qUdu
zKVN=6W6Wt?Ylc?L^hTeQLQ3yh-re)EXwH~HN!?m+tBx~R;Xu*>s4&vYvV~(wR30;+
zK?ns^PL!}qRORE}{7c+#!2rqSLiY)H&2FfsS}FJG5~}g=?vWKG*{$n8`49dLNO25Z
zpxrPB^<XJvRBqsqXq<P>UT~MrxC0Zn^yq&7GWag{pN?q(9gY$)t!8G+k_9z4YU&iK
zX0QhPV+wWNAPZ+HW)V`35-mYfb=IPmA#y}Mky+B|ZY9~HB8EQ5{p~QJ$38eLBcrZX
zz_{-*LeEG{N%z+?OY3w~C*&N9*n$156>|L*40YVcDC-bWLseEp=_))Y+oI=eR|HlO
z<~#&F&I!>BCPI~Ht%pbz$$%<{smjBXj2A{`No^~;d5AKa;ao3Pb)R8ytD4O$qP3$$
z!SlLE35%`Oo0K<EPxOk>ki9Q@idce83YFkh;*|;n(2OFIm6u+{oNJAbhHHh_auN5)
zL%)7rF8#WmOZ=RlestIBoo|0U&$`z-?zeWFF+OUwc78eIvaH^3cQWZwt$UoXB_(4f
zo;TmVS~TYCDbMfoeDhrV4SC#Ew1`M)?;n<>^sJYBKaSnb+xg>=hY#%8U%te#+pm6O
z_fNjt45_E5b^eH-W_<XY`0ab@asK4;Ik2M7J5hZ4N*j3pv%j|8zLAHgWz)8Q?#Gur
zGrxW>kD(s2e;O~>F+a#Jv~AawpYi1n^S<1d53)V2>uhiye?MP1$JL+SvwM?VW3S^!
z#_r?l{E&)TwQXTM6VCgnSRh+_cv`<+mS22>=dXuz|NH`8kjHvP9eZw@&ckQHBiU!3
zJteHAP?}*(DFngn#`GkzrZlY>g8`&zmX#VSVoHmkm?eVfbl0^M-kW5GB;AJ?8YFv2
zEj@}A(i62H6J2x=l^0`_Hk&2gMg_b*EOgifaWcf@W>EeGSiF>lYDuZYhy{$05K|MM
z$V6vR1<W2`05(H4rc#)PY9^}o!5o!<M3vBza%F{hgeneiRS}?2A`6a~QeLT{>F8!e
z1nQ(Np`!UJSs9FVt#gQaAxoR&s0y?T<_wfo6@`vcfi_B0&Y~tRf}zr+5dMg^mu%1l
z6_y_BJH%wE>RFT|QdAsC>O3<VME!o#b>VtZWQkBbprV5aNP`3j7Qq4)BA6%V{`PYG
z^k4q@KmV`aP7$E`))`2#W`A+%q*jd?+R(J$U-w?QY#pmD5hPWbE}F>F5_av%ocgpb
zvX~b3b3_VtLcy3B6}pIJ7fq>8pL?MoRbisXIO8Y<my}`}#orE5T}IBVx>y_O9{NxJ
zy?>KMG|%>++)IkGh+bG{R^WWiGc%Bk+}|*&e8yfr@;E;o`?BB9*MqNB6=oXOi`v6#
zZK0g9ZPp>}9WbRywF&*E)m`$=b;TUjKGggX4^K=flghBUXIonxN%b^UVBRZMoJR!V
z6*tXej+8yP$C`7_+cD%Z#&O;gW;DH87Od7)RV*jKxLkScVbWs)O`SRKF$Z*MKIT#A
z5}`DWOsYb}{jSYmO*8Tc2hlRvJKXA2!L_%T`*?Wl`|fPik^Q1ter7VKu5DGm&KSXm
z2c7#V+T9L@%7!?mUCwzk0ZX)y=o7SgPtoQHvylzmRUpMS!16QHnBu{8McgG(098P$
zzjBQj#3JKPR(h(mQD{ZjnCpe>cacLX$zs&&k1wD9a30J1ilffAbMQQi$30!o`{(%*
zC7YKl3*_^DaveTWR`ES#bymcBUHz$d^HFB{IDKa#pq(1iFLCU`b=lO$7y83oKaAUt
zaliTX*XuR2s%DP<VSPF!_9H%=A0BOfKHsFqMZf*Ft$*6?uh}=pEiaE~ea6u+ROR@?
zr%})OtFM-=7X9RJ?)F#b@@{$HvTpMG@8jdk`TVQ;(DbW!n?>G!@GqCWR{82-U8?JA
zy?q>i_<7VX^8NevewDl%zVQCCzeMV{ZXd8w76tF!Z}2SF>F>gNt{2@l%TLVe?Oaxs
z>lJH1k8&^KUh6ZDSC&UDCA#%&{aQY)l3uLB%t98mb&#lJMXFUL&;TC`q|Qv4l3tpg
zjp|vUTy|#jG|ZY~LM$X?nfIAhUP{a<#+vh9jZrgQD*fy#bS#||UUEp=RM&0srGg6y
zbPZ#aN)}a9q%@pdvJ7-&DhX;vl_Y5;CfuTk%+yXlRV8g>-f2sf&=Zy=&l>mQVq#LB
zl~gSsCPvIDO6>w6Ct!%E%qUZc*sdw13h7lt@*?ngd8Cf2R4T&Z4t<Q=3CS_ds@e+G
zq{(!cF`ZetHaN^iqM@?j5Qiv{A^@fU<$LL4wm>HGA~Drdnt)a7CIJfBF1j}Bsr{if
zslKYLC5?K4Q3YU!CIE`VzhM9Q_NPDp-T&_I{<r_p5C3q$v{_$0rjZn>3hC*?8KR3C
zP-$DYXre0WC4CWA&a9}KQ1CA@r;s{5$FaZ7DPGkJ<xw<2U|AQf(^i#j$=jKxHHVKt
zdg+SFl7UL(&=W~SPP=T}+ETrGj#B-n|KYz0r3ock2Dy|}%cb%blqG|u=0QYOkcHEc
zlz4gFo%5{Y@SCmv>eFLcv^7{$TT~_M>DQKrww**-vl!UEg6&m?0cu9=BEli%qgKe!
z?29Ne5!qT>5Sei>E$B$NfbM~+n0}jM&N#=N>m0KVcR;SDqtGq1_c=zMvX~%M)kZZb
zHWM@%H>YT270RTjG?=kQi7AG2$qH&SMq$jnTsS7wM2C5U=o~&nTy)Z8j@%xqx}7J5
z>l|`fa6bhpg4ssBmi7jPA;P0=S0IFfF{PWBiY?`bi_7x<^ixg7%n31PmI}Rig`Pce
zY*&lZsdS%x1&RpNs1*7PUFy8|nphS=;<ta5|JuJL_9yw%4?EVIw3lg7GxpD2SI_&n
zN5$^rY=MlU#YA70JjL&auF+$W_tA`)=XL=b788Fv_3F0Cx!WC{hX(b!a14mg<+3bN
z`#t?q-&|||dHNO79v@skz2vsoYU^^FfAUzrT^?Wi>kr}^e|6OlWyAa!Se`2n`*F8L
z5Q&!`t6lN%;V>@GvHFS6#C&MaS9|wmX}7{Lk7c(H`EXet`?~6aikG*P$9%WiY8#XH
zAI494p5Nxz-?VpaYkSX;uP^hXj^$T*eUQ#(L5Q9oYo9aUWq+q#+IpCIqjP-BBVs>V
zTWx)Q`1WD@#?Jk6X3Z}zbss5NT)-j*++RhTdX`F!k#rRnRIFIjn$EBWddNbAH*iXC
zTT5h<@`9R1z?HOFxe`DKfLWAf$*OD+u{lRmlQX3VM3J>z6_v--a9$o6$~{Dj(#hzm
z9Wl+cwF=K=MNw$bG7Z8)yTtHHL6aJx4H2S(-ZX~BnASv;0YRZJI%l!524W_S(x@FZ
z3sYibROwdviick!Zm7yya$ybEe4Bh<HKYuGQK!fi;aYp}3X0}W-rsRQ>l~6@b0>SX
z6>X8q5@s)1E{%Iu?{StD^8~kmXapl@osmQ3FJ@3l$;h^%y!xZnAQ!0t>5Qxj5|AG0
zkmqU>#bC*?>&38)W}?kRS_vUV6_(LJEX*D8g4^5v<>kl!$EW}DKl=VZ`Q7cfE`THs
ziqK#Hr8q;aZEMXV>)f04tV5%kT9!$;M0)Q~RHU>5yJ$r=0y$$KgsayClf8+nls5~g
zma(1Xn)FD>sH!YhhGuKMppq^PDY|wFWeL<8mV%i2&;G-Iosv|6cD<-9Nq3|odsUb5
zlZ*@qD=?3!4xc{NUtY$kZ+AZp@$)_#7mqH<O%^mmvL{P*1!$_!zE-7*m_#{XyPL7P
zhfoPBd<crmqQ{N>TfpIa^beLdDb#7u)4hrv_jCBH<T2(saVY%m-g6EhCKPiHtyB-`
zrS7IpdI`L?r*uz=DW<JQMvM?k7Hw<luF66{CPPJ5M`Z+c8=4|r+C@+ZmOyPmE1(qC
znY_QHgaS7jFAE9DgL)NVjY%|FdK^dfO>3@s!*r_19HLz$XPA^&dZI;N#C=ejyRNK&
zCZ)+-9z2e6(;6&87Uztjh%x|~Wlf1nrLGo3-svy@yO*zid#nHAzq)-t-^!&uryhfi
zu4Ikd982>lAabUjXMvQhkJq~_?!`iCZ;Uoy%6fZTWV~v-#&T-A?zt~IC)=p1<A&Zm
zbK%h5?DA;q94~w6g^wvU!h4A1`e5@d^WFGrtMhaG+{V3B_J-SfJe7XVQpv34dFWcl
z%l$JlI^Ml&cIfYa-_C*SclDv<$<+TWe}0``E-30XyUF%&iViv3NKD(7g`IbM`(eK5
znD6uPD$m&3FnXOYev`QTHrGWj`?kUSi@e<N`stKk`?B^HwxnQ)&)DzfHjXyiQ<=Hg
zwrH;}T~B%c#C4f>;W#l)vdwuq8KOw|h^n&cuEGkXMz<L<j<JZMVz`KUuw?}k;dAjh
z(V|L%Bc0V;q-Px>0cAybuu;z=AsLz7R2KNT#Ej+1yU2c|Fkw_p#a1W!W0_7})V#Gm
z1S&21s&UuWi^gRrAi*L5j|>46qIH5~njk0!DsmzVrs+lB0V^rY0cp^c%uI#fh|E-W
zvRh1=T`=$IN8wruzTR0rlhVu#XHa-Gxzh(^$?_D6K4QMG8lhss6d(*pbe&%&-$}e>
zTdWde<obl^StJIlxsR4qbxO~g5)LP;U=S5rs}fdY&N5MouMaX0>5b?TyEjWoo?c1w
z8K8tpU+uAp6ve1u@8m`b{(?8miQ_fiUcdbQ$G`oLfA@d*&p-b-U}$Q=)H)p=>ILbI
z;U&3snzks}nyPYY8Cp{<1g*|NXT&boi#C({8A35Z)NzJrk_L!Wc~uh;BNQ!V<}Uq`
zBPI)=1iY#%cqAKfj&4g+Xp=@wNk1b-jWYdT{rmqaE3=z+6*-yCLIIfpc)jf*2KN|~
zDTxvH`xrjPe7W-$9$(Iv6SoOt_O@;-mj-Da%W5*EK}|&^IR$3TF|2yacu_v;3`nw-
zzyYmNnNeDdyRj-$@(iMm-Q~)-=RPCIeGjd9mPg(P=ZQT191<-PkhZK#`-|bZCJc(n
z%w#rjsANGPF#}49gg{J-)EO~mEf0pl)DjaVbENcD0U~stiyR8h)>%>THY2x{mE8|F
zm+g_9+y}Y#+y(IKyYPcjXxCCnPTjpQWb&?yBF5}pF?$o>Y@5I*g1QJ4C^DxD`UR-c
zQ@l|{M4Lu%^P0VHS;CmJ!q_9LpseWYkYE4R<8wRyFaO2w{%^O>-m}T&A?ZviQ2zRg
z7I|i$pMCpE??1XqF4w4>DB7h-))?!>{K%uJ`8iMJnDy}fv6uJ^&&DjhD5Bcc_lnp1
zOoggh^5q4OPqJkD>BqHQjc1AI+b3+7lv6%_nV0vqZ4Mu|CB5;(bAJA*j+C!1JbtRz
zk8v#XtE+vti9~#sxqgGMzgZq$B&K}-_wllw@4w4OGby#sJn2t48uX;DD_`IvKgaQU
zt*=%*nsqL7=j~4sUza@b0gYn(B<BlXejYz#@T>97Z*-ZBQ*e`c;EWL$ucv5|y7V=a
zE;92#PLEuAx8<v?efz8VenB{1-wsHr(xuf|VmVwIOI4}@OFcZr?Xzf898pc;Ow|~w
zbSWwAs7RL7NZF7j%d+J@JgyO`U2^1Q?PJ!q^h8=SiIeYi{2cZy`J%-$q6?39?Q+N-
z34^#rdCtcUn9$gAw4Bu*(NRSK$WE1P7pQ=2QYiqEv_vthOz4$yq+9~SB&L_JA_;4N
zgA^Y^Mn$2DPZF~hXLiZ~5#H~zTrlonm6<spnQuEp5zb~NEUgM-xsIE+r#26<nU@F3
z$)KR<M}VbLbx0y7A;bujiMmue6-bp?+5%7z5hW>QK@(JJ8X{HG189slrp_v{gpVXs
zE<}}}x5+BD%};CGDJJh8buIEj0j(g3BK!r%=eYm;`uo@K|I^?7AOGWz|NL{gS^8nE
zDYO|gOU$YQEY(YlVX8@JsiZDzwWU!z{RkCU0WeW%4Yyra=7@1WW~5hyD3dc#>1%JI
znj>s8Im2toap&b>Mo`k>O%&QNrwMCmQc^WVm+5iF8E4+cocpf-?0@z@ff%(QpoM12
zsfo}LdE9B5cqkv@8Zk02dAN^0j^lRne$2NMHz<;e;rc{LwJy4C8X?=IwLo@V$Oc~C
zMI~3`OmG1uWr!=8A#2tn=TV{z0AA8Ul=G%;vtZ{LRY*6NnLKCYJcoXD-1dFWGqa@4
zs&d&fN0V?aP0GysLurVDOG{)$6NJp_z2`Y)F#^nzKqg7cBpI12l(ls^2N}^&hhsx-
z*C?$V>X|d!MaO<N*A$5v!gB3B926R6gqL+ci#*#}OKOzGxMN|4lBJCt!Zp`R`b$p%
zrlyNM5O1huw$>w5jpt3RbDR)YWzKRXv|v5Y8#usAmdNm0SIJxd`tkZd{#W-;pMLj$
z{=2_@d-r<yx;4M^*jn>4U1qt<V>OSr*=;U*0T{<Cb!pZk5ZD*?+i^A%R@Uom*RAVb
z+s3YO-zCx_bQR@+nwU9OlgraphKG%H-RgdfuhM52iHB6#+4MZmSDp_a9_+R5KPkV(
zWzPp$S{=UeUFVmV+n+KYp7HeVasS!wr|6UZ`rZ1?d(9u~=TGO`FK+l6ANu;vdQ3ac
zoKX+&eK}>-1o`Ff;=aW4-k-WWU7EF)f95{pjdT4<pSO~?#=DM}`1v?qpGJRQAHHsn
z_p8G2f@2R~y**^Ra9P;iy3U1ZIb%-GHA_z{0nx=5j`earJ&SCQ`|F8u*AZuhsMRr~
z^B4$I%sHA#H|{t1kV?#>)`uBqOTJpIE?BS3>7uOSh-%Kd4tE;~C8rDKv`6m85kSDT
z$C%0ja<YtWGj2%%IX&lubarhZRB5~{7qdi)ZA~D?gSCm3XcM|r2P0$XVh9usNzDP(
zX_s;bR@n6RNFUNW=P22%DyszPFw7YY&8mK_@g;Q85CSX$XW0r-)>+J)66<@JZ)$5!
zhhzbyFjG>}R?sDsiwRWbPHLJv-hp1Unl4(TQMwc>V`@;P(289dB3@O{rLw1ip0Gw+
z7*oNrEB)XiD99MKkWzF-vy3YF3-rbi%Yu}6ReCm+_KlhWwW1LefS4a~yy1Sn{_y3e
z|LTwb+5hQ}|HYr`%beV<9Ki}{Ek#5v=t4ybajLQPRrf<X+A0yAoe?=_N<-@pg;Xo<
z-nJI8xFy;eF3_%u%9O=~>}sIPs?3@tBf_VnoRbiWEP^t!EI_$#eF2+F>*A0x#w}tF
z9w`0a{LlY&k*e!2#BU;%dac?)FS%gc(@n-vW5lT7;``0d{fIu_ZlAK={Bm&fPhV)F
zm*pziuFKN2uO^pug^QV37`%$6St0aO+bDr#L!RgxZO?Rw);QQxMa!jpz?M?v2&B|`
zRyZ<Z<ZMssGb3+47=Ev{!f9<d7^#cMULBF}6tS9{RG83=BUen#SY?Jatuawj(pe-G
zF6(02W<mgWk&$lI&gt!fh1??tmBr&E*8W&^hiWfDxSOqjwpB<isArTcDlSL3bj-v2
z;K;N!B_*_4F`H8DQAsXCyn_j^-s<SW1T^k>eXipTD=<y!)a(VY90ItcBt4;)v^tI`
zD#4~7{uMs{cYbyM>F<93U)?{iSDRShHCc7JbYzI1i<Uoqt@}-ysz<C_q&XJMV?2In
z@e`s2#MX#G&zP#*7M3r^lw&fR@_x2e!H=JJj*t)EEpH!}jBEsRJ1t@li6*C1+4dy)
zbG=T!`&Pf!Qq~V|HTwE~l`mf;q_$S~H=G%lujYq$*T-SEk24>}yI(F}jmvWC$3NvC
z_IUoKe3<KdU)tcyTi&+$?ol4PW=csPfAr6C-oMg!&^{L#n;!W(=bt`f{i?otYL|<q
za~}A(PycRSp7>!|H*|h(b7<r^*MUvewf82PhC62Exo563`xGe=YBUS5asOOFwx;@Q
z@4w>n`*VF9W5(;8?lDl>o98HaGd(>0^rDGq#%neu;22ax#GI%~sgY!v>XhJVBFr$s
zEFgE2Hf`{9wE|kDR|6bq6d=;rN=iiFTv=#L+48z-MY~+I4jr(E&en|~6<E7wl)(^?
zA`BUfWQCXt2EalVo61W6T>4A`=czcNJ&NziKy`tmfI!H?xU)Z@fGnv5OLBn%N!hkM
zPbsOmSBMDUJM5tr(L8~LF;Sqd*&%U+JPK}w6$PotGAb}pI*S{vBPRt>6byw>W=<mu
zNQ~h6z$m0>m!exFn??1k15p0|cqIf^$-}Qt?R=#Q*`>3NQvixqm6`zsN$u8Kqb1ZJ
zh4Ukhi8EjRc>D4H`p^H9|NWo-)8FOGAXJo&fL*jwG^3f&x)Kqt4VWdZHyCnn)*ytZ
z%9dka$vN-R7itzNPcQg@!iV3>V`@=FHj^fh;zG&rylfJItaQs9)q5}0IMllET&}%q
zt2$&+CeO(IcAP#&Vt5Sve;LC2U)#DYKj<6ZF~*#8t+n?)=iGDeed?*IcDr%FkN_qq
zL<kfRBEg9jDN+8CD2W~VZ_5u+_#r4Via;PaHkfw1tGeoWyZ4@#z4uyk&he7|d|3X`
z-~SIYpvN0EMGuM#_FM4BI3^A^V^~kp5kliigTmr=r&q`Ox8ry}j_W7@3pBaaqRK@A
z?Yz(kQNW-csPfP#I)$Q6s)go&N_wS89Fs&bK@EH-XaQU3hH)SoN2YZul9R)kjvh4T
zjM*_Pj+uR*qrlA%OEy!c(^(w}$&w_MOGQA^_fV0oR`cL`v{M;_P7moWh#f_dNir#^
zWEJfe0vFM!wNk2?2LX{8#KBU>46b2ik+fDIsw7G|lM_a0q!H`^cu2F}H@K{en`umx
zX42~HgGkH?Zc%bvDY-kA;<4Kx5d{t~agw@qDeIMLfq`IB7{QUEAT#A~)_N{XD1Lif
zP4e5@KmO(AwdNg{H5OIn;4#Ude&>u#t<q;liquH+p?cOOwj!tz6N}p%ebh&c$q;}X
z%du8=rwUebJ^8R%&ZN!XUUogV$JO86opM<rz}=!gZfl5td;7&irSWY!cAhmak(Uz(
z4PIEU{%v3%@`0DSEaMyg_@d`${rs@<J6~_{EslKjU;piLPPSWnf3cq*;J|ma$W3Xd
zYts9VHlF<B6Q5lRiyOVX*gUzM=IK$_v6PG25A^bO?4N8~>tmK0OB&`+{JzilIOVd|
zJ8CO3G~RUBbaG>>Puv!0RB@uzdCcPuQp*#{1^S?Bt@Pz-F5f-p!^WX@9O*+HLNg_b
zF1wq|A^T7p2@Ws{Mo1MTGlqv&CZgcr^oZi_6y#nt!F3XnqkD*)Osq&9P_j(xP!<~i
zjd~WDwENgtT?)fJD5y5rq}6*}m3Z;A%8uH&Zl<C(Q&)ipJC`LmlN(KEq2vf!kOz^a
zE`%)e-BF`G(XnTkfMU&<fCn*ChKt($fLL)$6rhmZsU$1Y^`KgL&d@4lfe$K5TrjRl
z3yE<|WhU;RMjXX&TrWgSB4m4%j~HJw*MfrzORkuAcM{EzrOtyWlQnpFt`hLF$Q-T$
z1UamnIYs=PaFIql!l?*(im)pAR7Wn2a)aH7S?eb3R2$}0HzZ2k%85k_6;O7Xldku-
zFTel!zx(pv{1-p`zy8_v)!Ver;mX8K04ZE@Mj|9FJy%&W22m^2sR?IH3uDm8*sr__
zYfEBv&q?fNOZBVGnUhG1h=M?Xq*yQ)C7TXWf7?63!V0T}QGm62L<y(Xs4>};cB(2j
z(P+zfzu$v>;)noP{`~*&A3}ioM3h2;=#eFFSMqlk;mj%Ot~~m@-^U((^4s<H{@VAu
zxf7*KVs60FmSW3^s|E|gNOX{ZSwzESQ91)RrYB6#mD3(MN94?WR2(71u@MHs1vTUb
zUy%2Vnb;`a{pdMoQj4&hvCrI%kKOmtNz44!S>PmXgN?!Ef`u!o70Gu)A@`+D&r;kt
zLBxcrHfa&EfGE#MA+>PLa0`(n(FoVlGTh0L?#mW|lBTB;!V007zMKlk!?To}gF&?{
z>Bb{H9YtM=k{dOG5PO0}n7~mJ5eJq!?-_-ZC_%z8SU6kb4CumwO!q=$ZK2DQ5fDfa
zhcTEW5QQMEMqrtC5c6@%*S{QJzxsh?xs<xqHiJrsJV42qS1ziS&fykRa(YOWt(DOc
zoa8uGTO@7=Z?#CsDFqikfsUyuMLms03+NEvl?#9Pw9c<Q_au>1+5Ad!l4m*<nr|Zv
z_w{LQu%lbngKa(T44yvO-sQ--Jk#^mXwPGg`REU-A1-?Q(0_cLbk65fdEjMbe*GW&
zE9b-C@=q%-fq9E>Kid9mj~{1KY-_=_>|f>XGe35{pg5n7CXu%vaGm2=oiB9G`k)2C
z@frsY|Jd6{w5UApiE3YfI`28?V)`NLvNRK(!f7MYb9PXDp!K3#fdY20Kwy;XvevRZ
z+T(9y-lmQ0JxO#?9TCLH5tSz>9GV{cv=HW5!>BN4m}g^0CJnOE)TPEj&WBVSvm(VN
ziWo;=!MsO86v{PbSMrWR!)Jrv-D~AMcv(fG7*&E2xN7Rw784H6fDZ;|DJ%hb5SY&%
zyz=oi^+LR|59jLPngXLFr@Dk4WHVXV!=nTRm2+~UcQlEajKK*df!!lOg5bt5t6Kvy
zX8O!9ESGfS*-09q7RqE~Whw<0f|;^WM&w8;RD|by%2{ZH1fsYZsVZ1$2oRi<3OZ%D
zc#rLo{hmSvNFvTCM9c?KrLxF;EA_<Jche?5l7-2V8Dml@#3;<130@c*k#Wi7r^eTE
zdC<~H3(c?je%${0+aLa$-~E^Wk01VvznCxANGV*LSS1u-PD4<NsvVT@OfJq2SrKzA
z50!hmO(vGwh@x{1bA|^Bi+gE)HIt%+XOIe^)`H41OT83?r|Y^piBXy}c$QkBg(E6k
za%C|tT0~Z^vC#3>h05{m>&t%ZpfFPA%8UH@-~Z1L6?tX41reLUZUK()B;kHdqS&V&
zSGUbyzTMGYZ}xt@_rM-yqybb^8t1lcYmrsmm+uPqQi0YuB8xC@TyKCwG~rAmvZ63q
zFlC~rRMAI?dz5GJx8yB@4I9QDf;?_HB4`bjoNmsC<4B3@$Ga!1wc2;r$ss9FM-eAE
zF~85cCh8^kn5$}$WY+DXO)~;@)7rEy35#Ibiln1}ENE>wOeO-8K^6;ZCNJ1jNvw+~
zgE%Q9F%Jg^D_6uOr4u(A^YkSB-jOAUSWrR;#w^()*=cf6CTS}<lU33th$#=n>X?v%
zIY$=ZpxOe?TsTpP{NA&!bPQ(2de-so`ap5GjeP!`ns2Z7pYM!m4lUXROU^qVNg_%!
zsjz@E_DG%gtWP<2X|?gyhhg??PaHG$I~9mGBvsDfw#2A4h50BSg6^mfv^&PFJbtLh
zF}eq|wAS`L+Nmv!>pov^v3!`apWa`ud6^1YA2SZmcHH~;@sD19#ZPUkx%D67?Mx31
zE1ktK-EZ}Nx~v}`n!L&Fz<ZzmfgVofA=mf`KmD@5Z&sel_q7yS<LA7-jxTn+Fn{`7
z&UdMbJml?VzVvCI#>01bXj-)vk^aZn*J<qAiif3^!NfAZ@WGuPy*<R%YMrefs)zKK
zbi^?St*DAhEK@OSbi|!Ki8rT3N>!rT=5h);t>R+u_v?Lz%RO?ABTc{@wOK@@Q!S1Z
z_#B?YnaQ+q7`d~^u!zF5Hk&?>tIe^rbV*IJq_RbKp$X0zpkl1sNMP^~VGr)RY9THq
zg$3+Z)>V-3A(f#g@s9S8Y{@`o60se@P16_&E=u1VDq1+x+F9m1mB+9s6ye~U6q2+c
zZs3#j8zo3;I?=cjF98A(prIrI@PsE(z>cU3Xc51|CjpTzlElGKiO5ur65OEXQ$$?R
zBeu}W_8PL0jU;xQH72DycybHbgboKt3(XF$H4lab3h9~cE}Een;pri4L_#%_OAtv=
zk`fvVQBYu!=m^Sn$*@vENdODg<#fl^GRFLwUw-$)@BhDl_5c2V{`~*>i~cP=jh2>7
zYi0^VN+wIrsXVf7i7a4-){~NBZ8C31ar0$kAsZGHq6L`DY8FH^Cu~kj5@t_3ogZwB
z)|ipXxvm<aK4)+v1}pJ!5Fs)f0wP4MNZpo&B(#)zLdN@X?0wED!!wy`RpFHX{QvYH
zlTySHR*04C?pTn)AwEDyr%CBHJO}hX?DpQ@54j)nwoihBX>F2Qc@(OZ7R^FgDv7cb
z(R7p|T!?qkBzcnTDU}py$;mkhgybYjg7KW}ni%O8sSyM}*gR8+A}oo7Y|rp9%`)!S
zahozJ?R|J<)F~(^%xhzqSPD`0Ni0!Gkce0mEJ>7OxKGMF<}qYD<$fcZg%>Ee2PK)Z
zsY<J4BSedb_Zzh2b+4RO!l?>UgQb-qP;@5p0If~o9Chz^j!Mxbd<q&ZgM$WT6{ab?
zISOY;Mp6pM6ztSE0|KlT!zk;xB_TQE*wvUSi_J_8_RM6Tl%5HrXceb_{WHD2zrUr&
z6bQDZ#Eh&6k9MvblO-`Se0m&%B8IFFr4AR)k^4+vF9jp9M|;S+7MYz1!$~V+2bx??
zrOswY&>WQ03NmRaww{%omUYy2dMLiX4qs6h>hJpXb&^CQ-L|bb@#22>*=%_}eJ&ff
z`1-!@t^9hGcEQho#3*B_IE~YXjr$Azaro(StV?;Qf-mLg*Z#BF_A7cO{qA$2Nnd|A
ze<{8GW?X9h^m*B;>@W1~m-*#>@ZYxcb9so?yfrB9@%ClhHhVb9>8zT??s9#nW1sVb
zJ${x-m1bq;{%#($#~hDVIJC)1%v3OVyr$<k+Kd|YBy~+CcuyzIS6MFG3AlW^sg9TT
zaZDX|krd7Y$#bSKhvx`Xz+eJtg+wWX$!e8b+v+$1kg$Lh#?CBKz0b;d50elwBRHU#
zv1w$&Kq{n#g_GyiNs3bTAwpoTB{T+$Fp6L%z|>Pu1(T_<5~me@VBTF7#1tjL-cGPY
zB$okQ(n4@|S@=Foi_ak`<lTdeBMzj@2+nXNO-@gTYiL3k5r#rQdI1fAr?zyL!i-3B
zv7K~H(zJt|g50^KDCRYI)fh1*rGl9-lfYF-eKM6kGK`!=in+5iS}VI!ItYk>j-(|c
z)R~Bs1!T-MA&?erArg%}>c;44;&O--sKAodlUYWfc3q!<F<xG7fBE~r{I7rdum17%
zAOGa9Q79*)A|=*jn-&gF_n}(DP-*H?(}Hp)C!6i>_pO}=b4JXhO154eqcHDIbF!w)
zIFb__q>#*3&MUiFB?KXDj@KJO`*4SG<Z<sIrgaqsWok!b19E-bm}Gq{^EwC2?)?_?
zYT-E+v_|sV=S}|a|KLBO$U>27h@=!AhxZO{;RpFN=Q(0p&OMH9w<GSukD-w{wXDaD
zSxUvJgo3G7E!(05PA$O-YM+FRw^b(*FO)-rQYFfh*d90mlu+St+F+Ix$&l$pk!8g^
z0)*+2!fr;zBJI}0xc?28W4H~o3v+j;<f=-PG}j3C%*V3}S(tJYcJYX|ibc#}Tq!y+
z<M~4M?8!*0<S}PVVuZ{@p+b`;gfb-Pa6}S0n)q-gu}p19D8L#sp$Ux$GWMCZ1V4Uo
zDT!Khj9N819V_7`%Ol-hfx%UT!ox_{dEcSDP#7V}AjA^|@Y-OmNP#*9kaKp`LZE(k
zN39q|r9J)n>_6Rpy!U(X(g;foZgi!x*jd$`iVurPOm@Ey#vGJX>xq5p?C*ThG!z#q
zuw3VC)eEU^Yv!bRo0IB?$GpGA*H?F>s2?`1oS&iz{M*mXx?J>>Qf}Y8J>&}RZ~L3>
zRLjGOA#G9cwqAdva&FJ{bTXBDzQNCerzhs)`1$32t@>PQE0QyRJVtD^sXSkl2L14t
z@pb9v&#|n!ES2|i{at(^Yrmz3T0YQLa@D);zZ|dUnV;?Qp?&A=L{(M%pzD{tZ`Qt}
z6E|&55ARpM?-ReaWi6+o#jAaVj|f;)%X74iS}%;kuM+M@k82EjiuUMj;e`t&m?aO2
z%utI>R8+EEif$L^9EaJ_=lkB7XCye`qwiLj!<{H&W+9F=KRjC<%osDn8~LGa$v*ZV
z1-czgvqy*`-wF#vy{@&n1BbCP(QvYvmP9KfsVG4!NXTa?WqCkh$PyJ?E2t86$Rq|U
z6Ak1H21nsM2#ZDXcoQuV1HRDlmKtddbR^Y;^XOBHjN3$weNa2+=p2(sXwKls-~<yw
zCOJ20fD6;0G(gBrMUqsGgOal-CP!J2d+^5ExDXN<!9dak87&vGiBdSD+4L+tZp=-<
z%m6%FMfYL@S`z^Tu_xD@#&r!4V<eL-h*0=j<caee1OkSDq(!0>%1CAi2csgos&GME
zza8JMuRr$x@xS@yzx=P?{@Xv=opl}~QY(s9gmO4VM4OaFG%{OoSxZ_gB?0x$T6DX(
zbtRtghqbvTci{d_9@oM=XGH4g-Q9zt5d>VcU{QCMZlpRrXOTWVja`{CC9qvuCURJL
zSeDbeU0Nxp0@yLG*Za+e_wwFHffANQ`1@!1H~-$hA%Ff4{v#xj5Y2|TNjPknHL^iR
zSn|9be%OAX+wpbO>+bjKaa&1`1oeYXx{@f@WnqeyXsIMM3a_lrvWOgDfDKw>b*N^^
zjEm%J(89$sJIg83#jgQED5M)S%?nL7=0t=$!Fr@F-lsWR<uT)RAdSM@GkZ3fBsNt_
zAQ+dnrCfJ_sxD;}j@figN!z1Up3G89eY@G=Z0>V#PGy)-vbsfOk5=T?0qtc|>JiF?
zX{qAD1YRYna5#p!h`4oBU90y)ppy;;32CIUnp+Y=h_bAlBXg9ca4<(;X=MzPQh8=k
zfpZkWa3V-kA0UsiFelB>xRabId=d(jP8;8QYAGL|>0kNN@yFl2z0-ZiP?n{_?+}!x
zWVz-0Jj`cd4cv_-N9ESeeX`7#;74;?$!bz4JDp5o5@o5lf5ZK*<<g!%yWIV!zIWpm
zQy)O!&*vI<+vVOqRlQZBczvUX2evEy{*QBR`1nY5X9#`#v%LMtx0fhq{ZK0P`NO+k
z!5=@OSQ}sVZ*=IXeJt|0;(i@JQ=AJwt+J+k{WgDi@$%U}F6)_EedRB|>_7ONoB!%#
zd!$mF$CXE$FE)tsVJQz+J+&HG`d?C>r(FUk+0fRZ%yRrP-tS|67u(hrE)|;Z(GR;j
z@zboQP$}Xp&gtYct~q*+<#Vj1Zp55T4++m>=6oeIl2eF@5&@L6P*%O``9nPao$>Qu
z9X+COhEFc;8A&r(icB|xdS-yEY)3{ZYZ_pA4Fgrj^zyj)?SSe&lXG`&T$EdQo9S!r
zlSD^vpu<I&G|fm%)+8c8nXq08kLJP)0o+<}iU<H>2sFUznTe9*#6o3>N)(|2ld5ik
z6z0qfWJa=C#4Y%ok%8%Q&LT4IK02p4h)Bo`1>(lcB+8|U?Wjs*ghEm?N0=ce^n|LC
zgK3(m=91+O6OFGd9|A6Pk9aJ3PdAD+kW7WdoGY+fZrnK2#Up*t+5)VxPp!a4J}E~M
zRDl^p20_?Hwo@l=nYt*HDB+#bnX1qZs=klf0AWsIg_hHG@2@}H|LUK9`)~i{{y+SD
zOkVvkYPtxx=y7{bPX@TC+t5~OUBrVp=iS#7*0803f$##9LgcJIiPXoi={aIg>TWY4
zNd{1bL%CHVq0&JHkLei}LC+sJ$kWK97Bny*AmRl=RJAnJecqF6-;bBKw`2F|e)k}*
z+W1#b?ce#ge*3TfE5DUL|3Ch(AwqUbX*3bTBg@1FP~NXJsgvft??!$<-fq_S`<Rs7
zokgHkz!|NAmUV;3!z!YMhpY=Hn4R0gB2Z=7DOn>)7WQ}Z2hh$@iDnE>R&r-nCW17O
z5C$f9sXVT3E*X^RJHdxhn<+QPxb7b4H<PxKn{l2hQ9U!}DubMZ5)xThlClr;!0fbY
zTE~7TR%t?&1U)!gVs332l)^qz)>_<cS>2pq6p#=|lu|OJD3^$tRA<Ci5uwV6WZA$b
zP$F8HZXQ}18>LU?8fL*s!IdU4JG^G3K62cHv`~1ckVO+B$}Diw5>|Pn>X@OzB|K^>
zr58)C<AADshsXcm-)WEU@Bi(OZ|{TjQE*TM7jB@~2g0wNW;Uv7_nk{iM-^h914SD<
z%~om1j>~TeUMcG1mXWi+`)jw4ANl+9cK^xm>xdhC3PpMRxbn;C{mt~F)R5wMzs~co
z{o!L=U;3Bh3x8jqYNZ{1s-G4aKhGEV)b&Fn-}@i=Rkr$k5uy41`u0WnSI^6b(>V_N
z;ica(Kc8j$ROm<hi+(Fl^YmCx+>*ynzt69aQ<nY1$MT!=qQUc3zx>X|XVa6dU(vo>
zC@UR&-04MpR6A{`2U;)}KJMswyw*5<D5qY8g)F?s{Tg|;?bm!}&7nCsC-R`)XRNbc
zNTbTGv@6x>_YC&QCJki^E@D40EjF(_-VzIqKv|c^?eg76pLFYoHsRB$YBRWv3G-PQ
z6R13GS85zn$IaS>=262)xQc;l@gtc{RC7!&%qk#>-7>v*Nw(#K+z(6c$_Pquj%jVt
z9*dYt#KASJY*ms;3EQJ6DNSheg(aL*#P2B^ZB4W4d|_I|k5I%E*kB?^xCqr|9y~pr
zGK$0)sd3!Hx@Yl>n8z#++>g{vzzELPG$uu&o*|TR5H0C9%}7#6r7-v&QN5U=prs5G
zAux!Ngp!DB`pB3C7|flRGgWMN14t<tMad^)H4^ZQOq3?$#fh`5ng}X^LPe7-w%@?M
zrD~SS;bA-48Ii!CP$di2))4i9Xcq|Eu^-ptOaJkY@BifY{hxpJSCir4H6fB7(+uWh
zxmJu}rB%Ckvu-Y_T<gXq*9BGT`P{-Yljd|LP52ld%B(Wo!^NFwSU{Rnt5lYieZn%;
z0zlKK3tzVQw!1r@SCN89R1$)~B5bKktrFxR?f(AtezQKhPwbw;veoi@DgV>I^Z8%<
zvyXrK^GW{v@Bh2xo!~h<#z9j2K<1m<n&UR9_4}}6^h0(V*Y|Nu88b|zQiWYPSx=2D
zi}_Z0T{oel>fa~{m#T7k;Mi$f!-JQ`1hT=}+z->DNd)1g(d-nSRV9Ok0uJ|-f;gy`
zD4Nqun~?i{43B-EaqClM-pxUWBiyTGKt!TtMoJW2K0L?Wv@DWIqNPdH2j*lENMj$;
zDg)%Ak(5cyC5CGWkgy9DcY~n_dqy?}(r2*GOh73qnUh4=V@kn(r&VNhLXd)qI7QrK
z1mz%>3=&FSmrl?$t}Ce2aUDctDdI;KA;vzuEFg)s$fRjgcqvMX@J#Vpb0#ITW(!qZ
zzJGrHdz`=f-RnPnH{nW@$-+dOWR|Q^*mBV_<31w9)@3P3axG;oOvn-lk3^9;qML5w
zW9WXnz9l|AwNtHMf3mKFI}c>J*m>on?{9;aM>!XH`LW-@r&7<@_MeZP=68RxerWRY
zgRYn5L-Fg#A-3@PD69W`eOYa#^K+?fA3q(VGk^YY`mnWr>tFY~*5!k?mU#bbd)4vb
z+#b9>tn~6_{CqV$=dU)}%H#7=;|uN{$8X00JTr<mxoGKled%wA^Y?a<IIRT(X=)ao
zZ17w+xhPkz82SE<V#a(J^`oA)RbLB?9e%&f<DTV{)m54-*pmYLJLhWM&C<(>t0o;K
zhwtxneV^~|<I0^LBTu$Hgr3n(i<Yr{<Ujq_mcRRNv_JjZ`0bx8r{Bc=>(~bk3YBRo
z#MCYvRJTfwq8X`El*$_2-H&53J8JRdLS!-zBAFg1@kvGfo<uz)(qmWxT^Ee^Qr9dB
zB`l`~2)mq4T)0X@E67H1hb}|}P(npO9?UbfWm%Kla7mi*0`*KJ6`sQ#j0gtC5zOW#
z({WQ<<=Cmt1H{OxdCOeV2uoq=ir{eroFSx*4~GIIfMY79MQt3)Az%|i$Rp=#tRJO!
zNDr?vCpZb|{7mr<N~#OlPPI}FFPAb$5*T;QGpjL4_zqN(1@S6O3DM}ZZS}ajR+$s&
zj0IvzQtX&gxKv6iqTwJSaj2%}-tX(nFZ)0H-P>Qh@XPDiIXNQCbC}Q3s(NxRA#IC3
z@;F$900s)P(nv&yW{=x3C5bW>f)ZnzmS)4lMv#z53Pq5TN)shz>NO=IDz!;~$8nE2
z2KfP3MuhJSDGHvnJTxv+>WMN~%W=QWqYv;glY%X+uzuX?zy4>R{_bxtf9I1vKCklU
z|KJ}GEirfU>Q2yf8|*W~az6r;FYja6d*|crc<bB^Vl&%=Y)hDr+9-oL$|B2hX>!{t
zLn(!YO}CBv6dF*M(hx0iV}|ns`>S~25@DPXT#;6DQpQB7v>%zEG%_SJ*?nS_F?>=?
z8?r?A(ffX|Prn`!t-^wnxM&erkWdhjzD<vPX$@tGIj}`>7D60vJ+danFy`u`7YYhD
zX*rD(K?2US>B=IWsw_3#lt@}(A9&cx{g|^$DLIYTI?YIeM=!FaWORoz*OV-|)|`8^
zbI}k;Bvc^lGTc*2#vamoKd99VVu~=MZIOPoF-JogJ*9A1b`zqg=K)G4amL-Tw#N@2
z%kfuV{;O}lysr%t8dM`o&3!+5hM80GeRzzb)k<Nh+V5NmT0N3EhH`dWC{whq>hA-`
zepuz_kC%2UZ~aOwq*1U+#KU&77yy0vARCPzzFhrDPuybaeb0_^S<gJS<L=8=Hq!me
zut!~!k1urS>&p^Pr?R&8%U@lOg`2Do<@~vo+mEx~4lQl1XSw>%_jxb+aoJAKdiz8C
zyw9QY&%W2sTdPv{pYru(&XX-Gov*Tj$|KVa-LUh$JoDod*P{aBSNisoclPC@iO8}n
za&NfG@l(GR)6a-vo2;wDCpqQqWzuGOj`fkH*PKk2c8vWVu}#^sv}mjFZnqbIfAz2L
zcRzLgbeB(Y`3-LiU(UKRw`Eb0rLNBpPrq6pfBW?K{loL0JeEKG#OFtU|Ap?hUq=lq
z!aQsOo}Q{XfEXo5fcwqbr3D1i=WxS<u_G}`)hS4qmQGtV^#lngJM+rZo1i_EjI!nw
zof*;=PZcUnBr!wE4XmyrIZ-OeLzTiZDoW$=lk*eB&eTZ?AXGNVVc?uQl44E^@rlgr
zN6r?05Y4wN;%?+ZNnDW{GNG|^K?(<LU~t?}Yo;YY0LS3AU`&z<w&3J}TtMN8bBrqq
zXRIXC-JnnGSK$oXm?lX^iaMA)xnx--5S(lt1Qv)F04#wl%o)_Y6YOi)2|R*|r!tv<
zG5|2pIh~|R6}F(0MAqK@`0?xSUXH)~<Mnqxbt5>6i4YmMsVp}3^7L3Nk9|-91%as4
zhE`f(E}@eP#Jr27&sZv9tvCpc>-0coD4}L%#K3x~HtMNmWfPTR+v9h0zaQ!0ILP3j
zAhZSyjzWmS1#scoO3Q_4+VwhKj@$jPF?fI`$5tQD5BOJp{q(2*lfU)1KDX!dhwUll
zAN~D*EAx&R1C7Gi5BD0~X-w~F*W2#vpz-oXufELdeb&Ig`S02<e~o@fz(%jkI9<+a
zOtb02C2C<|t}?Tnw1PEAFIf**h*(C0Pq0xHfFMcnMqW7FTY*nlr=)0&a4>D@NAN}|
zLbut;ZFqPe(Vb^lPI5?0(~3yeAe5%wqqG8dE+`=8$SFy5bbkD3a}tuJ6kgLNlEO2T
zEC~!KlXaA)ITNBBG{F&s^qA+ej$vK`!I(prhgf+!2NP<77mCPM717*;tw{zg$%kh^
z$t=5}#o=MhwWQB^M=lLTC}{!XRD>gCA<9~WlBZ=5nf6jqjO(JJ-J{K95fWcNKP-Ri
z^!1<r{Pl<4nY}C?rtahsJ97;|Rqaj^K`ee^^s=e<wv;Sq^W=yWP)}YLGDsd{x8wYw
zeZRK(y3U%ybq+qhdOh=eUowZR>si`-ncrqnwa*W_-~H8-n>?O$?sGOPb&hv?7kB1W
zV}IM<CDt?j`gv*0Km7FdP4co8q0{4oz23&R4*l3l>b&`D^T*Tt*lMAe`~9zwk0<(c
zd3-8c-thI?n7^LOV>zXihvHYk%v(~|1j#1LCAC0~AL4$q>o)Z}-c+<JI_QSuZZGwi
zTbv)uYP_nByWCy~5xE|Dkrk@O(m4jX(SAE(GkMJOqH;&N*o%L8onzaN^L+YS<>Lo=
zY?o&}o%z({R83V*jX+wfZI#cpoR>Ja_OS88qpXkTY-|7hAIAHgB$F@$OJ!hqSr<$O
ziTPCZ3ML%gH2}t#Nt0+R<c>w4@fI90^O&3wB;eu!chd+p3L(l8tu*H4@}P47%se$1
zIiSoW67S%r<bjy1BnV2%zv0Evh0f{ksfvs=H@0-AMADMSEs}if6Cp=n%pOT3Au|0y
z2xKxPIH{=1;@Bi_B$92Z_XC^}jOhtxVOLKrLP$3ffFtE0^T_0gl$<n^0V04h1yIQ#
zz@w;u!w!}N79bcFrE+ANyE5CIXd}Nv&cu5LCpkodQYwVgU9~D0Syn-$EJP}Gs33#p
zemlPHzx;Uo#V_}N{-a#eLh78O#Nf8FgpVsn&EuLtq-lbZR#d4hOe^cW8!6S~tTiR6
zRtv<f59;iQpnbX%h{dZ>sU=d^T9?e-Yh$71{{G$r3O;hSr3GbCTaww+I7N#fgsH5u
zK2(-<?)x4-uVIs?AZAvQQ;k3U)u%uE&FR;l>#skamJ4Ko{G<Q$e?#8ViDc3tln$3U
zr_VXL_<p>_YA?t2cD&ps83n0*`9sx3R9J-BnY3||`%-y3pM++U6PG|~!ljnTOmHvR
z2mxIn3)r)5<icLrc9maEbOs~`v9SkqB^%_CIRcznu-`fg5@Dd@*r{4+`!FN;%wtT>
zg_aQE+?~!%&nNO^-%Fm84;D!+^mxH`(d#alA{stOVW&mrr1L_fXQ(oQ9Ocntu>TE_
zwPZrllykbuwnj|J&D}|JWe;6fHzp}85<`|nvN;iihFdxz+$m$uLXBHB3|=p`4*?eL
zkdxEyq)eLoBm_zc1my%GXkp1fh$i0;N>X=BFGK-iJsy8kr0ie+#r}3wf|YHJz30U4
zA^>xY2tNv~73>5gh|9(WW%!|-Ze*F!_wuyH4Sv17+Ue6HE_(j)%W>QB{Hgbud*AD?
z3VDvskKaj^dA;6mLZ?$(iC<sR;E!vt)cGy2^`(vLSM#N91rLq)>;Cf<AD+w8=_9@#
zxAa==>D*2t)#Iqb2Z`|KbNTj%m?a;cnp)}CxHa3B{PbKeWmT5z596<AoF4hR)N_`#
zsmyr0kMTrLkJf}&=51?v*iS#tFCN=dJe_5mr76Z0@89AsR)1sXl{ibG+EmTs+t2rT
z@%72;Qfe0tc+mZu?J`+YKV-F{jQy9m4!=f^r&*rp@>o7@j~|z%l#BEQT;ZakM8bh&
zrb!XZ11y}*LK4!{Tg`TUI*#M^yB{;vQz&uR0n1<tuo;DC2pAfepfOT765%LZ#sTJ@
zYqPy2^|HaIigcc=HTSML<)JaDJ8O+NWGj(XbwW095l~761;_)wOMT#y;fh2!AONv3
zgg8iwU!^T#LG6)jhZgG7qflDJ;xQvkQQwX?4!c9tMZk9>%2Z-_CI|Cc*)lzIVY|}$
z5Gi2x%#?-ljk8je2!jfZiCDsK5CWJ|fSyE=XXtH4k)lDHAZTXh0ogJlNhqE29#SRJ
z6Oajh%z2ea(ndUhL?&|wM5Hi)1d4KkiHWNxOHrK$B_Z;Czuo4ypKkxZANzm*$N3U@
zG`4+0P>L!!N`cq#owO3;^z>oB4o*@KWkRC+1XPQAKw?A?GGNmzD(Yc#WTGyH1m{dY
zF=zUDVK_S|*@DQqLi8F3<rbqe$5OU%G9D^hT}y%(QZJ>+8pHc=nsMLX2UxVUR!{5t
zd}@E|x0jE<{rD%BJfE7b5H<Nn|NDQB#K<Ctk&PIXK4ZVdo;LY@+vk1mV|*Pvd`?k1
zZ)-#pG$Hp++30pGjdeQ}k;U()r34GJRXMe4n3kuN;sJVuT)1QoPh7}f5H*;vWFVQC
z9Kd9gEa^s>G214yrz>*L3CT{WHhNebQOEUehCX9>cnWKnWwufl6QgFv97#20lnP!>
zs8;R)a`eMJxGXt(Xk{DJSWw7k4&o(JN;~!p5d>P52>1jgi325VRL?ARk@y>AS!#nb
zRVm|6kI%f{Tv)a?PaoU+8-)e8s!Un9nC=Hom$~=kLVf6V7P!m_AlfOB;i#2D3j9!`
z<T|T_5r=A3A}NwCf{<gw8Qy~Lzw9q>9=d4k)4F4wGBe1E9M@!WaFcUI6g>{KhVw)5
z?pxKoWj3Xx^jbj2{dz2?PmiroUfs%SiwA$;`&-g9Q9f-rYk|hsKOW2X>o%70-e2~^
zq@1NrZxmQB$&&Nyym5Pcs`Q4}(eLRWzN;IR*T0UJu<bMA%O$k*gW^;-1100E-+mhN
z!k3e5nD4jwjrr4KEAvtm_iy$RroYOMAB%X^D*md!9NZhJ;zQ9|oAw5i`%mLtdR<~Y
z>A4l@3w`7LdR$N84_G(ZUK_ivC*i~~kFQ^b{6;>v_8>BbdXN35akrqeZ6{tN#y1&l
ze*0tpR!99V&L8CYV_8lQ7jI?Rw2QLH!fE&$oGHnPbkAgjX9}eUZ<Iuw#8oBG$lHhe
zFF(Hj;f`@w=5$deu+o@{xvv+H8=2e!p5a~?Mx=>B8OdaxMZycv2b%F>A-vx^p{Q~=
z6QR_h)3Ppw3hlMQ6*@bXMluwVl#G(KV(tVnkoXlMOiJ@E=|~TR3<Gi^l0gTfFvc6n
z61Nd%n0a*T!|o$E{UFKvV6$K<tcj2;6?2MGq*AAP78}AAXhI<{hI<ISQSIW4qMXJ6
zIgy*Fh7*+w`CVfY*AUflCkkkip2V5RpbJ>DTat(mLP{*0ZyW{U?2}Og4bzw<bx~>w
zPf`jZA7BN&au!mcBtawkeaDwC`@i@XZ~y&I^JkRaBO`^OPLpO@S{fs>4+O9Z<p5<9
zpw?0Z<V0K<2rc1}5|~Ji5LND@!*eDnNT$IHDAyHcMb%4P2sG}~?*@>!X66M<CE0>p
zJP!t=Xh|$<yKJqktFD?x<GS~G4CC~O9GA8}U*xyH{rK_I`Qyja<MYF|jzYqV$bbHy
z{4Zdh+z@x`EP2l!Bet+(?DOb;H~rep_|cDprhy{^Q<6Q2LrP2H2F|L>%Iku%6s)aE
z;Uy?Q%R&^a;1&pG@6?3-?!?imP7imEL6j*>lYs!z=#snxHEksyG#yF99h2sLkE9rL
z!f(5H4zDr-8F?JR%;e6c<OCV1R8@f4ZD1D4$lINjh%oL`n`Q(PM2ciuG8HNT-*-tS
zNm51zt57(YV+OM>9-+&CR~{}KN#Xm~xjwVMQ^3Qb8z{*F-chti^45ea??)*)%S9Th
zXrZVrksFO<8bM6;#Eq&=SB-JA$CEh(I{OTn2(TM7q!n0A7#HKqXFKNg+5rw9IfC{~
zF5D03mZ%{HQC);=kYN$9l8iE_msZAYcLQZ<^@tu<tf%Mg!uJ4FK&!tDj=GV*H?*(}
z4e{>V>0CfBulv>P({};O+x711%ZDnI_m{2@e9~oH_iwh#rJlDF58HR=T7G+4wv@mA
z<MAebxp2I)m2p^zF5hiTny)|QLG;L`TYbF5w=YNeEZ;xK<)g;i{NvC2SJ!{_yRsea
z+?9>yFXQdAZ)<+8v0Yko#KhaXeT(DH^g#uux`o6Zx3~C8BR=5y0julcN{wgdu}82S
z?AzyZzSRgCSDH4juiiPA&mmclce(y?9v|;_o7>0y{Ok4ke0iwr8nsBJ5XtZ?Y}CYQ
z35OG5?up{8o=74>2MNJlHi}>kFjS^4wfpP!U;MNWn|-9PCXoaSfF|=!TB}*Grj0C%
zNGM8DMAlGZE(ypn4YG2$_b3vx=k`FvrA9_X<yN#5BG1#MVJ1ss9IQ!sA{@+0ZPl?m
zCP@P?@E%&&r=y`L8)YI&iB!}^b0?c1!*uwJ93yiMKPWxej(tMMd$(FLF-Vys^sF^A
z2hHTDsT<Rvdg1Enh$NvwQpq#nX+eOvTM8fVUKvFZgs`Nkp2^LnVgzT0NP;CQ(Hm6B
z3ojr97+DvA7#U5Ars&--Pw4^ML8R$HBIG=;TubJ{^DV1pGMGtGBmEfmdVjsymtXdO
z_Sf@Ye!2Erm<lV;2x5ak%1XUcdKT!q=!%+}(<>M5lf<yCZPEL!gJ?>YOPPBVCL~1>
zl%Vr>o+Aks2!>QCG=*#7GQERy4g}Xdjfmiw6vaHlSy2dHgao=sDN8F@9v_aG)@QdF
zGu&LNF3a-k=l0#7eEgH&oPYJ}$IqvF=3P}~2_=$$_@DgS8D`}v_B(k5sRfVgn@79u
zV@}@#Z}0Qgj|f|s6KYHn1$`FEBxQs*lx1DmxbQ`*P%;tMrc0ynB49e8Ez?+%T&NO(
z#T?kf&xsPXrW<lkT{3nO<)oYm-x=vtj27PSP7Ccv%ov2R=Y7W{8i!#xNIZHGky0R1
zmcneM5G4_7b_c2~Dn!XtBI$8uN<O-o$28-ywAMjYng_)u&_)35?yGuo50x-q7Kb_~
zi*aU3RqnK0h(;O_5F(nyH4)olFVmY-(s0Kt;eL8>7R@p5QCH5>D&|8-xO%0+(vPE1
z^5|45O;2YKgs#<y8zLlHQO~JJWT~9(GT*=5_ncAY_1fj+Yvt=LTLYPvT4IK-Oa*!1
z@mYPc!nACUF`d1Kh*I2-vB<ieKdf8uILh#DDM~bZvto9AH#u$WcG%u$`TTtuubAZA
zmIk4rZ|_zvbYYp--nV@C%=NZ--@n{!dsw&^&++B$7Hj_M`wF6%_O|2c1HQ*;ZR6*c
z`St3Tg;5K3%o}MdXKSl!{LB3>>6VYnpPp-S)wg^bbboSth>t_pdXh!`OZ@VK|Kg+l
z##iFANP8%2jhC1HOP~7dI4`-Dbum@V+39v1cQ#62A83`@?uAMod%oZMs9rAAr1;hE
z_pwv2AL!|`JUrB=_2KcfsxQshO&ezv3MTSQVvc|&6n+g<#z-(zvMNQAlVCC&qB18D
z5Tjv_ezNyp`j4+~M{dfc7Gq?%mla`%G%m~$B($E<JsdL_3xp#A4kYG8u{lWuC}xgv
zL_fkeQd$M%vBXqpDH4s8Q1}i$Nf9Dqaw4F3a)Eiqoidb@h&)v74$%}!QfMXHGbOW$
z-GVhn@xTbe^nJhYlN>g!PNnhQ(+*~qa>9NUiqe#8i&{8CbmjRD1X-{uS)@dcAS7sK
ztQiARAx=^_m7S6^vnFTy3<aYR-L=#Rq}@r*DV!b5M!X_sc8|tHg_u!mWCD^bLWqJW
zMAYDi*EOM(#PLq;fg^Y=)w>7zF~5B~zWsFj@BiiTKYZ)&feFIOBI38M;_dO_`1Y0R
zR#Isn4^Pk6_qUR19F27rE~k}E!tMZrz=f&1HUJc&y}Nsu1tt8*0xgy5D$Bzs?)TEd
zN}<7>B0OUl)uNnC=EF0kXrnM~r&{>b>S<j|rRm9&y1m=bdF^9(Tkzo|zxnm|fAU@X
z{Q1+TQx(moyU2+(sF3_$|9Ah}l+$|u;=H(X9tUo_x%+k854!I2?S}njCIzb`t%yz}
zB$NUwE5_ielp#b*1&GT^wJ?LUfc@a2RFZ`f!Wvnhs0z6wBy^@%S%e^D6W*f<IY+D%
zM$yQe0FI3C7}I71bcRu2TISAo8=mkaP9IuTr~n0_MbuLe#1z_UfNEXYhTYStiDH=~
zXSqDY^=mAfh@)*8?E8(ixg8P=A3@2SF-ZjJzzRw#!kH$CC@YRTB_~(dkx~RC;S7|K
z35!yrrZ}()rx+y@%k73NQ9^Sb=3)no28B;Ggn$(!BC`}+-;>X@ziBO{6+#+wg<}AU
zn5YZq^>tXL%NPeTu%2{WEei_s-g&*$ab#O51d9~P2-l*yG<pNgSyswUJ%t}HTx+%K
zPD_k$P87=rqkE~HoI+ORNyGPO?Rn+h`pb`5FQri3Z}T>f^N0GpE^oJer~2`M+iY*w
z<J)bn=e7#>{q=fS``ngz(p~o3{`$Ur{H`=#=CqgLZ})Qk5XE4Fet2!`YRpwOzki!Q
z7W(aX?a$i6CS}gwo3&G%G9R<7pLKi4_h04@y&sqP;Z(NQR+}`E<DM_q{WbOTrCg-6
zvPee1^_N$B875Em@T?~&4o#tcr?;=y8_X`b968?NnDf^9`dw}h%X3>U4{Oa<+QLV2
zQ*t-JzL{?9BSbg`Y)@fkC1=h%yC;~zAae+UGCL6`FHV%85Va7Ka&OP`+b{1gci#;q
z0g6b*<kFHzh3Rk?6oO?3d0iWNPoL%)L`B85h#M737vg9Et0&N%*G86L3Q={JZ9~bj
zoH_3#4<OfkBVW-n-H=SOx(y{{T{sdUE-<SQqhO|VZs8GTfJ9lGBpew>LXU|1TZGfK
z_rWsVm*DAXPOCC!GVPEj<UNa&IG89wQ5S>*lHNfaS~Qu*9%a=uk`~#aCyAbj40p;T
zML6*pahRSm6*RI{ndEGclq6>@F@scMcWUB2<&x;a&5^+m97A>S6wMiRlesfyuFqVR
z!-+ZWNAK~|&-Z`+)A;XxJl@`HGC2l#&Fh`R*(vY$*0!9T*?HN<?QL65(#<7`EMY-B
zJO<k0#_Xf9Ob0X1NSY~F=Co08LKvkmpD4ko^e(|hBBb}3K53L(E~nV1)HBVaB|(>3
ziK}qcRweFOHtu^TE*aMM!-l!vo9gM(e)Ij)uYUFL{PFbM`nf?gxsWO_6YBEk|LMQa
z8RNJO)|f_fj7-1BF^(SkcAvYXxy+yf33n9HODS`tk=+lj6-0<wl}pRowk-~<D;Qio
z%LZ}5WZJkMGD(A2Zh_3au-{cKWIG9idPJ2uQZEQf>zR$p#L$>Gvan2<BW&U8-Y556
zu9fnjeYZ^Pa}tzv%RXvla1yG>fJ-SLP0B(TOspgbWJ=bW_j?4StwP2#(f3UDtyYGI
zVjif)59i7{o!T1g#oa;|%xMCiT(}gdQi+k5kN$p~R+1#I6_F7mG6YdA-G`FQpmIv2
zNt)Njvz5jatT|XX>VlA(ho>lef(txWQKfW%K<2V8DNGs5iH~s~BW8#7xFsTTx)o1s
zg|s^5B(Ush!i|b7#x2t8vg}9T+{i}h_t&|eK7BanmF7@#7LB)8rnR0H^eZ1~O?bbF
z<#Bn^7Uli*{i_{<T6MwO?i$a}3#^)Xpgw=*(^g;p_|+cl>2Y~F@%T31N7=S=)@tLp
zq7TT2?`WNTjJbzH=3`NCzP%o{@v`D^<A$$4|6=!$Kl^;5AGbd6a>V<f4@<^vp~ppr
z_%E<NZfZ{#dM;W+>PZCo<zLR@V_!FV-0CV?&K&oA{bjxmlV8(At50#_&P>s7^Yw@E
z;!clUIrne=vO9gG55M70&-%F4^|CZQmmPX$>EKEkX+kk$l3)gJOhzacA~AuP9Pq`%
zCHKflyeN4jkQMC2#Jm8>5n$pba#=@xwBP^ocJJ^MPvR6~w~$InL^gzqle6cVJi?uY
ziDybOI*`+enF%(iQQiXH2U1AU#I;ptQpQ%L%^S-Kq7>Ja5|xx??o>{YJ%vC*?&MAh
z&P??*m*fbmjWaSW90pYHz_^<%us+5z!)}rL9J5!rPu+GiHL{#q8!c5L$5u7AEDj5=
zXSd0oynVv{no>xTXo);Dlj<2TK#7@2(}t60Ibk}AM-ss7J5<wWk`{kYBuxTVK3>2e
zJu&xq_{8&0L<nPK=>vewgDG>m6iwSXHF(PMon#mH<cO@t7~j6lpTGG(`Q7aoH{;}T
zw$WvJW&{C!P%`B~${3t61PUZdN@mfeG>{R1Os)@#X%Pp6r-jcz;usxCpzuf<S(j8v
zUP{c2rOf?y42;<i?<`Gd<`~KDVbG1~5EiLRsZEG@se;&#D3zm+_k+gmoijdt`uyGV
z_WhrH|LJ3XXv?w*3o{3blP}Je?(%>85B_I458{X!IF88G?q82y`i!qvIvjSl`y4ch
z*Mc-@nuk*YB~%MnElISX)aAUOB6SnB9>}Uyng}wvBon1myO7S{L_DS5L6oFWfprQA
z5vntN5!;Qc#0|p{h|)2cI)_A#VL76sPr7}WCgN}8nsXw<*g!A>NhwNUr7975@Y2{O
zWh9u3hLp~hW(}bIphoE#?TOQbjJ;4RtP8i5rbnUZOcD^m(Gkv4!!pvPcO%&(EtQ}?
zjl^q2Z5Uj1nz2?;1oOkA5o+huRF1KH;QO7l2|Aor$(6}GOUvu763NL#sA^H!CpQ=o
z&jg!H%WW%>d<weR?S`K0bVRuX4ez<Cu$!UQBu{U|Ws?MHV+X@+9)W^#dk;e|m-9)*
zEe4qo?tR{Qe|%Kk==zhlN647R@Pp-16&<%2!T$U~T50!wl=FF|M2)xUr=_eYWYWLx
z`?CD@`^Wro-PwlQ=TF<ZsbBA3?(cb8f5nAonPb13pFbp{#`NpmT*_l@mxm_iUw7$P
zKY!NBIF@+3-|~?@Jn34hDlS`odG}tzpUVfYlC3h0gQLuA*iin^n#^+EN(uDvw?A}y
zicjCk!w|JH-~D!2f1Pi2-yd9(`g<H}U%$iU^YW|b^)#3B!>Z91W)h4mt59fS=HH;6
z#BNLgF$Y(n8G%ef5^*HNL!+A@!taR?R_84U934_K8!{+HZL%$-+j(@q|NgIoW)<~5
zLv^M}&<IvA;G>%sICHx3NFrA$a~_pdJu<Vn&$g;{FjGV=vXJm5WZ>YoSn`>XKyuY0
zf{EZJvU6#<)yRd%?$QvGp~)pF(pV@%M8Ff0(<ux2peW?`ATmGP`|Prtz3=<Tve)+Z
z^IOb*pBXl!h-EoR@LF4XYHEgO6Y5wV5VzzYG4jdF1EPb@kuEej|Hi^RojbT8guuxq
z%t0)b*~r@>Hjopd8I7o;E?gMQl$MdW?J);R;e5~f9i=&eN`X}125F8ET0xAsYiS@4
zy7yl9AO85eug5?A1^@DwZ=Ga}1SZ{&nZ(i4pjEk0X(u^9(llWzrBG8*i6G6903}ED
z`QCF3Ue-9^!Aw1h1Q+8-kZ|Qw)oFzhDK#*im}Dn4Mgj$mB^87aW-o2!BvB%1Ri#yw
zR*1E5PK!R~B#s>Cr9M1upU&m+`;VV4^7*l8Lwh)-dGKrj9~MO-tNg?N_<u>n)16#%
z_?UCt{TT7}Ci_0FyYCZ;q=mOsE{!5Il9x(HNNE{EeQ;565%f}8jU;7$tSTusSruA^
z1GOMmXfP9;m?o9Vz9%<0B?JVT$pB&JT!DlAa3Waq7?By7LDhO6mP5pDwkMlN21C^|
z(6|(#QY$&dEQSOv3=4CiYz>}zOcqjxL?n}>p;W&I=S<b;o@*6BE-HO^Ar%I=%91(3
z8iQ&hT32_kTv|bA(PWYg%7vA}2a6YB260Fx4_a(`VTVRx)r#w#i#w?-r+DvyP;y~)
zmFk0-HRnhUsgiFkmqe0=wH2Y^Q_7|4TfQIn2|wbx8>Dcuh*2=Mq9EHx*HXwkm17<&
zEvTEkU%7HoBA2fQ`tV_GH?2e-a}n%sULKZ<7CMp&txUFiX}vQpr7mi(xBXVj_m^en
zSqO!q)rPd|8!itGvt8dfpRD)t+fVEDOWv-BZN6d6S(g}pcsa0cpU;hZzW3X;V&(1m
zx!67LeP$`=+UiP5{(5tM$WQC?Ty=5#av$|vKT)M2RozCu+~&IGM=DP%%PRKE{C3c{
zt1Zv8gq+Vpq;=zTdi!Je*2|}|g<$aB<^EHAyV^@Rav3QRsnh29@9O8r_4%}2WILZ*
zYb(Byu9|P8H9R=GC#6v$au9K#)S!lWhor|UaZhQ8>7+`@Rc4&XgFwinRMnAOeX8;f
zE<rUjm?lXS311(MAAY`lxyEsyMGGsXQkZ~+c=llUbn$iN8Gu7Ij82-pEJ~R`hUa7v
zFGwmy9M$%{@-}6uQ^s6YRUYEOMcqS$WMT4TN$45zF0#1y1ZOm&BXcDTY)e2WM7#$Z
zqGmez;PffY?nh8_8T~#F&~@5;z20^Y!Ep3RrSVqhDC-s`AeF{KY=i5@WQ-&NrBzDv
z!tqY^f&3lAl9dXAD-2Q3Y%_(}W@u$1!3>Cyh8s~*5D`N)(5VwD11ix27Fn<;W`*5S
znajp@2$`VCQpg$=m(o-^^CYQba({n+{j&elAI4w&{^j+^X~=$%I8<BNGKrg)AOr`S
zAEN?{*&aUv2ahP{bEFdyq)9}EY6H7wrDP7}=;CR1B@(1YL2gu|QE99TC6;M}5_a#J
zK2<O$6N6G}NlGw@XWdSmB(2fuR2ODyvzuqcb%)~9>Gb*IhY!CxJv?p?PnT7rZtLPV
zb1*R?oKH3nSx@p0|NZ|td1hJ+@_G2}-rvT3<o)pXK5QSLsHHMg!!<2R^7(;|yH%BW
z&k=Gu6J?IE)zDfRb6ut`O9M!OvJikJBQ`EOYjV`!HPev{Ia3U9iv*D}?vq_}i|Dk}
zoIRXKja<n*!*j$0(v2dI;G-vwbhjgrG<o?D_r0>A2(1eui*S<o^kmjfGhJ&TiiOD1
z!?-l?^vObPA)i_xOx`x#ZwQL$Ix|J4TbO_&D^mdTU_P-1e1dq=BnzcRx{JvquA@h!
zvcpk8kiiM@A<^p$BThz1#AIzVP?-}Eh11*wMqDS>76*r+C~Y6(c#|Q*#Elb7$=~jJ
zkRE-cFcI~6a9ONmy&k@tEvPkwM0xmawKr@_95i$Ipr8oj`sw+US3V92O|!fmylhca
z-rur38XasS4W6p!R`0KNi+iwqs*5bzZ*vn~%E^x1H>qVQbKa+=_VZG<vXtmwe%a%r
zuKTGFqa9!G^YpYXXobetvEu<xqK$+oUvG}qHf@z9?e^oHK0K}ue$rj)bN>GKL1*52
z6=Zv+pTFS7@$s>ym&eDl)cE!~|6qO-e-_!G8AaaW`i9)c%lmAfcni^kb@lu0_`@IO
z&w8})CM0)bU-Rj6{rItc_pqL{wzgDXFDtR24^;B8YW{?nZYTH)$wEm9W2#V99D&Ae
zgu8RYv<y*n;^a&&Y31bIINm{numMSBi4j^oPzf3iz@-AUT6x~T{I%`gubl!kdl6cC
zAD4}~g%Xw}hF7vqGEFI`Nzu9(=`j!CHd*E95Si}-l}QOwmL;9ArWNIo6RNX{GO`sG
zLRmdYOVCux?(|5s3xZ1_pd>~1%msdgmZZgzGH=A2k;v%eg%GpHbsy<vzaIVF-sT`L
zGm3M^)Q6VpkmpA^CKajHNzY6pDUwb_CQy<JAzhW{6<(=Sq-PL~2ta*@f@GtZ>4Q@v
zGD8@k^-SO#14TJ&fC7cHO0ks95E7h%1anLY%d*J*DkLzbc|$HFL!}F!i6tDz@%rr*
zzx(}{fASam%k?_A+UP_|l!Vkk#7^AG%mZ`LWxACDnUyo-07;dl<$gase470&v=L9$
z<{0T^r226TCm#;>vS?8#APfy+Q4d`2yHV8<Mh9KKUs{Pixz%#n_~B{${z2A{LAD?j
zYZhCUT27a@-~Eh};&z18PiMU-KR-Ww_)s4%>$Yes)(X4u97&WTMf@ma%8-BbzxsPX
zc)AVDe(b}qhTF)m@1sv9PDWZi)#4LaB@s+Hb6Le_rVx3S3aMq?^lVF2SuTnUBC*Lu
zMJgzB*(yuQqzp_@ae`)Cvn$ln2sSX1ln5KgK`3%K5mMH?kE9YI=*NtJ;o#y^De{PY
zhKv#BQOvzDGyn<8Qq>ZfER?(yICBFLAT)Vp%rHn5QzS8~gNQ~4K<9<uM{FWy97&M~
zgn25ngjz~Evsn&m1?nz~<fO8&Me~tsOPgGUIm0}Oh>2u`Jmj&1HP^xsqm%$g%~K@8
zV@_xFyJ0OLo<mAwQgu=gjecDO!Nn-H*o|Vk-bPw_h9^+m+*!2bv9n6Frlpd{TIvWZ
zgSp{g_S<cu>W8yxHXJ-#4|iJMGH`ihkDNSPOLE%h?Pj)|+Un)yesrR>YHN!p-DlMk
z^WAr&vf@-)@qF9IewSbU>7`ng7%x6)tB=AlkktoK8Oc<ah~pYZE1&ctsO#CLQy?$3
zvIk{<*`+Sa2SR6o?61R?Pi0M*iVA%1^`y_MN=ki>_dn2WpWM8Z{FEr0lor=l|GImA
zuzaMAv@XR51@d?~evW<%%fxtRTI}(&{Q7zMa9);p+EfczLYRddNi#(J-O>CmdHfO@
z8JEmENnuZjQF`VC*ExlKmb|AvrjGz599)19<|r*^3LFx|jdMDIZGaDe0yT^@JB#O8
zef?Gb@`wFvCmU5{WSWTBV96js29#}ijGV~@vIvbq)%)Gjs*1xV=}gk>QL1ZMJV7K?
z3baVDWC1Z!bYVK6fSE|ajHCz+_~4#|;83EQ@q%CwNRwzYBDMoA6d-?3UR<lsewT&j
z-Fof^#_Yyr1mBN?Jqf*)CIxD}7G@co8XunI_@>fGko-pF`xGlNLJFZLT8d!agSSj4
zmJDXjbVMe+2QEB~Dv>CxQzj71HQ8tyLnYsn7VS4o&781%QZ1Q*Mf@GIFqkwls-HHd
z$waCF%6{Fy9{Sfm^}qV7pZ|K`Mk0&MnanAm#-MOUO16*`2r-v!We+?Ex55j$cMh|O
z<Nb~50(R<TfmI6TIC%1Wu<DVKDB6mw4c3K8);hZpc-WDm6BE2-ztXsI*<^HTE)H;0
zySC?F=a?L0YFSR3{rvT=DU^z3d#oRx*QZ}y9v`=-t<;O2c$SAXgUC8lu`mK5sj~K?
z%m4X5__sYXhRpY??elg+zvs*DM-~v-F#HWwMr39|G0lun8n%x}&&k13kldsqGJIWI
z<3sBvjj)`U4xE*}5Nk$q10@3^^?|~K+=;U}y60&j?;>lKj65<+TF2y6kM6@JGLUIe
z$fu3{u-He9k)b>Xg;Kuv7U5BdvfYE!8=ts_kK`I2B%;~9MZp+Z6Q-$BETU=@WH~Df
z5t6}k#<W@1A_yfoOC=?k0HqDXR+ww!L785Xh-+*mZ@W7ctu%*7OCr|_W&#gtj1uNt
zc~B7^tW^?B$KhNQjmX<7$F9NwaNcY=$MFVPKwK$RQ<p9KKJKwQ=Qsim;-xC+0BT>?
zV%>wZR?NEqhDOJ`5lbm_f6pxPSRPAi-z7Qdh~s|Lv(Dc73+Y)e=aye+?`%BUQme<+
z!ko6c<u1{5aeDY9_g_4woSrzNeESx6IqDYMcH)r!>c^Hc;}q+>+1oL9mMuV+)kfU+
z<64&Q&r4J6<M#SB<wJolJ^<Wr#ZK$?y=s6t`s*DJiVJHVetgZ@sBzOCODojl?PuFL
zc;khXM_DeC>DRlzyxIDjyqIpGRq6OTUhnhUAIJPWV&<)nd6Mt`tp4f8%bC|p*-l(|
z<8Y~h1VwV(i76<}lwz2)nJXK-c=AOh2*|la?1(~07II%GcMwbq6K8+}96JQeJ@gbV
zxW6;q11<l?+~Fn2sI`<vDFu63shl2ymVEnp+<Ny(At;SA^EPy8L5Uf`D&ZtCiNiBE
zSQq##Wpkn=WRiqHNQ4(Q2CZ7bSyYInG(}<J(DD@QS`}PTjAAkvTs4eZNd;K~2!YEc
zxA&kV$PA^tb3Gw>n){dsEkPXv!}(RiZZVAZ(alNMMI4dT6l9}7bX(YL&J+cfn$wtx
z>3}@)xMgdEHED>~j6o_&Be`-6LKVB&x_~v(!J6rW(Cl#cQf1x~D=3&bb0#bqS(<q$
zThvv|QVWAXAOV#oIihSFNh%1p(fg5q{ri{S|BIjh*)Q?-KCI}y4^FLG>e5PC`@Cy?
zh}fBHT?>a2Mr~RfOQ>XVa|72!z^PmlCdv*ObD9)OKkU$2yn9)T$~^0$l1qghJ|_)}
znWjd^5kaKP$^;*AIxSDn&cfrE!_}dvQ~IeqNUb^Pf|`9?o=%VF<>|xY)2HWi84njN
z?ZR4_TnL$-!#pBD(wCSomL&g|fA8NV=d`^W4X2lE_ua>Rn903cTO^wz!9-eUb`xPX
zGeVg&Ws@~|(p=><4_#_wU8P7#**1b|fdr*nuBY@nq%wevl$v&9Q4)n+cw9+OlmbXb
zCJjo^BtmA6u*@*2)kfGcCLWIKdw4aAs|W2iqeogyk(AWnW@{}(#BeCXJc773FT9Zf
z%$d$6OUs$$0ZmyRs9$*xh+y9GwA9^6iZ2?YOCG5g8@;hbx8Nc%SSzw~DJ!x&qlAtz
zQgf-;SO@vqqzNDQ8baWdT5u;>U|z+QYaP*3MUb4F>}3g#QjuIF;~u0IV4<9h%*%85
zAwKS%Z--q&r8TCR?-5mwJ`<qjqBfU;AS%jxJ7;iNVvL}eF>8E&Y<rKH%=sQP{{IZ&
zNwX}=njYr8U*ql(k(sO8r>S#q)kOgXfIxwSFpy~=(+{9YGNVXMHKWNi;zYlcW-<W~
zz(S##+ni?ay_(F7aQCm#d7gL}?t-iD7PPJpnTOrJyzS-N_Pl7nkM0LusGV2zaO4`L
z3EgL4(eomzeq6sE#4pcUb2*i~^@+tFPBh=@(T>m8ULMw$wJ_zn_Sg5Ze!FccHA$E8
z)dEY=kH(a+n5y64T%so8>wW*_o|lbE=&kv1|048q;$JnQ``7vV@cNBCo@lWmpNbIO
zzs#>;xa3mga%g=PuKuV0)PL#6S2{*R|Ke|H&+GP2f4h9QZbeJo8V*{TmgYnp9h5#T
zr^(#IXK+KR<QvtK$Enz7YK*MH=~yT|ITMOVu9&v4-C1|=S#ozMa*KWw0*XWw$Lch)
zTvAp{Bn()&LW)Erj2A`OlAc~V`N!{n9=9%Q!G4bnscIx?Zb`r)MZ-{R1PFeRTR_OC
z2MdRWOXEe;4yZ~ilt>{6Ig0#>lSngo6A6X~APIIPRifdwsX^(UsN|B<Je@*wsgXvR
zLKE<jE3O~zWzRbq-;TpoKVI)MV2)$xfrJkxX3Uytm&XOx2(v6TkHTk)Q0c-ez{Han
zOu-ZqBJn}sz=jxHT2jcIV{2(GV*)~w6vSy1<n^QzRxg~BXj*-t99+(%3U>%$4B_B*
zif~F|B>gHqSPG0Q6Wwz1e%;?b_P_hP_y6tZ>lZ6_`4;vL_w}^+bhF;=zVHHZWUG~6
z&L?hL)1vGd_hVp6X`|ok+NK#2oD_XzkcGv_;OfHEQ`ygpoYq=7Cj}YYESW)qfU&Hn
zlTxEaoML_X0k)@kv60r8L5ZlwQ?h}GcwTGdQe{1t%X4{H`LWP)DNC`cA$%gdayiGG
z2Qs7j7~wsNx>osr|J#2?)<>ir{kZzu*W;s`A$uf?mPklYBoz^N4Ni?vq-YXARKSiD
z4%Bi{FIaQ!#e?-C+C!xjQJYMv_CfWDki<Lq!eLo0T`BftPGR66R-{o9gfoTbEkZF7
zaP(m=m>nK@47cvd-7+%wo|5F|Q%EI5PpmP|iv(4r3`SKVMlh$lFwY#Q%8JuD^59Y|
zk?;|~rIuXMcS@Nhalckxf|32|rv;8oVIH-RkQfsKnlXcU8j^G=NTc)u$5MPB%r&)7
zdc?jLHP_-EOU3k%WkWd&j|4$7j5v_j1GQ#2aU<(7Lkc(QMvKZgruT_)8@`<6xY|8q
zi3Xk|AtGEX%!6fF#{{RZuxS`hetBL`ePLFJ^O(mpU4kc@&spev-X3<k&bK>R+k`sr
z?{jbQv|Y-v%AD4uy09DSqRn1Huifr<d++6Z*-o{@?mhNxT`rZ5cmKG#wcAr|-@G)M
z;U6}b&!_WxLs6PO_KZ`_60K2q<vHb3YZK6ie%LV1r%!}krl$1K9_8G1dy%T+Paor!
z{{1Jp+*_k^dEq+!^B>(FaM`&0tb}_06yovob^qxYk<+aY3Hhyj_ucss=f`g@r^b~<
zd+=8BGnW$>))Vn1p(Avd&~(A!R3M9%o6Dp1Q2WYd$t)lTgmOul@DwB$A9;~sD)-Pu
zC_R08p*WDF43~$HRWwOjf=CsmX$Dw`*r{O5riU&oZtePqKkU0<>@EvYA!ftWqM@yr
zcbnzNE|VS}T$>ANVX<Ikh8rT1BM7hp7$VCi$gPzqRags%Fd?f*Csra-VqZ?|JHW|I
z!C4Y+5zGaOVCIa$<l;m+EuOVs0|{+xH}|u|{kGqeTg3SKwuj|Dju|}C1$h!tkJ9*8
z6hKxIXBa6YkXt1#gzg8>BAFvWF+4LP2uU#KmFG;(Oa>xgd;}Fn)))pQX3E;aQgWcz
zRB%jWFlun2S{y1_{Iu}!Y&B@rr6iEViM;Oj`_JR=|7d^nr}w{q)BE6lyz+0qU(V0+
z+spF!BDL`QV6hp>835_3t(F+kYR=Apg9_!)S~!ABoA;TO5-d`ubHHLLJdGqMu`UHR
z5hS90A6DD!5;bFXN^wup>9f-@rA|3rcsN)yjUlbH+P2!1y_B?Cno686+xgTUFPF>9
zxmJH%r4;5==7U36Tr7^`$bor3W)nGoYAebA^1uIYgZOY9`xry6Ge+>eXDT5gF?oHG
z+_h{rP1s$Sql67{X2xL6IV3~Y3g9f05>=~8=Mo%A2rLECrCw5ma!+n>%h<S1Bq&o-
zCM%BvfLetOcT(+1ktraeT*<DQ>P`rr2nP(ckF?adk9}Hzr3#U9I8)J)QnXJ<TQdgN
z5M4Qp!60KPs6&KtpDeE2GA(UXTOx0a6pqf4cgqUPMy9NkT-lRVxmF)Z<d~ir&L~x^
zR^E56MM<-FPpTMkDr_OlAucu(x>gKf4xWTMKTJ@2Ai<*C_ra;#Gvfwh-&P@Ts~S_Q
z`0e<3k7E$mN$otnD;X15sIXV5Sv^8QAYvudl5RP6l*@8fSJtI0<9L_h8BNZM9=r3>
zE{l(kv(u#NC!vYs{p+4j`ck%jtx>3gPYcb1owz*H;(Yzur+%HQZr%z?jt@V}T9#GN
zU&}r|@{*snR`X;#-R^#0M53~xkJu^YL`yGxURTeX2bgQJk(ZM8HKyoOD>{_yxLc`<
z=GQ8loF4M}^_VC9VJ$rB@~v!}_8*UT%A?>DF~o*~9c`IE{doK``u*K*)9VlAFMhZF
z`P2HM>uD=xiX18el!%N$Dp$`k$DBtApCJWBoKA+bh(npXFY>1;FXqdVUq~l#qM}Yy
zJP8@x*l)z?E@Ys%nQW0YX}Vq{6Qz|ZQYyy~RdcF(K+mkg8md(Y7Mv7H$2dPvTKk`V
z-me|U&Q-!p(L0fi$;@W#g=JcgDHiE&H-o|>Gcsn$qYIMP(<~)exadhq#I-0BX&$yc
zm)J=fONyLQCS?U?=$RoPPykXW2a=r|63UUh&1K^(@Xk`B6%2>S;XvPysmGk-u-ld1
zj^TkpkX5l;E5w^JNv%=}vqtgcWUa{_#Em$~Gf7B`_yDaa%C@5}2nRz%!@)7R1Y0sP
zg-H~Y?3x1h9taFe2Ct85cMe3=oV`edI3;OLrxTURY;E4aOs5CTn;m)mxPSh``@jGD
z>)(8pw`o`lE&A!7{jfYg-T(Mm=RtGo>A`K12UEx*!XyI+51CA6GbIr@BDtzh&#Wtx
zl`(CEg%7Wq(=o<UYHeD>iHTUv;X*NDx-q+<alv+04kn~<wz9A+ZnJ6Ag;j;uD#YnV
z6n;J0rl;+pp5^h}+Qw}y&x;BnsUU*shV~?M%`6kSUxzK*Ms{k{gyetuH~%s%dZ#|(
z{hA*Z*EbW9A|fkEq=2|I=joy}W~n8-<Z*XswkmUyZd?YU*0!9B%VjI+B289Rfi5kb
zAeOX*6j37-f&(1@r^d2`b*44MIf)8~7K%Ge$ULfWpYBOMY`7D=Ps1ti_qY2XV24JZ
zT~uVT-UUf(L19>kPm*XPQCX)_%tQd;A?!PN(PMhvlS`JEBbhi52;s~wYYs0<4L3e4
z7lchIm2D6?GeHNZlyu{hkf&Kv5$)7nYK<|qHev6+N<@O1?XE4=>dd5s69dEs*`)7S
z*5C*sC#5-@pc*g}rktS_7UQ<_ZBA2&6r{;*cPg=Jv6{+Cx-1;CuUrW(BHV45*T>RW
z-AQTAzK`Tx9*Hxe8=o(FN1tQ&ad|8;$Cq(j@4cMc<#8QfGS2Z(OPSTD+mZ{)X^r>m
zvA6rDPxWzI2r}ouQXbDmZd*6Iv)fv~TU)8DNB__rZJaKT=K~$L&Y88oXql;{+1rTq
zRMs2FqSWKvsT;`3JZQ&!KjeH$Nip!adlWAr&!`V7mv2DV{CMr}5A^gP=N()z!^eoP
zyI=Ql#r5d7`=k8Re}4IAzkU1`+oH9Bv|=V16MZBxk`XIzUlKj|V#vf<D~oX13M<$<
z<uu>WL+S_QVm%p+=w19*LNSm<a8H*ENlxRIV*wf3%3Qgw6!+4Ykw6tUk%SaUCLWN=
z1Ok(A5@&#ba*AkYKIZNBf3)rcAPyU#b)!7k1cDTWmW)8AA5(}%ay6Qi#7t|=%*Me|
zG-6wp1{WcgU~bx2S-3Wosp<i5m?)Wj7mDx>Cvs;7xI_`Lp6!9>VD2$beE-U=_{flI
ziEb2gbScMSG4nW%56anxDNhtlcG6fEQPQ?zJCh9~=1Ss3Z6h93HwG~rq>7Qef)$zR
zVN%hJFBduv6wVN!q~ED-oKt}CDe1w<!o(GG(&a(z71*<!DMHv14d#kWagIfz3ycL(
zE)wr45BC20_2;ku@F)Du$MNOE@kHPfcf0@bPyNelym!0b+`{&IiHvYo7D@~cp~g(T
z2MR6715_o+DWs@mW{{aDh(?fLW)N3iI8Un}Q#JeHV|E|m$t)Cs+B7Xns47KfZ~-Uv
z9cwj~x_&ptowM*F>S!lvMYd8d8?C4Iw3OwNr@9slp^5c5@gYp<L1@!rd`uI5Y_dKS
zX`9q0|F8e%U%2IPe!aO5e;<Ih1a&Zup{Gj;n5I;vnGxXR5TQoSjno|#wo9tGZ_<nE
zT9r$-2yQ{`LN<BZv@I?N>m%FMd0`(^FJPyP#2J1IALNB>a74x+rj$f7TfjZpW7ddF
zo8yW@FsB_JHghs&xF_v1N)xmVl@&@wQHiNkwhEyfOf(l=MS?>ncQZO8Ckc~tL`d;a
z+oh%uU}V>wRg@`&B%_3)R!%D{Q8t+~(%k~8nOQ~r^a)5Ph}4l~y`*`{ECi(Ww$z!B
zAk-|P;d^+Ig8L4xQ`@AWnKDHR5bR#CFxw(qWgpl3*t>8EnMG|U3MyO+i7PD<Bc;gw
zAjKS~7xbB7J|iC=0LtW|EXV6TH*TBetu#_6&bMK8=M6<#9>*QGsB)6_a27ZQ)@P9f
z?!dPBMx~zV`ub+NK9v*2d0unqST5_imIZ=5?iS~hoTHIr|DgLar7oBGz$Te9+AJ(D
zrxs4ZzQ4w4VZHj-JqL|_bsE%4N}=5kY_y@)2F>|;O*+$KEfMXcZR~h^i}y~k>i1kf
z{!Z3!aqIKT=l(k9vG;qow|jEi{`K!3|Kiib;?kB%2Cc#qTrwjDN?t>j%>A%a%p&oM
zdXcRPsmf5EoDx33B<daogDU-;8PlEO6!xR@BYZ}9bVp932C%C6=@TooDmSe{F-+J}
zPQ)IAIX{>trBY5vK?1QSYes?_$7nLvGyCJ8<?YMozx&zekx){ONDq;IRE32b05|0l
z8IUZ?8e|OA(b);5Pznha<cYJ8V_n2aE|m$SOnHzpa#6M<cD@iIf-*X_6L^GMILM|i
z8^#^Xjy08e9Nx`i&H2R#7&Iecb~Unof4#@;Yu_11=N^WdqH80kew-dwU4^4p4xS<#
zMJnUa1vH&FnS?k%1b4&)C%FpGj6zB2BV;A($-;2xk{UFK#FLtc27nxxB{7J%9L|CO
zaX45?M9PWO8Q@4>nlPEJW8AO%@rS=V{y%@{zyJJpqe8sc`v);;p?mM^sbU~@16c%@
zveB5!%ETpTmLw<ULfplovp}`6+dYyF?<0*eJwz3uQkF`zQV3Wp%G!86z2A3=IvZ8M
zw{nsi0Wi<LJEu^!F+*!PHyMR7%34TOiY&C$uvW1?o*p0Qa$3uCYo|*=NOh6w^AHvh
zglyt|y&ltJYgx~muFT>nMgE_E{qG#*?YMpIaeISJckjv+A`#w_>H7;PaI?xs%0e<E
zV#Y36^WGIs!Oqa9ZL_hgB@&xhv`3Vw#FT?c$aV=ivU9nWP-0F=(#Tm74qh|@>kIhC
zT8T>bk=Zgi-II~dImtbzb1>l1_YPK=ABTnUfFuc@*Bm`VgM=Hik`vWdVL?(v5=cp{
zw%hh76e!@~>{Hf;u~h1gEOjv{Q=8?Wgq$~VM`V!25mJ?eK$MkqOy;Ora)JK}$2xB1
z{KQ%L`0DZy_A&TniCe}-IEsUoCA<U0lc_X=OI2_NpA<7iSkEnX2DplZ(^|vPr|(R5
zDu@{~LzHLEES0T@l=DVD2RBP|Vwp$0e4;t3PRgdyv$0!D7v>0`#b*P}KJTAz`|_mq
zUS{d<A68GL<U+jNUUR+h!$#vO``#YDm6mm+UyuE$>+_}R9s4*A)<@OHSlsB|=XKEY
zqheNf{&Ky}8ke)SvEV}e&2ys`DmazReaxO#x3-b}c>j3)@cP(3HJLYC7yh`1HoagY
z%9MCJu$=36Tj9!2OLQDRzgv0AZ-1)~5q-K18@+#i??k)tczb{StjnMO^X*f;{P1wz
zD%VBGDTy@z1v40qutNp!hK-wAX^chbR?ZJ3#K)b+9<Y>inA3buQHO+=^vd?(iP1~(
z_pFFmsJ6im)nDcG#2oFSl2!{*I(bi8WqyI25W7)B-+@SE1Ti0;1wNUb3bB+3QtC^U
zRdxD!{kuP;5NpoK?v}#`2_)bpN{xP?suFV`Yr!<hiCADmm4e($+dQbHXqL5=JV2y%
zO`gJqWMNFoNNEyp$&Z<XC<$A}AsHq@%PB)4AYL$MNX<AvitK>rB<t^?^?H9Duh-t)
zo%-zeBlj_q$)^=$D?qz2iR}Bs<pGGYJuT`$6w)YCRmnWd%62E#T$Dl+ld5J4V`e7R
zCHkZk%807sL1xL6+q2Aj#tQ!sZ8Y9eo=69bh$2vhGPw{2v2Zv*MI9kThYh<O{`y~j
z{M$bse|NLjBN8~q9o)h#3!13SnU+#iLY7knLX=x3ia>=xF)7`nVtaa2ro)|>!y*ZT
z)5DMZpri^H8?q=9SA~Xj)p_s3Fme_u>d8(X*QQI(87wTT6ywO1&pf=e21I*!DlT;r
z(>y&~&QJAnT9>swK5lEn!r7Wld5|%Qg4i=i{5m>aZzN@Ii)v-9MV1L?`Jeyg?;^&>
zQRY6bQ?kOGWzp1GMQR-6$mtA&C1rtmhS;gn@R+lRBr_>hu8Jg`<@~_QS(<7dyb5Va
ziqwV0M3pOx6c28+1QX@V0v?v@MmE8W>EP*l=6PgLQq4d*4QFS_*u(tTX94=%dcuNd
zz~``GQ=kqYc~w4c@U|^MJx~kBxFt(O`E>Hb;L2gnM9xu)X3nA%E>(?94i@jaG;Ak6
zJ~-72B1;ge1jC8h#}Q7{3ewb^i6mw%tL@#!eOrVvfz-NYn^T(79!~5t6DGnK?&%Ir
z@ff*7i4Y=5C_p>Ghma)Kxqmr&vE(v+aTrsOL>jbMvMhzo_Nl<A%6^22DARH~Ojg$i
zmEj@NRW%6a%VU#$zmE^+*fwp1kNEhQQ)&?}Je^kl?o+vbDKYPZygV(ZPOont6UW2v
z<dTJM{`o!VRGv%eH@O*I`_1cePB`Iy^sjqgUh0#Ut*yuVF?w3nwdC=^?VRIn@*`cG
zvD<a_*Vo(foqls!bn+no{J~81vPo%TBi!(CmNS~pScUE}zrOR!bNjO_Cxv0&bNtOe
z&f$aPc)iWfb3A{#{CEHE^1Fv+-A?7u^NG{b1lfs_5D3o{1W#BVDwRn~Q9=s)U8}%-
z46-SRccKgvSfl<0nuE5aFh616y@=<t#=(~hElXj7ziTb9qwtE!#KS{*jzGk2$bijA
zw*XwsENS}D3F5=a4vOG7z)pjbNd!Y5_qSW=V~;VMm~%?;n7kC@R5MRwBC$Er>M#sI
za-h1>Ng{-_Mo7I-s??Sfu+my_lOX04vgE?Nq=cBeXk-v2=^BKHDZ$Q=!%%=&NNzkM
zmj(T1L{u1a%rM*cdBDTpr`sI2(e85`o_A*1&dHKlBzq|#ZLQ}`4~zIH<*YKPP}K~p
zkw&^DccK;ZLx{jDQP3Ea6gkNgg(*fdC%}}^6Of3c3|<^qgM>0rD%s9byl#>Qk~Ke~
zJ&+9%6{gexFF`(E-`~Ez|M*M)Uw_&E^fsK?l*Bc?Yo)3uGZ!KuE~QEVc`%bKq_z0n
zf=DYRqjF0>rg(Pe%wQ9rxBKq8@Qg{n5)oFV+M<@0he7gq+wb8=H({DF%cALqWF&!u
z&Dp&yb#S&;4$n*wsW2kF)_yuaJY49!m3q>LwOvjqOr;8_bFG<40kWA)Ice{QH$7dX
ztVNa+mnp#nk^jf<{>o-vlfQms3`}vN6|F5c$Q-(+@0r3jW2xd^5WzJMr#8WAN2j*%
z7*%tXSQjX5t+3L;g%so{Er+k0u#g)O!xu<RyM{9%!37%b!iZlH22y~E6vTup;eE7E
zx_?AxYUh2L7&>TjyZ`P}{B-q6$AJ*Ts`XS?A~^{sZF7>87JEd)!hSs<srN}M8*!<;
z-+L{*Ob?dgw}2GRV?-sjm=Gb=8t!JH$x>+^rmEb?4j<{7G?q#hMHJ0jkSi^v9A=_L
zEcH~l21qD#-$OX4YI<=dcwypNBoEfc#n~veBJM_)^g&EfHWWXONUpxtG%;R|WO8q7
zmCGZ|VRxXFT%-&_f-aT!QGCpHiQ=Hqcu(CB2g<p&&K7j-Q%|&bl{&jH2oF>iRTk&-
zV>?Rj>8D+)o?^LFe!pLjpx^y)mgDrx-`gGUa@n31Ej<sr=ks`Yzy@%qy}y4Dogdcc
ztc7UU{Q8FNg|}MbYu0lDilD;lvF~qj^e|f6gOo`HyS-tiw1--yW!QN8O5gmY)}sM=
zxA{wRxO{tRYY#jCHh=!&zsy&coV>ptANWpw^WS{?oxFUyEsx)5E6KI+P9a8jj>SAU
z!lSTf2!WO;h2W(GOJ$@@=KGJ3B&|j@yU=)N{+M*`L)j0}ka|L0CvD3z<@A(hYXgy%
zwD8azfm-w$;phkUd!O$_>upw#;+c*JEMSim53-`kB?vQGB*m}1dNRRNR@91z@BQVU
zvd8e`Gy+QzIi^j{8kXQO&{{1l!|9<SERKX*m@_YGqZVmw#LCRnnwQ#QfU77mXL5nC
zFq}iCrKr$Cc@;jxo!nD5_B+*rNS_0!6g`NGMf&hux*bYON8ER_iMji2AM?l~LoTP&
zpFKwuKdy1?q^ISwJmkSl*j7Dm1%kEkbVimD(Oi>Koa!TdaA?Fm!Q#f1aCl%&J~QnU
zP9DU?5o$e2*~7S@R?Z0~a`_caL!(R<&TNbN$kPR2Fp|ml@o^tN{`B#m|8f7vQM1<Y
zoHJn7kYto>r4)}KA`y-7O3>|rX8;qd<$*(1I)=Gt5SQWxvT1kDfYn7U!VD?vd23{)
zlnp`*C>?j6)4J7=SW8__4;(O!%IxN-wD95!mt}dlJkFzwZqWxVDI|5N4=n3N9-o#|
z!&0<8@J5r`LUVv2>54d#r_Ib3bD%8k)VOic;B3Sl+(KCX$G`Y*57&>w(;_vg)B=go
z8AKvPf%fDAfQ2i^B$41^xXUOpBUx!#xG^l3!aynKO=wqjB1JlDq9`MkGg?zmUhA+X
zI?zO-MGOH`R-)uFQ<jY6a?0rBOg!xXqM{#@jyz7z`$QS{?m^?{ySX3cQECtiVL5E>
zPHuHQL4)qy+E#Q#WL4&+Bwz{5D4c!I<y<GRb25p9??T!ww}QIhxQfm0P~rfg5HYkQ
zvLTIEfSMv%N(xOku4m3;Qc<2NJb-m88KL{ffr#~`+U~q?lMF7YU4_z_C4!68$pa!}
z!_0hZQe>-RejL3gGJMtbIDCd|Yq6Q(t*myvO%TDwi9PzFatw<AC!N;QeGd|DoX5~(
z=OXs;etgWmJTyAgVvI@O{kF7Q+5352>HH)-(BF=E;g@Hz>ysZj<b!KFKi9nR&##~5
zG#-De%Tbx}`e6qC{$ZoB^}D_2{cT^q+2rwIx!&UF_n%(l`{#Dq^!-Z?PX*Bf*v;{F
z>({&Daohg<`*r+MN%v85;qus;^cdFf5NY&?x~J{^7(&1OhQ9UckgfX1wBP^ZG5lt&
z{^4&vZbiQP&FL@8%U}IP-L_?nx|x&<kYNOLpDpPiVh|A*k`xaVQ3~RQ`Gv(Ji_O?)
zk$I;au#bsqeZl*OmJgSQj0@$VwUz}!#yQqTesrNh6?wS%6uViE82dbOA2~hEH|8(A
zJQF$5BvEFlo;dGZ&x#NlMG6s<*?@4G2r(~2p@r(pW%=PufALTB<%dTWsXMNBw-LjO
zB|N8Q&W^)atJ%INMJa^TrbRi^TahVCeQZk_D^(hz4Z@<5?5xDJv40S8N}+IqdoYvd
zFj@sBS->;AB$7dn=_DH%lom6|rj;^x*m1jc8)hHl=#DAtbpQDI3P0qVZ`!xtD!RYD
z>2@MEI%W+(xy~ckrIamV&#c@OAP!<oNMkvOnP3S9A#~OGVf7SH1x>9WVsfe%cNTTp
zN469DL1hy*ijiD-rV68IMIkyQySli~+xz_b^T&Vrx&NoX8^7E?%t4?SPE5W?NwUbC
z(~(LwF;44ZaBj&`#K^Z46T9_sbk!2VPE5lsEHi_WmjXy8s`w&p+e)iQg3lR*Ic=v9
zGO#9gRd5EQLUU~!lR`l%Au0!KRRSoiWUVU8BF~Rc+lkIE%gVlNWz(rt1qLM+We-4&
zvmZw{qDHIC^^^<}S*Qq?0c)&>{Ez?SUm4JQbQ@3xl@MY~VIp>AMG>8rt<u~>(jBO&
zA(Kmq)JELz!FqmtDYO?3s@qb7NSISr6`WBa02N_OB9VL#6vkja!EZ#XL8$EHX_VqU
zGcp5nBzMb&{Z8YEW16vy_vq-9a{$)&jPAM<(mWAt84M8V%)(?5x-Asq>_`ZhoC`74
zhK{_L1ThJRvsAZ{sD&*{je}eUqfXAcP*N+xazEN>p%Da9pQa3gZkySN8B!&|YCD%l
zV$ryt+QaOYOB;EIKGyvvLL4JI<WyoxTP(p%lhcQDQT2Hg77@)Vmn9N8ueMJ}TIpuN
zsVHH5Mq$104k0LrqU)OTAmY%35`}Qzp=*{rZWPSzqIA!FPFp`MpBnDN*Kav|DLqZ`
zKI+pVZt{8^Bg(gbz8!xQuicba)o+#(lfE9uw&?ToBKzeS@%s82->%=RZQNzOjN5$o
z3x9e#t;^~D`MAmTR{U{&IA7%L%e-IjLuokxWk8z0>-tOj;{A3D*)DCVWBu~QQGHOU
zA)HGXIwlK0KhSCtG`_rdzQ`HvG+Q*RjxVp{w(oo1-^TTiKV9vy{`J4U{KfY#=W@Dy
zmGa`mH4l<Vy?7Y1c?6D~>6-jZnoK1t2}?rLj@&tqJ84dNq>|H5a~8S{;KS?M<sxQH
zZ*h8}xT_S=B9ZRKUWOeQM&9Na5wXWI<tea0I_aYjQ6ciR(MQ&Y;G$9(!t_phM$V)n
zeghW@M?#rMl_EeX0fnrKJgoZiBB$@x{Ql2~AC8~?^yTd}J%@Y9!U)M0mU%g)lJDZ0
z;*k-;A+l0hEA8S#lu?UBk`QPm^sLpOEJ-cXGI<sBBrq?$HRRzDga+9qT3WiUnL<92
zRRW&Vjl+kHdDz%HP4`2}^~-^fo9~6!WaFCKsd1lny<)$~y`%D0o1~d$dpt>>-0#p$
z70xUGV+S=Nr#KLWI1>aZG<Q-3pgxjLX;;z{XQJP^D$O3tS>`Q;4NGNA%S>iXMnaU?
zDMT37`!VM0ZT#0)`w#!+?H@imQQ6ItqmoWg0f9j9a;~K=D~VcPwsU<t;d)0n2Zx)*
zkxWZlBf6IKF}GHGfWpBrq8?F~A}$THfSSmNNG#nvCLgWF4DzVUnTs+Pn-&Cy;uuFH
zgD`Sf$E?)>VLq=dj>~zwZ2b5v53-!sURx6c6AMiu7OCJKuo$`Xi4d1XbW@gxvQC^H
zaC{}IBn%(&KmFNXrN!ayEXBE$>hq(@8HIF)MXodh#f#K2Gcuz}*)j%*+=C)Kga|aF
zU9y<yrfu#joCPdIr)NDrND)d9CI?4BYoH`d2fIrH`Cv$+(Fuh~DTadpmZUHV5{JRU
zp<%;3)6$&pF3f~I`ZO|6N%*WJ4lk7SSpCBaL%4Dpl@smVOW+ux3ylF;V%~*l)C+7R
z!j)k`?!-a>gR+Nacm@p)=Il3aPn=gPT1VJ@RJD``&{S2AM&nSTOd7(D(7RcP+K$WO
zcMIWV?5J%<_ynbsk+fF4QcR{mT?$7%Ewxqu!{?pFY>w$z#D-OKnv+sl!GfaMd>AXw
zxky+zN@KTLRU`~X#C9sBh1@N9WgSZ%j5SN$>I5^17>YhDu^)5Db-Tz3Iq&11(WHHM
zITfcP^UK%!`p~v)g!t*z-H(Ug@?+O!tGJmwMq4-jajT&%`>XHz5Z~}}F2$0@b<fB3
zLDhHe1$W<Xp$~O~^FHslyuaPG6fQ#dz7J#d`1Xh8<3}0e_+n9HdsJr1^Ajb-*V{a3
z2<Dgfk1sqf-#q@?|L)~qJYP=9y4Cwt&V%A&`w)9aiR6m3kS$%qr!GN!W+sINF=u+r
zh{b2+cw<eeO?U$9wB39u3-R_1hRgiDY@cARYG48^hUMJnl&tHdQ+g+Ywpv%^<t%9h
z_pA>(?o>79RQMfosyP!1W7a`vNj>S2?IXA_N1!lSBse*>dT{ZZvQ*@0#p6rE%cu0G
z`T8|%tm}i2$9;Hm)l>#PW-fdjP=r!lR;3)JWx-N3S)itIu3Wf=&GlJ*2cU{@r8E}l
zNpNEU%>x8T$xQO;T!bi*Tm;J{&D{>S>N(Qw2w?a7wEGA+j@$7LM(X5DCE_yP?(>&l
z3P4z97`c_Lh&yrU43rBb3p|jLM6?itYf=%$hU}SzBB3?MARtObCixAh0z-_{gb=w3
z9n2}>Wq1H7!jfEy5U41JLpj99Eca{w{15Mc`}be}u(usXx^O9RbSe^I3@EXa3xoyT
zkM;2s_jh(8DYTG#s1y=qs+paIj0!{h7)Ext6^?!kUQ0$_g_zJ5HU)vZk3Pk02y0@{
zvXns?AJu@f$OujJ*-JfVDruft$s|5$aVv!L<HO_A`n<~dQkJ!}?NQaINb;(22batN
zO*zt2s9($0+P1Vyilo)E^e>?gREP*BoBa2G{x2tJKfIned$zSnQ>bS*N>ycN=A(y2
z68S{cKs!a2X}e~skOia)3)YcRW+^9VDhp0a1BoBOMIO!!B6E|A$^c51BiT}Z;BgHV
z5GPwgcs((t@k$x2#UjY>u!&%Zx_gLN4+<X<e)qk*&pFdbN|M$>Kx$1cr|2KLNM%HX
zz?n-_H?P{3W%2Y>3|==(51r(}O%*Dflo3LxMQkLCN)FMoF7SgiXHq6bMiWt<wVnbI
zYSoydyAr2?S0-om@C-o-#&P6!;pN1IHQfQHR@w9Ea(|tY*Qm9l7EjvvyjV!R-F&}>
z_fa`nRY!Ny5Qzv<A_o}rZlLDQ79e!TN^W4T<};S%taP|}swo~2AMW9K`DUB1u*12j
z9!u=L(;j|$(h^sBAHJSRFYS!YuJbxRMjhK$w^72Z$MKr=hi^|CojzXGXC6g#%G@<L
zJ&)IMt*3RNCpnr=_qgBMa#=WTf_9j9tWr~J!TtF7u%kTisp^&%Hs+~2Z7b1!kDYJ5
z=HpX&eBeCfazb75mmkOSXdk~E|Mm0iFZtUap8w6Cz5M0Fxk5`IV&>Y$=h@altNAna
zcgh>eH`<JdBdPVzR6uC3!5HvNSZD!9zo7~vuCP_)04m{Q*+drd$+-PORoz?8JN3`z
zWuiG2TFyl(<O5}6VrB@a@;$SuJ8E-U3O-ybMqzqOebgL5O!JzOh$3{Pt7as#z>KQ`
z!Aj%+hb1AUDF?Nh?M(9YVDzl{`g{hx9dB=5IRYgqsT9gk6skllMp(#&mef&GYH6UU
zlEkG@rU;)($xcbJR`DKKg+bo4p16~R69m!41G<GO6G4KXzm2=mxJ3^SfxCCRAKqz>
z5f=D3#@Of4XAs+*?k<I)>vXhAl(Us{>sO~*O4Vd3g+UFR3?)iTxQGVWX*vkN5mYJZ
zR}zDOj0&QRhytojlNg!X8MilTkLeDsEWu1f!Z9I*GO2)qeBO`G@#V|M-~QD9<3GHA
zImR)KnB4|PI*F#Uuq-PhCE%W|@Uj#n`Oz&%Ni65-{K>|on8q4CVHuRQEy8u0IU_Pw
ztqU`6`g}V3j0_ty`V>qH0veX{r|3uc*dEWB2oFB3<CwHo09sdP5H%^(f^e#*_43%t
z%M+iUUS7_fJxU=4r3?34s&FAlA;>tU8S{GHbg!!lgefSKR45ynA%<|3|LZ^fHz^`S
z>{G;@S?0_LDN1BULgA*u6hawIxyU$n^5G$t&dkKYELw$GmrH4M@Omx+Sqjyat@9e>
zB!m=JX-SeAbnng&b$oz|m&(Qz2Mcq|IPNaOJTYAyiG;BSJdp!_KQeeu4)^IYC8k@F
z(4>%z9ttiiDYJ;t0T7oaK9k9FP|jnibg*!Qd&me9pCb|hEkM>dV46$>i?-2yc-@$*
z=r}HqeoUbtOOgbL9R?!e3Ph?Rg@bwRGF!rGb5GS0Y*SdkG_80}Pq0^)tV~HVK9V@Y
zrB=1u@^C7c_uFm1yOF@d9cEHA?=GyQ7!9!HxzS!M4r=nSrh<qm3(-Nr(<9;NQ^>(6
zE7@Bg?L_2GkdeCyZ`LgPEAMNq+lgMUeqcO&TEBf<{iEvE4;xbJ_up*yFLFO{qrE=r
zvr2ua?>`Q&SAHr>A%$ekQjS}?w(~=+`{yH{?AZJ1&-I!4{!8F&X0!gL+0RUas2%Tb
zV|!XIrQE)1+8mp$rjI8f<n^s@-|I_T`4@G}ey5yco^8LyFF#)Y)OY!&{>!I_zx?6(
zc{{I@MK~oS!w`0o`~z<<M&xr*x@+5+E8NqE8>>5L8pOIH?-nb~;Y89FkC9WPd6q}w
z1sI5<)XSX5ymQb>${jC0H;v!Ra+da3bby2sq6S(Oo*d*!s*{_bXW<Afh@pyJS$3n9
z%%*HS0nuPdE=fvB0I_&d6><wA1}HLFas;U)RnGFTv`-H#Qa}Ir@#XbPFf;@ShfPNz
zmN|R0Og5t;MN3m^8kN$7>e@6jTNU;6nnEe8?$AOM9$;otA+uy5-;*wco=vmwtdHnl
z<_OBj-pO`nAF<gOyAT`3eU6=G#~sj5-ym(CE^MCR*pE_pMl7vTzfY#REkbFFv|+h$
z(||BD1PLLDNNva?sbvvND><xCWUxgD=e#AII1cd2<T5=hnG7tN8MP@<gi12lg&X<K
zq#5HF?e_ljKmK9=_kXzl+~;o7LNWm&)+8<Ag9*f@!K}f6X3+hhQtDc`7#mZnA6L*w
z5|5xN!h+}&`q2*s0-;q(K#|I1<{o`UvmCpjg0X5U)3Ha63M#sIn87e(c{od!e(%(p
z?Y$<bC&M2u=k<kNp85H4y_{2JtEWsdpGXE_P?3TT$eg{;2FkW%Jw54?BAFntW(23F
z49OBP<-h;Se-V@_i}$;PplzH(gix!7r@OAFutZY^!4ynXilkE>oXrYqPzW`5TB?*d
zg;QCzx)x?8;mo31)+C26s33^Yv~mW-1gr&6UM;hM7otc^3L!%TXUq&x#E^=;$3TX;
zp6-^Mc+X$SOvN!qQd&qWsZ>%ms9C4=EC9$MnwhG;u62gs=wN0d>E=dUn#Rlovw}FC
zkT7W!JC(wWEpA6Dy}g1%qzH@HFc{9K#d}gA&sHo^!9%jtnl4LoGnHg63d&3g4pq(}
zlgb6OdoA!th_#g{G(t9NC-e`$cVOh+qblNIovQsly%H5=Ea5Y=Q&6A~b|RiOMOYDH
zLs!+zW6-f_1;L`<y|>oTQC4_ntxi_%hcnSu+froz=+@+E-A=LDkoL*O`#68!_)xpe
z*V~;x(YNQa&aE(CZzd`a-?it=26nRdqu&O7|IK>dc>)H%z2$aVE?&O;+>fHc!p?s6
zbnob1GGtpr@9);B`?;QYi+d}G&(}e^od&1pwZFgixsz{8#OwU|{eIMWd0774zkUAZ
z>G3(1bG4L;+n@`DWmT)IeZ;Ci<@Q}jhP872WBSg_6H5|zDEB>N22r>$FkDJT$_{$S
z0?DZL3NJp5-+uuRS-q^ZjIx|dL#oC&xBP=t5N)CXuGQY5WVC7Al8SI9()_&0o~oW}
zrJI$N?M@O-MUz8V@&igGhf2lqm2ef}q~x#+Beom?zflsUW(*Q-%d)iK)6;SA{r5k8
ze!b?*1f>tKrYA8cMbQ?Oh-<<tm!?UVvn~QH^eaI5mi!HAC?}I5Vb~}-F_TZ?8acxh
z%LTp@gk9@qIeO5V$HDVTA(`&`ZDh>*=r*UvVKI(5kG$R9yN!jUM-+2U7j~vX;fN?G
zmvla#>gd}hxI#63Yhp?uVac*^aY9LzupxD&42Mu+)CK00ND+=H?wmkjW=Y1cxFpqT
zNXdkT2nj0*V~*+bddKVM_y71`#((*G&E@5YfDzXW2Wt@^qo`7Pw#N$$Yg=h}o_TvX
z_io_~VxOG>>obcAPm&fM=rMChIZybijDVh&Z9!748~EY31NVppHU@~&%lcH#oerW_
zDID%hY*8ZE=8<5~BwZ*?TU%cqA0OzO?>{}9PUmyB%lFMbf)}_Mi-(lritt=*_!yO2
zwi2bSD8@X&1W|I&rtBW^oEYRH|I?rUwI-YQMGKLXMhYh>bPQJoGaVC^VG)-p9dq=E
zU|tQ0@YD_JM2j}Aj7ku3-L|3y$y7yHITU1}MeG1wAdKv+CvxK?;9y5mk$Fp>UW!h4
zRb(Z65M?IWH10Dp!pYqb_kKl(O`D;MjX7ra;EYj;wQ#_r=(OMxAu1!Ia*n|%0v2-j
zk!4XfFyV3rB}AZGVlb(%k;cMR;zlW<86#Lq%w8eH>Xs@Ys^POaSP2WHr?b^0D^)09
zL&yO!DrJ25vNWseSF|))W$)oUg2W~S5+aD$*3%Cs+V}nId?%jQqZ9GUVMd2JG5QD)
zb0{V)3y4&Gr<IRc)MPnzBcdb^D6pNIXn4kL#=6$r-BYT7la7e};Kv84jov@ccmk%q
zJUz&$X5DM?on;iiE!X{irLL#TZy%RDwj&BJl+G^?O>XVp+cnbPCtv6!TRAFD^Zn+B
z@pje+wYR)~z0LYeCnr%*`n}Is=_KvMb>euR77IVoDkW;m+c7WSw5?>`r(J&h!{@PF
z>dPhG-;O_i+}k(ho9`cf&~JaYl*^0y5pE#zd}2NjQ<ajJ<da;#)~85bGW(pxN2S{<
z*Kpb>r+L8)nlsEoR1uI&uq=?6V5R<^pfSX80ynA5Wo@ulZ(JTK&lKu=E#oTsn&lH%
zJxD;L^*|{e1})fQsXi*p6}rZRVwy>fD~Jh}<cJ_H>LYBrbu*xD0ZtN0H`nx$F_FcH
zW8O%KxfD%ZJRW~@|Kp!-KfWFJNg8oaDqxr-CsEN_iJ9}Ea8_E&N|an0A4g#U4-zFx
zfdwYk$MC7gPL(i{*+`c_VGdw&aza2tJdL)@lzk@>BY0|tnPEgm$31gDaJSeyx_dmG
zBQs~Fkhp|osg$~nkzABGgrl}%Jv;m)*-n(KN-p9Y0XQKEqjJf<y9A+TCRu_r$v8Z9
zNgzW4K_0$5FbRUw1D+|KvLu6f1&7aZ^!WLY@Bi@+_rLwa?Q7!b!R!%1r;QOB3^+8)
z`T#eE(AYsC3zu4aKQuFD(9yZAo;%v=3Dy>FZWM4c#Po>J^@Q{aC&{$%+wA?r?zrDR
zNChCRS%fhd6bxYGEZRsrQUH*;hqhJ5DBJew`BE1D=K1pcd^xw=+OqJ7T4-K{lt2Yk
zkO2+1Zazth<g&GLh7^JX@sa_6IXYOGZ`LCu<p2ErUxf#6qGQOma;60&idB(G#SdH8
z%HftMl0=jt99B<CTq47}C+fB!Au_7cvX&D?)+&{kQX+1u%*&<}t|`vwJ%uyLb#g2y
zKsaF~9ONFvNtH3klhfT7g}I9p(%F!evd?Lu1987YMep~>*(sP>C2`>d5!c3=4yi&`
zg#{i!We*<|j7s~=m;^?vY$LQ%bRDyjOR&$*+Bh>DnS`oI245|$u7xM220Kv@QBIOV
zbR*^6Cu@Op6j2JQGNO?<P-aaYR5YnL7?Gn9fol4g6bN4)p8xDc&qx2;dmk~vnH)oJ
zU+<vIZUYRJ#vy&5($FC7f%l%Z_$D&CXH~m1%u1~bmE$PU><ns++F9<sRq10As<Xw*
zg=6x^{fZ6e298JE5wYW3m%&SoueaNsj`9+pzI%MKrF-6cu+sU$r&eRoeLwj85j<m)
zI*MJ%&-eSQw;$w%Trav`_uXTAYG>Pgidnx~>MG}x<Wanhe)!|V@>nshIgcryNbZVB
zqINrOU#G1neSBFz|NSv9<K>6b?;f7M&-M8?E&o)u8rV2)Sz4BsmRJiLk!FI!)_VP~
zAq{a#RFJuxFguq6bOIHNY4#{~6BX1G#m-FVHyC4&xD^xU(?zybq_n!Lu5%WiucDK(
zs-~62?ZW#}^qrPxOk{%7OQ|zRk&{-*ThzwKor@5h&D;mua9#<AqcTO%>gn*}&@7A8
zMhS3BN`NdF%AqIN05Y<&ve;2&=N0qmpXcWvfBMJI^VK7V%Su`TUWl?n1rA?JmLf{N
zROal|3c^?mRp#0lCK+%A-B23m4e~(Y6uo#4U0_ZGup`}Q6*r1W;Xx%WM@G!>BM^QJ
zzYiZHN8ozwgC!5&=e{6<YT9%PvsnO`6Rg<meWtWksPbSbWr5z36b?`voQ2YoIIw_>
z(^D(i%t4fqBe>R>ooG!qO6BMzg-fCj)FMO)t`u2{j4xTK4_x2gfB(np-~7Y;X&;l<
z&<JBHOdKk#s_tF~B^{EY3z0c1v%7L}A7!ad;i7S$Ativ6Jbl}a_bDL*Gjv5Bx~?*2
z6LuzT>UU4(x!cGLq6icbNzBM<VX)KbEN8*U9T65FBBzi>=4&nIwLYBu)2D~$Zyp{N
zd00!U))W~Pim-H=#E5D0$k{Ul3kEN`h%+j|QyCzR7P*5q&o1<sX(B2Ax8M8~BYB!9
zjoZF9q43bc6l^}%#vXHI7Gk5E2kJVg<Z&a=%p|ZZXUb7R7L0oNp|qt)Xr^E%7G<Rf
z5(?Qc1Ir3=l*aU{J+aCCYwAYh2HrA)<A&&*>fQs+ft(%0>=7ZlUu|Z$a_bbGd%xdl
z1;ywA4wo&CS<R!au)VYssS2{O*x_XXJ4;aqt3HlAN|UgvTF9)ng(O#anp=t(oP)zi
zDMHD;WS7$^GxTP274;M{IiKBAz(ExVVwdd<?;KqzU7{pg>yar-NzU+8l7Y;OI0!aD
zs7aaQ^q1ei{2%|d{OND*U*hiA-%O7FarpY_94MVhrpZYp*U_UYX$IX77NH1D0BtG9
zIOrbrp$@ypK9s7_e%pp?bU!unqYn~G*-E({4dtUgmj_*s*Jb#J2$bXLH*5c~-!O^%
z`TNt$Qrg$L4?bS^w)u8BE$_1%bstA~O*)^bex(r@IfBYomNm+8MJ!_obElVw%1$xw
zui6z$O#@9kb~{PA7H~PP<LkY*bJMQ=5wiH_;|d|Et=_WVy8O9(zdrx<Up;QiT1E1f
z@gVc}v3(czAoR|4MJ?LNWK_FD3v!IxS^ST=u2Pz#vfq2M90B>vWn(PSG)Xyr7HtSZ
zzvta&9JbUvJ*r@7!Ky^g5kyDII(L`+&_+RxSRj00qwbY|@_MqU8I!6rbEHe|;UuIp
zL*bdLxdkm?&-D|>pt?|+Hbe+XLWE%w_hfKgBdfr(E<z9q%Y`wso?#NlO^BDRoKKSa
zaR0kMz1`jj405F?AW@VkBC(a?PRq*44~-@(lQxDcGs}hqnr0;BO;b`&Yz!MF3@6g!
znh{&UbQU5Ch)M#Le20@cBWIu-QUv|Z<c6`f{k2cleRtPbs7()Mg`zUtIVCVj*^-#K
z0Gv6tszoRZqdl`-TTpV#^_;8-VzQJ9f?Fhj!pTywM=f;sl$4kRX31ov2r?PW00|Mf
zu%gs_dw=_|Utarv{sn*k`}Z3n$(;j5;8U`$h59^L7Jgv1EW#|L;iq-UbdLnMPY+q7
zB#1yHt8j*YWX|w1IT}whQZkelc&L>d6Ys|zZ06z0NdZS~@TT|`30vQ7oE{#I-3E+Y
zl9f2-z;a$H({^d!egAxUdHGcP!<kBzy13gT;yu#RXVfRpZZ1ZHl*?Aj%2XGswXpN4
zF@zQ9H!kd4I1tz)h`GuC<%fTr5K7NLZR{k7aNWe+i>Y%~nUrXYB&D|ML6QkpRg8#@
zcp9~WrIqSJQO8|QMQb5A3y~-niCQQ}Hl!}XhiA(eX+j>L5^aG6&XP9a769a&Qphuf
zC=_-CWq5|Pt~&N3k#`dAar6-xj*Lkx!I4;+s3}*UqnH$mtOf7_W+V^E5$uPaSt0Nv
zJyJ0-b=pxDo71R7A?=4(0vOZXCd!sRI6W2;P+}C>Mwm`xVdrEarA9t!DLQzBB(sc}
zrG#8yPBX$nDWb0ZdTn(rlIcKFd6K{UKm60n%g6nH{pq;N{eH}uhbW&Gz8O=kRlt#Z
zDudNRPK;_bEJ)K1S_mH#ZNfK12AM$`1*sj8OPv9_&3D^QTTpeM?bwlzWm%4G&KW%P
zvOE>MzUghIWv53uJ=FTO9VuVO@$O?i>!q#UF%#f^V_IvTYWwN;{7z=az_(?awE~BF
zl3}xls6opd(jD&ha>)#y6P9D=@<ja64{IMkkK5&GIae9?ap&u`53f=zjy`Ux-+puY
z-~IKsfBx<1@lI{c`B{$UJhPwmY*C-dqT!F!UPgxO`*dczqeSRtV_WdYtTp9gv&3;V
za`sQ6H(na^WV_<)Tk`NDN7gBoq&4xfa*)D6=q%z{C}*&S7QZbVA0)!MGL6v9a`A4J
zu2dGEspAXyggMi0CJ#zIbO8sqmg%AsDC=?%uM~Si*@H78B~#rgZE_NoMVNsUN+Ocn
z(N5qgbxoq&KTyvSgXKQk(=qz(KmFsCxzt8Jla&*y>40QvBQ6gcU27$*XMu|lFDe{F
zLbgj&#tF=v7MLl_B&2|r9AGHLM~FC|)kaW`eu%C<&8hml^Gt``#@wwOW5m9H8L(r_
zAhR$gL@uRKTjG9{R&tmK?@63&<gJMmWhsjE`;F_?vWpmsb0X&<g(oGElQs~ugt@C3
zw59a~K*6E}r8MVOuycx*f*jJ;04R3{-#@>8ew+XK_iz8de|Y_}Pg|A*7>Z#G_CjeI
zc2K08SG;!;S60>v?`V=;P>@uMge*&0#1AhUnGq2qhjkf|LqZ8;!KI!n<<x1nMA3G>
z4}<)wtV`mc(?iQaLK%^fuv>@72nm}~rBJIIm85m!mv0|m&f96R=aZgJN@A2HYK{S(
z?ufk$^&ZRw()GMb)RN_N0yB{ZBAL_fpd~edLV0I8OHQYh|M8#v6-!8~FtBD@Hj2Qy
zTAvl^w~>-%rYEtQMnp~qr>Eu&sl*WTFe@yDNbA<rO066&>QovS#6jn8G2dWCFeEu8
z5fh?hbCQS;N&`<Cl<urBBvVfWA0tH4a}H;>n8nAiIj3olnQqhd@XS7p)*5q2=!mtX
zm#txz)0*fpNZlbK(Yr7gtvPK#L1xaPWm!Do@`S#hn7mZ5q&6OrB%D!7LrYJsN$Lnm
z@k}brOo&G0B$&Psd|l|H`yw@?6fze0k=oGTIU<DM8KYAsGRJ-pPE_aR<^0XR`Zo7p
z?!U+N?LOa*PPu|5Co!}b6G|Y$<kH~1irOlx5A266n-H@1!iwpQ#TTR&p%J*0Q*zia
z2?<-)GJAG%=JlnW+}3GxDxQC%a%;E4KP+3b%Y%F)>)Mthj)6YJA9%e8F>}@=SkYLt
z$nL-V^8VUyQ}X3o{!OcM=G%@raegQllItrSmX5h?g&{Ka-tQlNTVD8ds$YLUce?i}
z&&&28HO%g}*Iuw~O0Fmew&jQKKK*(5_BS;jAL<wsyMMqRRdVt3TH+|%aQ#GYcV0)*
z7Va@RIdMr8NnPf?mG?j7CQ%-#6i(8XpjG_$KAZh~A6-Hgl&!3+twI-(ESxNB)J5k(
zB*f|iq#%WfqATM@D1lOnGwGZ|>}$49kp>r(7E~!g1<5LG3=tacqG?FS)bP8-<`@Y|
z185T0NW`z03Pt1{9+?G^Nx~GFfS%#*Dan=0K)1G4r~1qP_dk5xq8zg@7UP+`h=;VL
zr36*$D-#QpS#wZHMg;}8l{pzxQ6P~ySOOssp%{rmLIiRF5}cMq&M)EviJ%&Gow%U)
z!1PJOV~%<4(QgwT?}XsO0a{D=&hA)Fqu=$iVp1X(rZD51*Q)6~L_k;;aN`)M$$&?M
zYVq&_OE+JZ{MEdPlbJE6geD7wg*g%d$8wShz#Y#-vlMm|oAY`dKmYRnAO0|3t=!F!
zq2&|_X3xqk)#F~b&79}Gx2=%VB0`kILYqLVxp~f|RUgr>qX^s8Q*};5RhuMhVIQ>>
zp)BOy$;EuAar&Gzjan<=Qmd*)?{!ha@Lb5}Acs`G+ri0>Q$01x^W*91;o+N?^6>Kb
zc$QX}w^MC^5#6p?Y8ZPAPeW*0UmNPyR0?lsc}q$m=7>meGG%14L;!(uWQdT+|NWo+
z8y3kJ1jqt+26^tCmpW%~Q-zrc^TDrS$r>Zt76_ze26<sN8Ahk3;pfE*FH%F(Y26q_
z8bKQK&;;6&%n44BB1B}AmDq_|3esB=lW>*?HTIk&D>KnFSjHsJ7+y?7$F)ypZo@}N
zJE-J-jJ3?ng@{+4L8PnzDAk>sI&vCjT^b_E{dA$QEFw+-O=XEe$V^K$O3k<{aZm~}
zq$tw5YmG?Pvd|nWW4M4gI2@A7#O_3ks;jIqvN6-jK0V1vq~DB)RRvyZnAfQY5-9?j
zPk;5>(?9=h{=?rN$8rD3Zugl<>Fz;=Se&wuFONQUcVPJ>buyI$=2#)@p01onP%R=R
zw~<_RT}qg}?}^ImW7EBu4?P99^GhkW>)?ljRZhC_XW8TZ%dqoCPjtaJ->&@rF{$`+
z(%iQyD-{|8TcuT{-*kT)ce{SgJ08!!dwe>N<@zPx<LYC5c+ocF3cn)}=^<;Gk>ksk
z{V4P0X=^v3$oK0Wq7PfGAE#pu=cBKJD;RMgzk53U%fEd3?Wfl6m2Pc4&#Zb(FH(KS
z(~AoG@HW5Xa_Rg~uYVhCt}k(r;!nZPY)+L&F4LaN+wW!j9NQ0>sP-0p_%BzdZDPU0
zSqsZnRT?v?4)P6i65YXNL{%C@PAbS*BeSrDWU$r)Z6gN2aaex*!jGRU7vfs<2I0uc
zW=W|iDKou<jo=!n0i-Ym0D{jTCNMBzBV!<|XNimiPzn@84IZGK>Xt-IR4QBxiZ05r
z>-B&8{ml8@b2**L?C`Kis0eemwbBU{Rhd-+EJa+FXN`MG5xQj|qT=X`%~OeHhJre%
z1qYnj40RFm3=ew`u;e^Idi%(XRGxkCF}jy=j5O@Mj}(d+yJuoDlQWw!Vl`aHDDG|)
z)|nVp5nZ`8@mQLsRVu1Tu1GTwNd%aZgeKA?h?pZR+?Wf(BOFP@N7537DiX9Lfy9Ce
z%9wOHe){F7xA~v{=Ka5Zo}Z5)%Ql8H?gq*LJ5A3*JYbiXM@7+bE5Z}m7I6<k0ZoqN
zZOf6O=-rE2r#NlTqaS0&n3GARP_nG&CZSnpmJ@8>xu~18Dn-GhAc}cDEt(R{iJ0ly
zw*8}z9MGx>Xny<ccc)8z`R3u<Q+s+YFN>7IMH%~U8AyeXKzN$n*>#l5wv`42wJv1H
zrc4e=+&Gz~lHDOo^x>rxlt3hl{13nRi;M}cP%E7rT5IIPn%BEZ&Shy~PFzC#q`@9k
zkeLArz|)1zi1MgXxL_g5wiUbST8q{qgC#{Y%LO!3B2csqibPdP%V6kAJUH(_q3m3Q
zZB9vEs=LBJJkK68MS2P!9Q|WA9*#aNx*b`=Z1*;<JI$0uwJ1Sh#!SfxW4J|?B(E7<
zSs^iuPC|ge7Lv$aho(@(463|{m^-V*R2H7b3o!z$VIxU6!G%?v-B`$oS~#Srq*7**
z3io56rUW^&Q<efqkZzt~-H=Q!xs*Qr;pOqS|EF)uHu{h0KmOtN<Gpu!>z&V+QwgZv
zi{iZS5#1)OtyRJiAU$>~G~04UOup`Uern@`S!Nb*EA6}Yxz~k@AbX)2Y4xGJ&{2Bt
zgSbg)SsgiKx92?7^;w&Re2jfuc5qoYrOEIp&t~bRN<)17bo>(YR$u3qFPDdB-m22)
zkN$c)^t*U@I;-XTwco|a=;2A0u(z9j-21Tj=4CB={&JtU`PCjC=#De@pZm0O-_1Aa
zAJgcvzI^-i&mO<M{M6QG4vFI~Ytr^SX%!k&zb*I!mG3aFc|Y{5>3-ZEaQ_7*Qcq@2
zg^z^P_{^s@O6l>qTz{lCC8WP)MB1Ws`G%HKE0eay2O7f#RM4jNS~93Pm(?xE5KPI<
zgoRbsQ%>gnkF-8VAnaFbWLe73SUyqC7@@G_n6zq6(80OPn2E|fSV~giM1T>)Mv@yP
zESq(wszRMh2`nlu_|;@><|(LxEAb*I%t^`_#eku5TYt07H+g^mxb-hT^>!*tu})G%
z$tF!XuOzh=Nzy7Mg(QV23rb^J$t}2L;miRSNa52WeRyW7@=PkkGn@<42wsq;V}y%m
z*c7t4TXf6YjG4T@kHgSMb_JLvQbEoW!chbOYs(za7F8I*JN2Ba77r*QEj=Pwgr;Od
z6dYaF&{O1$6tkpAlIlsskU6jk%EtYkpa78)#lggtM~si#*W6!!{QDpI-~ayOb!KFG
zP+>=@NTg6%RLr$iiUfn^NM%xKjlv@a%qHE#I75tGW_MyDD!DHG<La47QZkt)wT;cl
z50EIeIqvRE4R*l7i`48>Dwj+!`M|9Qw{X+p(~X=(>)5vELjJJe+i!k&{Py9~(`*l0
zWvOK()S!__AEp<Ks~L^l9v`ihR@O?TY93rg6HrN3c&Bp7V^59*LYXs7*5dc{kpJN?
z|1G#`9I_PW-~_=iK(%_02=K_s1aZ;grI2+^q<K2^NU54cX~3#QcrD68ZCx8rYz^(X
zAg3l(2!nW~oRUGgcv?nbNrJIEuN8+8rI<`=g2||CoJl#uGhj}lu9~;$RpA{&4#s%h
zhpP7*RwY&>6*F2_1>7frh+SBvD3~xnm<UPbb~9^$6Tyq>s7ipzDn2+U(rOYbXPAo;
z5^yUE9j?j&_hlollX$BY1u2WT*F+Kpk_S?n3&(H~^&;?U<uO2(=)&2vM>su@-hcQf
zmxsUpvy!>Let!MW*Vk8{4m>RxCcDw&qFcHBcsDQ?Uc!Bl#u&~EtZ5N3zZpvMcJV&F
zRWG6#<k4%nY^ekiLRmKMdABU<n3<KZt=zr$9*^HQ{o_&|g!cPY2dh6l<#MOiRGeJ3
z5nX0mM!fIw^V^<VzdYy@HTqb}V%N8ipUgh}_WT=fxN@J_d9+j6yj0_T-`{X&)%B$8
zxb9}GYo&)F!{^WOb{|K%kRNtUy`0bg`ZuS4^)H_K&&o{r5~trKZ)LvGqO_f&J{JEa
zys+#}FJoTIAg8O<Q}maTpZQ#%C1}n3Fg!Q<N+O)|xH^6QI?Iafg5_})sjahC)+UK0
zru~BoAf-e=X;`WLMi?Q@Nhp!bWCnDl59Ev8t|D(yF3d=&&PIM}^rt`#EH+&79#Soo
zt-CNK6Gm9&m<Y?+iHGyk6me?I!G*Q15);L%0Ieb`Apj+2QXnV;B!E;9GY9EHG;|ey
zT%ES-AAkDgPp_}T8P>QK^1@k?%2~CvtuBGtS`n$lA~I8yeFvD>;FD3)|Nk7}X}2s{
zdKl)t(;gxsGtY3(y|;$0s_yD;bYp4)0!S^IFeH(*O;Hj6(zNu&f7WYxS<4n>iY74;
z1khu34R<)_WM)L{z2CuQKaU|nNTseA>M0Qy_Xt5U115tE-ogsl8aT&x9vtr2wg_vF
zU2F^;a<Lt-nv$bObmYRhAPirFb42gpfD~dlEE;hVZcQtMQdmAn?8fLoKnQ?l1R;@6
zbbO7sw=mzsa_2W6DzO=3UkTVff)R4^a+|a9eui8jbcZ;<-`{@K|Kg|be=(pNAPNl%
zGDpryv;}Er5vF3|6wWNzdjd-H$w)xV4WkG%2vq`L5h~*0ac+>SF<9U7bS%OQJJw_N
z9#A^FU%U-RA%x(-lt&dI0IG>X+qygAu%V`H8@qO9E?C(7{&v23_44K2?cK5;COOh5
zNa(G5)xNq?2>BMSn@TNNmpSEJIhl|F25JH#$b@lmqR=DJvjd|cAYfFEci=K0^S}E1
zM@WDuJ&YYO3<xN)35F|KL}3y_qwp)i6bX^gza>nBiBXc7<rL02GkMj9SrR)l%YH#3
z21RwjioVjfz#vJ02PY(p2t;x<lmsIn7qm0LLOB~bg3Gu9vYN)#U|e@Z*|yERcih|~
z(%J?a_U4GlnFPc8-~yx0vaS;05mRCeN5q8ULM}|G+>FC}VzWddWSs(0oQP2twBhCG
z&L9}Tgn}dSbV|13^ui$(*i5EON{x&Z)zr}vrO_dR!4wdhk~phs%DBJpZAc%~TrDF(
zaJ|X)@lQToK0U?z%liJ8@BUMJe+4`yzjhcBIr(t8ST8eAgc8Za3xfeArvz>~2Kh3{
z^}01kSt&*8f#{XQ94c}?%@uk>VRDZ$Y|l5h$*$IH^_r_CzXl|Jf8ES|KFZCs=ye9J
zXa*(Enf0p_OMiMgKjM{gesN6Lrl+^$8Mo^mN4YKeZo%`J-;b@&!JMa&)a=~_jA9a8
z81?E;kG8^ixS71myLamuo{?dyQy7k%KDb+c>$g96c|087VSnxCCrih4d$q%Nd}q#`
z>qLP*7)DEKHaT2RJyz}@@Ri+sXqHoZ3mGXThf3qIUx=Q5ZTq#gQ$OC8S?VI+GLYP#
zAS<WYyqn9lvqS@IgbFbS^zI3I%Y`Xr4`)CJfN*rM6g_fml;RAz5RwNtX6-~VP|$IT
zNDK-I%w}ru2{C$e4BtW_4I7XXrYw?^U?8&-r(rH&?0sZ1r|gbE7U2MaVd0LjLt+ep
z7zhJoVOXaee0wwM0e|t!Z{BRf29<)2kZ4Mk3goHqyhtGc5i*quAtC@3I3I9y%oCAA
zIH6#SAS1(oPRh!WdJACyj}Sui*Z|PG2N8s~0npa>>BRef_U>K=;C{XMBxpuPiNdxP
zZh6TtFqjxYoHhtx;^E${<6tR0(Ag{47yv-cGuMjW`ECBaALFN=!6zTVr{9myK7}9r
zz`py5zWQXdTRgu@jGiWh5}$oi?>;t2w7m(MMvVO_-oAVM^S|5w=AHGf9R&+hNf1V6
zX{v=?1Ie9L1PzF4iaarLR&p+^O=}N@Axf|;-c)fHtVW;-gE7?dVG4_|6+3Yy+Z1Dj
z<c{5gAUx+>N+N*J#-)xhFlY?u)Pb!diG-24FQpzQ`QWop?v{LdAvZUtS%#DvHVoMU
zcI%O=UPjw5D4ddHijt6|A`c`X2FequQ*_77K4YvA3AutWDWeZSkA{f{|C^Ve(NMaX
zq(`*QWp?sDG)E4J$Q3G~54P~BqCz;Q2sMK`+jR$pfRtkOI!_UmwlYstq)6h-@T~-i
z84`LzAVvnvlf+=2z>$3iN~kr&0fMXoUkNhR8vX3Rs6oB;QscS;3>dHu96iFI6OZ%Q
zk#x6KGIjUpIU}ZuZpd70FfkJYQ=W^vA)-VK^X#J;VK-q65dseF5J+fCo|Fk}m>StY
z>AnNIgJIMKVHiWC6wEpZ`!$?_m4z@DN8;8X5T^=U1^0}_rc7IdAl5bNOyET9A()G0
zz5o3F)gS*>jiKLseg3QS+qY$J0OnQ_PK!w%&R$BQT^dLbrjehUEfe)YTQ6y*0^p%x
zP)jNddk2K6@Ypwilq%tQixy;R_=2S|>i&KoURo{XCeMVtx78<42gq4wniUOs1jF4c
zNgkK?VSL@}x{0M@IhLh3T(8@pkzUx0am=&q+_kOfeABb#`<Wx_HqJxfIA=TKko97Z
zHZKRsXzhw`&f}RTIUM}?86RGjAAjfehrjXR=^fnP&)28zD3<SqyCCo^!bzUK^g1|^
z!NJd)woTM~2F7E6`I__c&hiK9$8`AxiULe}zVubEk4Iuj4^uu)l#5`_$w5^k(+HRp
zF+u7nk_MNdrWdLbF$a>#LJmZO0ssMt0SYh+IMNs~MKDIklG!?x3>a=Dr%nlh#7zlW
zxQy#+>*!d66XXPfB%BD5M#+p2L}WxT3|S!}(qiF^07T@DjOZ4@0E|A+w~*OTFaiS;
zB^IdJsoq?#?d_lctSM3;q6;Rjl7v#8PJV%!!WpWhzT=dLk!X=<U>0T$gaHA=kx9{+
zyNRTPdX996QYaLWd|GfYHXX#i?XF-z(XY>VW3jDUT&}H~4l2Xkv@7r!kbU^X7^sGX
z(5>;QYJ{2*vBquzy8>DR5J2iaV_l{n{wV+9Kfe2e9~@6@xj)vIA0Hp)`9wF%IM%h)
z9d3@jP5{iG{0@KjL-~y#q?=0DFXOzbM!&9azPSAI&HmTF7;nK1$*o%l2;(FK(%VkA
z_k<yN0<jLBCP1xtzMxb@i0zD6+=oRTK9ZOkvSc9O#OP$^rm>{Z-4U?dBr^{nBwjD8
zMq%_il>|hpxuOoXb{4FoD>y+OVA;gAJBUmb@8`qq2X`+|)9nYRyD6qLa%Kt(jBbW)
zrH<~Sxq2$-mUBuKxe#*7R3I)egc$(BS**{28)2|2Y}c?s4B+ZClR!A|pZ@0WMJax{
z`gBw<W5Q;~<eh2nND<x|d3bT?>=^1ch7&u3pql9(>Q+I33kMc140U3emncQZVI~Mb
z2_ON?l#Fy(Kp2vA90kxJCEs_J6kx#aAOR7BghnuVGam!oBOk!~XhF{Au7*3M-h3F6
z@4(3;Wg8KQnNkdb%&xKIl*pQUU=eApp=ZcJ!$F9M(HrrMq@<A;J7N-q;c79GWX7;!
z6UuZ5-IMQF4+%|d2i&u8w4F<hrW;943JRPv_2H>H7!ZPoqdSBtqYY}j*GkuhnC6@Q
z-T&S9?>;JV`TFwJc=t7JEw;zrgcxa_NC!{~G=psoOhLgTRA9+zAR3phj%dUp92tYd
zB@n{KW>-r2B<4C+(=z+%n5D&aBjb^#bXOAe+wED*2EUT3fj%u*fV!E0VU1<HjqU0K
zbmScq9qP^DI&aS&*v>{Zr5E?h{;RqZJU@eAKRu-Jw74fmU)OD(@RaF#(et+TWsr~4
zebzUs{QR^wjWDcztFInzKRSK*xgYO~#I5MxZTxX1NVfrWLfyo_@pNncg05eBR2pwB
z-Q(JAnG>7WhL}u*8_V)F@V&N(_it_8_pK7P^l*gbP-)8aHK!SX$W|!HF*|1N17ZWd
zXAT2sG(lfz{|ZZ?R56V(=GZhXD49JlwJ`NPILyVcmhi%Q28?hb^Z@ei$#*Bx;jlLY
zQq9gtQ%Ie1FoQ8WU`a6`8iFDx1hgR3E<A|I6_6w_2LjnPU;+*#=`oRaP7IVB6H-GI
z93$~w=c2&<KmO-8@6U;6Va-BBHl^?+Q>-=3!YO&n#FzylxXcI_bW%cw6lO%=Spbt+
zV1Y29(J3|pfzg8##R%^}5KRNX##mDVkAAtdt?vq#^X9Nz_r32OSqKYI@RY*P_LgdP
z8vvDJU>hKeh|P^@vYcJPrWp?T?&H_5|C@jQ!5{tR!zXY6O*bE<(_CgwdgZ>tP|{E%
z3ZOjO{VV>?hv~)3awKQ=^g6We*XO5a|NFmt_ZMIJH+v|7QsOKCPT>H7H5kOLQ&YcQ
zAp=9yD8${IASD~CrHP1?kR+#EEsGovoENYb#si{TTaJegm_bvJUcHY!<<goeLZK48
zyCHf=PUcX`FuB!!CX1mCijD><j3u!i=hMrFn-?!mH#f`cBj)8W)5tS{8KT>W?gFD5
zF<?%Ra>aQfuP+iCPZAMG23Z}{Q4j>kAk>T~ktcFo!q?zT10V)S@SlC~Ll{nTv4qqT
zj0*|xm=P2rAdsk7A7R1H(HcgC2L-4B+Zv@{6k#Qr=4yp8Pqn0(`wkSsN|Asv8!#&%
z6O71cI3WcPp_Af{Hid7_GusL>)4l_iSTM$5Kpi`D2PD6AlH?7P)zZ@TwP9B8j6R6R
zb1O52j)llj!BKLQ<c_RuV5v2_Sui16G=r3(B0`}tNGhcwu*aYbhKTHK#mvJfR!!KA
zl0Z_?;tB8X)xintzBj3ip+VroI}?W+WU_GXp%&n&pbZ*I5t+eK?w;H`gOHCGFY`x#
z@JX87AGiI>C&z35`P$VxxVlWqfF+ON&3q~>7KkL27>6O&>Si!ha-sz0EGQ({yY7vg
zQUHvu<86#%IUc}N@cL|4l_lT!L5BzILltk+u~&bFODFW9F^NV-IB$K>X34M&&gB6L
zG-w$DE=qdy;#i*8MqIl#eA+@@yew@^up=U{JJtC5mvnw?@2`&=;fJqgf3CVk<F%3N
z03<#v>9fxtK6`yT@3m!)cXo5I?MCWm>7m&BG2g(ZbZ#O~KELW0f%XmbA%({Df}lAf
z`VxsEPdbd>pM888U+??11tQ(NigY;eW4c*T6>KJWj1bc#(IAC+4qzTi@gB<!5+f5x
zsHFtYoNh2WV4=2&xFbd&42WSalMfI?z)o}+Bav=c8sTCv*!Qs$jIBFrS|~H+113q1
zh1LMKND>;96{`=x(U}M+1&7j1T~RL{B*~BqAX96E3W!KR3<v^Xk{O5r05O6HZDZ0l
z^80`Pmw)@kIr_d-M3LpT9MtEU6tUErMN>^AOren~AOeJgV<2LHfSP5T5w3^?I1#l#
zAms2&Fv4AtnG)_E-91gQUo1)Qd3|n*u|c@W^R@vv3b(87I0Qk($q9sq4`B|Lv00B*
zX;#|+Y6EM4v@G{8%6EVBhoArHpMLaPrx(js(>>kYr{g64gnekUrYkgCpWpV41|H;e
zn;-7x+e1#83h-3?E^i-xxZ~-|fA@EP{fp<nf3#f<f*r^LWzNwM)#<d%celemuAL9F
z08PgQQ4*>_7HuJO8e@;1j9CP{5jsF2F-Wr8JLp2;WAu^>LMS*KE1K-13j!D^D5q2s
z@BpI0nRS=~xT!%=H!yT(W|-#Xxa1f2ukLT+<*N_x?r7#X95Zp?P_!7HJkZDkOSl2J
z%(-AsbHd>zSwJv`3xrl+K{se4WFqY72woI}1l&7XcG)>9!QlV?^B*~b3gi20iYwJ>
z!yE>}OtHJ7g1Rdra<(Wn`Wl=;4WYS+7*LoaA%T#ikVvj+LmyOxNiYEjP`%~YV!FW=
z0KkNvCouDdglI{S8MUDUn7bs@h`s{dpsk=Q4hhT<I!4&A82Bs-gR~D_RX1-DBUqhk
zAw)3eut7EkFfcHp5t;+J4G$}sBq4Tp76%v9VO@Iz8nk)$&I*p5qjYSH009ProH3#L
zIKM?3(bd3nSSg6wKr&V?zVaX>K~k9S@q88>281DiY8s>+5fvmh^Ep2Ly^l`6^Bo@N
zc9!*<=gWV1e|>M~rcOyJ@zB;Ik}#?QVGB-R*TJ03$ATggfnf0<CO`liV{3s4OU<<7
z&R0w6RF@@COI+Ta?cfi!R4SL%JhfCYaokSH)dz&!`CL3#rUKx7@Fg!oW4<#?sdzG+
zGi|-xOmNP+(>~UVY;-eD3*VMZQDW1sSZ08}oiFddS-*Y<7e16%$76r*>$a|ZRqo_5
zoNpc;zW-aVK5f&>rF!k_#rw$f5netZ&m}*r9}~YdyrK0Q-v#8$ZU<`%#k*K;bFFIE
zl#W3`(<~Hz?%Ugoe$|{`#_?`)OZPo3cdnf5D@ZpgK{^9Gf#isSF-Xkw?4GG3(|eF3
za)7P@nF26-Xki{h0h=)^Aa!7%8F_au&1&i^R6qu`%&oia&|!2`nVl+7oznX}9XS~x
z0)_7bZNtJw6VgBc5ZA~|7R>G{f*Q%1609B*3P6aGNKoJvq7wIj1ONyYh)f8<65W9#
zMsV7$@%3N-j2sYgs+nB@CC|(K;>5^AEKcZDO5jZ17=*DqCvx|a!3Pk9A*qKr4JB}*
zZWG7mK@N}*29R1;AJ?I-J^E-KV^eDzt?vDCWz1VB3P-6lT8cnkR98aMK<zzJxQi1E
zAO*%e)#LPVO275T|K$(=^#Azzcjbl$P4{wBOTO^q*XBIhWqba5fAh4rWlwi}b6aPd
z@_wSd+C}sdkP3PcEt752eBRPu{lEX}?R9s|Jy4SZdO(o5G0@ukrMa~TWr%1R=#e4_
zylD#|HX~vrGz~`@+D0gJyR;4?S&e-tdCr`hyY)zgI+zb1o@<HG(o#r`nUFizLpSYf
zc;B0v@48wj+C0rL9W#xGhnwSJdGX@z;Ra5Jl%|x5gLg2}P&5q!>)wMXda1#6%961z
zER1C!rJx+KV-k)L$|xE=5DD2pg7yLA$brb(RT2aV|I3d*_iw2~DaM%5HEf`8PO)uq
zoVhuW3&~K=XrV!eBXToHpn*AQLNr58xg=pt=sAds;B)|<M9-9OJge9sN|+TX1r><l
zf*1iwV2qU5V8&=jVl)HqBo$&057>q$Mm_rov=O=uHQ1Xr*!CDHt>$*Vx>A4=0)`_J
zKtRbtm_n=tBb$0;3T5Fy4!s!8x42(J3Ee;#Y4R{2z#*1%3?o*|Co7DZY3;OMK_s(T
z4|WA&Bgzn>zRFy<h0@4Ef+dT#5d=V$bRT6g7}>G8Y8Q}s^j1HdKl|g~onOuP^lXp6
zIKMl;{nh>$b>H`r5Qvl1P^)0JHmSEGBbm{>#26%0&^<*}<66mk8!jMS@pQ`tytVLH
z=%lw9TVTHoxj~)|$Nec*i=E(DQ>6)`bjIE{o_La+TJAg5qir2Ue4-=vv?glT6f}?t
zv~Po--t28!J9=4a*^YLpJlKRW_LQkTUZ2L}o9m@K&;VLMrN6`Nbc7`~-{R6zV{vp;
z$S*&9^+`Sc#ys1xl&76f=;((TX`62=<eb{Y1h5aoUHg65#hyCy*=P<83_xh;r<jE;
z5a0NY?d?1BIc`qyGL^$(<z^ah=?I3Cu9@v?%tZkMX7+1faxBS%c!ZQe&eu)&2|2r^
z6vJv|J3DOw#dUO?yeY+*QbC}=%+UhLG}w1!a*e&IZlUE!OHJ8QDd?S)vOF`L5Gc@x
zXV4_^gdh=#&;$8fZiL#A89@-;XbMd{1zIKngszZcAg2*fC|Zz!IR+px;^boo6d)rU
z>y^IwkAM64&9yKwd19(Xu$1bUlM@w2Mo1uueNaLsP!e|clyYKs5DKI)aAXFW5X_|l
zB=B%bAmKQ{I>K;B8vEAssa-Ed*mCiwzV*?aE^Sq*8XDf23oysfNYqr(0l6wVqNm#z
zn`=m|**|=J`h)-W|M0_q@rR#(#Fd<v8%!bn$*yhepznXZy}NAdG?q{J#a*2_gQhT|
z6f_wf8fF*92#PJ)CF8rEKls`I_V51o*INssPykAqLp|l$(J&`Uk@?2^p3+oL3C(jY
zoHHh-9#AnToa$Wbx~@SWQ)xXYN7|>>uj%dtMo9p`nV68cV244eMC(pT5C*1FD_O6n
z+W;fC;oY4zFao0xI?1+9H_M9`hnxG`(`~-JDYc{-ok_^U$W7~jFd#EQQz?|CNSY;g
zbjM6V<}=U^XdN9v9hsaE$&m}h0Aojih;U*cK#owr%KyU$KSn2{Kvy;xJyK2~RmK`z
zER@L+iBbYdAWSS!h_!UxvwLen45~p4DZ!D2=ag7E5tdoTDpD}9m?MHo4IIQ1;UH)B
z=2*F(p&Yr}kUQqL9uonPw*fV`ONd0*z+~OrLy?ENHSflKy>u&E?0r++{ep(>$f+=(
zJBo-qp#!DJp-n+7H6tcrBGusRFqDIWHJ50H*oi@$1_67BwFqWH?j}y%J1$dXp~{8?
zKtup)%4HZJh>YIyjW<AR%^=JKDYUbM0z@0#06KR=Q+CpizgIu{ul^vxW8d5UH*eZE
zpw~DnBoH0y!jj0jMGHVO>M6qlmO(X5ShDqj0X(HidSCguMPdlTIl{--Pp@abz=(EM
zpALLla%{Y=*DYW6i2E108ef`Gz1U=!(=w;^ZRiXMX?cNZO9*Lg@Z2Ax43cupQ5>#M
zwqw8ao^l)xW#OW`^Q;50KBzsyn>YJaFOT(_%Y5*Jdi>^Tw>CD4R45wc)AHFz53i5M
z!gF%U=fIDd-|6(a=#G4a>A-2}&S`zy?TF|1ew_W1TsGWq7~Xoib*LFpPassr_M~4v
z+rGNo!^@X-f&-0o6P%81&9eR)>cP2$-!Z(6>4aH4IOFbU7J+Cf6b$wa!k)dn#ERj-
z2HKF_9Gue(guxP>5w?_4gqsE1qX$IWZF7$msbWDs<cR|Vh&@0Q6qOK0Kyo7C<}RZP
z;{c+700antpdbPpPWUZCk5a*n#K47OM-j(_kx-jML5PSPZX`@e)DmM0$foPS!~W}c
zPrv%IAx;^@u@oh6DXHSXgfu0$QtAMiAvuwd4j@Nh7|PULDjHH!_XKoh!hnQra~dEg
z+%ISmf~pR~{(suF4GV|9+6ul}>|I^q+5$wd4Pw!K7$S6QfN0R7CE&BdP)90L!5@C_
zgMait{f|HR<L`cQ+NLt2l!RMs&uw$PK3~85)p<X*>0`RNn@%=?GZN+rmisglr%bhS
zohWBab&S$m1IoKReEqXu|Lxy=$;eo8syUQ`Ahw-KWp^|n+n^+YNNOk$!>o7P2PSD-
zw@BqCIj6%%uSUC$p}IpPLks{wZz*z%6+ys7nbzJ?B>xsF1+iqxx-1pCT5Or79s&}3
zk2>XnObp3oNpg40cc<Hz_tWXb$C`55NW?jidMFGJH8w;vX9-G>7s-r38<i?JFf(#c
zcxXZ=qy!oW5s-N_=L92=fItBgAW(GT?4$Fa{q{egn!we8EOAn>BFP;<)dN~^7+BAP
zj0X)MaA~`$vs3Y=gjg5aHx9^$0u+-&I@TEx93%r4$z+HQlASF>1RzqtSY3|bjTwQ&
zV8_uRCh6Bm2$aDaL`E23?%`~q#4uLXX&ZJ47?iBpcwT+TC0rb)D#;-dLE%KDyCE{W
zTgiEJ0_F)Q&(Q*esSPYy5E>{_W^BX)B;m$HGDe6fA*PhIdA6kH!q`?uLRIiI5m~rH
zN!XPT7`=B)Ouh?GOtOo!Iz)hZ1=SHdWJJjsamLSn@73*ZewWu@+gD$$e;?}W<5R?D
zT?F@GNvgYVgB^IzPLYN;r8-k6*w6te8QFSxoM90fhh!RM09dk0g5G;%%35#cB5SO6
z-I6~Xr+HBBX?O%8jXV>ioxP<!uH;Ts?s9s3YcxB`b{*w`v2t23{{DGvvYywtxhoIL
zOdxSVB%p-g6xZ$C)+c+rrzn^gKSlF*+q-kyRyIcWDlhJDKbh`6``|P^yHBA5<is;z
zI;3*7{DAFk;tA`GKmT+*PS}3d(#NSk`2ykhP7aNrY&`3jU#I7<{QSOuvl||WB;U+3
zMP>p6Y8Ol0L2M-6pSZpQUIG^K4O7J+iy7NH=L3!_yLOrYb7-FEJ(L+0)W~6V%xo2<
zLoiYdLV}s>2`QmPTRXJgBSX%>hm;Uyp5Pg{!nb@!1Wb#!M%GC*s77SY1jy!zLlFXi
z02zsqvm1yv6hQ!ia9|EJ5^Fud4pJ;AFpvgC2HMq1A{88vQXl~NfE=M~zj^yNf3-es
z8G%wp$c12;rnFGeKyWED;l<%d`s6?X0YM=gh|C#PnH)WXIz$dE$u)#9QcxkLfdXv<
z@9GpjT8GV{?|o<99@q1ESoa2MfG|SIG29bJ-ysWVA3U6o#%h3kxVfFvAN=tj{_g+z
zzx&ZYm|xFOj;B-?)F-{1$2QjY-(26Ud;Ly&{ZTzkmG?3oFdx$_d8Q;REG#DEDHUd5
z63Y}+sSc>~rpsUd&;PLzhYwR{1^~-*j;;jY5J<v|oKR||T(s>eFeqC0k`k&Xl335s
zy9EJ9p69}~Ye&ow0MTMNWeh_qfQGy8;S3^4JI<Lx^|B$CzC{l(AGkNgd8|94vL_*a
z`Et3tzkl(1KHSW=m22UYYK)yUbU-*D5$FXx3#*k{gc3Oxp+lk8X%bTc32cMHgV2y1
z9TnXNWd;Q!O9-IguE9(a?88&#KmF`?(2@3)ffJblgE{sdNT7&Wm_mplaRdOk=m<h1
zfUtoQiFg>5Tmpn)Mn0BE%v=ydh&?C^X`}_&h-%0Xzfo!I-GLZ4fQsf2&1eA<OivNB
z7a+z+1|ve%G`w{Pu@O$pZC5B`>+2xvV;35|b4Be|i+Jk^rS;3S9CZwF0&yQf%Z+(w
z48REDNVrIVs-OT51H>V~od*a(C-fMyB1!l_5=LTzAOge*$_(H}z$G&Pa8M{3MPPCV
z2z10b@%7xxkpX?d7yvFN5x7)|`IbKUr~hz1nLVHP_t(a)eOa_$`_8Hg$i8mFEFB8K
zNaP$Tfb*e>df&&GPE!@QUc1H~DIH+~0QE>TvlnZuti(9yWn8<@eBN8VuQvy=OMPA=
z9YY>`oGC+c%MQ`zrYkS=>}u;4hU45f%rb-LfalTQZ|6o`YJN3MvZo;sJ8MF6$`$v^
zdfD2L>*=9<kmot$x~)(4v@;#2NLlLb@v{%_fB5Q@(>w=T?i46Qh465T>B^EpSA2KH
zi2}&CMd_Qqewgf?=A8OuxpM)pF9B|A|CR6W+Rq>DV!oW{#gX#NbWWKa4K(WrYVV@n
zBBiZQbT#5L)G6>J`iPRz&mI+EMLcLi<{fi&0N<SL9WiKHM$f<-2y?Sw4yqI#0LG|(
z`BJs-HsQQbDJ7SLjtC574hb&mGbVr#LICduGr6Jc0OB!(100O1_&HKQ03ZZ{5r9%y
z3lM;C$ruBztAm*fbl`*lVOJstN63hgU5!YPfDr*5DYWMJ<=b!Gz6G)+r<x!ya<mcP
z^^hnpshyz$lskcIFd<Hu8QsVRf@{ra9I-<vKtX$ssdyxJ2z?GiryWq5hFJH`-LAId
z-gLCFH&?S^@f-kQgPag|AT|ept@i<db+QPT8Eby^s{W&Y`bWS0-~E@Lf0iF+lI4^O
zK!4Y-wm&{T|Lo8E_5vP0n_k}@vvPD^j#TF<Llz|<LIk@q<p9KJfsCXXhop=|B>N7h
zum9a&{OninC1uLw)`)9dR$?kkjRB^;xaV0^BRFP}&SUIRP+ScPro=vIv<8{sI`#<Y
z>OC+GLcu~1bO>xQeAl5pjOy{m<8quIw&!O_Nf13+Fe7-%WxIANqQg;YalOAg-puub
zkMD18=EK8buAZiZ-hd%cOf}qX(6=0s$Z$GV1}r4bnD0?9<V?7uMtF`G2-(0<1Q^3O
zkQgAq2t5EmD!B<I8y%eaPhNipqnj|6Oua=%Nq`IlBsCL4LDUhEas+Z=AWCQ`vnRsn
zM02czK_yRzY;rh8a!E|J01l|LAOd$}Bq?Nu?k2?Gm<Ga(>LxQnCtN5R!H6JH4(y<r
zS_?#UrQzzVWTRnwx&W|`9&XpZ_j7b07qJn^&>|sN5PE>?5JId{J+LyGaUd~50A~iq
z=!40T7{S<qGf?D7!b!%UL;yunCMy|72O)OEW_Xy=c8v(MfxYD%Y=J?Z(Gew6=0G5H
z4bE5)nnza_9>MMaBVp1Hf3&>%$A2h(ZttJ)`MtMO<21HS*Jy@)*dU&29zrD9D0>4J
zWFN@lsR*=j*{;a>ut>t&raAya^OX7uyJF83Zy-DRbzJZ^YRq{^dy)+_ioSY9ILf%H
zR&uj)v;@SX<gk5o<n!9?^~bfmqiFW!`T1!)?a2;z$6Hyj=STxK=|rchP2u@*+sp>S
z!;9n6mUkC^e?DKZQ^MS2K&kcqv-_LRZ|{~4$GddB=(G?4%&#WybRxDT-hb`qtE*Ti
z_OJW=g7g;n-SFFTebczG6V<{u6u$PKzw6)3KF_{92(IP!z#I@0#WSfpWNqv4o2=i!
zA#<*p=GZIIBZ>eY39f<JG!PG#8J)<xL`0GSVtp!p_7uc7z7yG#<qDSF-hd;j>ek05
zQ5Tt~#66Wo2!McN1g8Ll$V|hAZ~_kjQ%7@8(v<OnR2>r_1P$xL`V52w31TD2K@iA7
zSHO(gf<`!!qXVK@_!f>~2%!KAMT3;p6SF~@BNU?pMo?V%)W7(7`}%6#meb7Im!s4K
z0#Il`7MkxwcgsrvVvH!l)*=G2JKg|1yWB+bZ($199dHm*5Lp6Naa)l~=r#<7X$)U|
z3}g3x-Q#*4tJ$DB`i_vpO*!c1W(*w|BQgt?Isfpxr+@S>{`HUl`RCtzoo;4Iha+>3
z>tjEU>#u(L{I|c}_yNE7lha))tdth1loAOJgn?ASck@6bSAzg_2Xt`cRG7X6h$EzK
zzkdG9zxf$CWI;0k3^`2TJoX)6aLz%ly8)p9dW1EXoPaP_3`g2JNK!`tGbacgW*8Da
zGL`5`3R(qwvVbdkDhtMnJ%C54GMaiIj9BJb2W`EnGxQDww+|2XVZNQp2cNw-9+%sN
zk2PwU*jH(*az$$Z-k}(FPzcE>dlF7r83L0ktjLa0ogAaOVL-y}$V$d+#)w9OVJM;_
zQ`rqBvTjC!g*f<6KK?#Hn3e!f+IbcQA|n<XC>g`UDAh=vfD0o426G=0)&a?7B3I66
zl!QSNCdma2)099-BxCcO2!wcmBw)`jUK2LNESN;B0fJj_t~3J3f^Se;2!W&7V91_e
zC?g<ln^zvpLx+yOcj$SF-nVVzj+`^mMwFS%r<yPX9e@S`Wnop9T-r)=0X0CUK#U+j
zFh~vng{U`Rq{Obo!3?30n|crp@JW0?(p`C?DTnoxYiy>%<R0MyK8LX~&I~}FnD<5q
zfu?Cm!+diBk10O;(c$(76K-#HC42W2z~o<TU((q6Wpt8Em0)irao^1}Xu^qyPE~X-
z1h}=cN~u^TO2HjciavB3abpuFaZDiR3o7l)Oo<QpNO8Dc$L=%T#4_{Lo>99e;WRC|
zL5$R|`ZTryd*gCn%2?sCx3}l7-Y2~|-hB3On6J0e2*_!%-FQmgF23v4=59>Ib5i-{
z@%@TV8|CB?0W0#utK09setCjZ8D@aRw0@M~iH=L=OVqon?+jvGT(8d&s$S!givMy{
zj`32;zRfRlT_ClP3g7&Kzj)K>R$o&--4-4sHN_WUVtla2E8_+75@b+v$PL;VrE{K@
z74#tZWaS<%t8=Dt3F^7rm>ae*$P9*=fXle{GFCivheHeyef9*gcVEw@=)CZ;P^}e#
zgfg0^8B`+>p#fFE2xH=v_yDwnDMC1M^BxTl-&4u}4w2l$S_lni#wdXUfDzd{AT-4i
zIspIwJcbKK1Uk{tu>?khFt(5x(GdY6VMu^U0%vLO`|~$%B_R5a4AXQhq|3A*MWo7f
z821C@l&w)_)`l#Y3nE}5vW|*SQGzgnsTg1cAteeLfovK>Y~S}0s(szKGd3U1WZmt2
z-J_IY-6Zd$L6+V&9nC0vDop9P@IUzd-~Gv-{mJ)#|NgsoSSxc~*q(ITx5xA2-~8w6
zmz%!)?fj$Py*+Fddn$9zNE%2K;eg-Lhy>w2kYI!Z1Y;NkMyN13B@Yj$?P>js|L5Pe
z9s$lkpgVR2woFLsl!d{>Gdh^7W2ACJeE@_zW{EL2Qrp0whjk4>5u_OPFpsrS%}(a~
zrXn3dG#r8Zn3u(eXQn)t7+y-cJgz9I_s-1DQuD;u?XFJu#}_Xj^3B7`Q_;B~N=>nO
z%D#h#4=DS2_snGyL6BO6bK*fF;X=ItM_30y@RbO_Ay{b!HZ@qBgP;fIWV<Gyn9whw
z%!L3}2jPG9>5qvBy$Jwh_U_n*Bw$nYZpvgHuEYg{c-t`%AS0n%caRCgm=R+@o>Ec)
zoF+)ABz0yL>Nwpq0<aSjNM<SSAhBaQnKl%0Rv<ynfJ3>36=F5W+|NOThKSue2bwjL
zu7JdQSLOA(uN#@ST@9#q9|I!;hZ+G0Sr0^qnu)|Dk|amcj>H<_Ky}WhM#Yu{5r`3l
zHqSUlm?3j$q(BBBc9^CBhi$Kg9RV;9Ms$ZF6wW#%5Fj`LM{pHqa&#Y<0#(rV01X8X
z$X?R&s^0(L^W%r7fs)>>V~n`M<uBh~pZj)>K8OnV8BjothwnEj_Ent|2&C4xYim@n
zNENG`aK43hiP2Y@(7YC`*8A$aV!yeoc|+CyzU`KGfcok<$Bx^;guw^GoNv5euh-`7
zf=_$Wn`t^Y_qKoa_~sg4Jlg#6{qY7L?0Trxn*(v@+5Ae3vBTEMCYFg9_gK%Djnol{
z^Jp}PU%kBl;p_W5k#zKKP%uT37ijV!`PZPg&hKrT@chR7tosY_f%52iiOW&;cY0@0
zUe2c(%LO)vufEtWFY)#U55C9^E!U`b;u}0E(dOCJXHxR?#C$V^kSBvl_AARuEF&Lf
z-=(aYkLa$=<<dc(sNOqPiroQ;AjAW+!2$ajOOH5?VYsf{N+8kkt`x~LWlCWx5eAxY
zIApZVQ2>D`2OvNW3}}Y2MI<0cWJQ9ODBgi(M{-kVcbFy4P!}>qL2~v0aAgv6Aavr{
zk$`AWWpJvMI4}$}+;_nMKn8@AfebvDKq}(=vi;)A{&=l6a!$(>nYFO62-h-XuJ13X
znI?qk2!4WoVME9Tqp>oSg2QkO<;rRi%0Px5I)sI<+X(GO2Gs%g&=KL!_U3uNK5p+j
zT*ew28<F?j00G3fc7!_R?|gXo`~Su7{N(@oFF*f&{pb{_EQu8T+|PRc@{99d|EG4Y
z@co~hzW?e}SBWHL5g3S$z@duJ&^S~h42F3aPzn(=RTKzB07!|TIhuu@uk~;KAOFky
za{~$Spd<)|Rbc=#WtqDwharN<WU661W=_SIoB8R@s^*SW&}5lAW$zsfvLuc{2gx!E
z`{>zWnA>!-_}*QBR2h->KE{Y3bm~lA$hwDckqKO1JS?}bjyDhW?&Sw_@dNE?A?%xr
zsLk3ggX?HJVIqQ(Fi}vRMRw$ju#+U<t5Xq(a6lRX0=OhN$6$*B@r1|$C${$%1&Lr^
zfkc>t95exS{<BYi1c-K+VC=#Sgn)pz6A`!)<%-1-!F9;-NI@7w*t<0d8<|-etB>v?
zA?$|=6;m>m1tb%s#F=S4W0@$F1j86vgTW$6x`v++t`<PnoHAo4h426ym}c^Bz+e^-
zv8S+oC<Vm6HoIzf*bO3L5chRJqLia6=E=at#}Ef(AqoH%Mye1d)IHFp1h?)92}TSG
zGZJvY<N%72D98$iVFY-F(F3!ql|-#;@`;jj5R(&&S(srVMoeaYAVLgaZab$0Jff?=
z_;l=>xQC{SR$u4u{MUb24u*P(t2w?s|MYrkxLve&=rndN5(aKPI8U>100S~tChuxe
zc@l!2fl|SW!ru20PKBc<Zhb$r>3~J&iTrH)6Yb@oH`9DKFC4TIGvGW^oS6lBTOW15
z=vt2XF3m;rzFp4n{ymJFWqvt79ORyZLu@;1z9W5#^E>c)v~^RYT8qBJ{TlDjmuCUy
zNN7*PQ05<f|KZcq?fsznMeI+JCohPnheT@>!8hvVnXWa)b%&X+;q#N@Yq(hQvmU+M
z_X<%yT_aEOv%lM)z~Po&KF9s*nHQByc{a$`up9I47e)OgEccNYxgH`tCq8i6k*Yb<
z<*nyeDFx8cw{MaMkOSXzDCyF2U7_3#MFJyo($3_LqMN1nzw|V0u%;6iDrrm0iBO6@
zyD&ml7%ALsV3;a$G~~>vh=Bw+cDLOJAq6r!3L-fc=tTBq<P!pd0|B}>k<@&a*qOu-
z&>5^bO~93~12Ym3<(weG2@yfuhGL}%lx#-C5FrE+fQg;SN0jR?ej4W?bDBY?iLe$x
zAhYV5*8}O5jtf*lb{RV)2}6bk841ALMF6RK2$BUB-#en%-lI;Vg(K?r-VCj`%h%VA
ze!lL{7q?-S_yX0ft20+}D^yCIr%yk;`ThUupZ)fK|A(J{;I|8Lxn+2?c4=pQ`t{$v
z`}426y{y0aJ2xLJ2T`WNizISHaMOSYG{p#q0PrBU2vtO;7>)q|=pLpdfW$$e+SXCt
z{rUgrmtSt8350ap!9!Spf(Zywil^mPVU@~tnPb%mAd`KEF%&AxVL}b>!*!D>7xc7D
z?%-<MYS(igV*nylGL4*AT+lhEWMMreDWIG{Lp{g^-11ybsUC!1ef0YNetvzhTBb5N
zAVCz5D>V1m9VgPW6Q*j!hkQZ|LrI_=but2>3}X!|K_mPC1Q6z=!RVUFhdVQ?52rlG
zI72!B2cn}h3Aq~otMB|40x_cZPFXUG=MsIS(ZDz&3OMQ@DDIJCA5=pf4MvtU+>x<6
zPa?zTJg1q5BIO{8goVhN+?k1hg%JlcF&Dr92&M|Y0U`C`=%|Uw$(rS(AUSrnJ=~zN
zqtduIa5(qvIs&b0BL+*YX*cJ?<lP~Hi=tyNc^|o?G*wVGYn1ZjXnjBuN|PWUy7NQ=
z5n-OtyBGpm%qY83-+P^B9qw!?K}ulOvE&FM4i8a9tS~loz?vZC-W9=|tAsfSCN>fS
zY==`qh4tK(2q&%mdq2E=_(#7X`qgNLy|use=4yMaS9{)RG{xW^F%r#47#<iBK{{4N
z&p4BLxD2L65Su`M?3ro6zN48+<(J1C>%3oi9nV)!%QWAnJ9wO6m+27NcxHp{Lt{(l
zZM)c<mpiI{V|{G2zJ+Hh<%834k$YShS|FJLj#_GeZmst{wWh`?9YHV8aP9A|K49uK
z)9z1ST)p1pAAbM-)7!&s&>&ib^6Sv|IDeG+8PX}Y)?tPB+0z;O!uIukxRq-Oc~(B?
z8S+f@y_ca^{NiW$yl-zd$mlN~s>NL2$KfR#4R~bFRyxGNRPpja<vrbe=rWTg7fS??
z7PqGRf$^s_9TBg6KjPjZIv<Kh?!kOLOMOPU9}s97;7~HyKAyI13$8Etu%u-YDbz>8
zN6ym#A=^VZAcirFM*a-x5N?bOk&#kZw-Aik;Q~?s1u&QzVK+}6_sq|Rlam1E(444o
z$`C~8l4UrCs?-FG5t(QpCPGw0;Rr{F2x4aNh{Om02nZ~WhUO&bU_KxFoAvqEUrLBj
zr1@0xNHTFrj2?n;GXbE)PJLKL<V@on<tWjN1Th09=f<`=O<0lv1P#eI+Rr$`$SrWR
z{d%>5{&-az@p#oM!QQ-sGNKZhv!ygI^5gG)=MVq&|M2}k`uNjREb~OAGQ8Wbv_1X$
z`KNCme|g!`$J39$cl$uKB$*GX1Y5Xw93BDg5b9v=X2BT|1A&EM7>;Ib3nYY$EQ}o_
z5s>L+?DqKQfBP5z`N>&PeW3)QC1>y+3^~o(I;>}e46S<~-Mvh+jfN8gIsy=i^)pMd
zaAuF)9kgH9<``z3Lx(X*S*BQrgC#JoN5YX9nHeJ?81NDiG!wL#32vwP=Hc|>j_)2$
z8RT>XbS3n_5fQ_P3Dr4Sk191);#^q3JFo<F$Vt!;JDL(2Vn(`9lF$}75e6E#ATyB@
zluZ2^;uPYn5CBoJpAi%P*^hr0GTR7=fZi|(Uax^7jA5N~4X4E5=u};(xEUBgv^mHk
zd27CF%_&Gk)F4?($5bk-FE^5c%Lyr1BoMF=$_a9#Ko&PsAt&!vGx!V{*|v}qutq5L
z2$E0^Zs=)98CyWVYDv~?1fDf)+s_?sSCBA9cA#825kx{s>_bXMG+t(O7bajLNF^If
z#Q+(5U20cDP9Qx4U?l1aQ?afI$QS@4A!ciXfqN&!lpGKk!vioQNkSKh&SaF29%GL1
zB#L2@sTS;j+N5<6F$gK~y+3?;{O(KGEM4D?%euxAUwyScZm`;V)<F<GJS;C0kb0`Z
zu@8jANK`;&s3exd41L7Y=m~H+*mc{V+FDO_Swu!`xLq&r@kU;}IJ`)UU&-s<QsN!p
z2vgRzts8FIJl)A1=OdKo$MadA&ZFJFTBbOpHhacQ=$r6S{4HFzHu&0bml<xB!dF>G
zzqZR3J_}4WTU>jy>F&k%AMQUn%*U4*S2$+8zW4hK=_7_;ITjS}D)jDcU)UcDq>Hah
ziD)!jKFPS+2e0__ooYXgug~inv1yL!6(49WS34Zix=4J{^TO?@`oue#zX7=S>=MU`
ze#JL;k*ZvGrJa!t#f0cFWB-Ng9HLPU(1C2FoQQbDEnP00e(ux9tq*BuyPWN-U8iF@
zzDx&9B^6c_c1qkF=>l{#CH5Y;B2|wR`-Zp^WVgbun8+XnpvML_F|_~_7so<@$YcWH
zM~Nq%7D6=fzyr`8QUDVm1b|~nJb(a*$&Gk03fc~~BX@8nJ%fj%P?!V6HjsKiCcBbI
zBuIH^eH~x@{r>jpAikW+48}s3DPcx5j3gyNAR(ZdyAZ9xKtW_2wi8zzD?ljU2Cdk@
z0H6;)@14-s)l}P62ihJo7~j8NFU<$#%|=UNP9t%uhr7G<2fz8l|KflA*WdfS<HskM
zmW+jQ{YEeQ_RW{ifA#lID@`B%=KO;X564)JFKU$(&j=BK;1Hn@8jcojfe4HMfr9{{
z_h?<$RaG!2Q02;qgE;|<s%<+yef|9J|LxzK1p@$AK&|QnDFcQoQ#L^m$6OM-c8Aif
zo2eTr8`&1a0zIg(ptAu+VqpW+)`%<3nHVHbg&ovtJ!G~(8y-x2=^kTeoK$z;)e&x&
zdN}awk6zr}-yMrBryRNDyhM9~paD7B=8{2mB(8-)GIAHRoO?J@D4|(n2n!0>5!?b2
z42w|Y!nQ>U>fj>bhGr%}ib$*CLB@`0MV=*`|Kx+;h~Wi?Qo*^x=*Y}DC!x`u5hx<S
zF|#?bHjvrvB6BiGo)Cty#9*OhX+Bi$X>hK<NNG|Emz2eKlO#gG8N#8~5C&p5aAzb$
z0t5zgG&6GU;TbV<Kyuq50=o{K6ozT{SQ^?e!SIWA9jpYl4`mcW?!#Mfj!fu5a|#@T
zC6R|F0_~YG_Kxf!tnSjRA#*sGQLV_7V1N*-Th0)J0K%+EPNGy;97YHR7&w~+;4El>
zC1tNUkV*<B=-p}xkA$N|I8HCj+VBd<GTFz!@yYbzG3;@?*>G=6Up@bl!1{XW&#Rjo
zdT>SWtxhxe@Lf>^(GeLMz+q;QNM=3oSaR3?_0yZ@j`K9#z<fKwH_vMuJumb9veZ_`
zaN~v(Bn6TxEp*#2v|HolnD%!k-QxYVHF>V5w9M0__f=+gTn@rYzPBCp>9TdZ?jXgE
z_cC!z=gW2D<{W}k;d)-L9fFq^KmLvTTe?Yz%h};I!;{xnWxTiB6ZR?MO*}T(&qEiw
zJo_=xnt&fWRrV@(7rOcWT!vl#X8#fgCi>t9xm@JsLDmFzPHHMX_7l;&ksR@6^b6rD
z;3qoXKt0f=iQh-L>$E_7;v}4aiM;T>-DY?P@-nUOphUML!F!$(6yCh>*NyF`5KDV+
zb{TqF%H4gQIp@US0W7FM*@9Ue;}O#0kZ^ax?joubopApez=D|&K!8IWnA09sF~ZOV
z#Swy#L^`-Z$*5>fk|2OEh#6B3^9aC1;DqED9RNr-ZR$>@7@iEsor8fGE!cGc5dg4n
zX2@<(k)mT-`n&7X&wrIrlcZ@9WMZl@5$D;_89J&VQD9?LKw{6H3SdVk#1YO&j?u!+
zx&Z-<tECA{ho*JAY8zm->pte2w_iQJZGN$_yR=X#OUm_oA56dh5C7qh{^Y;@;g1e4
zXRe1+qD$YleS_;4KfnI%FV5HF{P~Z|$1jhI&4-(*45Glm2saOO^@vb6cLYHMgy57w
zH`_Nw=-M<Uua!ke5II3GF#?4_?;W51=I8(Z-~JToTL{+KMj%x{>x?*p1Dx4ia){#q
zmKM_~N2!>^dc#0O3UTd9E`+YkqX7zx5#q`<WyoTkyJ5<{ZMg{aZp^(6uR@sj(F}27
zI;7<|m6spAI?n0%@^Gw?GZA+j7qjf!j${-G0uciXBb8LLpb003M697C))6JJAz=`Q
zFhLKZkQ4V6oe?Ccx(B$ryI{CWfB~E|Iuo)JVB>%D{U0*}wt>txwwfV^Ckwam#KJ^`
z!9a*H8b%7Rg3y7`019N12ui>hBY0lWc$%iAN4cx533U)7B!V0?I1T5_V`G*W9bAKN
z5H1!3L<kKjF$UC~>?Zsf`oR873Z851Pl%jH^QQf3p|*qfu?%qEH}CEpDcHQp1lbZN
z3^2~ZM9gGF$%1kvbjs{d)pj5uBD8^oEEpXWF$>0S!IV|47Lw%Q;u;3ULXikOO2HVB
zg#wi*i@~s@qopdldS6KdDA~R`_<+RcvmyB&40`iGul~s&=EZ4$8e4K&+t01tebBzT
zKEKy>w>?s-Zt9$}j^V1BD(N<7LZCDy*hlJZ$2(6wZSC!2f7`L<CEwyLC+o54LUWq4
zAF?fHavwZr5JGEm-otzi#FV)*vcfvnXZ}XP>oOhFE%h7kWtySnsMc8IX}vt#MLE)x
z9`2W;m*+8dy`144iR3|N+^(_HAx<B?`1J1O!#*GG)kNTeHz%}0d=9y1d#`zhXEk)c
z1o}nu3ES!uLjH(o=<8#(sf?eFckkm@n=W&`eLYX_X!@w^UykKKEqi}v-^Y~kn)3EG
zl0o?xGXS5`)T6w#zSH%Bce6vao$>kwq;R@(BEokLl?;X-(tgqE(2Df8R2k{Gt*&35
z4J4LX=G&S^nSvxU42z60)T_Y7B@L}Kg5+#+Qc4UV+#Rz!44AnQ;8WxUy`VJYATwuh
zLm>)rgiPOphH@fwCkU8{o+!;8slXb}U>)p=ZABltUEFPqP?doU0hEP>f`T}KHIfQp
z1Ox=`5+05jkiY;vIN3Iq{p)u$#&Q5kDWMCBQAc7bjAWDuVIXBdXFx`eVGK6h!v)ci
zM^Li#g8E9T(YGkCY&*lYog29L`OW3L@z>wHdwSF>(@rvoDVI9uAAR@X_y5^H|Ixqr
z-OoQco=&+eNcyag*Y@ts_2>Wj@fX{wC;9ZZ?!I%HC(QF)*g-vDK!^j8tGb5~1V*3(
zMg;5Cp$=CcVh*4oxiBM1WkLi*aArVuB^o=v`Rl*@U;pQy@8&QD1%pEd@S0G-a!O2~
zJrRz8p&bD!XNADA?N_5fP)9@&WCACqocg*^DnpH{H1#9}ZG{AVi{ODhnYmRAWFv&Y
zz9|Em0YE=Z$NLWsuO5ysULFq%9VVfvNVk9pMhgT$MeLZcc!7~PL2{v0QbFk802l~>
z0B8w;ED*Xw$)?QMfxsie9J#{)!V2IBhEsERLGhL_6Ba}buKaJl^EtX<7U&}{qLx!-
zBsOvmM><d#WEpKxmT(kc5b-fAu`GA7tvF}MNK}%#9Q+_YInhMfjcaj9f@E__fDV~-
zKuMthl3@vM9K12i2x2^Na8M0`Pz{5~0fr+V-8_6in$2PFio-TjZM^Ext#?czoWjOL
zEK7m3Ubk6TsxT5r2nut6t5d3K0HM~xMJRh%U`A4J;yEEJ3z<{#=z!tDLD4!-vl+pB
zAchF%R5J_eo}37P!ZEWFQ+LRbkdZ0UL@*4P+Sqq9X4{=Jz50+o_$NORe$waQcxqoX
zpPWmK%^n}Ojc4v1Na8p%k*Hemw6JrboHH@G@0Z~fhmRq0{q^<SslK>-xV3|Y%_l`m
zg`q;(wIer5oIO)mLtpo;kDhN>C_z{s@y&MOL#L(QzPMS|qX8ROY8Yay)UW*ZGCFie
zoafUmXWE!(*SL-&=%p_;Kesj_WTICOFFvm?ULI$d#NTQn8k5ZLVV+|@Kz`h}RQ9h2
z9%yZzH7*3L4abTIHvqREOg?n~BEA?y(|GaG;gJ2dvZWMX4f$@?uQds(1ZjdzowXl?
zoNtBlO@a$~ivxSy*xF_LCMtHk8T}B~FL@Ed8FT{L@GzyBhaqnca(2Aqz2W|AZ@ta0
zVCH%{q%;C53`KbiJW|jAWn{M?bd&@`pc%n?N)lYqnb<*;(y2rEfQ&mN3eSNUU;#p5
zVL@;PK(a)!f(T||U`h<DQgR3}U$L#GmvLUZA4iy`TfhXs$V`$YIRr5DKnP@nzz7Hn
z6buFQz=RZlV33UoNT5i-eLTO#-b$v*!VEc#rXm2yNa_d;NRSJR4U?NN0f4Eoi3m7j
z540V|s`+T%eBHxh>pj}Kc34~6cRXMAN5p+_2azlvzdZirC%^qW|KflDTYvP~$FI`q
zaLDik+qrM;`gnc(#jl^g5x;pkfA+(>k894nEJdh64Df(&#Rv~4Af)gJ0}G9|+U40H
zQzq|jKtwI4iBl3J<Q;JVMiE48!K|UZ!B_w3=l}Nq_A?McMPV05rf_ibAzUe=XROB)
zj4RcdfxK<ru0Zp(?MYq93BUo9AP%O!8RT#u>Ln$HZ+WIFiY}fA)7>4y2En^Wm})~e
z?SWI74y+FkH!mJ;?;cL~r*xPUYzjiQ=Dl@bH}c@<o-z_MO_C-@FOV~j%{ZeIl7XPp
zLO49gnJEQ22Pk1O$?AhWA}xrZXraK+P#J<Hz(I&R0HGTHt51Ir07wZXNHVP*355bX
z1yfpXVPCOKNQ0F_fB{f4c!N}Ed!7h=N8dWs$fzPY)m&&WBALQem?4mehnGX96{kA~
z&k)}tHD^X0K5;}P??jHi18E?DSn_UsBn`H}aSeqC8qFJXQv}u?7*GsptXpJC02W9B
zsgjA$TOb-x@uBSIEKY$4IRKE5a*$6tup%L646oDFIt*ol;9zqk3GJM%ODWbPf{Dl>
zn9%|?bV|(Kp`vLwAa(*~L?9U5G9^4vQ%6d<cTgiAuYY{^;wK+qJU6(-cfa1AUx;7q
z7msaS*XPvL(ikqlp(%4JEC`dSj?CF)y#Rn^GAZ`FKmK|hr`wx*xj#nPhVe|{X{<SX
z!5TzX>M`wL<QA^>G?E;TG-qyC-+LSRaW^;~PDiiYattsToHX4`Y1?+(W4SsTvcGzL
zgf-bn2p-eEKU+QE_9o6Y2z}RiDGwjU-A8w2p-2KYA@-OSwkqj7vhd@@%={u~Z~Eb*
zvTB!Y@Lhrwcae^tmiYPhw)q!ZzrRoSpG@@;$|3hlW8(ct^^2yjOZ$4u7}t|;S451P
z8yF-7IEi<%@mw{m>^_RFC~f2A*~$s8htz&WyhlFbAi6wyS)<%}1U#FqU$uAO@4unj
zhxD+dnlf&{NI*T1u7JEEABfZdf*j?*&^$?Rn>S9rLk^4rI8p?N0i__sR@r~8^$sC`
zh#*<XXrZ3iha%%xQ4+{PYUKNXf=^%Q_ST1VDx(Vk@SI7Ch-4a)7?QCw`i>fCd*Cbt
z1dP#*6C#)s5d$)gjhL7c1&MePr<cdRd_4BIb#&Px=Z?8Z0wP4Wz#zcD!muKc!7-76
zyCWggh#pu&9JeP;M_S)$WK(nG>*Hk~*!Ho>yi;lJ-#kGVxVbs~#t%OIt$+GYe)P|N
z|MMTecy(8n3}k!TwOudkH{V>}jcqUK;j`tFPj2sNs!3{Qa<GAb;20RFpx(g(G0YmG
z+YasWJkl|wkZZ(Xbf-jAI1w^nJ`gB!um~Z=#1Uh=;+wzz5C6;m>z8f;U?Eu5(_Cv2
z!vxWoF;NX`)?Lc1?;bsYDua8tW1gZ74?-GJvavu=N9B6pEL>9z$fz!GsD}|Fi~6P^
zsb9~kv2J5TAoElb626{Kr@Q&p{qo|TPKS&+^BkznsW&V*LYZI-s6=Im%q)q`FzqR3
z@PU>QExe&{3^O+ZLhb|@juwP$ZYDS&3WEY_xDqp9MNtgXsKLQr00+47|K+>CNk~Y;
zoj?saGKC^WWf+5FC+Lw<b|oML&FWSeh9P2*hq6w{WE4OtO9nERz(UA31-VcR%!w&c
zSa1p|<PNxDxdmV#L>->84Dw{;-MQd)4XK9BHK8lQnIOD}BlrmG8;!KP>OS@;_IwWT
z05V~0k=-$z%_+^C0x$~7K=P^Nglt2W?A>eSK3qnR1QFT5y?LH8j%}ps9$?Dz>|lw=
z9iy9CAUTU9f$$a>C5g2i5*7CW1VnSe?q0|dv5Tn#?z=fthHwl><pi&O@aph`?_%2}
zMgQg+r}OsZWqY?ieubNQ0`J{Y<|u{2K!L8;VUq7A-Oon(s*Nn-Y~P#}65YJ2$0V$&
zN!Z~y+0e}Zz;9|w$n11Ellt?Rf+ZcOR)D8VnDsn$$$U4TWS_TZTDh$q>mA>))P@^x
zc+7+`O{Wj;miCrBX@=hNetC}3{qbpkwns;o<?jA?_~=EMq|%1Tu_UiYXbkekrxU&3
zp}*ID7iaUjQ$IleT8|5-S6tX`4^w~4_~!DJ!{c)tJ}ApudALD8;dp0h$>VAZ!+t~Y
z)y6N=dNxtOS8!-~DzRj^UZiG2i}j#>PC9I!BbA<tJQ>Tc{rIw<PkDRC^*Zv=;V!-X
zD$<25>+=`=?TqCJbx!lnhdV*{j_G;^xL7(3#DW2MWy}BqmW8hv5Md?;mMn7Wd)PJj
zWF8WRvXUGDhK^H;CrnJBfF_taI(ZwISRZ}Q0FRu}A^iOHc=}bl<WV1@%;GEYf%mg~
zYszd6?hu}W0}Y*m#M>%!auWy*4<tr}K!N~7Q)P-CT-}JA5__FXzA09}la!NR#2Z0m
zwn!BKL<~#kt`R`d7&U~!9Ag9tkcBl@1W<6dR6#56=RW*fm!aD}w#W7I*uFkH9uKb{
zPQU#R|G^LbPk;1-pS=F;)BD@oTBt*N*DiZo&)YX&t<QUONFRJ>`uKyTwo<rO$)sTk
zVqlO*5P_)y2{?f%sM+A=8hlJ8XEF;Q3uho%2vyL4E0SYEi4Z10M<3>W+vv;x`1Akm
z|NeI!ghPWcVG7sKAhTeOR1$-Dq`OH>BS08IWQ6CE(AFXc#6d=5G*=xG(9G1*lCgIj
z`*bKA+{P0lkKsU(2)A7uih0Bs9p!F0G1~o6UcJ7(`{?fOu#~C@#@(DH)(v(7GBh<N
z;-1RF)S!x>5+L#xG8<eYD$sz)=$#0GH83J3qyd&VAR<t*LjX~P89b0)ogrz&C`QgO
zAkWnIaOD5#(;pE)M4d|vW64O0L=lig#CwO5*u%rfofCxtb09(t%K<%*b+AhK0GaY6
zIjEMSPZITxNfSpnBQccf6hsPXM%lv|LNF}A$&;Z)H*g``0aAEy3ebfhxhF>knj8e%
z)j(PgYwqE0)UBHfl%&^H5@X6zh?v|u$&B2c6D&pg#uzbx1Caw+i3EfaQ!ha3C3D{f
zDhBrDIJ=Sx(7Jodh-M-Sqcgg7MnOPs8b*SaFzuCsPe8`a6Akj>0D?g101O_eGzI{s
zdFmhj<KLZM-%+gb__a+R#V>x|uXKF}Ya7JSS1@4X!HyYi47)hgTO1zdnblKbG=0A_
zpXR&!G}XgI<oUpP&Fdb>u1GT{vXmh9Xq}0f7w5#w`ZzAujr%BG4|IdazDZYie%=D^
zr>R=;y<WPDFm^n_e3SV3?B}N7&V;b4$mPk~yMCcbrup>o2lwBRY57oawN_b!7J+&n
zRk<BwylIcuc=Ha9(8@tN+1SSX;?NvE<hXrZo}ahB|JD9gbGf4#;O?0FROxFtCbfqY
zPo_Hc-cq8KWx2HUDVpn{3%um~3~IEA>4lbR(-AMjGgQ+KkdU{L@^$61>dpP=H++30
ze2YtI<Bq<0H`Z-D0lYf!r+0ZeVFM}ef)-TW_nx)G)J0|uWJ@GUlHGNTAafU_F?k_j
zX)uWo6gMmp0Un5od9j2xis&~kmAyC{AsQBf0KFI@KD~?eaeLgipMT+S!dD;5F=YmH
zMGU8dz!_2o00aRy0E<8XCbtf$;4n^Z9-KL@V7sum1857%m?*ir)Bwg2nRF^4FJ}1Q
zV^c{_kL;^L%QXWVbA{%>DKgS%&V|5XsHLDt<nGXeQwPBo#u#DRF0H`^v0d7ALv8Vm
z=X!Ja<g=H*@h|_|@BKGF`rc=^4|iob<fM;!?(6$suaA${$JRD>FY@7!4-a!qz0{f#
zVRcmUuz=<riOdb%$q8e0CpI;WU@9ce!T?Sbfk;%4j5r4+A_FcA42VRAfDxuJuDyKu
zzy61R|L?w3F6_#j0fq>sDPtl`h{FqLC$%;VD$3X%J7-{Wg3xX`C}N;+00cTT0z?$X
zz6WzDscB4H!;P63Z~zVMySsMp?gl&sqv@+xHxIX`S0A1p?hi|KLWq=6_ZXYY5xoyS
zka<Tt<j90kMM4p>a0{Nmfh`aWAaF30>K+gspjdcd$4H(9q~d5%7U*Zx8gK<l?CKrV
zyF&>w)@`I4^2Y!AvmXWo%xMg-(hQR$;4mRX*Hi?jTWk&fzYO8&&uv?FALji=Gv{1u
z?Y-ajbhmDGB#WddQGyIAmI6dEV%YwgAW$SDM*h2e3)0vI^2IPL$Fgj}QUY6&Em9<l
zb*s7EIp=Nm-fOMdj4@Kr6G3ot@)R+8VDVl>f*~PB!J$6on4^p2d@#|>^;13+ir{>p
z+zBo*fJIf!PV76|?r>r!dPC_+`G(>p>_Czw4T=)9We*!Ku^CJ0X6)<mL*Lt|8@cr!
zV;cb<dk^Ozf{<=4lJEe{l7YxmLf1ar!1+ipWpkdqA&otT%apQ@(McvI8<De9By&A3
z5tWHSA;XL#0$>^dr5RZj9*JP^3~eI~P-c=a5*R3}K^iNzSRoWlT@R1-$N$lvq`CO(
zUr{Y1r}k&Rtglo5_{^o8ziCpY%j-E(HnXvh5EaQBpgJN=$(!wCZ&L0LH>KY&O@L0!
z;{_YUHp@#|eC*x!tyddu*s^eGsgH5Z-@mqXb5F`QI*)bPo3f3*^w4tT)6^1k@L{+b
zJM-}8>d(8k+Ur10(!Pt8w$JwZ{do<Q_wSBxkMDnadzys~X8I;wp9W*<JD-SWHn;IA
z{`9%ZF>f8zD>L!?eBAxb7rA78`q%rX=eo~$_)rcPoZd{d`9b_j5}$E9yT76O(sbr^
zb>2DM$@~<{7mKZ9F8uXxhQ0*`?8xys3HSLe*Ubheyx5ePml{)kj-;RBH0^kc^Un3_
zpmhEIxzmjtPRq0(r|O4;^8znK>%hctv#hnz_@4A85_@OzPlN~(kES$x8Y0R(z%afQ
zyAXBYU}j9JvV$hLjOK}KOeYxws!ZmNXpJxD>q}jK{<+cQ(~qY2w|PQlI1xiggra%e
zv9FYKkcDy38eW3KO@+b-2Rel~5hw!|gB)ywnE+!8f)cqKtC$IEo@qI9J-tI)()m|B
zD#@f|K^B9Vg~sNZI5;{Y5qr2KvdY%Y3Prg1*lI@`L;Jq%Te<Xp?bDu4`Qh;8AN}Ev
z|HFU(hkx__$8YbC4>^q$m+#v4ba}mAzmLJU(J#i!^x>z6dn#Gdl$n&g)35<XZ#Fu@
zLWsc>PMsX#FtaQGVkp?d2~Kc9j?9`8BQb-)AR&YbLo=ZV*?{k>eEU!T#XtL>zb}Ur
zUPbd%GK<=}ITH<27>qf%Bz7}LV^9y#oU%ov9863>91s<#d5|Mmm*d>rCFyb2zV0Yc
zh8|Pf$G*q5TTp5Mr#vY>ynXZTcDZ|h_vPDZ$&zSjDzIS|gXb6#7{kFdREf(Q)+%z)
zVUY)DX4{++1>8IMHkf3b5hFCim3@#Xg2^@nK!&{$i3pGb<jfO%t7%rX5DJ8h&a=pW
z{Ifr15!29Kg_(kNbsRalDM&1QY*Sccf%_!T?oQESn&LW^qin0~MnoyGYbp^7A(cWJ
zMMF?SvP)KFw;{`2Fld}r7Wg&vNF$J!(RYj#x>HiuDQS>#L<hg=9^&1Z#~`ZfZfKY4
z-K+R&d8|=k;WS)#;D($;SOUXyVL=x}&JvJ-sYe`G;Hokpj#hxAOu!-(Tr?YZKuE2$
z7$P9wLk?syMiWVceCC<P$TFP4Bq<9sjYh<z(kVEyYC<?q*iELg?_eha?-b(iJ}mG5
z<V!7|ZS8)%qxI+OpFdqWFt+{buKWsxdaPp&imZu<%sbpll3GWPYv|!_IVy^o^U(uG
zdL8X(YwHW-_;!jbYFwPVWqz|v$IeE6#ifn4N4`1S-A}V`up1e#7r&F-=tkz0_`C;A
zd^qgBDqz=G!>)Tm!l0%(UtjV0b9;H+dODO@Pj8P8KYYB=7rH&Boh)ybP9(*8m;E{l
z#fs6{Y@!77uvO9=^5qR3ell-=cK!bI`R5h&Fx^l3K}g<bdN$naFvCjg8|mN0VRZgc
zx;*zRGTcuS<FVwAHr~tjzg+7Vsec=MfYk64_9rRQ`3+y6YLmQOks7Cirz_Uefqy*?
z$M&q_SKr&d#_7v^X!&r%W0&C6H<`MZVwRIvYZW?2hx^3z8nT4LhN3m3L~;NVk3jDP
zHCsj8F?l{BcISoJY_Jr7(h#K>D#<j7cT)DCak;Et{d#+$k+^*EIQ`%Unzzistms!(
z1vr8tQ=l+8MHz7KfDnTq15t=pcLhVDg(Hk$JJA*)DjY_}T|h2M!ZaFTkWNYVJW)N)
zSkH2P&a^vaNrOos6wC+^+$2edNx}Z)d?K09Hz%iUx6ba_ujjh+RrqqAa6H_6c>JCJ
z-T&r?|J&dD(NFK*+)a7zv_03$SYN-te7o850n)LrF7Kx=-_A3#9MUYpARq2~xJFbO
zZlnN{)v)GXMht^FMBKe^!38v!Sb_q=Nts-kNt{%JgxM*S7)%Z!_&VU1EB^E!{}2D_
z=Vt~anwqJ?xodKekPv~`2omUResg<%;)LCa0AWC$zY}Z_yZ0EdfRw2l^f;p<O<Lw7
z6a`7)G9rj6C8+jm-8j^xMVQ-gJrEz4^6v5Y_Tl!;j~-43pQpmIhZMICXOFOT2ayBJ
zrZQ_<Sqtk34+_UvB07QHc9zAhldnKEzNOw_5qSdb(S;S%oNQ0Xq)KS8iM$HTkp-Pu
z+%)?hwg4CThrjy=pr9U^2a^&76$y8xXh>kP2+p~wa<swo2-?LHg#>IE6=u!OoH&$X
zqEHH`YnH4W95TPBxR4MlLj+bxBcwt>RznjAS}I{-H|)EB*ax)16}FRC?PMm@YNOI*
z($+ER#j=gfTknI%W>qR1lSYJ)I)Uw!(`pWKBW49WDWOJY#M)hi-H^3Y8379KZCIqF
zU?J{<X2S5E3)V*L2pS+k_!z7LHy&Zf$u3u>E`gG`+mNYvGi+-)lwO;qFl6k8;9ynM
z<Imnb{_)?U@gmH_=JA&w+jl$mwtaVQv|KIxIwV>(7P73c>Qq!q3?ZxF%!iwNvW;As
zm1UJ)huLb`3mwF5d-hM~kFQ?mDc=>o8~XK1&;IgUtCIfU2Y1s}BGi_>@36!NP1<fp
z_}Xjl<4^>b+_tv)x_Rrvk4MctFFdaOa*kJIa$nxQxqo|l`;g1xuq<(<^oR<&T}kfy
zx!datef(?_S*WV`<;6+;nCb9X?uU-^<>#;4%cA+9Z$m%)?y{fh9=_ibzV72jjV*0n
zPvk4**M9g3kz1LQ*_bN(O)J?}CI1HWL-Fqf;YF<;xc!<=$@q>@ymqA}(v|Fl=~7>A
z^5xIR`gvO)<@SC)Ug&U}?6arY`tZ8Jcj5`k9)M{S8u@4=e4{k2p$CfYx`-R=IU)}X
zQgs*V;Pt|Xg}8g3h$GB_MmB_3E)QnTyc1`5wZ5Ta|M>0tky2jtewh}X3&Dppb0z{a
z!(0N1*@*+m0ghn-VipP{5<+vy3=!Ww!o%IR(UmAg5Gx!;mf#L<7$7a-N-ShUl>{-S
zH}vw9`ZY8NnUjYcTn7h}bpi$*8C!tERu?AswpAyx-p#qXv_6lK_}!a#fBgH8|Ng)C
z#m|lpZ*J$iBahY2FYWqyyX@PV_;{Q6+0MHxKhd{`!(2E|W$I+Z9SjdAiWV-8h_(;6
zpaP!A+{4&Fa1RhC0XS!t;Q>)){FV$>1__feK#7<bL=+Tk*c@~@r@#1r{g;37Y@!rH
zv{&1kPzqzA9xY(Q&8Q74>XeO|q_W)KcyFf2F*zD*0XQ7IS3l0=0~oZy(fhWcC~Tm1
zFo(rxt@d#1jdX@gi@u%8mp^#-_QU<%jAc=s-*CDH6^bzk?i$E};H+}E$-Hq2b_FIE
z5vrjU4kC70fNP8(jgVu^Gio2)kqWuNCr%?kM6=7_a2w)YLo!H(0>aqDO9@-O+=Q|G
zgP;EO2sQ$-haa6KeCR>OX2?V=<mw1EfI-vZ-bE!%Ct0@<3=08yA>XT{DN}Gx<(RWo
zq#}};8_h=oL7iNJnUu*Ws6d3G^I(r5kib9;_GD#3TPfZ1xDd$AoEf$|i;rUGYnK?q
zM+ZjpfU~rQ2#Yy~8|i{!8p#6cJ_M?h!zh@#$E40+c9^J9SZD79@^H_Y``$bIRE#-f
z#MyLKvj9_oBnBwNbR^cPbS*G5=HLYM5H*Yf7~C;C4>36NPP1)2;()UK?%(^#;jjG>
z)hCie>|fP3c8}LTyIf<^v6~h?ajVfFqG`KUqa=w(wQR1_ayasFuP7n;hGZMtICsEV
z3r~ym=k5H|U#iCGaCrZ=?63Snl@Cu_1uyw=dT=|$h2yrNYr;6)3HCI2<kH-IIhC^4
z@BMkRb@MgRQiy4n5&TlWe;w-@3N5$G{o8!{$-8_=Y2Ew0qzyE$BnR47_+?ys8{v_C
zH<FIHRo;i(ru#*vG5+f3`}dz~J*9)|hqCa^w0zU$U0R#r1@X4{x3)}a%r<r3RW3u{
z<WX*mk8Q=_<1hRmNy5;z-qPX8#sS;v$wqz$9Q6Dxa*yfc=9s!vb7@qI@1L%}*obES
zowxZ^CP|VtXklCoGf2l6kRUCD0#WfRMGF}mGc!e0_=zX+?z9>4M#xMff{fiml`F|_
zr637+$BYrgnF__|gn+0u_g{UrfB(6E{St2<^W#BozChO0o+xZ&#mLS<KA44sB@77m
zMB?O3B1A4s5e@``BSsLLQ-l$Ch>{1{g`k9HdUSJkV{U*O1Tn&#e6S2od2Ga=f0;-n
zXH#Vc1%XD8f(A^Yo<k?%)SXdxsG^V2)jSVZyLn5$_ty{q?jOGYo4@nLTRPq@d8+K6
z_SZ{Yuj|v<^b1PY6s=tf-F-;6Q<)@9oCE@+)|`UE4UQNAnE5avav>v>pf;StIxz>D
zNFgB#_Xt%bEdYZDlY}G%{gzWO0t_LB47c6V^69H@{>lICzxmCEDW&92pu{B`VG%-(
zVUmcDL?<iU>tKNdt#vOH3^i+R;=T3mGFnT5UIEEsdYW1rrKrFt*i=2*h|;${Y^+Y4
z^UU-(l`ntv#rrqQ?QNNl^IR-TMNHgmq?xTpA7Q&`PNlK$DWy;+jg+d50SDzkw@#!z
z$Q-<hWQr2o3j={E`j&<}OQ1L$ZmbPB0xdKn3inR6b;&uZ6C(y&50LyPKl>wLI0S4%
zI|maD4`wGuDs2yTz%#>5x$VT@Dfw#)uE;mg$t_YxnKD`AC1vHJ&Qsws!B^o-*onjv
zi%v-^5kYc9Cnk!91WJcUMyMfhJ;=C%64i}hD6lSYb0F$~?>&<5JtzzV!}cAmkFAH0
zA%uD6eI0ouDVzr&c|bKJg3Y6(l6Z1QNT&!lEC&rEQ6_>%xW%lwkI`&&mIK8I=>x+|
zxNUu!lUnC8iFIm~M&sa+u$U}turfR-MVidRn>ljbThuys;ZytZ-@99W`XlY%T7T-_
z7++fZ%dgj(dY8Hy9W*zo%^VqRIHkjp6hyVf@^IumRciyrA<NPG^?LD&G9PV9+ShHr
z#x-R<mD{^?#Dzv0iZ-+y^>osMdsuk&W(P~xgecr7i@Qnm{_vxjKJ`oM)i&WW-N?i_
zHX12ff39AKrb+Yh;q4C}9}aa+q=y`I1Cfx;`;q0v@&j*VW(emoR@}<IO1k>v@1%zk
z+sFFr%k|sc#;LraeDk>QH5QTWB=X&sA9Z7tC---gUi%_65+?NYkoIpiT{mM2-FMBG
zSJ#$+syXU0x9<lp;>5?R^Fy>LjgNXvq$eNZ?PIKN+t=%F)KBk{w&mXQ>4ZE4#r(6;
zL+Weuk#UMXC@~aF8Q480sXH=9OvRsL5nutX?uDCiJUJf_BPwI;gR*i8RSH)CqM$j%
z)SZ-}EZ58a^y%{Fzo@UyZ+>)mT=MCd77bbrhmeBqEDBQi;FN+=nA31l_7M^n(}hH(
zZO(<f0@NXJu%v)7CH8O<jKSdmk?+pxMvOre6f%Os2AMN8%06&Q-+e`UB^F9UBFK~X
zHF_oi>NRp6a+3b)c?v4tN3&#JFdyT`e<S~|{`T$fzkmOOa(XzXDa-!7U)%b$+PA-|
zcEs|9@H2h>uB9L9?I{;YbDpy?1r0;&jui0lK|u($KE?n*qO)jFABG;m5deuwW(J6&
zchU?>Bn)K`vnFMCV)pPL6$TtEpcdQ*O&|Z_tN;E#|M?}1iKH1%b8we~F$&0hbSEYy
z8+(%CJZN-QB^qe%)y#&l327f)bJ>-tcjkmFu~#*O4uTBHC<dszZ#K@|k}g8LDBmCE
zpS-)fyVKj7`FKwy!is7i;o-gyNjBVqcuE;eDR|0B3Poj_!<z?#0GXIq7$LGSKoi&1
zg-M7pg29}Gx^oY5A-6bkeHn7YGQ^#bn+8*NWeo{9k3b^%Km5TT59V35*RG&&3v->-
zCKc+iVayZ((nw~IG6pt7GR9y^VT0OL=H!8raA1p^W;e|l)2Z0mmPck|F;V5-DS9BK
z)=0!`Kr)9&I8hGkM2Sac+an`{KvT3mLc-KJ`{=QTbJ%r++W^q$c}T0BOL$9RBlAF(
zDJW4I64FeM+NroVok~)6BhKX0GANQNk8rfX$K>0ti_&0Wzqaa;PAU*;jWbyrl*EZ6
zu^Nr;#>S*0JS7oU-qvwirY?ek;e(01!QHwUB{Fihl>7%jdw=-yZFJ!Lyw}V6^Xui8
zo7`&ON1RSmTd$Rz+=J`rdDc0cp}HJdht%G(^32?ZpWF7*!ZF=TGF)H#cB!Mx`840<
zUXakZQ_8ldbSgNLnR^}sG!<w~X5<>&4U!P(L)TAnwszh&o%D8^m(J0AAKmp@#VMeg
zioSbOj(0hgBBSxaalD5M?`Pz&=H8pNA-hw&M7obQcGtqtA03GE_N$L;gQfYf91ruB
z4j%r#U>lRuo^?ET&1yn(H+)chtfvRkW$fSf*CSmn**`^|OqLib=e6^z<WrmuZgVz_
zY2@0YypX=J>)`Pjx3|G_Tw=VwwC5xbh3=;`=R||J5}yV;YG(R0%6&4Ju2OD>8?{%`
z5p0y^fx{GoWOqBtxJE+Q93WEhjvx+Zri@-S0SQDJosgN75k{ObD!9_X{`vFu=byVw
z@^CoZCz+gPVs2_XLz#K#6fDZaQbF5-gUyjByjm1kRRIv_8ngvUgtHCNLe_{fU`7)d
z42fuv1XK8h<lt?G3BjF)P>P`NJx!By@k>qTU$Y*-ojoE6)h>&UPK<%(z65W@`;Hdg
z3sRzYzmxvt?@fPj_xLzX$6RhW#~J7Ietlg({$_veZT`KqG=BY!eGEJO-gM7QDIX3=
zjKCc35r`D%WE3pu5p53_$ZrvOz^b{KX)uREg_tO4zzLEl5r~<Hl?eo10B80fg=8iH
zx}+3-4H>nTU;VRx_K*MDzijRZcrYo-oNbRoDKt%F8@sRzf}jz7z(l-LHE2d=vtHN1
z24it%3}a#j64c1tZPs~sjJiAY@>)l<8Mg_pq|20k_;7mr`0(Zj_outOEHb5pTqKAP
z5zR;RHW<dsQshEhM2!qW*GS}M5iE$oiFqcv8YPAqC6F^Z(G0fG8N}WkMI&V*rAQLt
zlF2rW7#!|HVh_&%D+ZG}K{(_;{?Q+@1yYA2Y1SB&vownU?ZbfpbtpKxvy36rY}N+d
zF?Wko!XQP62nK0Mge6%{4D}=u+_WT)&0>K`_!;?@=Eda<5*8wFBnOffW>LVMS>S1A
ze+AugAm-vVykCQ6)U|=JZXM{&JJ8lKhV3<&Fc?JvnbA$A+?bU^nS?>sJ!Lazv|Kw;
zZ!tKO-KkYg1z|%vF%y-j``{o^rD3CcnR+8J@@byNs*+75Wox^c2%##L#JeNeh!Q(b
zlXbXv8{Q(!W%NJ`=Q7>d7k~fnPY)B;@7gEE%jcJmaF_nn^I?(Fwyo@w<QVk|y~&~^
z=UzEV32VkQNtSe7ZS=EE`+O*qp;>#~&pY!;ZXb_}`qcbd9>qrF;KbT%-O+bKk;5^u
zjE<5?b&BD0^qt$@tFKpoG1PfVr<)^Q@YKjl>vhC3S{1jnoaQ&Ti}Xpm&QrB1_a`~d
zjy*cl)47Mo1tT@(9I-^|eE%+;ZY)>()o=Fie$}s0<~KK|R3_(`BnZdq<&HOY{yf@y
z;m>_}OlykctMzvof7Q!_d^*<y!fx!`R4wE+Fjqgwr9&?1=78nY=VRJ2de8k#%j)Sy
zpDt{ly<7Wu?V)^oSRM}LxDtP$v8U^1X$>4*ge!9PPL=l)<(CMej(8coJ%v;`+=gUx
zfp31D<@Fn<D@`{-$tk%d9T5m#mNAsPqby7s5TVX22n_c)KikKb{d_h#$nA%8EQyVB
zCU<Ys@Sdh5Wrh|)b18}*Br?oFfrb#cn3MQ(;0D~#2T^4tKpi`bRS|=eIGiAT_^iD;
zcl5-CZLpcUg@H*ZL}(02;o<Jezx+zDf)XK!Ql#W<B~+JUl)}1Okk3vMr?>do|2qBg
z59a$=?%t%EiLvT-t=D#W`E@<76<_G%2a&7){8u}F=^uWicO|8i4=F2iv<SdpHYkQW
zf-O4S!#p6F!$F;FjDXWDi3q~s%s?oJDSG5dB!Qh6L?T*<I|E=MCa@3(A;>*^Kw_)q
z7ytNw_?Lfrj*y}(qa%h?*C}`3Y~2Y2HVO>45j$a~19=-u%3Q=+4dRlfAjG5w%bM#}
zlaR<D=A3h2=fMtgA89(e!+f7)x}Eq?riat*<GY*3hxztF4$CAM#7xq?r+`_w4@yzN
zB9v7+OJVOkXY!$A9&Sv*)V+#eqprlAS_^XM@R1`0wH{7f*n1-H1hW(#EzE;6*x5*O
zvNn7^c<%xanb;fALH@z-{p}#NR>@hWq)<kLaD);ZQoNiDP07_b1W2y2pzc->VI}rP
zB9kWdobrLUlnQHBO*9WyCRd-1@ZjP!RWd+@B`_EWNq3BE$WHSk*G)Nrce3nxBfO=u
zaSPgBJtSH)NnH1Bcvx%_zKyzD8{T?%mtKVT5H(>V%AJh8QaYwttoxJ+q`Z61y7EnI
zXC5p7E8)P@B88Dm(8eCnN%k5!PeAWofPjW{uH>9zbc6s((?J!m?ncbP;s%bW0fS}{
zGh|Sr-fLu5`to<=2mispllX;Rw@$qN>tABwx((2TiO2WbkW#4I?lxHF<kziB;^UN?
z54Xs$UbWfzLVn1{$D-Hz{4%zn<=t_aEYpzdwTZ$9FO>Uhzr?5K?%lm`DKcy0#;jRo
zgoiCRuuEI*eBq6l4mWAiR4-J+Ms5U<^m0r4Wxjcwf9K8PVJjBYI<&ZE?+eBYc!qj?
zY3MeRsXxFkF(=OVa>rht?9ZQ`zuDKl%(oBomx<^5qJ~2m`<`B2LDwOZj-$_O=wp^|
z+v%O|Zv5;Ym(;)V`TczPG!ALBBRzkn-Dx3B)t4XgxY7PAy*(sdLW$zk$B=c8wD+d|
z)ump$>!gqO(>y1976vI`sdvT|YQLd*igIHnY4s>(=8WA$%?|B4=8J>Bhk%f*J0WTl
zU4!u}*8^oiBG&?9LO^p^2n~@SCXTatrgrTw*X^rc?Jv9Ef04g<n2t@-f#w39gOaOB
z&lFdOq;U5pUV+49=$kW#2Oy5&kz&6}Nn|{T%>@X@Kukysb4L#<F{(jCsW+kh31ta5
zfej$viBuSX1i%zT!tV6#&nbk294-{-9X7z~1J*iLI2~O{-uxu~)}QEyJs<DNaW3hV
z*7Mlw_2s+$`>*X~;NhqFF6--`j~B0xzgOO-<HAKXPea0(M!1K=VQ@wu5Z*j|1S2Ss
z?MyK&nK$90DF7kv;7kYtF+5m_C|D8=VhIqDP~R93=3r)u5J*@s5q4L5zVe^`zyIt1
z>Cev~R(Bg6q%t$plzfa4a-7nXZ8Z{zX5#LfC&^x@p07+f*z2{2rqP><pg|MFxoggh
zzBSdf_FmG-qN^z>&7PS2cH*1I!`t`A$A^d8dp=~ALr&a`1l`C7Doja4;T|H1SPug4
zl4Y1i7$C#}nK>*BRBj9$!a*~W6C@JD7_c58EJUj?g(Yt!h{y$#u??d{cJXw;=u!s?
z5a`lli2TDJ|310tq=1dpq7ZF6#4$jbh@4;dm=+r*6S;-+t&f$A#&E{Kl(Du6Bc&;$
zs*$Q@Etn`dAPbc#11%ku2g7Aj&`Ed$Gf|IH;GH9J?UXrOhzSZH0c%(ayBHCT0iG-d
z%rMwNwWIf_hM`*P@a`d@5sgS1pg|(4%517)Ey5Y-%F>GRUgNHi>0l!3AZQ71Ho#M@
z7Q#ZtEJOnyBeQ^G^pGS54dbF}G`efb*4#WwX2^h)KB}uz@NiM-GWF|qgal0~)gS%j
z=HYMujB$bQeXq}7U+Hy7B9>^M&r~urXs@r0IICC%rnCs}t%2qxjnQJ)>*)Eo2uE7m
zc}HVi?hn&k^m%=L0TJn87Ban@pReWhV;_g(;VzduojSEj7Y^+!l2Tvkx{YhQHfoZ}
z9Zx4+TC=^^Q0J+6FXY~NdN|zt@a`dbVhmppgVoZ3LnPKp?7W#ZcS+*a=EQc$Gwh3d
zseSzAcQ17x`QiBfX1YI4yQcY*&5vBe+{fl#nU}R6?y}9}SUG;6{%70tLB2cJwEOxX
z^37-uslCv>j&)%ZzV4cWA2_B*_ng+RV(v`ZlE_BhUg>%7&wHae66G)MmswD%*4f7z
z0S>q#^fq2MwHG?Fq|#7Qb%~FW9;JbjF>{{|zGXRoOBi&z^;9C@wt345A6>HWtm2Y|
zgaj>Ic>4q)i9RZ#eXh@aeEnS$;y1VR!y&VZS6#Bmg8popI0h^Ex<^v$h;gO}k`$vw
zGn0ivz?s4TcT9paCF0Joh;~J&^-H2Qpa>$kD-#7d0Jol$zeh)?_#jxNVZ)uhA~eLH
zWSh2+XMOrcluQJ}LIe;{iE#7ctn~21_IrOL|L&J`&vJS%w}tCDUSHd~ZZE&A{if5s
zKKvl#`}pb?TmMl!ye-GbbIxU9suG0Z@Q4m4ferE+p|CcFf{48n6bND_MC3`BD3rtl
z%#1J!2ADuZ0SyNUK~#E-2qY3`2$2&M4)9=daF3wZXZs)jpa1nQ&dS!gl&Q=-+5G+<
zTLm!+Xn<`uAg3I@Pg9}+cxaMZTQj7Rtqlt!ko7)b%F4nbgPF3ZfrOP6X3RMkxad4d
zzq`3VEyugZ`QiR>I^<cEMJbc7LlEfbDP@P*2BpNzOXhxwG=+qO5s5Pa9A>Q5dE(Kz
z8B+v>q7Iu_FATNrNmU$>PF5T&+(C&zK7cp~*5HW=uwl~_;Y?HPD%s^9{k1<PWq2bI
zCg;)FZ8Rd0XbngN<HTu<K}xlHE+spx2D>DtK1`)#j=jk-WpdM$GL$=&DKe=R<2iUG
zrX(|^N?>vjO~fmkk`PHI?!+8oG<x_%Y|<Kq0OqEl83X8BSh&U5hxOguw#{ga{?e!<
z>D`072b9xIVLi~;opee()?Ma_v|w6tUvZjHtJ5ehCXm(;Y~>K{-Ml0YCzJ#Ng=<bh
zh-Pv}?h@OeL?O&QA`uBA&D@R9l)NWJcZaa**hd%cYuGk)Sw#KA-+a9L{U1oPsL!LX
z`^#I96g#h<W!qaxW+;2(w)a#k$IztAsJ+N?OWxz!Ow&9|$+}<1>uSSs`-ACL>M*pX
zH84Fa7TzxHwU$dy`c96wx5v^;vtg4Pgfpjf-de4(_wu}Tq>|qr4&ImAoN^bYC=heA
zb;$hY?c*1Z$LSIS-miSR2L<nyqLMZnrL}?U*=OBo(m46J;ux5}kpA)VXJ37M5>`C?
z=*{W2%v9U$B<rO9?a-q@28L7SzLdLly!jy4GU)5}{auQ0_UY|>9^-UHf26oJFS#n+
zZ+&?$gMrBH6FutCcjM~1z4j@mdahRd({(%#^hS?|Qr?v1)#oI2a(=em=o;=TaRJx8
zoUkrsy+}SwzUjtvov?lMQe~V_R%ZdyxYV*;d>+g<9!}6;jD8?r<!~gAXuHrF^@6&N
z&);!9kLydneD3GlzFs#z=2I>?35Y@FP`idV?}OXtK&JKFozy>dpD+k{;UF8)sSpM7
z1Q-y?@N^hglk>-r1U6totTs~AkrG*Cw;p9CbFvBwP#M=N<wQ{DYGZO+sa28`oWdfQ
zLGVHH^xbGzRvTSN61kcSQX2CQ`p^Cy{=*;54<#LrdCALMr}16g`t|(mpFTH#%x`bz
z#pL-f>(i+pew5!WY0mRaNt`8C3b^m#;UN*`tvSR-jSv?lxO4I-5_>T7a0oy|AdWpE
zvjjVl?*tI1pa?LF6Nym3B3Knc%t$kFXv!cE1&4wA;OX-({^Gy?&wlar@(Mx{wSW@U
z*PY2V6>p<Ra#M<d2q`*tZeft{eS;==kLDu~hKxXw!*VawNs15&r@;w6d|EWC3@4Fk
znoFX)`FO~Fb9XwtfAetUT)^rK1ciGFhm)~|dnFbr36eNvl8BV34ia?$Vx$p60dVVx
zKvEzg1|kzy*8?-D8BG~%a5fd`-61r3aFX7eF;5xBWIcj3%sIjah0AdH2S5G2a1)}~
zU8l_5lsPBaoK+As8XY2$HDl_Kl&D9rTUgjE+&XEqKF^u@;G>Lo$V7)(px|IKWU$Uc
zU=2<LT`&@o5?_c*AdxVkyUcEZ$b<ntL3UFWn)(3Tc;5!Z-D!AjF=WJboU4x^txAVg
zErV5rC`?HK4^!l>gH;h`k$Kv850|7IPMc}s7DmpZ-n9g=unX%L<|$$rBjMUctrki%
z<64O+m>5GT4>n@tAY|jgMWctXx<?2&Y$u_PtaS@1<I-;q^+*5SAIx9;uC%X4?eXo|
zd6xd&_U-P+u$<$(8tay4tk-ilnX{x~y@LtT5uJI~WnRSgl(vCo(rHoYecJ3Y6uBP{
zMdp#4S4zFc@>cGY@A)-1bu&4NO(&$yTiP$z9MO~TvfMu8yk4>%s<PP5L`q(_Zhczb
zEN?!fbazkJ-mC`Q5nV$I#XQzQ;2^iAvUg@gNqM&Z@B!;*>R)~O^`$*C9}cItj|WUC
zK0+t4*8q9E75dK7BfId8`Qb<W`Z@91rYWAKtL0dd{dzB_ydOheopPbxrmB3>!y3nT
z{T#kub^WM!Y2Xy=kot>(iT3mLb9LKrx9GzgI<=s?bb0A`jrun1+~*sXd+bkyU@hZ#
z@;#)H#BY2qE!_?)>Gh>A0eK_K$;Xv@t~{sL7R!b5Eu{%GSk0Gv<`*9`tsnjRJS^;7
z`!`QxOWqd~qd|0tX%b#0or^GgVe3zZV|;${(c&CEo#3To1|4zWpd3j&1i?HYvSK>0
z3o#>*qQR-}6Wc3fVr6p?j36C#5XbPLkTAeIg%2mUi;#`MOews%ksCN`0OW%yNQU`I
z&o8*F+yXa*O|86fQM~^H|HI#vAKa8Q%2CVV81-p<`re*DT0_W*kB{^HLYHU%_!{)3
zfA~TsDrHJX)nK89PHw|p13~a`?+wmDApvF)3J6!@1aBl92=F9Sg(N7c7={NNL6j0p
zSPKy|Q?`!OVw50u6&7YCKmrhUSZCspy^Z|sKmQ;9i~sVk=y-4m^&sU0xR3zaI*S_j
zoYXsd8(K&x(N-Z4La**b9%DGEgyxWW0;29+#6Xh5Myugqp#f5z4ol92!k$$QH>dmC
z`Sxx)-W_I&tZq@M12Kj(yM_~dK#EMFQ$gKhP6{GU?gJyy7{su#N4_C4loX>y0(la4
z50Ox7J18>^paf}<fen<L1_Qw^eS|kAhXe(86NW{aC_JRd|M?I8nlOdWjBXU@=0T8-
z2!TX6f;l>=rpVA~w(FRV(l$xj2h@WTM74OG1XETD69GJA29gl5WR9dftI@2nDR-m9
z+9PFQpk(ol;Z70VSr43N9)seRWOs$TF!mS;y^VTtl-M^L<d*l{#+2=4bs~x$oH&Ip
z(}<k1LMO?yT&_&TN>XwjE)$b?1duqlu)+i)Vyr8Bf&>Zi@Q57TmEiC+7#ZB%>DDrn
z7ADXzW+9g@Q}imBt3V>Ea}WvBs8k0e+)W+v@P+>PKm7Odp?dQ%T3x5S{oDGSTo1B+
z&Rgkw<3iHic5bbe@aS#iS$d`GFrKF3vu_GP15H};G<)ASzXr*C5^hHw*d@TV$^5?D
zz-0Bkqas5|ZjN$!*<bg*b~`3^O2>S>lU&=#3uhV*3h4uVw-G9jH#a|gyKp3bYJ4yC
zA`^0X$NK7>boADPwilf4N9X36`HU|g5}&rOUq1cCZvB{U^WFRVLkY|r{nl+KFT7Qo
zJmPKIe-#hMtnVCO)pH)-e$w=U<1u?id=u@V*vo!%M*pDaGp3x@=5&$iOVQ79`~l9}
zzGCX%(!&zf;*;3ZT3^nAlN?XeL2}xYvm8dVyHv^aIq;y;JZi{2dDqx9U7^(J&R<Bj
z?|ErRkJhwYOv<n0_<^i@IR@O~!o8q4&kHc8_L-&wyY)2V<9WPz8)o0UUiFb4@8kZF
zD05yk8s{ZtCSD0n+ogT=%gAN;E$7nmL+0HNN6wR|^DxPh6q4tVL59Gr_AELn4<CpK
zr`^+Jy<?bTCt_!HR>=$PdkBbwh8waQLYcc-yC$KKLm&c9B@~R|h(W%wC?#@v!{2-p
z*kezgG?sVv;mi2L-=pKP<f|qyt8Jg#)33MqVhdn-cgQtp<JYJ5dSj=1+|Ll?DV2#b
z4GJ40;9{ePf<mmjn<EK}2pa_fAT|QA8#oJ*F)=9-2UNm?;KTs|M<B#0%-kHXKo5Nb
z2FMX45atMoLn4U9f;%zqyZ-8*|JncdUwv;|6(qyp>P)6xG)uH5hspYYn=3>ZL6pLX
znV~_07^+rDc;v&02O$f)z`O|Yw3s;&@i7sx0tZgM?K&q#xjUAdyLma?-rU4tDwDc}
z5fvI6g@l7}fp9L<C`RFwGN(#V26-Fg8;cNY(Bh-B!HJM6B4gBOVhTbJEKUuag&K^>
zb_aVS=ZMfy5%ZAF6SEUW_jE*D*n|}zf}8xqpZrlcaq4}9geLN!n1yXxtnVntuufqb
zK=k2_l6Xggb!7#sS-6GF>?4mZiKLW_p!rD3V9iXFONhy!l%2a<VonwW9*GCTIu-M=
zxUljR(ZY5v?}vXThwhztwsG;!Bl<8QH?p)}x2^6&#9LblM7r<B%0fLx($a|2^!}8*
zlX16A!klR)?gkALmDSi12_<d@4FHM=1;!vG3PTDsqF~l=oqf0$qBKPv819qCpsX>v
zt1}qZ*Fm`y(J_D#p1VuinoCIqAAWH7;_tp;c6O)B?n&BT{HEQ!?{`7p*3GEpqLSB_
zZ5x`F9M{n$NEWZ#1)_q0jp3JZOw-U9$@*U1Q_(q1bCPSnzV1yj=R6Tjwo11x7%Y68
z3UO<Dy?SeK)}(2<IZXAe2BZ7T&TSt){Pp#+kJ|b6?Qy<69LkWywg>BK`hom4vM?94
zvo&^ijNV$APHxj8R8qgz{og))(t$YM-ySEPZ&ubt3i~voB^uiZO<{4k_~B<6FZ~em
z^|{wp`;4_s{Ny>)ehd3tb(;DiZk~L3C%tt_6f@0F!naAk?Q!T^8!xXqz7EOT^QCS(
zQc7Qza`#rVg*#bAHCncskJ@iJzj#gwCzhu%-|(2|dM0w3;W+rtTAyP+V|nb7uxjeR
zuw`Z{VD>#jp%W;lRGXnNR>&RY)I!I3v(4=OMnAltkMqnIPa#A>#-$9f?`Je!pU^C)
zFg-@xd^x8cNmX_&ON5Y%`0Dv6lH3s70m7h!D?sKRwnY=^PhkKNoF#D(k+P1h%YplM
zu9KTlPzx{7cPwO`qhXquQb?r6qc)b}281DzijF+fDEj$h%ya+gpU{tv^wZxzJR&7$
zo^-p~%U74rFXOfIn}<9tQ~fJ{ZS*?x{3bsx>5w%^Ng<lpgTj5l2O?UC0`46Y+^MJs
zCnf`tLy|kg;84(SL3Ltg!VrjqDZ0A@#KdedECOyp%&a<z1i)Yx5Gw^lB#{KjID(Qp
z#><w!`ltWDfAK&4YBX0+VJU}+e3VQ<V;0c`-VHA8T6Lb&q;O?&HmRdaVU-BNZwc7N
zgaYnxqh3c1O7JdLyAM7rh1p1MGq=LJ9G07h!_C8VSf(j~#9a$V9eElfe1zq~5vEMe
zmQO;pK!TdXQ~}K~EI67OX{InlR{yOM7~o(qh%g9}5N^VO6bK2Y;R7l@1Tw&?7B)MN
zG;=fuQ^*ah1%YLjfAqV51ftMzois)y3aE~{J41yO2#Q%GxQM$;s39WEv$;ppGMOV=
z50<%bh%!7_v=DPD(ySbH?4&s+mr-Lnu&?j}Fj691nLOweb*IGWPR^Wa)Kw&dE(957
z!6_o!dX{|j(e`sS^Oh$Y)LOM3ZQraMIB{$}*qN9|xARrBSbNPB79i&VQCK83Vk*Uj
zi!~;PB)E5uG{OLoFvbW1g|$Hwd99K2Kx7+cog|cV?)wOFp+i_xXC><H-6&wg%*}xj
z6g`6WAO6w1hrj+4j>e;T>*Le+_0ux_u#}gV*U!+&D9Qf3(Op*Rv-GWT0GY7`|CZz^
zIfeyG$#xl)t97E1C%IWVu=wk_<|WN~IF=aAciWpamcxS{b8FuBRVp~4mHQ8;yb6^X
zo~B~SxUT`K+qJH9lbmmt+Yk3AZ$yULJ<nJN9bA0TI9v2+gv&lguPBl=9U*r&<M@;M
z)mN84ZOQBD;WXc#luN#@+|JaI$_=DC9fT!Fq_RJ3A&>ISpRf7__LWc5z=19z^*I)V
z{4mAlM@swKboj28w+Uu}czp?83HLgGw(`EeT>aC)-bX{e{ZMYDoGec}>BKcVjaogu
ztdgE=xr=s4?drU$-Di3oK4NES8%yT}gYIKT-d}j`k!}XNU+?nuYd;20AFw3o%u>J<
zX%10NBC2uuv-tcJxybP`Pd3N*sYKACtjvJkVPeKs?a|9L%8@mbn#wWpMx;)}R+KL>
z6^mnxt4a_jhG92OB;MiXSP|KJ!)U$6Z)stWx`UlDF6LQD`ld9VP!=L$jNV+*APet8
z*+i!?IARxZ??I&KXQKm?l4L6d(DB2crZ3-=TU~Bh+!)>JQ@g%iUq{<Azx$&2_w@W)
zKbLX((%&8<1(pSf#KB=0-JBf_V>pv}maqXLhojPOY1r5>3`7WX771q!bwD!U2#+K{
zBA7EMghVg~g?p?K3<}nwDGLEX%!qJiB2FSiB0MTF3wEb*+4G<NKmPIm{J(r0q5>xB
zfsoPQ!6Vu_9E47Zhk=3Y+tn!r!}>lfdu<-cqDSylX6tK_BgGC|awbi)tH`YL?VUOo
zVgaXwRLcFk+nf94=Kby6&2(5?$ccjtVrbn6GGvly$nH5Ot%;LF;~a)U6l5Gmph1*a
z28A++lEWqZ5(M@_>=D^{B^#K8927*hv9NdMG*KF28<+u5>|nLk+{uH4yJW_2!zTae
zC%=m@m||?CNAyZ8OG29#aG+K)r`4_15V6FvhlhEflWGAr0o$C32$p4XN{7g$5G<xe
z;LM%NOtCTLU=gn&6DMWap@k?2LQIs*SY#OGBiU>4kq8l)M+*Do4Ky61yE`sxGq0}E
zYX`OJ9;5R#Mx(VNVe)Vnm5@Pl-85xHP?8`@g`}I1M0oVjPBAKH;c${HtwK|%b9D_j
zD2aeG!y^(+RAA8}(Ex-}iP&12Qmc`O`v9|%GMTA_JB##CItv)(s6YI7fB*CcKSnCC
z@7#X5pQ*i@^0(iu-y967_&Uy<sK@5q>h=mdDOTTy@sgKGV8cq52oSY8&PIUHTr!$m
zD}`_6c0RUSDpWfJuFIaMGR29iUl(e9jM1#zoaiX8FwXNFl*u~AioVq=`i`CTcr)L<
zy*u13@St(F>1{@(lBp3N+GvIy!z20_%xTKZlkqO_hQGT0^67H8QJrq?9}A!9aEL+H
zHQE(Lk!GZb`7Qgvt<bi#^_zZun{J=@@TLp{YRpHzMsAgEKaKL^4DlPpev|muV}7rj
zjji+Aa0uEKrfS>Xs`=rk(>&|_&dV3nE;2e>Z%am><Z62oY<DyLvfbw)_o*+CZ-y+q
zH<W!$U$~igC%hQHw{=Lr>WPWxp>4C%yk2oa&bKIJ>5$`9@&vkKKlkTzzf6?h3X4o0
z1g{6G!F_c<du*6eu#y#;!og(<;$WXJH6<P}k~DaP7YQc`Z#lLnp5Q}>Fh<v2ZR=sl
zF#<WFn1!M+s<VIutRfiUV0LETDl{NjqkC@*Hcq`8j|-K9IARPqsSZyI83zX=vYat$
zytzqlZ}TxyVs^p$&A57d{>2_2XgcZRW1`j0dE|HY@TTN7u}CR`o`tLrvKS16fXF$>
zB%+29!@&sxb|WU#EeK@HMC24OAd3)WP~;F7Hy(kpf!HW#q29M>!%QeT0GSq52@K(g
zV2yBu76dX!^>9`qu@U<8?fIYo-~Wrhx(J1p#OfWpdEX>Y)>m(X$8Zt{msHZ1qT84c
ziN|Pta2744){O=+HJVgSd{po3+(FdlM9oM_%E~NLK|Vba;^yZ5=HcPyro81iOf3l~
z0fAzeIfANrlGs@i@_}`Dn#8K4Y}A<&OCvT3LK0$yHK7Ie5<R=B5QCD3!Ur*g)ODh2
zDi&Z8gwLK@SYuAi_$|VLF{q7LZibtP3`$B}7-2>Jli&H9Ogg+vW*;eXmq9H>GPiXX
zm2hgRD!t{|TIIX<h`qQaj2^<>w2-ryY8K=~>?TtVW+B&Nl!`7I;X)KlINpixlsV!E
zH|91#qzhz&J_udI6D1}j_l;Y1P1G+jvRidG8t}n=n2o(-x?XGFM<03gDq=R@Cfi~@
zku{o_RC5;hP!R}{61n=;KrVR-YFv`QJiAklS^*ATxD6y3<SfK{gIj2kWWiilqTw+u
zIyTyE1cgZ{6dg1bKi>Ghsia(Mr{pcV_ee!dZtmJo{)6A09v`Lu26nDbIbWj4`TQMt
zVzRyOr=E(L^=j+jO>z+T+Emg6&$BPW(Gb1O5_KO)R1{(s5}LU0eMy+32h61eiX3=m
zn)+qyiRk7^){*D?G`Cl=L(NAd^=&t-W3}~_o+I_{`uMPX`EYX-P8LqXRAL*`LJYy#
z#uEFb^(3)IIY<&d7(G7t*T1-YbzXm+Y1r}I{ps$6-j#ar9+^hD2~6?&?0L_}cl_v1
zBfk61-UePg-c(snnZDWhDE*MixApeNZl}C;JzVPPE=95&+;8aAYI)$7*Cx4K*M=*e
zT3oKYP<}t@ha0h3rWfxYV%^-K0h?RTcZn478rwmiKkKQ+RD3;Y`>xAf>h7l(;9m9}
zZH(AL5^OLVX9a2U-sonkXWRspB0S>KsebLBM_ad^GtKY0Y%w!&7Vkdun0Dv8Fiv%l
zxTMs5gyhn9;ZBx3v9v?P3u5-DSyShveG3{Et;rj1ugE}%LVOHd8|-yp5?-T$MMFeb
z+=56V5aBe=uFSz0EPerJc(1j>hDV|Piav9F8O#Cop0R$UbPV)}%Jxi9Sx%~3N<%~`
zwpuT3%k}lS>-%&&Wn8ph;0x{^W!fhSq=S~E3bqkFM))ugGa|yAf+E<7-6Om*$8d#N
zV8Da0XeQ?bQXvks0nSVo?jZ2Yz6OLv2;qILHpKUE<9rB7Nu6MUa4+N|h?q^d5W$&P
zNVvN9YUSI1^)LUc|Lota#%WSocV*={k-3bKr=rUZ!@QY$C5~t`s<*m1P2p8_wsz@e
zB_|Ar*?dSLm~R)DnDIOvB(W+@p=MQsRs3c-loGc$ayv)9yPZ?uklC6=fo)w9oSY&F
zpvnpC&UsNH(9Q_$Ev$gam}%2+1Uq4Po}r{xor-&rI5Ts&5H&{z4KgC>CJT?vAS`U&
zNEU8a2U%hu<!BeqMJYw&Y!*`h@(+LK4+1gL6#Hffjfj-2_ecktq@9Cv;-X{MqNJk^
zRq-y(prQ_*)Tx+_L5VcDlo6pR1A}Es!a@!rlmtmw4r<TH3Rj|)yhl39$dt~6W9nB6
zVlqe#N`o(;iN+q0Fls=|$p`w_+qw2i`;<p*ZKGC%N3Fe3>N42brU)mV05DA$>;#<<
zj%dnRWLOPPv+Ug{yd01cf@7YeT~w31MQ|UL=X>YjT@lhbly{yEWW)Qgv}6EbIf<&f
zldYAzv3KKOcLWK5PC|PBXg~P7Kat0uVLS79b)B|)?%#TQ{DHoF?{)8N_V!kLyFTsd
zw9K>Xl>>gfD;!(zvl)CFp%UltOq7KOO=(G}KByFKHh5SiO>C#U7v8Cxix61L>~E%J
z%Cqg!v`;rV@078lO2xL?hCD8t+&$j^&fRhH)JakzVrGrL%V@|aU(YdSx1F-k7?LwT
z%-pFz|JCJ1&R81re0Ot5D<4V{w2|4)9E_VIx=|G4BFiiLmt$Wq=b@KZDU>XmUuZum
zo845hA58jb+)f@3yrQ(PdubXYN=cWX{N!VizWaH_X7y>)ySp?Ur*z4Z<Ke{LUwC@5
zX~r3~pVPQW`8ufOehcPdt&D^Fw{bg1Kc?%G%~t9o>$lDBy>FZkB-co@Jp<#yDNqiY
z;BiRn2yNW0Rrm8UB$z~UNLc|EUqmX2QQMQwT9jztX^EQq<?5M|XIF|XN!chZkr0Ch
za^fArWIDjyJL^%Cqm^m@8cZOe?P8juFX{4~BSqujSCSM7h!WIU(QxF~?^t%yn~g|X
zaNgWmXhK-fuuW;Kqnk^WbTkqlHI>SaU}!p(W6nO9h2k3R+1D{HPmzvRZYRyjujsc%
z6HlHsksPFOo|3R*n6q|oOo@GjS8^RTSnyjB3LZg3I0zcS$el!kgB`)fLJ*;F(K5^k
z;7~?&CJ-|d#jw_69ZrqnKpIIjpad{!&~G_Wh!S`P1z}h29^urkb^42c`rrL`|DWHO
zRoNJc6pqm)hAs(-eQU!zQD@|7T1MSe(cGdkbFZyY3zo!TAeNLeg*RB#e3*J4$!(bI
z`_}d*P*<8coo)}u<MDpR?agv3O{RnR?$jaKz|?EV5?(_jvjt@;(AdNo5-entuqcfP
zHj2V-rpm+JNUaVNCQxeb35XyeHW(O!42tk!!DM<%I7`d{aaw`_6!pEyG24aoz=m)Q
zSLG`Ir@!%cBIQ(fqd{d6C-Wwp-D=$i)}0v<Avni$Ge(~fqbE((<p_ZZs9EAD3Psl;
zC5cAsd7iS65V3Qa(FP7kV+AJmLdm%>p~EsUf-+Rdjbji@#vUNFnaIT=5U}0Gk=m8|
zXuXY*Y;=IO7w;OQHdit8sEwI&;qI(LAx?xamWe`gxo(4tF>7LYm^pGJ5o?V{r>!E2
z5|cHfK$tHRd>0ULC+`GkAF-TheU+PRzDwdknsO(ajg7;h#e9&vY1%#a9X&E<KHkNL
zzyJ62wqW~)*2Lpdzxj-t_jJ$k^*8JJa7g=6`~Is}DzeOy_PTFzdQ*-!5zV#{mfS&o
z?>;T%<}}V*Lt=$U(5Q0M(K$*&nrFVmwYFWa`zYb*kOusa!d{+ZoUH7mxs08yTE#|p
zUfo)iw{PzsrsZDet#udkGpB}fw00fa&VBQWT3?w(&8;N8dx!RPJ-?hkF4d<b@%W(S
zc0!-a{u0U{MPs9q#+8a<nfrC6*Vndu4xh-?GIArwDstizM(~73;*X%mGM)V>)gJo!
zd)W>!WBcW%AM*Ltu%S|a`QAzK<xeGdQp(q$!#?uSwFbVB=g*c_Zl}ObB~Rw#WKkZ(
zeWgubSfV0F*Ec$RPbPjJ`b08~Ylq%wZ9@a=iN~f>b3JjMcI)_jwd-@ID)S<#SVo{|
zOA;GJjq&P1B8wMxW?VMdHdvkJ+*;_+NIel)QelIddq?G7l(x_SGa)0cDWb>j`<+}r
zM*Y_HNaa3s)RyV;gvkdz=)T1yU?El5VAK(h*gqjjAh`&(C(Z)QeHdL{9O$;TSg12e
z6*m@A5x#jyX-qdaNh+{<tM$82`?t^a@`;vrdQwS;@O5|z79wytP@YnTimQ_cJZSie
zNWQPpRtknp!70!q$RL1802pusxpNL;?%R;eTo42b#1KUw!g{E}jY(LT!}`_wx}si#
z4nf8;AtjJ76I3}mJd-lJYnjN6q62K>HC)cG^l$&u|NLM7auDSdgRLKyqkCgb;gO*3
z&cW1Kj+vqo$!N_1cj92`F6FR~_08KaY`BDvh^Yt?B~|v+uyHxyYH3oa-oAe<pt}!u
zr<-*5=Jrq^NvE`jQR8D=pM~;h=GKu!HK$x6cO^I%M5T0$aP1UUhZBdoRO8@KR}=G*
zl&HsGu!dK1CsFa?k{qH!;hVHWBqiTn3yf6AeIs`z3n9QV+&e<Lt8p`mF8}Cfe?lQ9
z#?@kULS}1_1G-1yV@jm#v=e0?#ANQFZU%EB1W|25<k~2usN@G$+f!aZE(yekqQ+TV
zlz2i=%tw_H>B7SUnY0qvB@v&!z6D(kf?&&c3}>f^-KcnLWNVKlcC#?^6r*qEn`3Kc
z-F&sMk#cd*G5`ym!MbQfGeVFlrOcxwZhcM=RTA+=(W|5^5$-ff5wG4!1R*(vfhV#-
z5FcS7dL*2?PBK8+hqDsJB$72O5Q9R&BZPaOZd(U?k6}Xc=I#3S@BfL?S!|^q<LNKV
zZ_~TX`>*$ZyTzA3L9NxW?`1wI`c_xzhqyVEGWOa@nWGMxCM}t$iABIk(;|%<h*G^G
zmod^EotAihed&1FF*0+Ki7le{^SQ_5SvjD|OtDt$IC{ObZOhB;;Z43dsbCguB<g%C
zV~7lhnCJSkS4|07=4lA){Rh66^{c;X-+RA*EQHgX@=Z$fW2CLd3>%(^P{=kfC$Wpa
zc|g~v-<<hl&D!K3sY@FyojJeK?N3BXJtXCKlD=`iDf`v#c5m;3&+*)&cJZgixB7gx
z^K<AA^P8OCispm1ywX+IN?av8>egZ|9bPu3Sw{8TwVx<`9OXu@DazURqh5ZgH`<q<
zmi2X{3vCPO>dT!d#?WNfn91c0uNU4v#^De>4eJ&MwIaS#89KiwHWQ37OYE`D9L?ek
zIfYBVPPD($#GH=CMw9{;jY(TzB4Q`=m_%%El&!KP5{{;3XEtHG4nFxf@_B`PO%cI&
z%}Z|k;NH_iCJGF4GAU%nQywdLefBa-58Ax5u#H&JdqrCly@Rb4G~>$mkL4hGb9*=)
zvTStuc3iIQ=~_R(uFnT!p*O!<4!M{*<=#mWg{Uq^ogmDKJ=mj1garmWcZ+dZ4U^{*
z6cS-b93#bWa+gBR1g8!V0USX|h=s!fKAaNqEEqsxRF<GnQlPea{w6wN+YOrrAtfdU
z2QgDL@XTloAp{$o;fxr=csLFpfAN?9<p26FzTJ2XvsQ~FLbW^<&}bdQDKnhnAhGT0
z2}D?uJLsr&c(iC}I!nJcw*lXCnz)3USQ`Xix2B-Wz7zaV<ZzttZqn(f^C{omOi9{O
zB!&?;cM3~v`;bI1ND(sPK}yVpqC%LkMxUK`u(}y|W*B#M^@JKSvolh#vmpxwMQ~DQ
zA`uG_b0G~eB@lrsds1NwQ6`TL0SB?mKss@N)xxGEL;f#6`LWpMeGFm|3NsRpgSaGS
zqdqKWU5AOg5IY<eBtF1ILBvx*8$?2#XqvKc*6@@`Y0@rtk8CS&!{iB9PdAzwbm1{O
zcVZ$;o>azl&_P%c8!>|lRA{s?;i%RbF~_>OiCOgu+xF3@MGTPb9?hDnI|3jcT3Dwd
z!`U1qb96bLXy1s)IVQKRTtKyXE<B-k1^PgAXA%r0@}%L0w8Xj-hv`vt=We#SOe#dv
z2rDGUM4ZB~cg@<}JBYe5Gr+;_H3$<j?%w40f9EI4Lap)k%lgeBy_59x%gevHCtH{F
z;@?^KIp1|zdq`NhH@}wJ*Pcg3qHv+me41^%RtzT<P6rWNFW077582$rcC44}iN1fi
z$nE{jZDCQyfiKq$g>LUcM#?F+bA90}m5nggl^^ep_xH!b6V+ye#>%<yA&uUx4mN^D
ztBdcS^)!K1Z}T`F=;;^FzgTO!nW7(;J}(QG!opg6Du#S7t=Yjs@6vP`^Y5qa`)m8U
zHyNp<>x*QHsi@H|lp@J#_~BtuvBNd&$Kju2D|3DB_l(A)Kl?@f<+Glzqru7-<?SoI
z`)S!orcZ4W-A`T{wX454qm^4}GUZo6ejWKCZR+w8yrew)RLSmQyc!ADtkcJx-<5TE
zS)(6w?2dqii_&t-{2D&lMlwEHRLUtzhP@z_OeFjqItwIH51rez#BlEmsToOVXMUiy
zGJU6%Jrw~3Is?qctOynAF`OtUm@vX=0JI1CYs?d=j+85AyfVF<m7nA8-RNbGJ*5}Z
znVCkYSQCN7b<pitzvVofPur!I_Sw?Z_pMdq*9`!BK!m^Z#xmvGgFY;$W0`JlkLii}
zbASG{KDY5{G`{h4!WXA<cT-N2aW<OGD^rS8@{~nfA}H9SxwCn=OZ-;9+UlKH@<d5N
zE~Ee~oY;w&;Rw*!2NmW(5->4m3WBOr77T<3i4X`(63hi-@6XTe*#eX87pcKhB2i9D
z-!EAY5s9NkAZ%lvAi;<ra15v3eERC2{5SvAKmXo+j;+n&DJ3Wu>#NyvmwYf;$isbD
zWe-^rB_=J#*+)+*gGR{*c4u=|BNI90(IZV0jk5FL;d|}jNQYT9Euwcf)5Gm@cRZf*
zdFIK)*{#XJdSw=3gqcr?MU$!}7fz5tqT~lk#OVT0p@!i}atN5a?*m{3k%BTsbMi_t
zz$wNcgz2PV#2r$c$=ezvNPsI)B|IWQ*`1X=m^mV}CZh1}Nb<k`;U9IQL>^5dOcH|N
zB(ZNHD&b>{P!&fnP&0s8g%Kqac?A~>w7Rph7cN5XqlD?iD#FoKC{&Re3Mq)h!bOB2
zJLrAH%A*F&GRWghmYF%Eka>7G1^WghbFp^y)*(K2SmHLKlkAsK5e7ALC}|MQNRfjO
z;!|i&ohgh|Z6}ooFbFQ4T%~9X@)(@R$+AjU75A)p51+)=T@J;XlNtdu&l=$&*?J}M
zl!Vk0r9E~{-Ul)>yGZh0J38&$oY@U7?|-Ck{?6Z`GV%JAt=0}Vy6rE2?)_(PTYKu?
zt?l$fKfUskqFJZ=qE~#}&Nh#?kD^Sm?M6p->b+KKOp``|^=&gwdOXb5K>NPtt<j1l
zKip2!GR2zF;_-)deYG@A+*Nx%zxFn*dEtGdOOxa2?xDPSL)et|XRyX}uWduh@MukG
zdu7VV(ou2>d2<`bcm8Mp>gB7;byo~857RWwIjtnKkIgenC&ktDL8AHTPAGHy=JNTK
zKKH5UlF7R8=F(oqo12tQhy{C<_RTn=&|6E-SnmSgRXNQ2QzVfW^zE~s$;*;%kMct;
z^N&jVCb+|H=X$Y&n_!&DC{Qn4G$FO4Q~i+fn>rn%snW&DQGD~`9PNheyFrKP%6$B^
zO>c5b7OUwITPLZBr%}I`Q9~ySCoi%l(eF8D(}x_JI}k<d%g`Z_xxQ-s#yLg0GwK4Q
z95El=89q}z%S`SaAtAG{S+LR|W&lF!ZrGga69-Yu4kDV3OeA?BWA4z{8|GI}57v}k
zP31GnU5pkk1ak|w;pfn{`ZD`qdio7Ojk51HQksr;%NK{`{%$_r&t=H|oBFxb%hUSn
zukDl2eU`g7^J&WUMe?n3=Mbn6t4_j7Ndnf1$cVx_5FA22#(wQhognj#kaGkR2LeLG
z>H{DHNAwgFtm1HnGl)ctm?)UsIV*usnJC!7N{;&UaeaESb+@ra>p>75BqKmObjn~A
zI0DTqvw(O|o(PDsMe^+x|MEZmZ~yd{*HWps4H}uUk1d7^!`99oQ7EsQi?EXuFbJpP
ze0OSNjLYhl#CJ-?X%r<K4vBT_mS)=bozkJM<{W)cQa;Vg<NeM37q|2M;jviGDHR%J
zaRj#N%+Z8sSWcWZPi!2NQ%WH0WF)k@N5n0mG2aG^niD4?Wl4dE)M&U9la*jW7PLW<
zz~O+{PDIgn<lso7NOEIu?f_4WYv8~bLC$bjW-pXG@#MYA|M3t1dMHJ&o{Y5^3-u9Y
zp}o&HGdNkmLYNE|pg9z&0u!uyudQnqj7W(?)r@=9S;{;MwRF5;2N*2Dg^3D8*g3dI
z7tTVsAa26D6m}R5kPyJ+#^fG@MZ)%|>dM$QP>Oa%6gRIP68%zJ-__PueJIHuo_YrL
zXr%1U3K9TXNC0S-fQ~>Y=@2jlSvYhKbu<zk;Su2|8GQg0=rB<MnzIZuOYoM2jX1g~
z3jmAZX1Pcky-e0NP)5>5S=~G|WqkO9!~Ng;yV75<SGSuIKfiuD#@$_sU;4kXl}?Ap
zPTxP(itT33v&ZnuwdWGYA8XmK(Ud@STldNhA>AdgM`s6dneH+=jpn3N9C^A~j_i4R
ziZmmu`d&w&jImwtw7)hUI&EQlW%}@VygTKz7ky-*=D}r=<3uFho*)sS{oEv)3dw!8
z!`=U%A^hsqu3Hboyt|DtW_ijgZ~NM=q$rZIqcDb}z{yDtavTSgEhIn9KnxoQkmMi`
z;#dYO8cJ*lwg44Mv^Lw`+~sR;Tkl%yDRa&-#$B?nD_`HOPk;URJv>&;LzzFnJ)jia
z5Q~=vF;m+D6#C$I-7J;%w9ylu4!C~mO=7Xkj=NoV2HJ6TKCJQbUTN+ZNZUi(-xMXE
z*SH+0e%t6x+8!=-_Va===G&29qyZ;LA3EGdE0o@?^m(~%h>tqGi4AB`#<NQ)W!7mi
zyB)SiEE@DO>4U+TkUh8X`_!M-{cV00;kQ-*`Up%8PKqwgFG%N4o>!g^oWxSXE98I}
zuU1~OH-t_5zOT<5Y9ACXPGj=a66FkzIOevPI0m|cEbk#tAv2N&>^v)X?VJh1Fw(jx
zBb^l@AU4Sfp>b1`CQw*<h9P91PS-@A;^tO8$){%<H<TN|1|3s!2X;j0PoAczww%Lk
zLzszu_r>e+<+Qtfm4}|_*`J^5bGzzEKc<fN_~qw0pN6Rf4pwGna%D&ZF{LpJGI}K=
zWOPMvwIBfTcIoCVb^%Dp%!c9tgn$u*(AXO=LX6>&2#FFv5C{q&3jjkPm=h<$fTn^F
zp~NX<w9E5)KJ`xxYz1Qwk5<^%fC(^3l9Dq>F|DRbxCtlnBob~Kadw&>zj^qR|NOsw
zy5t&*a1!mnPDnloGZSV>=nZfz02$E88_q{8uw80wOS^8&IFL^zx7H^>1(Y6`h&fYG
zS7l7ioRM}zelbli@5kfK;W+rThjf#TD{UK8S3xzRP!gh)fC`g&Mj;KO0FJI9fsAH}
z{1n_H2Ei7f;p9|#NYE1kvh~PFjtCG4DBYt#kC5&%Q`w+JIFSk9;=E(24uq)S39K6@
zAt$TM?f|@6fb&22;wP-eJOCr)5rNox0Pwb`p)CuANdy9UuW2fd;<iQ^A_faY0CUWR
z0GP<eX%J!o!*Nccl#)l~VPa?IRWM;*1ZONgELntjIHlk~Q%D>}<lu(N2|?T&v=Olz
zAh<%WEhbmDWv#yJ7V9+{c_2h}aMvt_0h8d6vM^?jZX)5q2I#FzI5GrD;%+uL6Ga1Y
zVIef{p`j^)iIgcB7zc-gvX_W(ToE&%Cy&U?Dk9c8Q%>N)oKeEUQ({)F*xZ19l`QQC
zzn6D^_YcV$`W5|AzrN7@E5d90=G`^_D1P)he6(+u9<sl&;sz$Y!%c3}rD|g?K-SM!
za`v*Db!#0+r^!)@pIx<u^lp@-j4->|w${!z(X_uAQiW0Id_AA)xu%sT-YOIv4|m6x
z``cs1NO<`qgq8u&1y(4dov*%bEEy%KRXohY44BK?Uwrfadm@Nom=6beF@sKXv1U^G
zVg?HH9SX&ud;uEj!Y}euY4{1MNQ9MvhNE<2={U_EjWi|yj>cQtdfn2nd|0M&P-<g`
zM-P48o*kR^;W*wn@8>*zhV}uh$hfHxY)6524IHjzuur=4#$UoRruf>&J9Yx>UG{JR
z#B-NBs%INKTgLpPc3&<Zu6V)AZM=Qwv_)==15Od?HljE5xE@k_M>z=0hVZ;g3le{W
zX|y=B=WpXu4FjgzHt!HJcLQHN2h-3Kf(=oOILC@i4F=&zk(jGHc2b8}Q8(y-&48N2
zo?3_K0PM)JA;2Y~@D5$bdQK;$;RDRCYz01j8^<GefVhS^)XlW%Q)>ayYjgt|r!S8C
zAG|r-?B*SCs_^_+uh;e5+Vg_#Hr^cb4G+4o-Fw=nsE7cZq(~`2s1zCy&=AnT6~ZAb
zIG|qK1%d-N0S;_L1P(?S%#Z>-J3+tz?FmVM#odV!lY=V(iVy@i0)mnP<?L)>K*Zjj
z>&N$gu6=E?uG&|UJ+~9G#F!zoj3cCi*LSuo-Wx$M2_{AhvkQpVzWeT<{nP*MPrim^
z#;I4uj43lYqa&at5GLc})ceZn+8aR-%JrhLo{|il2W}maRaef*dvi+e%>!F?Wl};k
z1IZ=ga40YLhuhcl?l2w?l4!^Pv?3rnfU8lAt_!7NOi~guAR$pkH{%YJU`S*Rk{FN}
zLmdI5d!&(FB|t?mNPERJp<W;q009a{M|Vd_ts{zCLZ~65v9kv@1_sE55JHSx0TKj(
zDaj6^nkD`RKlmMlfFUDt_yTFbZ9`%}BLvDr5Tsz|Ku!!!*rgeciQCmO38Di)cqSvE
zT!INj7~PT}6CzHTSJ=HGc*ZmUGDJsY93+4s5jz@mlH%qNC5CVW&Ldbw4NC0&N=r9&
z2t=(Ov{cZ(Es$$hYv4+dBDh2_5F;f-rFjBJkWR=5oLrf+_DBQ8rZ5&_x5SfK=P}ep
z6-oky#vnMD86*=S&@l3eL9rxsCg{!yD+xzPf=wK%GZa7z1;Sbl0z3%9oFKvye*P8i
z|MpK2gW4%h-!JV)A|GMd>c?q(we4<;w_jc9r!Xg4EA%aHCm3^Fw(3KK=cr4Sy2UUc
z^Z=l-q?%@Ip0?}NhTf16=EH8XH5^olh9VOcrqZAMVQbZ{m^w0Juy1+T-@X}djvZ+t
zN54LWO}ux6!7mq-WHDQNN+LY%IE~OAD`;JwFYmE}><&}eA9lP^858!%v$w^NrEeh3
z_%-#d4Vl_Ne(f^Q^FzQ0ATrIeTYMO4_ySA!i1u*d*7>Fa9o?lqUFXX;eVmd>-%iU%
zV@YX0Pp_cl*X)apYb-POZdz#zEy<o+scwhr_&IIL<=KYp*C{P;O`YbE23MKc1^f|B
z($Y=NqDRP=8pnKDG}^YJRKjJ|Y-tcAWunvob5p+ON?<FGJ7RBQ_5rDNw@=%8bV>|F
z`5_HED(H^v8&|<mB}7XSUoGvxJDNtTo><I`GMKq_>Z7#_P=YkDc7W*Ph*$t4(xBnt
z8IwvnkUjD|Z<hG-HtlbE2fthy-DEO0ZxF4AZK}DgJ+s8I?0)>^?aO)IXOR<c=l1k?
zIiG8_)<=H%qP+0JM3%73J|dz<N|2Kj7gr<-Lw5rW#{g3^ihw|}+5w6M135(sb`rrr
zW}+a<;m!!xz=Ya47XU)&$OBmmNC56wQUnl%1V;b~5{0fI;gIU%Q-6HM8n~*Vqm0lx
zI)VU;%FI07#qw?a&G!Z!FnI^eBa=WhgC&xjAM3yRFaE2)`cAEyIIvjS-Nv%!T$DP5
zk103RXp~D1cQjWih7GI%k8L|qB@o4&C?G;35>l?ZBpMY3(S`RpC+fbxoo>eI?(@65
zgUngxk#bf*ixkl#y7x3Qb{2$0Tna&>1QdpW7%r)iAh#Kcp*Di-X2|R?G62LiVp2vY
z2xTleR4oDxgKGeTpml}8K^z>x8~{i*sN{~sjR75+YXzjRgeiw_7{#k$VT{gy|95^r
zn5?OyvQbzyoFPSqRKpUG?&nrb+#EVNQMVpZ*f&M)6=Vcb0;TaF>d94z(~MDO5zcIw
zGEf8DW7fb4TjiYqWOt$HVSz}DHvxfxAS3y6#DuPZj#7N>0O|osBXo*>u|8thw)UlK
zYrUOS7;9}!#Cy-K66zz0RdCBWAz&h6(auHCf-oh<ZV};WQgj4}YB-L8ZqDKqWUR)5
zYUs#>;Z>9)*rf#&4^KVZF`#!Hj!+f09s<eS3Xv)XR2*CF0ArZl9_ht@_($XomT%%~
zq?ZLQAL{!xzMLoR^0@VHpUU698+ZNs_K8-UZ#B(;OLf@At<`5=d?IKlAc-*?6M#d%
zT%Ml#+GDsGhY<+-^|IC$QDT_Olqt4LZ@5LiXfUy0MU>%YE?>Tw3XX`Tt91Y>J(|=j
zuqfXEI^rez0LkTUsO3w4_|3atUzaBe8I#1E@%Gh3R4AN82r$x!mUYb0oJ!OEom`;X
z*XaAC7dIh9EJC-$_9~|uuOfWskIxF&E8}^imxAdQyf&Ta^3kv7?U@;O<t6j&%OTH^
zA3YsYHzPIcxRq`z*BY+6KXPNlkBz6?hD6WGQTrQgr)Z6~3+-pkiuIQKX@lTT*5w?_
zj@O4e&EzBQwmu!4Mme`^CrFMx8ZylH$<5Oi={`>1^*q73w(Dc7Utv4>k<0ul^bu_!
ztInA~8_f}jwnd%@!XahQupvtERe+j>vNw+rb<=(+eWdt^FsM^f_3aapL%AVg0H(o#
zk6zD!M0Ew)^-m`n+-24UV0{mVqpb$ZR$FMTErwyz>5H5DH!t>gl1h@e){oEI<4IlH
zdZ2k8Ztq7v@!5QML3|C!DUDDvk>?Chl?Xh*%^ZuH0rqIuAhxyva+q3}V?yKrCv<iN
zB;pj%$T-Nt1<^1p067N&a=1$ZZA2N_oC6(LM3IOGsFk8onryqYPoMO<g%Q`M#=EE;
zoPY;N!N&pfYrlS6|L4D~TOvpV38$kZByfNp1w3xQ{Wt%=KlyJyoYpnv$-_Iwe6u4D
z8j&*PH0ZX1vrNfbhnbNR3DT5(L-5*(MGZG~u#`B|0Vxa>C<GIxl4qf~Kg^VP&vbvg
zyL+{pkNcN5DHSJxAVtg37L;sZ5lSpT!~$NzSHo#$Gfdt3NRc2miUBJa^e|=wv`ffD
z=pdn{QYMduqD4gvL_wdJOuV@lV$8tc>c54F9FYQ<F;Wh$LB{|W|1IRWSb$>^QN%4Q
z^MCr)@1O^y2=|_uY6T(|&})rcye_b;M1j$PN)Sgg7@0aCvWJ!c>eX>zj0BxiDG3;c
zTujoGOO8T98PS0E%x50QpiHp@Wn-qahqeJ!M?|XR<QsyA^M)ayGsClE!n%T?Ibd^d
zSKr18RW)yGbA`3LBbX(E5R+cASOP&Yz*Mj+5OM$r#O6#C0K^=gjTRVgv{w`a3tJ7*
zxFiWD#D>Ta?nG{oFeG8^OyqfhNaPj*84`m#M`k7DaG=%;QIG)ARs&<8+`s%^9gqM1
z{}Oxk({tbN==pEfk3XWsnHKx-{QNNIsSJFk@1FI&;ms%k{BT*eJlzP@1|TL=@0I8M
z*egpj_3f#(%eo<#X*}HJ)E8{AZIH6?-E>gHz8PjwgFJ$DRjrFpcc0zwdB2y8sapdl
z<e=mLV}CpWV27diHCMBFqCxmz*Em1?^~dwZ95`k@IN!V^83#mnPC(2UfCSvpyQP#G
zhJRS;NZ+5+E_l4P*b81^cSy!`s2&G7pQ?UPok8oSoSAX~7%b>@)z&{e$7O@#%&+I%
zeaufV0QDo6tCH~oQ91Yu{eaMrG1FDY65A_+N1KTH4VO#o3fj=CQ*D4oO=BjtJFeRL
zg)e4y(&0Xd!O+BRa(@f6HM+rP^6R4y1E&$WK@P%w)L*r$d#xP~%&Fg%*u5Fhuv}1?
zh|L5qXD*5`;O6GeT$M&rWgr-gG^|jZ8^zgpq`JCnsZr<VdPDGJxC;eciPR~cpzP4P
zxxrS!DZD~IVNUf7_~}=)JDMV%o}&zOSwilbo@AKPT`9NI?W>o(&!s;5_1*S#y<Qf3
zS~1<^7jw#4{M6ds^Gs09cIhxl!Hh=WXx$76!5t$2Z8hsqqt~<hWJIA3W=KL2z=(io
zEmQyrG0-dm92t=j$u$BQn1E6MB7sLR3y?rq1cwkgAcE@?NFmDg;k_@<@pN^i-fN(Q
zm&9Fnvm7%YUxz+l|HZ$(o-`b3f8+vG3PUAmm@Ue%^yz2+uYdF}|LxV1SnCP%2#mIr
zyH{bGUm7%+#*7+9m9nI%#Im^(b@ggdGStptw37q^DPcfs-ApkgC|G2_+wb=KQqqv-
z{R}Ve?{~ZTe%6T_69>7&#Ou>0g?-g+AnGE62?)&qOCkm(@fD(gdms`8;kDD?Qplf?
zunUU@5TQ2jBcTK%g*i9?BZUKy&B2bn5;A~6NbteY1kEugYEERfXIp`o3<ERbMocaT
zBE+^jP5j4S{Z0fBkt<?TW|5o_2ZHX+i4pVQ5y$~*F*<}J=GM9anWup|3^Rob1vra!
z*c~W`FsCp~oq3W3!Nh5W!iXut07uX@VkccAn&mE1%oy7SI5HdZK$yKhA((e|GY+%9
zI*e}plF8Oq>(-MrGiw$@(zX_i%2Aj+nj~T)9#S!|U_=Zh3gHyM$pA#u6?|26MMvTg
zcIp^J2-aPh#SJ;LXk{Q%BVt6sEDj3ZS&_gIsUsB<ZQ&_HLf;SsE{&i}4g#WI{qA`C
z`#-_}=-=h~Vf!}O?;asO#;-5yL(or$YM$ONr%wdCF=JEK)$SA|+ZJ`}cAVxd10<>j
zgKbfpUaoMFJnx5bF8=h;uD+!iD0aIv3<%xNzKiYI)2_S0<N9p8|G}60mt~lCMOQ9i
zzvU^lip1!!DS}sFblG$Nq_1D`?p7c4{O^8cwM5Q|;CR?iw=6PY-LMn{!lq1%mlx<u
zXMxN0M3t9a!nL1F!cd;zFcAn3-6p}O>*kN^WuvD((%RC^<RsK1CZ<o6Lr#yY9um>`
zq75lWd5K$R51*HSOl<&KtcYC5^Qw4A*1eZfTMUcAtFc|XbmG^zKIz2ZdklxQ0(tQY
zxmGD@JvcRxB4mM3GMk%iBIvl+K8<VJjlD&=xX)alI;{FvOM{`&%ft;O_LLdUP6l=q
zf6_E&MK2gIQ(P%At{Xsda^yi1QU?`h@YNY{$L-vuq*gpR<3xT|KPK3EYDlxSC0Zj2
z2p}pnGQf&kL}b@yIM(x0(uFi%mIU;$;l23#vQOiy-E?;-hr=)$JbrsUoz~0R>;Sam
z{ax%h)eF!8XrP3YN|YhZRI+h*fJN5`%x+!XB?VYhaBOJiY6XQEF|2#pv3JLW(4wiK
zP)S6DZU*EK!U6%ID>4x~023g&qi)Wb6UT3<aY+yu%wWwScsTau{q;8=Y~6JASWXcW
zQ{jSqnDPPn<}R=2)4%*5AK!80IhC;ti2GYY;}OCt7V*4${LOcN@}K>aj|!4^1kjg@
z(K_6H1@%)p-qq(TmGHWGCi7lc>UAZfhz^LW)-+_;I)N7^6vLc+tI9(j2Oyz5=3&gy
zvoniiuXmr_9`<s3JM6@U48R-?h~R+WMV(!o6SX|0F@s)6MiV0KkP)#0B@2i|;DGLM
zJ%^T%ffx{S{1y@>VtWSW@PuKOGDSeRuP01<U_h{lku)4yLN_Cl6=J58y={;k5W*&e
zD{=<y0bMXT5QOvp`S*U06B;BcnZgKC(-r33dbyei<=iZ6PSBHZXmbam&|F9%lXQ|9
zvhkt;q9l>INMvTrrQ{T4!eK@+cJ-VI3_KwTCwDZo9pV%63-m2;i!M<NDdD<C7F!!)
zi>5@V;?cVGpfFu381zlI!1c;a14^n(g*-X{0pivOkuyS5PeSA%;DlT-AYdMZ!*Y=t
zRtDGV0g^}d3Y19<j35Yd3GV<YfFi;OB4<(q33ForW#k+#3@s=p5K?WvwIDV|gI;ao
zw%9dAsXzEp+W&k1TUo#5<>&325#QX6e1@m*&hNhT*PoZTr2g^Jf4u$Zkaxh_sh^Br
zy`=VlMr$8)sWKxw*lHejIqMd@4RaZ&>~cSC{nAfY%7d5NX`&U*b}=Aq5bWXbd|Id5
z{Nix?hIU2DdRk3*TLB`naBu41*fEbDGt(xVV3#@FxAkv6o}NFQ^1v`oR?2Z0Vc-o3
zTx)pdq5;4-I$w<wt$N}-)QG6WH#W`6B=wl!qQ}YO3w-``dG`V9f%ZqcNdPYn(_7eo
zCCloG@VV>h#5V9@pQk~dFLXqa>Sgv|S_{^zX26=cFWXJR^&s%JM#j2E3g!c+$BtQ)
z@o+V}8LatqjSXTmv<jvHo{QV<8@MXl655mZ9)-QccyZ3CvkYtGTdP3Z>d?1`h=-<k
zuL%p@J+j>7$fgUyf$fANIo)OYU^$CoFva-;;xQuGvWg+@I2jlXxK{NL#10)d=JjNg
zqfH)XR6;U?%qNXfu@5E_TrR|ByD7*R)MDS`+P4Npt|zDZz3*SQ9(HQu%iZ{DoL;<0
zhc_jivYz_W$Mw<H=PTJOei=jRGIh4n@r7ps2t-LUNsa^nRY9vkwm=N?0JQMm(oo~T
ztihZNIYFzA0}+HH34#kqW&$E6bR!H9Vj@Ce3rrRS04RcP4g@F^0XZQ6IulD!C7{q}
zmXVN`-~8H7&)xx>7$lrB9_L}wGzlM$iR$CO{#U2(TOW3L7-c`?F_&Ee3WNd~c}f~L
z-~12%{XhAq@A_JM+a_W6U`Jan5gYP><BD$MPksk<6X)m~AVssF(9Q`#wW6e5=5P<n
znQ&<)o<L9m#&IW1yW??ppiJ}eP^O!FoW}cOmZ3;O&_*oXG#Zd?6cOy4Mk*$daOeR}
zKrX~oK}TQ&47Ua2;H^p?A~Ap=Ab0==1cMmaXs&3ggd%P&XrzFG+94MRjHTi%7=eT#
z12=PEs}S7D0RfUd1CQ>}0}~F23m`im_>X_^d+r#mM`8!++8{HW&n<Bw6UXQZ?TUde
z5k$m@QnWeRhN%!mRE2Rc3o39UhPmVfi2?^IDVVP48MBHR5F-za?f}6O9RdK*(FBZL
zIw=8W^d(@7OcVss(G#uB4FFT<wnX--*4tvS0sFREAYU3<bYg@~4m>j<K-wj_K+N2l
znX(94i|TF>yVBPUI3^Ed<fvUC&{LP5(7-4HEh4j2v!t}C6DXlzIC!!U4@_nYIU6WL
z>d4I9fWR~&vAI}NWiF%r_&>PY{q9fs`B(kjHS!@y;riHq(f#+nWI5&Kars7vfB$|w
z(oi3_uMJDajCfkNPH=lHsgcog>DsNWt^t)3Tcd*fw5$Oam3%iH=DJJCJ1h+n2Vv;u
z_VL;!@r%3tS2qU+BJ7;0pF14PV1%>zpj+i|6J!Mpg`2{=yY>0>{Ig&8n2~30He}C-
z!vG5gpoZlPWspRWcN*xm8?IoI)LY$Xsl2P4$5Ozu_Hv}BUv0bV<(Zv#a;$Ls!<}Am
zzou=^@mBL_*QG9vtn=$P`Sl1<tTkN{VYqApbM&Qe$kyGj2#4%FcI0{}TrJ&U)jnLg
z9f2+zPU%@`dTMf%6S912X_h60BCgf7m76<GZnm=)N-$b@dPuPo9<~umAMN^0HLUMG
zLKGYK+x~VObBr`Bjikl2$8HwaNUziOJ<WuDmdge9IrO0CPZ-UPuVYiK&m;(mC|cwJ
zdyl$i;1HxMQoVXygKw}C3KF7~i1UR8qEZ~#&sU)lXo#(1Kbw!|-l<=c9j*%|+aS;R
zi<k53ly<k%%@M8Zr*GPs*VEQ%&f|oKV>&;|5=ggEZgJ|21$H+huK<+8)fB<q5u+!e
zFmX_?XS&vo88d-nDG}<R9!Mw*V4O1}1|oq1BNI3<0RSR`GM45YNdQBDC?&9sf`JHf
zVkQJ|mhdKo;Q{0*vtK`5e)X;`=YT<bxbQR}CrmTNyhq9BKmC_ae{-pn_VaF@CC!QF
zI}vmN-XHSRVE5|j-2eH1_K*JJH_wjDm|I%`oEX_5ECp4pf#Kyib?iVIKE&1x5EM)u
zu*vSoO(EfqN@dsDJ7UIyl0mQp4MUnBiQ{gN`y=m<`|*%x(3A}r0*UI%%!&Z+F-lVm
z#zZ)fdSds)si7LAjP4d>g5lH&H~|nv11tq_#U2sn-W_%x*Wh3Qw+J9e%%%WH0MNnA
zNQ88SG(l9;?o7a()7GP~Et-x26$Qi9l}rqPJczvVKlst#LV!ro)p#{>58;4{FjItI
zdoF?&Cc*@|by$c=y)d|W*KkgV*agK|F%LFz;(|j0g6@0}U&4umyd*N>DTh&niUTkM
z0%9S)LS`p{#uy0FgPD3mL<l2LN5CF#=E1bo{#%ZH^^VE31K_66fPyGX7DMnPtph<K
zHw_eWNsyRZU<R}XDoI-ek`S5-Bx6KR-5c*qtlhg=Dno>mD-&V1Pyql143|WN9B4_B
z1Q1$a65TeJOwo`)jT$e88DX0JNB`c->5DJ%=|f9o`N-SPm-mz2yqW!DvQNu5`yih8
zA@#%cudX)TflyknPan78?l9dVZtHcMt2S@YTR&CjvMW|i`??Y^4T-1MyK-4u#flu@
z*;UOyJ^RIn;Ri2Xem);r7|j|cuLP{aK;Cs}Fs0x@H)Rgm?=TJ4Yi#cyKRrG?#I!5>
z!P+n#r(wLO6l@@Y!rF-3EVwXYUv-9uXK1U>H^Qtm!qP@t;|5ZEj<!7g`q#Ysy9Zyf
zlo5w{dJlY%x>Z433EQckF935o46^5fo%asul{$KBeBIoiE$^^L+f*)?%4X9Y_619c
z;R{@U4lvNR;;cq@+&>tHY(@I>lDD+n67@b}rwOhV0!#Vek!j~DQ`&6J%6$MjD-F1r
zE<e-dcbrPEiubAP(wqU(mU9OlP>N)TA;@yX1=<bh6ODTZqoH9#RgvuhryD|Z&)|*w
zhQlbT0U!twD=H$HA)#l7M@s|Jpw`*kE+YOA!{jhIC%_YwK{s&sXdCsuoh6Oz-RGCL
z^H;Kac{h(Vluq@sz5V6-{=+)laURQYFN2_+xB9Bzea^W$Q5tq>P9t403^hgc&`{5S
zTR5888f52-Rkm_uVPg{X2m=Eu=nbeqMuBiLVr-6#TnM5Qb7=4Y<`9NJfq@_r92B5T
zphS#B&J3Z1g5gLhbm5dBaQXFLT;5%+s!`$HI1iwRvJ)R)2(Q<F{m(!C5_K3-E@jH)
z_SG=VEQruSBIkrh;k^Iw?Zbcn|M+K53+5ymwXm=OWdUrAi3?G58AqK*e>%e!-nRzC
zNUf_6%m@&W&{Gj~iykRwGY2E{ZIInKB{Ff@hG{qqvfJVAm~QU!h?RsAd+!}(?_e8E
z*_v{ZRLaCcMnGUaD6w_OL>(L@1W;S-0(^v6dD$?7v$~c*#^}Jw;R=v2D|lfGLIui<
z3NU+Lq9h<RccBdoQ9B?5a_Y^dBdbRO2}cEB0!vO57?AhaDyND6lh3{a(-=w`N<{Mx
z1;eQjcR}#Xz!14`bPm9jC<#%g4nZR9X%@O#lRhFz0m^$!RG1x2#+w|C5O63UiO?(&
z(h^Ys*;s{lPDXh4*dsFZjfVo%gZ6?2y#oa~YZ!P0kXuBQ&W<&<*41I#x<jg$>a`^y
zVubDxz&SD_jVXXKAhCM{CJZn$P5_Ju!vGMFMBIUb85#h1;H3p~<jmj>Xx%v#2QDIB
z9aE5$a0@B{Os!YJl!vvpO(6u70y}zeRgUDLlJoMzzxTI>pZvhD9|T(2e!2eDQC^L{
zCwyMo->CoKV3Xtay#7^MtCd?Gc3Lm6;j|wxw)6E9%NH+EAEWYl>0Af^!nC>gFz<I|
zOkTVaa|_}5NLIDN<>DgK@ap*T%NNHa>fM!tDOHV>B2Hj(UCN-(KHosEH0*FBoALb1
zZ{L3B+prr-DjibW=5ffoG<xmx4Z3!mFfNehkcg%P8_1!Z&j>kZjUA>24Z$Gm^VjxS
zxqRI0v6Y)M&7V!N!a%Wsr;2eD-}-git|!ZPJS0L$dcvDqTFj(-9s}~W8f-xI0{uyM
zFR7Y&5IslUNfIAc*<M&YIPPR^m^8MOYOj9rEhm>gH<Y=oM#C26PHMN6c{?L2PTXbA
zD&Dq!dcx(hTmv5u{=>leb4;E9%5pXs<+LKNm0zU(5UQm`Bmmm0A#26C1dj0BBhKW>
zA{ReJ-T_v|9M=<vMuJ$y)G=n?u2E(*4p8u1`g=OQ3@ZrLqV>+A7q~sxb@gQdZn!lI
zh`w1Jt{C&}e)l4cpXc5FFizbs-`ls}T^~Nx$JW!U;iinG;`-!1!tP#fQ=*0PKJ6xE
zV{Oql0P^sT9N-G>wS#r-4A@LY%#u<d8)1-#IuUvx208Npq=7_?8ij<AJ0J>afH(#O
zqlJQqV1R*;SywE8?1BzJNDM#_!4$559yqth-z-1>XbvDy=8}O^Mwxd|CMoOXPygcK
z>uCGA3@ML!+|PMG;vhMjlOWnKPw61tcVGX@|LK4DlizI3d#p@J01*_s5hY21m;fk1
z(!+V{JxMXsEOVGE0wd>~jC`K=aOus%6FZo8L86dh-i=byC^;h>r}^$?&bPa}J#vOz
zMoi(=S+N3mZ5TijhDgO&5+`t^KnQoph{zEHT?vBVAYCE3I0PhM#6WKuU<o>SM$!`E
zs1QKTXwHCz;M{$P!i1FqiOD1)1|X(DcSbXab3j3=)O*+*zC<7n6T(Jd><u0HkALz9
z!IBQg0B~*2Gt6^nH**YxjLe1|5{8;V1LBT7urMeoqFohBqAX^}X`Be1EYV!33pC-C
z%a{@hQG@|A`HrCj1(uA~p%dngAOV4?fsT}#Ebih27~T;HdJe;AD>$QdwAvch9?<#<
zldelA>OcTs&7?4nkQ^mpO9KiB2egR93`oj8O@SRvBa+pMZrVYU87I~N?~x1<$Q+Y{
z_dxM*vgUy_h=gH~p+TMkjSQCR($(CCA$bQBZp}ii15gF+{qD|w{D1fl^EC4L5$m`8
zZ(#Y+ml>C`zFi*kcKm#z^Kk0xhv)U?i!}4FonmvdkX`cAWqr5#VIFU4e*bj2+QrL$
zR+OV%*B(CX3fY2O*gGS0ioDg>O!9ml#_7f3^{Z*ZXT}76?L5-@sD#m1prloBJ9%>o
zjKiLJ-{555{ObMrh`0OQoC-U)pt4Ulo`V7s(}spHChXoKq&BTBXe6`14Ff<9<N6qR
zi0QLyTbFmMzz*7QP2~s_!&!DOaqEsm)!nOB?n{SpKfJn?-7e}H<58YIb=!oPxH%VO
zR7!p}(FKM^H=kQHxU#J8>_w7DI&Snx;oH_4*A)KLDPbEaw?6RL67WU$H>nHm*P3sn
zb#+8_npjX&ANyZ_bKUTqUtzwfulD!-QEt|pvO7D>Wjpl}Aa~iHwRw74^f=}ok-_0!
zt`9^j=Dh>Ua<X~C)w~n(BaE-z6xHLjk~PJv|5hUal!={tv?*F8n4^CT6m)Kp$k8qf
zF;vTaHCMOmw_r08N@}}Tw})Gsk21_V@~yx9{`|bwx>w!f?alBiku``zz<u28nWqA0
zkRfqMG>QnoXbNF&=Bnxr-NVQzp&Ecw$_&bm9%LA30D>3+UI<Y#yAyC?q>un)a6<#)
z6c7X+PC%Xn+zAPRgt-xk6C$QW3W!L6pv(#L1_%+f{^g(5Gb1LDA#>1J=m0d`5kH>(
z^q0>cgXTQu{qAOuWf<=7hqSmS_nGa=)1K}N@Al^_|JDEV|NW<bd0|WpNI5|)sq7V;
z1EEj^03+6lELg}_2(s;TjU?8T$q9%8kvt|LAI+*NI5H+FN!+27q1?dWe0LZRZ{|bx
zX)Z(IRMa<zZW;#e3IwDA6O<qT3<x3$1r*?BmV_J;L)d!`>>fzo!8yz<0@a~dgrN{A
z0$ON9o+KI&M&wArN+Ft>h6;dV1Sldh$O6a|7f-h+j!>vIg^ZaE2e2NJ0Ex8&yO$gO
zKmGJ~g2ESLN2Gw&w67RJ?w$)fl0#5fH6rZD#6w5Vj+WC)x~M0k9OfJ?jq{iyL(?%2
zMH3Iq7+C<&h=3S{kS(}CZp5RaA~=(V9ndU-7&~-CR3ZsCObvVpeRRzc-5a{PcI`aq
zwJnNQ$j#j&nWB3K5h6ee^1ukL8pVf-N{(ef_E-%G61ZTn1F=C0pdcD?t&wo>4pC?r
zLBw0bpkOA0j2=T_aLNS}cJthwz}=A;V4uBqB<|?gFi0aZKyVtw{r)rh@_+w#IWa!}
zyuS~{{V*+Fxqp52!%^;2#AW*i))mIz-RG#w1Fiig6^_g0@?__7JKr*IxIS7l&cncM
zO=+l7b(nV0d;=p0HQ6OKuxnhmEx$aDZ$7)5`Y`S}XXgNy6xE|hztr+7v^kc*bDv*(
zYvpz8w=b4&K79Q*Yrdav=G|m1Eivs#UL2?BKtzI3D@Q`=czBbRN39ot5q$$kv;e_)
zao5)z*S9fVw)ahs$1wvR<S-V^DP0-TBLu?M+qUA;{d~cc(*1El#p%i~zHk$F21BVA
z%X?`GWPuEnG>v&P;R_72m3`Hmp5FUJ^i*tW-p1N6XlCA%f$|DG4IA0GcpXMR_slu=
zoS$I0<@HkG8eL7-`j^*><F^0mX1=7)hh08&8`HxB$VJkQ;aSs*)W~;B;1S#a3Y|A)
zkWx4|mpeY6cv_7Q1|zp~MD{Yu<!r+<@(mceMl_2qxCO}>OEP3?U?~x!S;du*uSf|}
zO^dDpeEy)*n^-qku%EU<SavboOox|~%y)@SuzYBr>gD4`Onmne_FmGJd!gwSPnpL^
z88~5Z%CR83w`wM4&(_f#BEXFx6x^99dJ96N8L<NpATl8Q7CNSxp*dzo05-&t$N_=C
zC8yd+1T2^+!jOrAIDnG?Sa$<N5&SJe4hSa9%t)?5Y--H=_VL%(Uq6R|2MY@gBJ%;$
z$o6pk#dqhA+eW*a{V+{NyO+a^fM~sto<uqv-{9>q+!n~YkAL-x|IdH<FP^X3sE^~|
zn?iQzTnf2b&Iq2dgli`t28%-Fvp4N|QEi+{CNXmc07kLVNn14Y%t}L<(RqIym<5)8
z|K`>Gk;dI_H_<Rg8b??+P&Z`95pA`!8=|{+B!*#xVCGIdpeYJw4AK<|FdxC6kqq6S
zcIOeHvjvcYDOf>h5gZhl36@Iik$}2US0KS?A!0^=hA4mpy<#4bIzZxR#1Xw4r688x
zJ&EfzVR%D<g-q0d|C7&uAB~(UgLPtbLj({)qEHC82=p!JIEN|$x*K-W070x97zq;)
z=S=1Sqpp46Y03qHlQS6Q116;sKm~I{C&t8N*bzFiYtTLN)teJ_K_gV)pc*|fdvh!#
z?wh*-^vxl=CR<Kdm}}Ku0SS8-3Wbh}X~+bcL8uhAG4T>1oE*SATcmyA-UXs<7zI^>
zk)sBaIYeR67L<@ZpjS+}NN|9HP*lT0pyC#RUb|5X8YvnXA`l8F5Euk@G(rM%v8{UG
zs~?un|L6@x#M8HJxc8fx{h`;o?mo}ArO>s#`?S&L>yU2hv*|;%H~5o%tnd5BULB7=
zO+(+B4VsIL*Q`v<jD!S}M&*Rd6=7#$eD$rVHwTu_?xy?Mh$GKrE~M%T8bVw_1^cx%
z2Lxg%g{(+wxqW<h{$>H$z1V3XyDXiCB6s(5F%zaV>h)Rb1~+>slT&LwV^t?W+{w6Z
zupg%4{d?{F@Ij&GG>$jBB!p8`IPRn`M%69}vv{rg;ce60C6(itcaD(AC)T%6W?3rP
z;xs^C3`w>Na?o^yGH6|BTcKTXKg8AzZu$AK`<W|O@72WdAY<=2Wt(KOb(mATXMOK^
zpJUS;-d`}B*80gFY}3i!o}O-A^6@aFKFq^Jt4o#ry_&-bs4syc!)k||d=Ky`(g+|*
znzq^~5B_Ys())n*5qJBD9H&Q~0LQ7?ojji1KSkLQPG(@ehHnT5*cQnWEZkN{#TKnM
zS`BUrEN;YoLA}N>yf@hMgt05b>*Kg9>E%t@&Dfsx!()Bgw(c-Z`R-Wc5#M&A*KzlX
zd6TG=1{Ne@=??%59X;6n8pc3CdbQpZQy6kex&}lA8bJ)8pc^BB5CR4TKoaT%h$0XM
z%-{|{AOy}VkcbTnlZQGo2Q;Dpa)1O0!x5>lRbmDfA}4nzL^L#se!c$LpRIjV3rEY+
zeHv*VsD4<!zMkJ#8q>7D8SeMPd@HhofM~2AE>^1yIlnv%cl$K@)~8?okN@=l`%k~F
z0E{V<=d$zY!<@Btp2(arR9crqi5Q|n+fGr2e1DA7rU>Z92`I}*v)XFCr(7td0PGmU
zJRtE1T!i;Czqr|tQ=g8<LLQVUn&aflHP9n+&nW@mK#qe*WrTn+U;!7v1`LXs)U7r}
zc1YBXQa0?MDaMEfmIb2;F}P7UKteWWfYz~$2;g;fD&pe2Ap${jpcst7BC=rv%+Sz_
zn0NLLxl@RTl4K%q%w!e?_&@&X-*IO4C3bfx$<{{Z2mo4L1cHh<3>jL7Tu{4}9l3JJ
zXb4^<Wlof}mO@m9iLs1%@`a2^j1vQQrvYUmUxSeNfYd{>wH7QO)xp>k)(vF_Mvnv>
z9y9AHumDYQsaP1d?mQVak5SrntJ+mJ2710UZ5st$R}+L$q&0>XQh<~wQ?}?NBcZTW
z%jkh(B?1#>@vfd^Fom4@W{^0u_beR}XoX?6wPVyK69Ays+ChR45CEck7(#+hL6Ds>
zF;Um(=-^O$^C2a;y^}Zp>;H)RZ?pmLJ`?{wzUh!&rPrEeh2K<LQ~qq_Uf}v^d&tMH
zQpt`VKCN~$ABH(y{e&Q%zZ}!|7gco^zMqp_(XgvB0?Y%OuV$M8$h`kzru|-9mgi5H
zrEv7X-Vm|Tay6N)LYt1PYuvsZv^#94hsU=c+|w}4CG{X*JC6ICm~KmeP(49-_((!f
zgrb3GY>%pr=$Lju&G+(*NBfJPLz+4dENL!P@<2H-G4$SZ!IiWGZ_SpvZqB!dyuZt|
zQBaWD2zZ```oZ=)rm?EQvVmc=ECT7aBh93;06`1hKFQP!_Vw+8*K4zE8?F~^ce@$p
zAuh^Helyy&?-t8Lw$}5Qts9$vy0)i$-by<rJ^uJbu4!&lK8UwoW2)-Aj=pcc*`>1u
z5=yhi7<MeUI0yy=2J4`$VSS97H}Irb-$okUldKk{A@5scc-oSjeMZpS-H8Wa>n%*L
z#M!Arg)n1d93*=4kfnA~TZ4Fkbij0fd;6l6yO;TxWdc1v^zYuSC+-Q$EiqSYVewhq
zz2-a3lC2vt5<654t<4&_bv24Ly4luzJGYA(6+o6^&;U@83kC`WIFNM2;*9Q2XcXZp
zkre<*5~7n6rh&~64JBa&vPT3GMKDq@qFW+F3<x4-i(C+l+z~9$13bXPW&QeRmv0tu
zQS_2>-gDaDq1F1c-&`IxJG>Z&VY(et0Z8Oc_4#Rgy4KngO?=Puo4awJU>u(wF8}(!
z{6~NOy(45)^w!uL4m-|cTdrxK6c7{`oPxJyW2P<4+Um=~({Y%E+&B$HDmfu1Gi}uY
zbK^c!N%yyUy$l%+)AZ_vO!soT%fkRH>gvdDO#?ZaI|q8BoQQ#@6lrAd8o+69Sea9p
zlamvo2y8Vn1Z1~NRv`qVA{2y?WA%JM@5lhkNZ{mP;+g;|QUYp-3@JqqOyG?L*s%pt
zKp`_kjtGtjk?_IMkZuD`h>0l!6ZXn~{L}vh66ZkE8kmFu5JU!Bx1{6+O1B0O)&x?p
zWAlh0RsjdcNR$gx^PF-)&525ZLWTC3FfdU_0bvkCbO2`Th!SxHmdFLIQ!>JXQ-EKM
z63S*$AOHh?i&a5ah?%VF(jp9(+FIXqP}8o`Ep5w&1e-xOB?O7Ufe08}xg&dXbaDg|
zumChlLvJ;+sioA+Fg1jr?5qV4jlFKpm`v9mhz>!}yAV4X0wDrKP<RYPTR0K}uv;SB
zt{Y2Ki%fV40!_f%PyXKV?mzh7==oD3l6W8QPM7_cQZUomzI(pX(F-S4k?r~*_W3^9
zg`c+TQswu47x4-oK3%tAxGi|`{%|_ul(NrL*SDv|Q)9%ihM=dqt<EJ4$K%VFsU%by
zt{tSdKoG;m1vM%VdtLX(!7EKW$<+9C`Q_zW!KZ23?{~4fX2^1LBn}1!S{B11vDvU^
z8q5LK$`%d7L$X=g<#l)gAHLU5kC#iqOo_)`@x;QJ0a&e4&QNZQncgp;ORt_@e6gE>
z?u}!@pw?l)uz_kLQLw=+k~MH!fil8IhaF2%nUii?liGIN4?d+@K5x1UxjW<T*(T^<
zcs1Fj%US2%y`<BJIBWqkKfYZq65l;-LG~hVx1Zl`{qXY1hMQ7_(nrTzXlqMJo;FPl
zbRy}IU&(UrBxzMBEo?{i8foM*1D|#25J|?Z@ev;ukY^n39WyRh$SY1e&-?aN`E-uS
zFx^2$piI45jMyJB9n>|#L#rF9i_->uL_l`4<E!JFR1P%Euk(OWKkA2%?P0NUYsbTY
zPw-6MU-Qk4+>~5^nS)V;$!mCY4Y!WAxsv;)m#u4P1r0%*fCEX`6j0E+OJ;V?2{BNe
zG9Yw}0AOYT3V=je00Nmk5s?C>2#3HxqW}m91Eh|D&Hx^cNWutRFe4#gAprwq;fU&*
z?egL2FF#(YF*6QX_MG?k>ZkKxe7)!l^KRaa@M1`V^*-W<@2~HEv3``bc^>!q#W1~k
znQ!;J3+J2f|J`5wH~;s4wN!93usY-%3T!QciJg%!CeERTrb`-ze0u~O`nrGyW}jcY
zqUD4%NJd|~pa)ubVJ2eCFZLKPCfbkF-OHPs7t_t%RE9w?3;;EGy9SRKwP^%ph=5@f
zZ-E5doJ+!uy}2NH5I|5O=%nO`7=qBlTS^E$&<#ouFp@<Ok~v~blLaFLnMFbj1$T%!
z$O10Fd+U`_KoD|7AVzZ{1mwCH?+_X!Kog4#Y9gZO4e9Do`0s!91N3SV5RiB@tC<yP
z3PE9^Ab}qHd$$Hb97vhjgT}<I%a99@sU_%|@{lOV0R_r{P$dl^Lkh(J;sg#fh*N<P
z*bwr_<{?H-Kn^0#h=2hbIUpio4?xuJFd@`#2z3J-D`rqJ_YTpBD<Z75Dj)(W<OAW-
zk+P>Kk&G}1F%c?(0Dysk8_8te5IvBMtxJSJ;55Ny^RQG{K?V%t%oKz~l7f+WRMkW(
z1Q-l}!~i_tl*p-~L$iSJFo%R4BZx=+-9NhB|H0o$*0??Csp;^NUycbr!q<2{mGXlx
z*&Z0{_I8K+Y2GWO^Y!f)sl<>!%CcTR^z`}$_sKApYm507wd!Si-lDn`3i58}SWE$B
z_tUSAdp#u1=BaF9`+9kFXBr?$UG;|iVrxj@gvoaZ?)^9KJ}vC?ct|DptE)#!<D0vZ
z2k)IUc~q9cC9!qhQDBMXr0pUx5GKz<0P4ec-+x=p6YqA@FdXDe$0L$ZX*k~YOGSH-
zd4#n+T@+d`FFv2gt4+sLC(kDz25IcAQ3P=BX(!Jci@FfftQDb8WL@)4h&sPQKf-=@
z89u`@U!p!k-U?l-UJ+_t>hiq2dw72Q*c&Kb-o3N&>FLsz`fh8;*OxzKk_qY_Pqy2a
z6DIj&)2p<cAzPQRP^&5NGZ1|sIl^QO=5%@9@|>0n%@*!d6y*?jjmDszDPkzjcX&Pf
z9Fhm6fj7f^^*o7I3zEfgzF@fr>j4Ru9+$T^3>J5=uC<{WnLR;8Bu&Z75ALSV+mQ2c
z`$BlJhws;SPrBadJmq69(&OoX_b=^^bB2<IxL`u?E!-lCZ690`7+B>nbuZnqg+v;H
z24GGO2o+@)jFeJP7f6Nztp_P#hA1A&nLQG501pu%j24LjDFTr^gc!jQz#}Bc%oECn
zG&mR$f&(FHN<_rwffgP-T4;oq$DjXv{cw>%gz|XIynpS>yY&~}wwh!b=DUH8MEXo?
zZ-4#gAHQzb$4#a1@i^b!?Ou)P{wCkeT=068KmUjS)xY@X4{LW$kr0^@l42RcYZ)bR
z3epHoK*a27Bnde|N0~+f8Yh8i&K^-$LrSC6Qyu1=C+v&EQb$e0B>R!?Z>F0a9Y)N|
zNs#&`qi9ox?0`mGK&fC(YRtKa_p5+8PpGQ|LbYHc0Ph*>>S-if!H9-T9Y`#okaeSu
z!D2mtKmqznti+6<=!vlb2>_G#a1{4oKu~l8N(c@?=pD-lNC*xI#Y`bFAaN%o2Loj^
z;y?cC_aM-{ql>a*bru0rA8~ZmFb(jg;ysF(0dpmiaEnmp3B5uIRFSX(PNgt4iAb54
zfD3`ll)L9d8~`AKSP5_;B<w^?s3|Z7RD?t+OON0IDVaNY3SC1x#E$wkI5~><?$_rH
zq_=uLb#?%KK!U&VZSAdxqYuuJs2XHo1M>h;<V;y2+z`2jVK@k<glgyj9w8(cTes+r
z(A<+@fCpQ^I853jkplu^7=XB9>Pxo}K{FOgtj`N0c5CiAOi{RX#O|i#5<mUPy#L)7
zsAuo46nyttvG?}jaqY!^_`|d>^y%mAd(|KR9^VDmhx+XjKl)urpXfuqzH2CXKUyk`
zy(Q-Hp5?OE%@->`SZvj+Ki0v(kN4wOUk!6?JnoGcJ2=+XQ#b?@2J|w<rAM&=$~H5u
zb@}l4VRhW!-|Rq;pf^h8`0{RFt{?`?Ygg1YJX{hGBD!BreO1Lh9#h-Te%`jX@6L4N
zxeP?d<6vnXw5F-_-2`S4-Ep?(8q3M;3eVSJU&f*t0J7<RFFLWVnsNYhSN2XY_Hpm0
zl=QjZ+&kRX?zI(gT2=&Y>x4>W8OrJWZh5{Kmg?G}c296!s@hg-Ew4U&Z12B*`1)55
z-~UEW-@L0~$IpMV`|O&An_;<l??XbHiuXfq?<@_pbchB)!fQ{*d_|wG+!Jk|+Vmn?
zH`lC$h-~H}yr6POMQdvYiydp9^V4(KIUqC!>Im0!%xd^qk1iWV{$%Bg(6cG8r&g#z
z-uDP$=pfMomGb>=_oLhSwv2(YBibJI?Y6yt?scU5efATj!OF|H*{66Ed*<USAQ93~
zioUt2RyPAvh!7>~Em@8r!8|Zmk&-f*B$5f(#2g`^*WgU(ZVdzhi%SF&1tYSNG71P&
zAdoW#7)Bs5Aarm;nJ5gr6N*z}Z<I$gK$1Wz3=jkn$U=k=4Qbx4m&?z7-7OVDzM1oG
zh|4$YU%&4*rx!QlQBpR0{`K-VkH7i1?>>DvZzdb>OX%_SVZP<^*=@c*<{3ls!*}O@
z_J8}wzkF{&0OrVy;Y3IYyFqcsTmq1}fSL-9`}^KE6w!W_-(rNPCmx3=BrC=M{k%Dr
zh82JYk!X?Uvd4V;vK)uu_J(&e3paoc!B$(qV5VX<qDNs7WG*CR4gkm^WF0v{q!=Io
z8PuGxV7M{mU?=a29ty^wi3vGu1s?#ai?9<)3P(~!iU7t4Fa=CN-BE%CIwEs)Wa^$6
zXu`G;CJaZzpaKGc$Q}>~%9J(-;lKaGpCCF>Nz?(^1DO?pJ+NBBkv$A(3h;0sFu}~A
zPKgm?$OS-BVHt+pB~qD^Xovt4f=q<UDS*c$;ou1haDfbnBVdBj!;=S!ZowVsK>7?(
zC=m2QjvX`wp)+A9gjaO}Tb7ze1zw)6>$=unYwy*-g9>!77CD7FAP6!S0gOV)wR4)n
zTH#`<jw(U`jA#LjEKUd_)scyDV)Fq@n41ETle>mHO^Lb*I7%1_A(DCTJx!uvWbQ7J
z13_ZZB?LSH>1SWOIR4=u$@WXLJ<Ye-zqXH$%jph3d%e4p^!|Tte;sglHyzHy^?iN6
zr#Mcp-{Hw0->-IfBQKd+!?i*BQg9Qy>ZM*{Os{X^bk>VstJjXl!*G9(CB2sA3<)q&
zLkJ5v9NT8my2UW`OFg_|Kl$-yP<!{y+vi6r^E~cn0W8-?_cGqxO^3yJ=IfJ2B0z%y
zv4IpoiPI+wMw%g^Arz;}_n#UZWQ=JnStaKtp|(o`5w}O$IBLbxnx&_Y72aRVxEsgj
z!x4hnAmyv?we!ue7-ns0#4{KLvM$Sbx+Z8|cd(_q)widUZP6EC>kD7s4YHV)LC>kQ
zovD5JU`sFKbU4`Iw$NddyC2tW#WJtBe|%fNfB*K!zw_dFpj6kIFnZ>G1z&imUPo+~
z78%gd8dynv*@ib%kCe^`FN%HN$J+wbLs&iX%<Hx1!U}yrx?V9%!oapIzk~&_ezM)5
zc@M3n)jgjQ9a{vQpD`)&G4#4c!t*nZ&M|{J)GOsY{UG1InaZ5f`~vC|e)@K~@)lbk
zghwMI8$VCSqol@=dIcI%k%pAfRDn%xTQ(xoNE{Xfj2<Hcq%4S5g)pFSpp+CDLD`c)
zQ1A$$!0yPz6EG4IIROELBMK)6a5P0EFbhg(0bu|RP&g`Jazb-w5@cfEjEDnS07C*g
zA_oSB5hmL7>9785eW);(bUc)j@WZbz-)<@szd00G;@w|<`q|%n_+@+h@$&rkyyasa
zbjbTW7P-BfU(V(BZa54$QS0TqpZ)Bg{KKDJmc}`VX4p1#mrUU$2FQ{lAZJND_<He%
z>X*oqUCxjMwk5ikDd+ux>Q<KugsKRP)|i5rhWSv2fZJF5``vhaDf7)C3(~->;9f&1
zkN{Wb3=l3Xc|dT_j);&m0cGCI3u*+lXb3z27%2s6L?(2!1~@rn$?mlZ<_Hf@K*(Z<
z2pRx}5bEGY6b4|<M1bK&WE=|`V`ejEuMyy$gFB{hFMuA~*1Ir^S{1f%)X4mQ`SBkx
zvVjGU0E&S^)KdX)^bP^!6c`4SqnqI97EF=^+>2I5GfCJQE2Xic8s{QIO5jWzq)cdx
z6_Q{=kCE^a41okt0f2h|c4qRN!4=F%#pr5j05pP*CIkxBP0?F-15s&!{^`>iv22JC
ztw+cJUe#EEN>I8Pk&rW?fg_n2<RUC7_vip=L{LmKNmtBj?qL9AwL>NhBW4RrsII7}
zu&QAI4O~|!9Nm&gPza)vC4x|w2n7%(*Iugy2x!M%%1(d$A5Hr|_<L9%V0e}E+xYcE
zZAbi)^PKbf=hvT|u3!Eh4NTW}%dZ~mXTRI`O`f0N`?b<>`r&7#e)4PU5%AN`as7bC
z{ls~uES)abr;b)Z2E2cfcE_;-ZWXe#i-rjTvUJnjtZ``1hha|=Lh14ODo^h&Jb8Zk
z)z}{>w$^gGo5$BA8n`YM7`7#$azbRz+ZkjE-6+CxcChyT+snhH@sQ?{6OP4ToR(E(
zwa5dDmD1{Xv?urT2l2LH*5grb?$fmsD`by!@RhQGZ)cd$JHqL7sebDC_Aej4d3gNr
z;pyRYdH(vl>&N$x*V^l8xo*RHu}kwM4t*PDJLI@+t8Cl0Wa#I+FUHS?VQf!#=cn?*
z3V9c?KBDsbPw&5ab-O=)0dY=<o>Fk)DhP(jTD}plZi8di)tA+RV@n6uDW~u{41Cdf
zv~7c&Axx1VS|9hcR!~Ayi?F5Oq4x1bKWF^-HSC4P*Q%J7CqEn`s4k4xkGMZrlL+tX
z&=(j+tC0que&=|&m6SQn1Jn=ex2Mbd3(_ct37GK)UVm9;hwTHUJ!b*}L7~0@6o{?6
zdskQHgbq1q3>U~rJhK3s0~D7X3xWVTprZpPaiYNA3J)pl9b_PDWX&0v1x1(uTLO%T
z6won^hzZbvDHM<e2wf5wAO?#gurZ<$<_uA(BnU!u3_{j0S|8p$|GQt?bT=K!n6o|p
z;&jS!^NMfKAHIG5tKWR{XQ!{X=~{C4#5B6&?I7uX-XG`r^>KW8m_PqQ1`D?3On>$d
z|LcGKrynT|b*t`;QHWJ|B)sOKXuUErJUx1|ju^uLO``!)!0Ij`+<3U<b_%twsNEsq
zbhF>xyfE#OxDXbiH!t$-7q9n&ANEAl1*scvF4U^!f?X{q#4#sgHU$tua&&AM2A-f8
z06HUbHtea3Pno+Ru>prC;)Ob3G^7&dAmYG*6g;^Hi$@0lhhc(fDB{2j>QR|WfD#Cx
zISfD*fL)T9Ic?rDNskq5ghbsb1pqQOm>`V*{_p*r5XR;pD51gPO${uoYIr6I+o)tr
zk(zg5l#IQ*O9pa5oEU-;kc2rpbGS^I8%#+kAd4`dK*#}MLy{;AKnNYp35!cdqA+GR
z2c%%a1>HjuL1k+p1A2D^b5a_yopcLZ*XC{2LalkX9)qfSH$Vws*-2j)Mu`v(A_(8l
zbUEWZMSy@3I%#yU+yGHWSEC4bHsc^RkCYHBK*%(6jsTEEj9kr#24e$DNqWwrN*FL7
ztt*E?WQ(Ro8e&P*MZkaZw`u<UKLp-Wy!TJ<6lQ(-(}ADUr{63;AKUnP_j(fjRr`xl
zN#;Mi8J36s{dL>jr60X0^2GJAU0;?##^DK{pSO0u?Qe(WAuj#nBW}iN5+3uoKa7w;
zv>|LZQ(s)e*kx%jB(<R5nlS-+lJ?#APv1S&G|v0sh|MGGX_*e=?g)ivjKyGxl0g+>
z;9S%sVl-+(>agRC%W{2qaO-@WgO9HgZLF1q0Y_lUq=d@r2B|cw>xals*XjPv?kJvw
zfeDCkL!vfFEZ}YPYri}`fBnntyWc#1_w~cs_Aa*|4dr0w-eY_J;rz6$*X8lUscCJ^
z`el`+$qp#;^1NQ(pSnzUyG3qy`0mpsznQw`p~Oh&5njxEp10jsuX7|YM-rfBvO^P~
z$f@@{WN`HBmXFspK0Mp`>3O|cY|k(ztOl(a6p0HZ!a$^%`<bY4SZ||k>ornrb?uka
zC;QdE`ITPl{Nv9P<{o3}A8p$AbM)&%^r*b|x`eYmpVDqu?$Z1P&bO~qx+VYQ&)=M%
zOH;&#bexq-nm>n^^FW(7L)mjdCPhGuW{`lAL+?F>H*vS7j*f{5g4hVj1LL<ufIEX5
zFrzpXCIBV_VI;r=3_y^GBLbozv4;~V@@V1MJLbfkB8<YpB7_5h&;vX?*p&hiI<lY#
zQv?Q4CV(Jha)<y2ySD4k{>_GmY08{ie0=8%*>ONSZ-4P`e)-D}-~94=s>ECMwP$xS
zE;AoKdo{m!neXrBo89>3>tQF9FAlqRzx?LE`@jC<kB`1>YXAeN1P}ydPm0!oJfLnd
zNKB(%FBF}IJ+uu01A|Oc83wHh$h$S~j)E!mMYqM>+aP&&GwzSO7ca)$es?1}jftQW
zGqxVqoihe|QznoM?tz)fEx1s2Xc^F=BO(wcvIds9D-0{grapyknAlOtA{dMl0t9tI
zrr68{V`b<;;z5B9y<s>}coYN+MvDT5&IFiHTO>f52^&NL6y$)25Qz%`8bdQsQY32;
z!T;$G{ua7-Q$|7&b^-=qwyJ<4qD%sm0L*eC1MW_R*&V@hq14TlvuUEPn5d*e3Bf4~
zGolDWwe*5GfQZ~^Qp$-*(8W0*96VuW&mJKZCCFU%q*qKFwr3~g;+3LZLz{QiRQ%dD
z55LyyqROPzz4lGL7s?n3i8w)GbWN#)vun?RD3n_Bk{!uI!_1S1BAI~$AYmE>YaY=!
zd2=x!wq__L4TuoNh#b_T17-70-Nb{yn|Y|Cfo5k7qk?L6Q;o#YKmRG*{NWz}KJ)wU
zSKp;qU&?sF(`o(Q+ne9vH#F40s6T(Y9=^o;%Z^ri_uL**`W%nA=yG1Z-r%RNC=YqL
ztdEQQ<n<6wy4lmm`do4N{GMo>#js%8O{q1h&5~6gU|D%L+qOEVLckK^fNdNB4sZYB
z7ngFgdwC-%%VlHmydUK_Cqdd4k1_RCaRveGXNN3=U<+_SV%^XE@#*=~MUGR9FX#5D
z<Y*cpNQ30vgx+C@xN=<$`&$o#jiTW2D$y_xhAi5dNGGa|^|8}#IqCNg>6`EEH{X5e
zhu8Zze>C0n=@|R_yFH(nL44)ukPbWAw5`_;w65*!@zg4Ncw5e!eE<DNn{Q~~ee+5O
zd7Wf8PdS(Hkm^HrKR%yN`tjE*r1stO(`j`j$)-c%I_Txf4eiD;zZ|Xi?W$`1xO3(U
z-}_TrFW2>2uS;i3e7uL=ebc^-bWBdWx};C>;@!88zxrl<|BLk(KmYJ&zxwd?_fL0J
zBZLI0Wou79-Y=cxbiyfFo7gJM!|uzQ`4yJ^57VUHKD4**x62{63bbLG=<ch$zk`Dc
z7D(Ar&RK{QEgURDh{=)M96U6_aSUOkU`v?L=(n;v<4_;~dF;I=N}4i852}G2lnGZP
zAcO#=lmIC}!x@Ev5!E0CVhSNdBk;iF$l(hiA_x&ufU^P$@Wi6%5L^(67!U~ofyASG
zZI@sE<+@J89OSJ&Eq%4N1OM>N_y7E_e)EkzoUvFG0}vVS=8S?lm;JcEdvp8pX8-yy
z{OHwm7*fHIZL7O)|EK@<AOF|C?g~K6VM2)PY@AXKP-f(mIW<bNyGEyIy>A<4K|mpq
zdBEN!bAW1R1NY_*G^C;TbzctAwmHk;cK`CO3`4(}fpQ`<OxkOK-jT#&(4>?E5<&pw
zh@6Nhyn0R)At=adngXvufn1yt1cW(27<w|)@Q^X6p$E}uY7_-C5-1`uCJ>E|IFiTU
z5vYd5?u^|KOpJ`jXxG3AV<Akao8=pZ>Otbh1R(>tA$4-Mp(rH&KmPdl4UmTfoN3#F
zG+{Oo)JkOl%;=~{u~`UEFavmqCn66F<Zz%Nv4MkRN{DM1<up+XVJQj4gaMLrD3*y4
zpg~~wMj#kmIRxE292GGsQlnrH1pusx{o>4|o0%)$5}0r9U}kVNRPeQfGMI!KV2uo>
z>KR%LIg67e912^_BOwA8iE4=y)R;3u;7}kS2$%u~22(*&RddQSx`jJ*2MJR&GV)N*
z*~mkjG)SaxO2FQ$j&nCh2FB=+(3L1L>6_o5?*HI_$IGuY#&kFeT;$`Qw@=o;_=Dla
zoKJt={*ssLn165_?9@Iyt=AX!Y8v7S59jA!V7|}e>vcG{<=xq)+x^tZb$!G$z2e<`
z<L`&#eUP#G43{pWuFl{#?!!F{yw{v3%+ZHiZ`Xc$__TaFzqgwdyTcUPZ8M&y`347>
zV|fZ299EMFQb3KkTo_SXmpQ9Wy)NzH{pHM%%N<L)Ep@X2!DRp`U`|r~@ELEb>2=-G
zc3N}KFdxcTb9xEsc^k$=2ewrQRG)Owb+zaBr=Nd&erzxd)BWeW`@cQTX@1e4hO~^s
zVQiRU-GfQSbewWIV7qcTY)_|;4^OoVuJPgH>C=LTpZs_?DDH>xvpnliQl+?XnhVc@
z3k*5<{$acS_&&aWqIVzh;}e}eJY1hIZOeVZcBx!DQ-S@Po$55;F$ZO0UY1usTtuFJ
z-QK^ahxfnQ-``NYS)VV@Zy!<`f%7_l{>g71E)V?T6}~8Z_qkhs`{DY_Up_v(|Ms}w
z(VG{zZ8>GIxSnLJZ)JX!Uf}VIX};g*-5|qOzy7v=du<Klu!HLbdy_Z6FMAezqT!x*
zql61IP7VRy)q8hu2o*6IpoKUQCT0XMhLj*Wvl|gb({7vqFbRq=Fep)pATA|E3nxGb
z5THb66by_2OaVv?=nw;jVoKqNq5pq|@Z)#8Y&#J1R@!@?bKdkzU;e7<R^5D=nwBZG
zVZZ|m@IV6(Y``!K_|NjhkmJAu+X4)!-Ik~pJ5bFQ$;)`j8|zm5@-M#OgdNse=JTK;
zY6gtR<mjLVXbOoy0V@$021Fuaq>3Je6hyV9`Rl)4t{vAKI^Hh#r@4zZetGxXn{WU6
z{o8{~b4x1@4QRb4Pz<sj`mP^VyYB3hv(2-s?#Wr&4qfMnQ=`@Go7aE+pa08WzphN3
zl132+6(bJF0)#bmow$|RP#wV_bxjRoM0d9qlLsLvnUKJ+L92!sq61ZhkWhrFqeopl
zyV#sxhJLdn1_=N_ZfXqb?80bs1Q2GF#yukr?iGzZfGouT4aJEmcvDx1#F@OhGI(tv
zvIgdcAvpyyP!_b{);zJhGN?&snt=#v1!N^AL}-(THE?w_3h3w%)FD7*SGQ(>gPKt*
zfRVhYM`%msgdsW$Rf4Aczy0n{7}1fije<-j=B5bHpc#{A2<Q-8IOd6nrE{s?)MJRK
z#*xUCT0!bLI!M<^NL!+?cC3;jYK7DhMN~%_D9z4^aHXV-PSgV%C;$mj19jvL+JcRN
z8_?R#pf)pU&JfT1;pCn<&uzA5P?nrqo96=NHfuvS<~|azrO+9tXp0#-8c864HrJ41
zEE7;bVQop+pfy_uP{6L^r6L8YiZL-7vqu&oQzN3Ft&k8fCy88YWXQA<UmO~f8lt#4
zVD(l@wn|tzMgRCmo82G&zGuhbCF31le>K0o^0VjN<1UQfEZ^K6!_J=f0Y-n{@-fAa
z&boflWq)`T_4)5e-?~#CZ`HS#_?=C7d+qzfd}1GZ|L}$_20jSxj&NSagG5r0*iIp#
za&$m56(a81++WS7`PJ7Sv~GD7<8CuR4Md?I)*TPwRA`_~nN>;wW}YUDlAQqvz-$$1
z@DE?yokq#Tm#N`qoyPmtZM3AsY5`G|(Bnl1v+HcF9xJSTyB*G{i+#>aT81SXEls-E
zE~<}hZu=X4{pxPE@aXaR?vp3aaJ=YsAY3@~MP<47H7O+1j?fr=;EeGi^$k|Le8_kA
z<?X&6i?)InA7AYrUG4f|i*E7MbSoMd)%%ug#}FHmTx`PGB}#AWHLh1PUfz!D+pllF
z`0)Dnw(X}m)P*j4Oq-Ndj5Z<ey45P4bU2^u!8d1zJNfYTjYAz{_hBFMx3A!7%-gdO
z^xd!jK9=_M#Rc>1ZBwRteSdgZ7BqYDWTUIgXp;tq*}U-jk@cUg`$vyu=;7g)^=7WK
z<Z-0nTo#8NTzrzwJhYMPL)SY50c{T8xq@c}vgYn=5Z%Gi0Sz&NHzqC=1RaR20aO4L
z5;kH&H>6NA5*jdxume~_3@8Bs7{Q@AGJ1kop*9XZGPo<TkgF4e8Gr#35P}JrI|CRp
z2Z9DJ=uU!+2p~)dlo7!d=Wl*HeprmOZ*PiqvzF;Mzk2%*`*&YXbIuR~ZBuYvZ_YxQ
zsOyHV>(ja$`21qEeX<=MU-geRF<DHdA<i>@@yGw-|My?LJuaT1CX`xBmx4P7MM=R`
zL)YmvlNhZ#Xq4@Uo@C{;%p?h+fjXivHD9el>^SX~(qf1dLI50u*PGq>c{)E|U0$wM
zL}3$goEiq2Ckq>IifU*^OdiEBuz*3p-pLInGj<B(xw)d(Of<Moh=4p;DG&y(M%=sx
zK!7qCtPu*45(ifUBx?x>)D<9t8v>9SS49B83<lJcIC)T*O$?ZVS`LH`BQYX0qU6>P
z5e(dCfCd)Ov%B;E>bpNS1T<~LXlMY=YN=yy7}#7w$;C);fjEHI&ZRWRYL#1pinTSf
zAV5eF6bXlvBQ?N~xN=}(GGZZy*fB3I31tJg05F1yEN()A7>l>aW==CVQ&5yOFB7;q
zkYU4IuszsP9o?5afsDmk^H#yL)dI7rug<&R!NFHjD^Qu089)ID!J7L9u^Fo{^s7Zx
zpur3z8iAVhd^ijub2aJ&S~Urh3%Qs>l7#5&*ak!;S51&zS;3?drNC}E4lo<!1+}_H
zC`v5u_Tr_T{o$vWb|`ze`DVEx{p7J+3^IS4zqu>jr^D0Gg?pR#%V}-TAFu3yAKo5+
zPJFQ*esBuccjLR3Yq$FNEVLVaf1FAOEI}Gpox*m>)e!IuYTKY45oJF{fg;>hS0>W)
z6<6|heSf#KJkay?3QJUIb&`I!J(tv*S3{d}C#j4LyH>Q76CyxN=nPt?`(v$nE<IXZ
zpKnvEB&NzDq#cSEX#`2AWmuGWf4}samvtA{Vd1m^DNF3RK~;8dOfk#6<kfuF4)^<|
zU+-RCT|Ms3(nQot)c{4awY%#QF5sA{)fuBfINeRnTg?qA>NHgf>cNd}-W@kr>+Q3b
zn{}VAqC-y$F^cnKvJM$)^~24aXZ3g%Nxdz!6h{i%;LP3QPr2J>=udO{=GD9XySLNb
z-Eo@KG#Anw^24;gJl`}Xy|aEOmG5uwk9XJi@5g`m^3~gKzP^6@_QNl}c=PN1&4<^k
zkUCkKR*$R0yZih5d_R8p<k_RMk3Mc?p;MMV^)I^a>BVsVIPly)d~^JADR=4|Jxo&1
zAf3t8fZHwV36^ZQCUVqlr8$C0baTtaxi?CV3eu62g8;ZY7$gpc&{$ZZGzXw4;tqk`
zh^4V1t(h~jKx>GO)WDN_b&G_K0L0*}0wD$gQ?LROfC8YADWEf$Ik2Onp*I9}b!2jM
zhQL@IQGytrP)X-~z5e<5LFakF{j2&APWgEFo1cI6^?Y;8&~MW!g3!P~z-hO31;V&Z
zOhY(7U-yqLSI@4(WlBTfWQN7)?DgOO`M>->{JU2NZ>>q{3B2EkJlTdr<LGP-i7lIf
z1t%i|%viaB;tbW5n8FZ|NXTPa=TgmWE(_;2H;5?Atv@@z=>1|l?6$*M4=G@@*#dYC
zNNB!zC5(i^i!q5Z2|;!NCX%My59$@67cVXdKq3vqi91*(90-66Mj~`mGIMZ8V5#7p
zSq6^g6=m)9L@Z#7V@C$Y!Kru}Q2z-7CbENRAWO>XkbtsRa*AZZVRl%7FG!>i%rj%p
zSY1sUN$>>#l!nk|a71*+o=X7&mq6GWFxxC3)-r--UQswH263#e(5<{y<3ht4=KF+B
zO5}vD4uECBC_)j;P-X`w6}M3s3m4FpEzU?jG_VHTx%U8jqu{(k)y5t1$ur7ai!f&4
z0rilb*qaHFnmA}9L?Y+L(=iM*yQiF_HVEcwyh_>(6C+5@i+i)xnvVBm0^F$Y$psDB
zG7n)<cFF1<VsuxQFlR%w5Y>x&#BfpuwZ5x9#Fp9JWeyO>#u1K-D~f0X3t)>N69SY6
z!kn-h<o>@e%On2ivX7PTf4SW6w4KW*U8)a$m-G96cuugM?VJ1k+tulZk1ymPUmXtL
z9BTJ0{?2D~`pOQ+N*DH>O?P`!PbCvX%zIy9TUK*r5m=<a!U&~=NL9d~53#_}!s)B)
z9Ij&Tn~LJp)WhaVh7dKYJF=9#M5~5CW<b`sFWnin;%gbdS{_=UCb&zxV-5)y3+rNw
z48c%vZnW}%o#u(QGM;D}BNDL0O|&lP@xG(H?s#Da388wM?~ymq?hk-<xVpGnw&$x2
z2Ar;uHcC*g%V`;>r7QEn+_X01v1mEv>?La5+x_076ZG4Qvk&j?UFywo{77M+rN1d%
zSKBJmvEZ_XZd`1Ayyv%f#UDNCe2J?R5qy`tPqvH;GNol+e2R9w-RFDz@+Nm!hh5zD
z^Tn05>2B8X?AteTWA6_Sn;|vk87a@%5wG|2>%9$cUx#5QL-+o6JpSpQz4+|YZneF6
z`{CI3hPkxWZ@&8N<@1XbFt1|&a<d)+o%eLS9^ZT@H&aO+Zbha$l*jnuWr+*+D;a_=
zYg}b^op?n}fe;~4sa2bSF}fp>2WAB{b0QLVVb565nbkTq2;9gV3%a0!v#Su2E#}OI
z7Lt1vce9x|DlFg)xHBjYfpoDtl1HaNz<|ck8Y3>K3?M;Uqllzkg~EW2#74kXaS(BZ
z>RHRfo8{Gqaw?@Qc)U5>96x;X?cMzZ(C6I9Q}P)k4uk035~I-l9-X`1=xmdAXRD94
z-Lt21wx*DP+_5R9hui%x{$Ky<7ax?vN<x6C8CJ_(cz?u>$q4|2B*Q{L4hA8$lT%S*
zXJ`%`v?duV0YO^ExmXDywW1a}0<cy9h{|d8{3>3q)*GkI7H!nf0U20Pi*+_20B=Mc
zuAuMMO(%>iZ{%>&Ze_C}5#dt9Ahj5?wrX+Ztr0P_1Sl-Qbt`eI4Gk7_bnCDzAmCbs
z-9Z`9n*sG`wF!VXq`@^KBRBA{SdAvhbwn0(AV|uAY1VRdSu@NC3A_rZiIWsy3nma$
z&>{d3nG;awHc4}51%d?i*!r`$-NN;)3Oli+*u=%fghi^UxHn-yBi2GGQqSn*AtEzr
zwKRBdqP3xLDbn?z4ZFd#QaWe2b`uNWr9$WkqU#-UuNufRf)SxEiOL8)aNgE>Lv<tz
zje?d|wV88igc3_dK}(FjXrDyuLZKrDAPKb%U6^wLwMrJSw&N5K+rZ#td7`sbSvc_Q
z(%EF9h^^|-x0H-H=)RIvui!wf*4TSnyk_R1XtQ9^Sw(!XY};t3xi;etm9QJRp0()B
zig#y}-|2(lqesF69saJp)8#zjljke?)(^)q_x01uPETRFe|Wo!AJL=D8+d)Xd$*ky
zz4&el_Jw_zj!nAH((Y+#r~Gi6Zs!iHbfTw&=c#R<bw$V)icpIMgcf#P1f6X>-JR~I
zh=Xo6xWZYf=PmELED>wXT)_Gb7+Nkmh*!V>g+gj-y5PwVQ-i+mBA($?H)k===unJ1
zoLhG$^X%9fz??_xac-81d*anjQh=;v&Z?;1$YSVp@tyB2xAyQbtA^qG+Z8-Mr}vfF
z;*OT$&GsJd>Uv)$^NGACbP^g>$7x@tx->5NP&KfGI2>CEbu=C3y>+qg-yeVvcV`y}
z+|6faEf3Qvy(?25w%vxUa=Ez~`mTYM_a7D>mW=IYIv!6AEQTkzITx6)ix;21IJ|#*
z|K-hoIT3~a*w~G1H8tM9dM^PMt7pTbP1s%MhyAH_eqtPv`R)jJpMSM`bXISdthLHI
zdHeQ3K6^8M_jf<qJ;CnD6RG?9=GEcmq3$boCD4GGH9k$>d5ULYP)>bfFB%9aU=vbv
z6sHC-*T$gk(3=*DVzml&W&wNw4!kU2rW6Efb!2ixbS;iSpn?#9awByE5r@i7uG$P7
z143hFY-U=KBiU>o1>L-|^a!(AG9+}s;K*(m47iyRFhh@;Q3Qwt=f<q2e!L#vzpkg#
za@Xg(hvnhj=}r%K^W9-7MlogQ*az#JJTGAD0LqexR{}M5tFYQ!JUvg(9(RMq0eMY;
zP|(Yve*5qL<WK&?Lu*}b6K3xu7=YFZhIO!@+T1cZcBvnab4%8HFr5ue(W4ssB0{ZX
zSoh#Fa+iFVYjNyr^MFLruAV(R>#S#5Cmd4ZvZnx!$&by<ofqR3;fb6A)Zh$xqM;ck
z3KczgM)M^QF$ctGwUV})R%Fen1(m8Qv1r?Ps|_8{5Y_@y);<ac%oCh-IxF9s^n{FU
zuBn5nj(|D=FgY25yF*9-8H6iF*2xh9G~<PNFhsD50b3(sb6aCmZ{&6)OhQ%xK+}$E
zhK7-VqP7eRDXTk<TF0_xsUB)}orMR_o>q$00Xe}EcxY(_He%}BK+!laF!U~Ly~JiH
zkrR@1$`CCWH8nt;8%82GEuzWs66_k|3eY6D#)N)?VieYHljUZ10s{45k+4zM%V}(w
zq@&pOrE6swq8%qSBtnk@?9d3Iw64R(q)5ADqd02Rsno!cv5ZW}xivRb^}IMHR?{G8
z2FD3PXw8|`%m@h}xkPIfITV`$)5e<vma$L(sz(D1gwR3VXU{QnkwqblUgl;D=f58k
z(D?V|`leicI&22Uy}x}^@^|FSHtQANzRA~>e($l|m-=Nsyqe19DL>o7t2;~6@vf&I
z{$xXM==S*V+NOgo!=~MpM4Gl;F|bLyE!42~u?Adp*mR!fYU79L?cKP(dfJJx$h<#>
zbS7I760glLdStpkXlF27K-+6tM=HJRQfIgsZz^|vpvVKxW}S89Bcd1GTwuvvdC2i1
z&NpM@M%%uWbec<gvfe?8shstEDr9ZZ5EsLAuw^ASJyd`H_Tk}nx%}+Oc^kv}H1V@;
z|7Iz5oLv{Y*_Xq9UyO&dC}Ak`9#)ob3>ry-&1wU=pBl6_9rqK-`uxd9O^$%2Sus0~
zo6ByT$!pgAy`DYPqPwo^EmCXy`84MD?;Z|^53|?R=3@-uaQETi`euZBdG$P@hKsX{
z$5(9`PscZh;~uDA#bJMYT@?dXYAUPE&BKR4!TDiqMKw3@YzVa}MDC?Fzy0Rkp&9jS
z26c~)-LviLlQdimY!An`_lGa;s*AXG>0DA*w%>^>2=N{1Id_6|0$P(j)E3bfmoo%~
z8eAJvXj+`vl)y~HtfAu+Ah>IDaA3Bo9t5>CG&E#xh(Q>=00sjWAp<95^@=PM&>?~^
zRtupJ5?E;%MVdFK5D*YRz{p@m1EUx*I8+WGgr?-w+zqgDz*ejs-_<u?kMEB2VJ@Z*
zcgNG)5A|kWT^4WTn3_unjFp(0m4v9wj!1-YSYxN%_OiP=Up?Q2i!Py}7#eu2>goFZ
zyZ_<8`00lnBWjfab;4Cli^D3>0xh_7*d}fulrs|&Cva3~96}6;ok?rl0`7V!i*HwS
zJI*Y+XrT$YOTt*H!5Ft!n{BsF&~-7uq3{*8Q;}W_&=$pPjY9I)twL~hGY{y6AgEIC
z1Mmvx$wD(|z|D}jEdV7THf}Ktt+DF~C<AN&6<SgEC~nS%Awe@11c8P?qqiWYM&Mvz
z<b*W_Z<QEaT~sv&GHuF2py&>P$xsVQbWjIsPDt4aps>+ev<QR*0OSST%tNIRnpvnF
zbOrn1t~nDp^=p`Bl@*!^no;T$A{#HN+`_3kHtd2dN9-7BMM4OLa@JuTEW3%Tpf~FX
z-IxHa86kwiHYio<M$M5m2^>*FtqV!-Wd>S6*vvF1g90QtWl|7mh|WIgf(E%FX)`h5
zj#>!GO+m~@@xg-$YeQa6C9zT%%rLl?+B_t*Ru_uVni2t11gr`kO+pBYMGalu1k+rW
zb?jw42-5_XTrqN+P)tQ=GGYhoxX)FWs)>`My4m8(`=syxBE0+~>W*}Ji|-eu&*V(l
zW_|T_9@qHU$0Xl`{jZnn4RrJNbUo*JyvEGi&358LZHM{&0vErxN>A?Uo7=;i@?qNS
zmy>JgxOwdzb)D!?X<NA$a9zSmD|6l-j*EQQ7mnxM5H>7eOO#zW?}i>TbvU(99x$dN
z2(!s>YtW0`wbgd2p}c>00HQ~aSnJx0x2);%m=`AL0s|BeT*?rKd9IIfd>`VXc_vER
z9?+oau{+L{0!YN>r8qS}nU_``_H~+l_}*tRwz^Cg(AD+Z+&qsk?x*7fhw)_H*?={9
z@v!tWO#2gH9zvh`6>rXnX+FYyufZSW@>3$j;jz$}zMIN&OWpQJ-8k}oE+u9N$=Zy@
z%l>{mjc<ST)mX-3rD1ipzx#qCTIsxnSdB{AJbtnI_%g=L@%9(DU*2jG8ESX78BhBT
zV3`)k%e2f^$+}whzLbMC3LRM`tP7%BY>O8G$)3Df7u%12^xYr)@a5GHKHF?=ZJg?x
zasKArqLGI+%gWCl*|X>CbZ`%hk0>P85i}_mD9Nz`3W`}q^Xg`X8D$gv-on}?6A1d^
zzA~Iz0YWqc2edkCh@i#0H3|o<(9E4c6hksx$Xy)~JMqa4%oGC{CAS6CTq00*RRShr
zP^Z`d%tkBDlXCz9P>R%=vwLRZre+3yJk4MK?fm8OxSvnUJib5X#SfWW8+mecRx$FB
zP`NNfVw2e8+zgyxO$cG7XP1}#)64$xy4%IjCQL*!#Nahx8R6}J_pkou^Cg7XDkgwR
zLE9XX0LlV~q4nUvOm3}mGHXz*Vrz{crXjSR&J1RNR!c!}nDAg=3M~jK)@qfZ4_()E
zvkqMs*3sQ+rL8R!0-7&gy|Qs8S_uo#KZylehixHJb0H!_ayRR{Vi?^)dk9C+0hqKH
z$CwpR%@H6*@uApBP;0mL*35wojTVOpNL-3Co0uCQIY3lK3T$TV1Q?uBB}Y{xBnrwd
zR=kGl#5RMABNHRm6Hp{&cXMj4CfE%A$(&1y1jROw!*Ywnu0_bTv6!@gP!{zF4MJ?H
zfy;t`<{Dy(Fnf0y%RQCc92gToX%dAJ;><?CjUYHMIFXggiMd%3(Od#bc9P!bLe-o}
zi7^y3)k;K$5TT90TZAK6^)O&CYg*kXV9lcQNFcT*R-9l?oQ5)&RqDK%69@<<hwKnK
zZ$^RUxeCH6LN@0PbyA%Bc%eALAsZSrvIs=#8lXqk>W=ER4x9yrLah!4$jQtKqle_q
zQXP6mQ^#mbkk!%MjfoykHNSa~@0`K%9&c{vbS3AHNq?nxhtZ$V4?Z2vN#4GmJ}l+M
zckp4Phuism!S7yRyVC>eG2cDJ`n}67SO5HW9_27Y#jav#^Ng#6oG_oT62&x+^HyqE
zuba-MJ{|MTy-OEXNe0ZQ%>l!X;`upG*J5Snw0IZY9k|96+X+P*^_<P$ykGD<o%2Px
z!@kF<*0e^=9@o<}VmI`9^guZzpN;U~G5INH*R!W*xI!Bna95S-bcERDjkx!DY4zRd
z_Hf!mSbt}?89au-bvP{*XIhwUcz2@V`uO!WY}OaU8qp++%TaTkYZtnUm(O}?z@%Z7
zntFh0XWVbwF-z~T;d%-W*E;VHgWTlR)>8^0fy{PY_U~Sg4+nKM4Ah+sAy0?*(^P|4
zly%x%?S|F)$B)mSZL8|d>tDY8<*z7o(xjcH;aOj|XUEgEdRw$1=4$MR!YS5DZq;id
zW?w(|abL4JMNGRcgl+U6{oqF*|Iza&-+y%Z%<|jQ^5wTnd}e;GQMa45KihU^JEj7%
z4xWh;dvlMf8B46x$QePcssaKr&_uo>8Vv#?G$mtZaI>RXL@*7Jb+$_CNSG)QKx0Jr
zY%-9kP*hZL(u&?8TXl1DG>M`UhG-dyI<$b(2cR`}5k=fh%Z%QT#UZeQD+r@$!{CUZ
z^L?9c%I&-3S9jwV-#(1}@!@c)UQUgNZO!8*g)&eai-*wi1VdtFEwx9sL}9(5L_oAT
zll9f3)zfF|O$&VpWEcbV0T>mbEb-mn{Pd^)&CjQ@`tkqr4}SSy{*@-DToVms3Z}dD
zt{qR(_u7aedeekL*cQ+IqpNoRaGaX@38D2v6h<@5IY&r~6oS>foQMQl>o<OOcDd_9
z*9(QBLoel^*n3+5W)tD&8a$v1uxW+`koTH)5I~EAGh(Dg+a~a0K+VCxHh?3R4#Z6n
z$uT;3ayQi}fmI2JP#qI+Gb2hy=q(A4PN_8@%;XoC?==KkoVX|ssD;{M$Pn2b0V|uU
zN1z7A8hQki#hsP9RVfiThtMp$VUUUp02~{n9%_@4n=5;4Z4R8kozWWvj4o-tJlv-0
zcDCYEUcgffgn>(SDC(V?hBz=bYSB5?7KkQM2kt#)5_EHSVJlENAckS)k}#mMV{I(S
zK%4f#9+<>?b|A=3D7Z`xhP)+*V(MDi)IEk;DX~wAS*>6IXsAm+th6zC32Py%tu`>|
z67{_v9wq^j0Ysvikp}VBy3_GKV03lrh1YJ`d8rQIS<wMJaME0{19qOO3^Zp5?nZz}
z*y?)FX?C5k4JL-IfSJXF%^`$irRm|_iX6th_LpJvSjunUcA3WK-HVT6ue|@;<=t9`
zAN0F>y&F%5s}SzUmiPu{zq@~E;d^Jh%`%?$%iUUfnmnnLVb`i&V7ZSrE^+Ja0HrE5
zq~`sgr62EZ@9!5r-)y)C)P9;xdf2Xdv3R;IS-@*t;Ve70lUj{k%BN0P-R4uHo0IqJ
z<61k4UC>(4A#Ouio^<*_xSz+09YBT->Wo;mLz|gb>2eK|jT}X^Ee#};4iP%3jj2#K
z&VIA6!{hZ@+a--C9Rhmb<)PkdJeB#R%f-djX4TGyV4Kb>!u5MwT~#^nF1pR7#PwtS
za2OlvmB8V!eD$uDH^rCKF7y{m2-|kZV_RafO^BPTbC=Wj>fPbX(>{;6&K&#A$Dht$
ze7-;2tT&yO6}FoC@SPvMNSh13h#!9O(+^+2&c~{8je@$gu<NzVp-+7YH}gb*3@b0o
z<S0{i#R#lUXD@%kP~RT*3&MJM^pijM&!m3&=zM<hJ6E6m<Z7Gt{^92M<#KZ)VYus4
zzui3I7f<^O5_~|pU|XCBp}99w*VZ)-0N||^ZA4DQwovm<#8H)@nIbU)9J3C@9bzTv
z1m;rN00@zM0c3&#r8#vDhz%7LT^g8&AllrtK_^j@j@1(^BQYR3<!_osh%Ok3!ItJg
z0E`Y0+&Gdo<qW0SGPS$kT>t$y)AjA$yYXQ!KF-QzQUGcgLvl_1%3-m&d27t9$kYsl
zT>#f9Vd{nsag7&s_2N5^F1UYwA$U!JEQSyOBuMaV`0lsA`m_JXzy9TiK|H_u?|#8U
z*SS_R$PS@Kz&7XDtx$E&bKk8H$c1$(2v&VT%3R5%G8e{m%TpH9M%mSygzbRy5)$=&
z+^%~;3BgkY0p#gmLR=?AFxZ+aa+MGqn|A{xR<*G1p*A>~5H$w~6Qs4MSz9m(WosM&
zqO*&*wMx+EMnVV**sMlIHL1nJIoQEG5Jv<<oJvB@F6^ekZBZe1Ff6Wt$Pg-u094FO
zqK-XKWy}a`hZEr1%H*8QHsmvdanmZm5M7l2*B^b)O|ipJsv(Om60jk2>P#~#IVv?X
zbe~60)QJUWHxpzBVrYSrIRIvifw4IuaByq@i-Zp2BE&$+u~7dM_`!4ZKomRmOd41#
zxwusj#gI5pEk@@^o}sC`n+dx%3WkEq=!&4tteJ*s%K7A`ma_?%cO=k7R!2n$A!hR^
zF$j7!;APhJIW0}RadSgtRa|Oi?Ey<&d>G7(njwqEK3RsLw_I5iF*>L=!v=z8&81s2
z8xa*ZFEu$S8iIRP?-`m^L(Wy32gdW&viV^|{s>kpws&xSSjwe8+lK8noPL&Hdwcc?
zT}Ih|HGNs}yz?N-tNQSvl`J!NsZiRVZtn&l)9t3UH5|uCEl@Hbag(&JSs@Y7rp0c>
z<K(-;VJr^o?JBNT=!#&}uexoQKnN;wKGH^x#}KqqFmS@$gmQdT-@dxP^(d+9y=Nwc
zMg)OH%{U4pdQs}Jo(eD)MecRX1vPZLbk0mGYQ>YNDJe6>hGxz4q2%l9uaDM!{NgNn
z1n4X<6{*<f@m@C%O8etJDL!5C+1chiR2;vZzqmW@n~_=Ap6xalt4_RL@7uy{YI^wU
z?f&+5vw4;x)zh9XANy3shwHH%!+IMwJA);^zQ2ESb2?>^nCBc;+cMw2{pxcpy6IjB
z;d;}({DU8yfAZq?@WpTc^k2XJ^?Sz9?LJx$EADpN?b&>Iw^WbAaN2)B+BKMS8Ifcd
zQmd*p=h)cebbPnJd6mt9_4&s?`raQs|Lkf0&W|5`_Q`r1Z~fah(*xYx9e}s-@?r=>
z_sQe_>A7%5j2z9y9Z4Iw&*~jvb98qCFtY%HU}h|c3f`+Ja;Mx70#I~o#A^aa$Oh=<
zB#7(|1OUxZVq^;Fvk|)oBqDC8XkhM0LTJql9eT88fzbQ9Bu)cpll9;p2pwDjm_#EH
z_)Mg2{BU^l`NQA;^xJ>?)o=dsaQ$Z5k1;RCOf6G&?pXn%s5(I$Hf5YCMW9AQ3=jcA
z+^nLcu<82UZaClckB9Ti&H9X2=ZOQxm4sCy7Q~x5=WqVvFaGTR`mg@_XXBxi$UUk;
zvC`%cxgP>@3V|I6leWg#iWjJ6NZf{$R_BZh)y`e}u2YoQi=hexGY6z<GEN5sj)YyJ
z%gf!9^K|w&ZG>VHz>_s@#AHy_JUA#Lg%k~ALUCb5129*1@(NhU*>UhXfwMC*15l*s
zO(SD5LkfV7SVw~hZGmp<ZV1JZ!PMQ+!Am7~gyf<q39&j7G65ipcx}e!VZ)~A)q{Ac
zK!hkj1wDc`v1SQdMnbJ4irK;_iss4?<evZlmWBWV5vdwT>>R2&8e*-EP8gc9L(Xju
zp{FA2&MUGNXj)XeL|k2@q>ez9gQ0d10{~%37R@LkA(fM_112Uk$Qe+8#1OQrwDi;#
z#0g^Jy10;|0|#QnoS|m)KnAYn+91%fqzCt@DsH4bR5=fIR%|WE5@c{g@nR>6=MFYI
zP)daZ)~Tcj1Qm>!pc2-eVr!5Xh@)Gv>XAXn>ugmOGYUhlOHyG(h)Pa6#sHNyoLY5o
zfW)bK1r=+H^y^x31k2Sku>?9D`S$bi<sH!{PigrpI2}rQgy-wN53>Ku`ab51XPaTN
zb~o)KJozZdjBx)jO^qn5f;=pz(|lJAUR=>;8R`_Tx+Q*u*WU=<tobV27=>EEl3{g`
ziaoq3!N+&5%P^d6YK?qs2qDDnjywTO+!}-BZ9G}pa4E)j+P}bdO`)rBJk$URY3*X(
zv=WIG?7C<X$V#L2Kr~BUT12g-g`vEy(gO`cT0&zW$jo$@nY=kUFBD{H>$|(d?Mb@b
z*>2N1==(Y?G;%rbrw^x=s>J1Z-BsRgsqZ;)-tVUmA4JmyrC2eU(30#y>&?yb=FNvb
zt@iuF{lkHi&33a{Z>!?;?i<orj>qoe@#^9c800$~?<ec7R#!Wmrg6Tt+3I-QrANcn
z^KKOZ#?AAO;(GVhpa1duw+Dvp#mkphk6yax!{I%(>25Z<A~OuD?euVh#OE)c-v9Pn
z^}L)ET|E&XFZo1GGGWBnrSP<`-@kbK<fEtC%XPT@y8QY#5AOqnp0!J7D~jj+%T*js
z*c5TV{2uYly!Td6i+YD<MBLC#4GAeam>Tp>3P8XO$*UCx0W#zmBYP?4)f#{S#5MU`
zVL;5F7E(e0EsFyfpagR^bWv4j2S;e;Lgdldh&TjPE4(JQ3~3+&BGar$EWx!kWzEZc
z_~K`8|Lhm{Zy}F*tbnuUUQ#jE#VK{<N-8yo0h}>A!MJB|K<z3xXilq6i#o{mQGX%b
zc6UCA4BhJ72jMuNIkFT_t7Uxi>OcJbKmPY$|HJ2p+Xp!<HV}I)z(O`*kY3havj`%D
zT8p@#DF!A^(Thd${n3&Wi}n@`F`$giVG&r(x>SQ)O{uamg~;SPP8XY1w;Pbp!)9@C
z^GmbLj=;tUt}PB*Yon%1qKQoexsj6<0iei;TWoL5wu&M^>YhOY5f}h227*w)*jWKP
z@Z1=^5`!5<L^NmtP^}uQkQHonUU4&sWL1&O-NAZH!fM_o^yb`)wH6}!WOODLypG0P
zc|&!N-5TJ?1e%462`jOqsh}7DAh~&msA_1Xa#oLolXDMITMHp)M|La}npGsy2B;wp
zXcbssF1qQ0d!xp2Ex`?y8$h>qF^lNV-pq!An$6Y}j%GcC+Umm8;Y>iP(%e_f8BHAk
zy*jKdpFsK+AWWcnRwhFAIbdsEix5Mu5!@Ec&9x#>=YTSoWgSu=a3#b9P&8Gj(nlJz
zgBUjS0VJp9-fv(W1A``(ld3if5Exo7+_<efXeTmo2MP|Rph&#pnib95lS3`u*66h&
zuY4XI$pB11vO)uh>luTswm^mtv-xA*ASR;Y*X4b%)#HBM;quPDaoDb--Sgf3@q^<g
z33f6albctN%kA?YUe?3jZu+xu3(q!Xd0LL|0%_lapPdUXnz7b>XtkWIoJQIVX^jrO
z&$&NKDRi+Ny;AFoWo}E?VQWNl-}_j?kVj1V)$P%+yS$>d4=ii}^uXykMAm%Iyvnu;
z)4^E?wyWXpSk`(Pm)3?JtphHu2>>(+=SI#U<OwjIEVODzT}Wj0XuZ=T_e~hQ4s?2V
zl3_gh(eLV^Ts(^X`WzwV>v0bqyu1vt*eG?nJ(h=uH~DmPJ&t<H&~I1y-iyw4TH>)?
zzkfTd6SofK3@8FD`EVW2p4xbtbUOR!_q&0w|Nh@UyZRlag6l0jL0yFq((2`pmfP1~
z{>gt+y87($Q|@=Pi>D7?+`oEXjjSz8tCv6dXtREtD5_ao>hkJ!w_44Wnk_;ypC$}j
zAgW|Th?^n(?(hHZ_kOZ_{OQM+msxLqdHTis_qQ9BPRGO9^Y6;1FIU@6^bPLsIDMim
zGe);o>%lCd2@r6!Y)oJP%8o{68UdBb0mx0kM%Fb)GODNrTD4||j<O*q<CAA{JUT=~
z<~*uL07meT$UI|1LRJsxZe|h?9Vt1By9csH#^5L9MAB<%9F-DTbM<Oo^XpgN{?$*v
z{>z&@@e#CR4isBEu}5SPkXCZ#ke0TD5GZ#OIRy+How$*P)b}A^;O;WeGF)xWuGam|
zS0riUxLV23JF9uKc(LqnfB9ej5C84I{OV?@3$Dxz1DO&y@CwR^iGxXv8O=m%iK5M%
zh@jA1kCD^-aO^PFI;OM^$IJo2mTukExmq_b#kfOsB1e~qR#!lmPcJr?@nX9j?7?%S
z0HC7-Sp&ePz9k=lPZWFh6Zm4(RR9cuoLtS_NsnNiK|yDS0G~i$zZ1$Xs2d$Uhp1-#
z%9`S2o)XjCTEbAF97)Wf!_uTh=L|u}4WVJ^t$+(-Rm7hB;4~-znV04PNgW&jsIevI
z71e`>&SJ3K5GS|5*sL-R5kLrMbE%DxBmZwc`dv^$2XzSj0IjjRhu|zgs2t4MJ=k1z
z5XprRoO^G1)%E4LBubo{#)y@O8&TpI0+DheH11Z&8EK8Q1gykB>?Q+HbpiE46r6x5
z!{X%TL8Pw0n=yME%qtm!I$<$gnn$XKQmj><9Ibi*_S~j|4b{yW3Jn^q5wFA>xkd6#
z6k8D3O<A;}765fgvbYn5QcDKtdngscuGS)i%%Hf2tQf&5Le4}kM3_1yf_cU+x;BWN
z)f_@>+`O1Op#)>smMwM_88ZfUtJul(;`br$&aFh;UUE<#ztY=nx!BP8S<)|-5Buff
zpA#8<_;UF?)qbXa<MZ4m$$MKco?Uj`%ZuP~oE>V|4fpow8SR&Oyq8tq`_WT?{b6Zx
zJIQi<u%VakyblTuSuhTU+^YdOgw(rbklxKaR#z@ACYxzIoEl>4R#FBe2nHlV0elg!
zjn?3q2rHngYEV6C!pX@5JM0meJg#8QgahTHr8QJo)C^L-n?HQ<VcMK+o?dREi=UL%
zQX2>%Vx8$^L(T?6T3udk`#8qk1K<o?c(k_M+`YYi_wIB!z543gjH~HkY2eF4u2HQj
z0U{A#U<IK;1$)NL<x}Y|%Q)xdVfEwxWb<V6@QXk7yg7UED1<dpv%Z~u@?!P9@14H-
zhj%~w)%o+!p8epX%isS)u+y*q{NLWazcyN>u$@jbH1TZbAAPiW^z7l)m$`~AY6fH3
zZ!cbY4x<%9Z$v4^7mv?=?~neY@Bj1NXFq!J_-xV-@Ag06U;py$G=p^7zx?jz_g=29
zN=HgX9R;acRr2DcSTn<92>%2b#jJTiD6P0U0b2wxtJazoQ{@<=x_V`-xoGov4$=Tg
zz*Lh15;mmXeQ78_2!tNc%@LW|OoPedlu!T@GcfrAC}ad)(bQW6tSHebBZ=1$t(4Lp
ze*5!({1-oc{gux%)>;CEsLuVsRu!3J2;Ptc%>_+K64$ui0U`%siSfw`K|}8Q^KLlr
z*3X|j+VSQxrS&STS9pFQTj5m)E!R~)=J1RE>R<d9|KG2!?`wv2s{pwnU}8Y>01{JA
zP9!XyDJj;XBtQU*sUaCq09x<LGMj7HL6Sk;5|Me6)VEqBtV=a@0GAMBpZMZ@d$x-g
z-Kt+@PJI{VNY;p;Q8Ua&?m5ODi2?yQSkL4gkw9zBoY`sv!DiHxtCFL|R%`H5Xp2#N
z0mOyE0OFtuA+b3qQ6f}E7Wc&oHEzg)wFNK|k*g9Grxoag=;kJEB4-E&LMFlpV8PpD
z$Q6Uuz_@4_q|FrA&;fv<m~0#neIYcLj{o;R_@S^jqQJeK?n#(J0x&ZORw*kAo6QV{
zs$lHm7$GVXIYX(4$%_$S-vhL$m4`KOLsCyESOFA>2|ar^I45@ILV&CY0xN(cLof#z
z(0dmZu*IZ7MGuPegoK8@*AanKg_fx`$R167JWV#0S+LAS3pxvPW5+}h+;OZBAUFn8
zbBeud_P}mN5I7;~fWirbo4R46z}m6^R?paNu}wV#64*4ch_|9fH3Lfvu5JvifGKmR
ztztkhkF99~Y|WTVz>7L~snUo@+vW3d`{|Ey{Y={D^zeB-hWzMhSe>QmbN#yc)yMqu
zF`fQ#`bwAGB~8Uk$q#mF-J&J#)|)36!=T<#?`d3)tA{<lggigN{VntzS)!8nP*ftA
z_)=CE;cT_Fag4h@Mp#9Z;FdP^cmg*Hq~_G1pdoE6E#<?jarS=I_o465sZ0<$bK{tx
z5JgtB6Ak26w3UTYJuGfGyBNfCj;mcq6SLW{4f#mufI>^Ye`t0*eQ2_8xQfH&rgU{-
zSreSxkW^9QRKs-gqqcZ{#_QF7KOdLVIO@a0O{sF)A0F=Zx2JKupA?B)ba9=I(ska7
zTVaW=8Z1<-swJ+s>&MU5XOFGUc|NXx^!wXqPmVwPuMe-L?XzcYet7fM?ftLU>x<OW
z_?w@<`}wbSkH35M>2BCPx8?mW|J}cvEpl8vdiI^>Of21Q*IoJQ@@#!}`RW%x8;^%m
z8M9`qtt{Dv$*t=iZ&urn&e9+J&wu|%|NQyyd}q7!hxX+cH@~=h_swaXWVpP1^vUzx
zkDhPVn1IA*4FSD2SSYtrsR5`Fh2jRl9hkd`v3UiI3qxSdDBy}nPQb(lxU`}w(LJCL
z#9o$(N-^M4Mq+a>X67hB;w*>`F4ctCy@@d}L^owhq|I6dRlsOYRAvVx0;d(QLv3zz
zGoGf0hoAlJw|~8S`1V+4Bh=s!I3R@t8NkpxY6Tb+0@pgb$~uNB*s7!$;%dFw#3A%o
zoAsm1&3S*mSzT<ryBxYM$e>|+DIKGjw_Mkq^P_M7>aYLb|Hr?&c|hcW&`5<?BX`n8
zXcGV`GzJU^PzZxqH3`8_hJYmC32Fc?toam~uvlqRAaqp^5z(s0ilh}C(IH8+F8HI%
zs~1<>-4j0>R%x|jY}z8b)+Dat*jQ5P5dvb|!T^GSNze)aQZTUKB+#7MmD#;fi5dWe
zrHrI%gQAPq#aOJNHpf)GMdk((UBQ9H3px{J0C%(q?&x000FlsIa55(}>Afrz!7~CH
zf;u^QYlulI0=faZ0tGW5SK}z!niIIXMGr2(@K1;Zq1k5RH4|CPO;d0{aIj|R5I9su
z<O)t)uzBsEb94l3QP2VNh@G0V=>!7SkbzwcQ9YkLZjdJMRrQdlHP_HKV0)#g6)2Fa
zA&4@_+yHmPZ%v=WDo|cpE-2`uI=b=}_>|BUTcq&7Xti`9lwg`GAtQ@U87R=U17DhR
zQ9^;LiogX-l&j6yZ&I!$okBGxMkBDsEog6I3`;|@T3t8iUQex65YkE<n|NVJN?y@{
zNlK%F>Ihhs6Xw?SPTW^Bk2GszA!j<;`ugwM)BnXFan0|q^EKI%=P8jaugguai%zaW
zSbjR+O4pwme1id&ud*Gzzr+>G`Uhv#$L0093vcGjmvn!2vp-&Z{?I?}+v&OZ?Sap1
zTyz#|_k^3>5TSxM-M~WF58|y!-$vN3XX&&8_FZrLadkm;Ds!!eLzw`3Vei*A*06Gz
zpe&U7y4nnN%(WRMmR1@Idg$^guVQHm8n)DRX8^6V4jI@F%Dq*t)1r!5R<}2&Q|NY|
ze$;DWoX}BX^|+z;w^d{1&I66QWNTQh?e7ow^Q=q0eOO!xZ@8pdmYxzmIVbmJnPTV1
zrPF0WVP1FAkkSCB64H>U-)uJh<ukWAkFU2s_#dx7x;XybpB%q!>!;82INjfW`~LOo
z%g0Y*=$F^G^;nvP&3B(|AEojAD}6ss$9jJC9ghxR+w=9!X*|5Y<@x=KpZs3UM$FC8
zSR#@lEht=b>d#(2{r>qy{rG$RkNzi*K6$#6)2r$0Z$Er_bNIk}>@T*k`|ii-({<M!
z(Y6o@*o19mssvY68o=yy<@uq3yM#d1(ZMt14hbQ1Wg}y14dX!GpdID~nYtDrno*C~
zinp;9cM+5zm~{b&0|+sxJCZep4j_vR%w4q;8#to7BUP)#X()sVNOf`(LyiQ74kc@y
z%klp3_RZ-lyS<0n(rR~+U%i2lz|HF%Xuwt+nY1)^MncOf*vUDS;)HQ^nJza%b-j+<
zcjr46K>`TAL`DoqQbRfeyi;Rb1TMA|x|=V4^H=}Re{-L?I<UK$t_E%@txPy0K|lop
zO6i<&Uscgs8UmSQ&c$oQ0i94w$vN~Lv!X{PH!a;T#FP$sG+tYfV2q%EmeQ~exZQ?z
zf*uA6Rebbi^5II&sk96rNa7h~jXYyg0>cPejU&68d2%4IlSyZximK7d`6z+K){T$K
zJIo_+G3g}~Z{|);OsG&4p&1FfD>B3kSv?G9nu)m-6L7Ca?461gfK|~Fx-I&FQ^2+~
z2*`{m(G`%Hz#32l)x<sl^jHom8?u0;rYD{|0qy_*p<>QJAprO?7eZ@@AlNDha`B?-
zg3N&dTWu^jIR@u07<<fsP)?D8r)ci3hyf`H>V(oefV0Bnnn*&ZVb8=~D{>?6J&<8W
z?V3O+i^c(LG#LnHGjFOKqnB(vgi=+j1Gu>(pcphwAV-*ydt6H$&1dD+mdj|=2ponu
z*47b7b5LdnNygo(9;*e$krlFuDFw>SqlyJ64QT<Sf(UbwKtNQcDq`TESrwJLj^`XC
z>U6+WQes(f84`JIxKgiH)Vhe5*1oI?k-d(E+VVj<EcwEAJ6UI5zG@$8TkT|X!PC3?
za4Hw)EnxcAj(0e1t~xw|X5H;5JI>=w7k1e6>zn!c_m_u<saY)h`1B*aF0_>Sc$y{#
zq4ad{q7gzKk7=_LNlsu$lP+vCZZ|eJJDrLH<U3tIDP_r}Y20+%5CK{X$e67O#jZ8W
z**l8f<u(qQ78k}+i}kfASU0TtPLKdw31w_yWl%%csh8^9vbNLos@RK9zqd+v+^}ZK
zfN=?HpLsQC+W`9U)+bVkbl4w9nNQ#BAC~)}#cH@Y-)*kW>uqXYOaszHOUvG53$rOp
z3=5P-P>$S%6>m0=)8_Hz>ha_Kmw$oWh0e>*|MKC>8|W|Rhj#PsXPN<8H}6m5-Mb*0
zW4_&d^xd@De)YG1hIu$Wyg7UJyXz;9LTsvhx_zDJo05wZ)y<W(c6WaVsN|tpSsc+2
zy87hu;>qRm!#}+E=%2lK`8e|J-_F0W>o4ygri&%|X2lnuerNODvy>VUOBlV0FWHoQ
z8H<fX1JF>zHCW&<c!;3I*a^U0!4_~qP+|ibT(d7!=YS-T7$G%=kh8V~;@)k{D{IA>
z5f#FK0$z!{HVu*#f!zt)DNqD3azjQ^Yo17=R99|p4JeYB!BWd{KFx3c;@^M$i^Jy0
zXWl=ns|PlZ@j~~v*|AqNgV2-asADz(SJ$c%0&vFyJrAjma&a~6pzk(4Yl<8?3xQd0
zG(PpgG>`}tuiVTq*f0<c``5qy>Hp<l{?#wNA*Jomn*dC%j9fSfcL6e!BZRrgQJP~!
zE3VWk*D3&(wAIDc5dhR30lSz62rR5-Ix+E(!ui?!_1m_P2TQt!c0WA6JR3qBFm{bJ
zy14Wmm3=`d+|e?c2iOsD_SFu@QCBO`%%%<rYKCsbSgJ<xQRbmgAgm4$f@(wHjh3b7
zRDEUz1}aF+C~7ql5C%$6r=}4cC^w`Psxgv7MMq<Wme5KN_Yysf$US(|5TzIj7$wPv
z5)_@xSHu;FF=q~=5ionJTQ|d~=mi_0JOA&#{|5l1E*Lq1wc!yi1ziDzov0P9E}Z}%
z#sp>YAc2}d0OzJTK(sciBx43dS*6YiT#0~UbkM*K#6SdnPeKF@f?<xjVr=RNAW=a{
z0WA}(ohOXwt;1T7oPZ#!Awu=m46IV|TANOF&R!j-Wwx+8oF+ZC0P2S11y;SP22NqO
z#?wR?l&Fu@Q3%m<RRK&0DA_XwL1@^3fLma0EpcjnpR6@!^z0sltvECi>{f_1r!Ewp
zTLW`9Kx`D(jX8i>B5BoVK&U*M713#lE)ClAAI$yk5e+YJ!!rM}+zbn)c=kNN8+-FE
z^Y`L5$n?ejHv>SE#}16rnwC7Zi#DlT*wS6m-NW$Mg|0Gkc-ULtYVGFSj>khrPHDXk
z%EFw-)UVdt((RtuJaap=bme0WG-1EZxmpnOs)+jVcA}vl(i#^uGmh9M2%R}0GFl~?
zZMXtm<^s!HLUp%HQ<ICQ>y5Uwg~nJ8qlXa46nrXR)Rxl(7t3*Z^Jf3@hff|2LFzGt
zu!;_q<YAu;4<~!|>UO`_;pYD4(3Jh=n^$9Aq^&lq6{m3d@k@fV=*c=yYdJmK+#c`1
z+A^vw3!AZZb({q`46C6JS06pQ{Qe)5n=kV1`~LY40_MAK--Pq4!}W*TZ+?9m_XUR7
zcX>Jix`Ys(d@P%@o1g#ba!=l7<MZ%jv)*;5SHHb}_r>w{{oUQtZ$e4a)6f1#e)sOH
zU;VZsH1=w4#1VWg{QE!p<i~&X<Oko|Jb%*D+h3esm4^@Q`WBny?UP53e)!qtcVdqZ
z+#7?s^_K6ok=EKYIIkS5Qgk&=JHQeE$N)T>4~UDKBL_kSn8DbV=Bz~O5r{-WMMtPJ
zVIpDlMU>GD{z(@d5!wt2hD?m$#T4DZIe=GmZ6Hj9?h=|i1vHRA3{104r)iw-e*5#U
z|Mi!@{Mqt;mVS4UHtRr@sw5keB-7Es1wAz=5L#){1-B^OYCFVm^|ae;&vs$`<m&Qq
z+O2uBvPc-$+C`$|03k%ju%(b9Oh|<N;HzN6?)@)+`WOHCAOGpk@;J4MA!<QXZ^m7y
zReSeVjNNH)Yyjv?L}1dbYt0T29JMS49Qt6bAtG6!+Mph=nU9Or>QkNf&8zXVXZ?%w
z>1OVzo}XVm8p4w&>&<qx-KN+ZDmwNWbZ!ILf)IcxbUVV#g1{~S=15SS2%^`bUNc(9
zh86)#*&6`6p{W9N<c2u2fdMHRFqkaj$cWw?*wH$t0DVVB&I(M3gw}wVRE-3+0R?~}
z6tE%c1keLaW>K0oCda{{0rdeUa3v+CqO`_xA~Qw8Rv<TO0i%o63;=<cym|8&CnZE-
z^r*qH0X8fEBm#h4<ieO#7bOqGXr31v1}q-M*sE)=F?W(k80sGSD-%W^k=ESQSxBqu
zW27UrV7yYZpz1<uCgfJCBUwgbE1W_x<Ln)HURla)O<QO|Z9x-u5v3)@FlXNk>CR`Y
z5C@{Ucpkf9jniz0qeuimlfLKz-YindEl^Mh?pc7{kRgyXj0SV@#MP=-F;r+1bfGOx
zQY3Au7MqJU54@;akblyVmj-M$#W83KT$Ko+&6YEpCF&h<0c<C``L|#1e)ONhb|-hg
zvVF9nPn++A^0)cx#o%N9WJeGGn7<+a-cvcf>-4x(JKV~GGvMjiZJ!@4UBUc8`=#|C
zoxEL~{_);ke_(lp^H`13#X5Bxm`66ShR{*xY~vdWE7U|`ER)D_LcZgb%(u<Z+jzg!
zen^#4YT64EEhi$A)aThjI_R}k<a_`pRFB@~s$5mOp^tl&rv%x<Y02lnxk1LEhy7hM
z!E_P56jMpU;{j;#{>f%}-<sxt_m%H%>g&U3tA0HBNOZb+7+)RF&dzqjChlUtxs+k;
zwoKEs96rn?pGrw022`qX5a$apFQ(ON7>3QTIe&IFJpatgetz>6UB1|ycemgE(&TLa
z?Qd^hz4ivR^1xp1N0ls3pM__iMd`o%_kTRj`N^X$uFqCaKFaenUcbJ-e}7s8S3|vB
zTB#6+RloZBFa9yaonp3WGJp`_(R%&6Km7Cu|NPN+fBa(CY5CdT?QiDeLq3*m806yd
z)6MUGZ+Nl|?Jea<-Wdouw}rHkwrH?GiZlz6(t1s@m|6ozPFM_iMl&aOuk5|S=*X=t
zgpnenCxKFzB1lRB!A%=@L|5fT{Xoso%=C=91)I5(VGrWu;FyKby%QsZ$$W$DDAFt@
zw0&!3neyEizy8&Kc%7HNS&($K@*`!(y6679>NPI}v5R4)^T8??(%21bks|u#u<m)d
zIFAD}NH3@LCeWbVNK`aA0}>`s$1r#Th6NV2-2mII^`XUh|C_)0Z~sq!@(;hMt@Qxj
z*QS9Y2lE+QV^!at(P>Y~6D8PLQ=rkw)p6{1+vD9$sUmSz%7PVxY^qg9Cj;tsp^rES
zhhf@h8fRr`H)9=-yY-GhcV~EUweBv~Tb=`~LT*(A$_&ytGM=iXGr>l%=g@=o7)Nq(
zuCxSeiXy?3BsKsCrr9+14Vx$Bid>7~J*Lh)vNuvg^rp~JAZq|hp{aI`)f8lzfqDQ$
zuhgKJKxk~jT8z8aY5+DTh2j!1FD?<?EkvX_wybW3*~l6Q2ecs67UPI><zAb5=*2Qn
z3u2J~fQ;O#haI&Eg|h{s$udglh!sPEC2JS_G-H(PM9H${2y8(CnmA~n4k(lfSPBZQ
z&)CMKGq&IX*^f<uO`Oc?kz&KPcUZHLVc{jPE!HDgl}ZYX=D;JQ6^#jXF+1R51%z<o
z20}tyf+}^ZJRXe7GJ6VSAy8Ety3nEK)&b;FqCm;obEuWEXHZ6wszR-@V-l&&DO!ke
z8gnbU7AaMY3lgFRA|WVk1Z9_D1xo`0D@Aq*=ZdWY5`(kx)Pc8##0@~$EOfQGlT&rn
zqH}luZmRolWVnC{bo-o>O8J$(!V)g%<z;94@zrEcpPZjX`!J8cET?u+U!=nq?<0H|
z){olx1+!6F*WY|dpRDLIP8Uz_zLMeg-Q6su!gD)`33b+N^-tnCP9+V?zOAvP^X_y{
zgEl*pY&G({Ot;r3zS{KbGl9I(vG|#ZW`m*Iq<Msh)kiLIq1cTRtcF?&fTOnH5Z1%W
z<~E&Nk0JI1M;Ga}RV$U&6oouzyWlW}Z5P*TZANfVg8Nf<+V9^@;WU?%iJt8C{Wm#d
z#Vo_n_ucNvi{UC55BJw!-+%LYg+PeYoKdhXt&=p5CzH|#!qDd_7sc~RPhLD<edk5L
z3)44Wte?E(Cs$?vrR#9G{^IR#zH$a^9R>p@qv_)6;_}nq?KkQ5U;djsRp^6<5U)1!
zh?dXq-hcV^>F!}vv-u1ljehsh^FJcRIn!r9`A@(4&A*wnhQ57#`Q-Qh;0K@nGy2hY
zE_bW@)93F_Z|8>-R9kO$DPCS}UtX+UTnX)|03}lj-kP_f2$ci{4Y%wgh7}D#5vh@3
zNBQ7AqfA=F8JVKlsDv;F>l*|?X=uu>MT^5~7LS6|%m7SKks(i{vm!VLWd(I60dPV^
zVyvTMVnLJQCg`0~7IW~-Z8}Yd{q_C(zyH~9{&KoLwTow~xK61X@K7L0Z85^wt<JUG
z_tQN+x;S}4IB`hbuo9g)8l7KmV~lHCMFc-mZ%i4QkY~4sa;bjD^8|56GAjmvD@cv{
z9yh6Qck}l@``7>H|N0+)r4tR1XAUG7gR3nwMO0lFxQ#d6uv?ac1hSJ<Te`|^&D_YL
zMv!@s#$6z{vSckx$P6R`-lzcj=$-VJ9kj9Jy4a~p10IiEgkE;5j)QAWyAAFO$LdP#
z02@5KL%E{q6waKxl}qKY7&PMvmQclo$uxsgqXbe(7as&*1S7(R5H$^`tJ*Y3uC!$^
zuO|{xchsm9iK`hiHZ=;Sj>{l65xbz5N+pt*Dxx));Jy&3rylFvA_f%10A=qo7@Bbx
z(5(zC6<pCaYDbg~l7b|L6VBj~Ys(^>004EQ+B8*gtc-}R86&F_2?K(AM{bZCO3mg%
z9OG#Ma_>5<v&yQ6RxwBfA~7mF9%*;(er+T`ok7LG0BqQV%m|?g85od+jW;!B00&yI
zV^?q#(nPjEV8c!uATFfp&0FBs$eld~oH7^zH1=u>a8d;iI>9W?X$eW%>?9mxL~iI@
zwR-Ew$*cf#S@N)s9kk=vB2nnE&K`oe8?u#b3_dM^r6h@YX|)Qnt8!0m5yBD}-3(J#
zT^K3=LU78c6h&=Hm`c&qW0@ht-NWkeo4@NXpX=}maOUIB{hCgjkK#r<{ieMcaeFRL
zFJ=E~dhOZHyWj0Bhd94n-n}2jLpZ-;y>CyR-2D1(`*!ROZ&&9pZg~6<w;p*l<yxUn
z237i9=~Bm$3&vyFtn<nHR6$s+u3$NmA6pfai*-=ng}G78lW5|NEs&Dy>>+v0tgciN
z)iONx(@n@zN#{oEmCa#TnysxiY_yc6iylL&8nJ~a)%vwp4CRDzpxd|e^l)}|R_Eh#
zDsSE$_INg1zR#Qw_xtgL7!b7M-TG>H{KMamtjGJW_wR1ry?(34h9@+VZd>fcEC|Ly
z7T`eMfsi$czIk@mKR&a0D%Uexp)MRg|LItGewc25`vrGh>d(gegJvvnI)8lr;y?bM
zZC~s@{JTFry*j1c`Qt~AL*!yOe);Wk{jfjWH%_@#sSSv_PA;ClB)_@(&W~fi_Fvd)
zjo*Fx(NF&4k6wO1efRnE4ZffL_Wk{bWjqqaxY-W<%V*u?cJ+8IokPwo_5=(Tsx55}
zt^=iD&8QI}8zVs!&4$RB9Vq0zEs@p<%cN|WV+kOUp=I)fOV-?cqXYx6ih_YFc7P1v
z0j7e{h=~HAfsJm0qK--pR0Oa?Xtg>)Kw^W8hN`1hD)aU0zy0l-pWS`)#txH&9!Q1=
zqBw^Ty#W!SGa_=_q5IHx@!}E>?}iRT*L7AyT1!XFB|1<tYt)=mGgbGsfvjwvr6eUt
z-khV*#%ZRrp4Xu{-GBbGfAfF)H-G($nwdRojJ*Xc(-9GO{dy?|F+en?7&Zg3PNpd+
zz`E%XRS>L9X6{hD?;%68A<?)vbY8RLLekT~wm^Nj)`!lURjes)pj5X!^t+2~8rG|h
z%8IFJL_ld7K{PbP(8`pU7}+5rj@CM6RU_4fxo@5gE~S2ez*+(%#0-v1f>0HicsckG
z$tfrpnClUll*}2eGGc4RMOD27LO`JPD)0B63V7$vk+oR^a2Lap5JKzCtCd2r6IBS<
z7U|f7$%H_l3k3q~N<{=f5U@pC=T?O_E~9A&(j-^|fJ~@9*sR(K*5*)Yy>2y&k+~Wg
z7IV)=?uHbkY83%wc7ZqodJzdo6>8GnSXqMjbnA>jh5&8=wx!@y+&lM*Ai)*U7vo9m
z8PSAJ6r?T4XpF6L1Vk(D4iLAN)e+p8IAOV?f<9$cfCw<NgPJQ@bHj>#3q~QV>jZ|i
zdSIPdOtixY5y6ww986FUn^G?#FpCeYya`$=17b0UPP~vqGXXY&uAwx`2vwJ$Fh%nb
zLeJ2a=AuCX2|y83Z0b5d$Y_w<EDANPH#hL`7ay+vpwQ+e@Bgaa^>DTx)}vIb-`>vQ
zWqR`2;D5JV*Q~qYM`!kMo9=Jyvma>0a`(2r{VHridhx8S!nn_l4R~AyTl^H`Ac0dC
zuuh$v_uLq{%%hNT$OS{pOYu@*0}qqAjt^G$_i5PB<<lX5n^#YY!<uhPdK7&uu=3bk
z!^2+rpu65_bE=4AjYPEWR`v3ww0pFv^*f)@Y7SaFn^;vIr0bo74q(?31XJJSRCzmP
zXdfoK%kAE;4~INXHCF?bAar(q_2j#s$l9F`Z@&Ee!?&+n)x}A%NkeERxF1$4!l-3m
zN|Ao>tdrzi3k2^jFDR6lw$uBshx0SNeRsTH#<{)v>RSj2(9ldm0IHW)&p-Z;{*%q~
z$J5XLtQ?nud59Y<sD7uuoezh%uYNntb8%T;tf_9)9u~E<do(?K@%q*4AO84J*U@(`
zKl%Ru^rIjCZhW@6it^3j^W}JVxZiVYVcmuG<H!B;^R(@`??G?O5zU=Qm#NWUQM?`*
zGB_J74P*3d*?}W~d285OWi&3aVgc8&Nly%l9-xg@!713VqH2%`LA=daMN)HLz_ICs
z@e103y=G00L~-LdyOIY&WFTi+u^B^ctXQ<RyxhHd^Q*6Z{l`Cl_0T{5$v=~`aqz5~
z?YPJF1r^6Ab$rmErF$0I(g_Zc*TKU;ycrUkxPlQ5DBw;2xtc}-WCC4^p|B5Ld$tNZ
zSvdD_z<vjz$!_ZxQM>K^=U@EA|NT$?@;5p<LJi4@z-&K<)RSNXpooYqbUUagKOECG
zm7^+@UP1{0b5#mzwWv!c=5sJa$8ff>;~2UG1SHN`kIQ7$8|ISL(!fc3_TcgS<ynk_
zK#Qq|4y_U(HW8+zC8{Q3advdap?FIIBWOZY;gul+THs^bBw8ZV0eqtjkrvhF(6y2Y
z6Sg8X)SzNs4M+y6Q>)cu#8!wqg4y~WP6feQBS8!<Y7m&57Hs04$&<oCQ%`k4*NDM$
z!@y8oIwH%4WB`?WvgY2T6@bY(AP2@{rNKNClK8%ql_)p>5R;2S)7Xav02+z5R;?_K
zD@j!w3QDD-2EwkgxUGWLLX$a1BzHt~);#gNZe7rW;pNtO2JW2%Dlof3!F~lc1s*CX
z#3U}YskFVN-u;L?xPdt$76$?%6b4;n$yo%a39z-<P&!=|ZN%B=SV_ccFsWdjh=!`L
zic~6Lz{DbiqE3X_J!CCC`N&ucvlv-X#0G)GknC>s6gdVj1-W^M&_qLoTIE>l4y0k@
z#ucbHoxwmG<D$->f&qw2gnl*?M9s`025fb~h8Z}Sd0OARz5ciy5(gU<&(3{)6;J=r
z-prXkNiQBp_@=&@%lcgYP|()P{Sn%5(ZB9~^y2OpHr;+MyBBMz<@r^8`1-VP+rOXp
zEmev^eYb;U0&^YM1q&%Js-ZCVNSq%I6x&|N52Zakj1~Fu%ky^co#nPkKG$K;`5xn5
zX_MNa);0Sw3~?#Lak&re)SzHWA0F~UIz3J{2iHXzk7C%%ib}~C#8spLkpuOoxq#0B
z``yJ|oi}uLIxSzm+rPQ1W1hi!6zQ8m@0;z}lkdEcj_$tt#fP`=3U~9&z}T2VDItW#
z7Y;iPJl$Tm){r^0<{;X@ySiOp#ME)WX%=qY+?*d@2Rcu2_4Vif2x-lx2?tY4-THhZ
zAOFtpUH-H0&!7L@;Tn$1L?$E_cA|cE_vW|n-oGj1)SOn`HmEPPxKG2-Z=OE7`OCka
z-v0Ijr{DSBk3ReH>Z9-B%d1N&$MMg<oexkfIpeDDHb4I1@T^a(LDE7E90fp|9c?y4
zvk<V1rs8F6v_ZZQ8XIm%W=M;Vq=p!s*riBWPU=Ji4!vtJP?!zV;54%sA!*JQ8b}aF
zg97Skh<r{qn@3=0aW^3xT?<2S=pt659&Mzc4(ckW`J3BcfBnlp{`<fAhkC+vwb{&v
z({43{7<7+NTDgCa^+n5*&G$)$w5xe;tKHCJ*GG%e0z1|w%#Av%*+kHShG6KXl?xlK
z+z`hHUoi?>gVZsn4Mp*@Eo}!6a(H+95C5M(|9AhO({TdsnL+C)mWD32VB+j~;?*W1
z*0K+MEQ|ZGT3wjkm-|{Q1o94GLIy*tQMyAr1_@ki3kd{lF+-jNF$Qa`Hq#K9iOQ_T
zXODKfZiri7Z@Sn)Y}Ku1bp*;GI-DFS#TioaE`d4)cMh0Kvj!Bznj@n-R-kS|Om0S6
zN+V}MFcfh$_YK7ZK`fpr1aJTz5K6Na(7a9Bt$Z4ZBVePJfxH=;AuwZ$FeIG7RUFkJ
z8!KaIOxPCmWC6*Yti!VR6>w&Ebnc<orV)uyvBW4=&=NueS+Rqd2mrKV*o9DF&IN%C
zQMHi+>=Kz8IhB^PiF8f)|4$K~J=?bBiDA~S8)M97t+ku8PjmasdowG$DwRrL%a$YT
zAc7a32?%V-dgG7cjTeX@2#l~L8<12ONyzF-l1u5z%*yumr`u=mz4lsj&N0TXA@zNU
z%%~1I10Zfz3ucQYG&zoi5DV*>$|dzjK~SJ+JKIb`O}&GjY(HZ>uod*~MCdD^00C^p
z$-$Br0&{^DFp(V{X2(jpdT6MU6F@*~)EZWCbgQ)(YIDX4c>>><38a&yFi#jx2|Z{?
zN(^CfiasUkUKwzZ6?}&9$A-h?QQeRNEhTbeQxgmzFG#_<og|IGj%px6!m~$hrZSKk
zVF)JWEnrTeo;X%@5J{#+nozd39;v>4H(cGv^a0~S?SI@}mnb*<{zc~1UVXLM(|mQ9
zrT5bpOR(kVeE)9$(VhS3V*Ba)kRKiA^7KkCU%$KC?(WZ9hcfR3(jaJ;6L?8EBA5Ui
zi(6v3m-1oTQ9Zs9ef93t-IFt2k~+$MdHXOv$?ZV~P`=FTW5+Yoo^b6k=Dvb(kKw#J
zsKMYDpFBU=-5UUYb9{5V)TK_LPcO&ILms2{X)k@r5V6~8Ymf%4FZ-f*^&yY&?v;LZ
zTpy8T+}R0{%*0;2_x$?l^N9NPx4-%3i!U1tk_nQ)u?4NYKu&f!?DMo+jxXJ;w=NNQ
zaRs^HY0W8r`hy?MPp;C_XRrU|zj}T9t?QGV*EjE8)n?tR0YLI$DAWG(#UK6X^5dKB
z^)KtU3lXH)<?Au+E@hg({dfQBtFONlV1w9QK7H~0k6wTAFV|(AE_cLOhN0b`k_;bw
z@bo*Mrtf|@Jbm8a{N?#q53g?h)XU*!zZ<6Ez4!MY&cpbQ*pP}Nnv=G3EZyk>v?H>&
zj=DMwk&18wqa=@LVCTZoT*3`;gNk8olwku%usjBgZVw<Ci=lR^S#bp*!aaHoG>jBd
zAXQXzXLJwigb6SL1tEnL1jTUiBVg~*+Lqq_`d|E;|HnU7`{3KJ4##yxiY}+!;k(oI
z6Rf9g_q^VI3%!?pu^#7UkNvn$&qSnc9d^!x|9dj>)?9KVHA_O>S;>fj0s+u45!5Zl
z0}^r`Wb1M{@Srgz8L6l8@a?xh{jdJ+|K}eaPfC;~NWIwHsDeke2E@rM=7e?YjGU-F
z)KoGR5*+n(ip=>?IA^axv>FSz)N>UKa^G?iUCmge5KydL!BD2&mLbP*`Jz6&WQ5(W
zjNGU4WRPAcDLMK;u9XG|60MMTlrxV3#K0Td0BY{Q!hjy#I5eUd26P}|r4UTQ%^Eu;
z<RvUIDMB&`u!v!#cJdg7Rjb9YgL)S=00-|ICrj)`hQttvv0^UV*2;ikL{2hjC4vT?
zJB^tVxFSM@NNM!NNixu84FCbqh!w09+L<o^h)4=3y7J%t?jMk;vWO6R1K{Kxh=f{1
zPLzDRcNr*6wKmCy&arv8GdQRq=^Px1JH$K)o6(LWF>y!&l*F!Bh&TneB#4xd1LP)b
z!4wfQ_!>4cbtlFEqKYyRE}#+~lsZ6(2-F^m1{5Zw(bcv4W7+q$k|cw%)&_u3f}8|g
zSkN7bjev#Q;tqhwV4ZWp%+@+}b7U|>0Z-uQ;08PZIb(7M_S*SCy@O;>5ayH+f_)MO
zZ*Bp|8o1BFZosl_jOYM?f~L&^5pyPM$%O(7pnv$2Jbdy)7;a?yQeWQc#RqcptR%))
z|FXTi=1)K1vGa1g`*I;m?c&DTFxofy$+Kq9<m)epdbya-KHdNN&AL|J4I@k1Kb@$h
z*@VX;)TayV_b$|<q}BsBOWb^T_wHd`6>&;87Y9_D6Sj49-CU*;Z8z(~MrvId+7c-_
z$r4HQA$Y<>yL~zw<VJ=ePCy95KJ5lIc(dfMzPfvTdz*7Xm>^SZ+R@gN-BmxWaOAW;
z*ss63yIs!dYCm6IcVDyd<@--AFD}myU)+8E_V(SmBd6;kdFQ87K(G*^@1~1k+A-Vt
zxO9undm4r;Igw~TUOoTx*$-X}JKP?Ycfa`Q-P^ZxvG3dQ?VDq8$$)^Krt#uteDOzr
z^yG)1F5moSdwJ~lM@_rSXHU|_Zuf&9-hKY_&wu{Q%`s5N{pEN6!~d1E<%`dM{pe<P
z{NUryKKbDvKmPjPe&@3fKK-A6@aZ#u_EG4U-?Xpp-o25vMY@=F`}y$w59W_=^7btO
zN#2nJplk1;u04RObMe}+K006WzzE6f38#w)M&O7Xv4t~+gs+x%M30on6x9qdf`w%C
zstDTC)RhDg3BVONB_ZG-3rI{z5e|`&vy*^tKqZS)JgPPHdo?@WJ^aOg_uu`)|I5$5
zcomZO^M@b9<7#U2UJiS{{{D|pD?p1yBl`d$hfAs6>Q*ie`|Cj&2i?YTA`Sqeun>bF
zfdE3P9SfOT1XIdxC%{Q02neSl`-zL;WLzdI>F%4i|Kk7p_x{_z_swxhYp)!@M%21H
zPzKIOnI(<Hti5B-7#^X8L<3XKCI}fgjj$<*n|5moWa^C>8vvcoMq^4KQ^AOJ%(%Z)
z2;kWoVQ#sXA<^@v<K^}IY|`<NMHt&T0*xiLB~jlnWi+P1GzS_cPDTcuhMoBWjELlr
zAesXqCQ23#=w>`6T|t8q5E6u<k^*sN2tr`6EtG)B9SM<;!8;0t=g>j|NJyZ;Ox+y{
zx&;pbp1N_k<H!)f8DR}#h=kw<fx<~Q$0<mIpvF8zcMb3(2pShaMO72z|Lq_A6+<Fl
z*_D%ERS*of01_66j*gz#>H-KXiL|Eyk&Q8J>Ipp}011OpVkiS;&On$3Bw!Ir5u76^
zN`@Yu6KM-hM5Z<|t>_abWZ=LP*%cwNoq{HJL?S?sWuZm@WMSAAwPbB;O*X|^dtJae
zmdBGvAXCYun@Q%B+?xv#4{RPJ;fW=P*G-CeMMTAY;@ZX`x(amVl3RB+_YRR=O<fs;
zp)Tf*!g$^c3njx~Q+MOSz|t6jwW&w}MCz#I=rB&~t$RSzoJm`EwV?8BcsYJD%Kp1_
z`^#<mgYmL4E4}^l{4l}?-x<a;-+p!a8kcVql{H<n?VrKjJ?+*s?3Od=>06>`<>~SD
zo3#mY*<E0sE)M!&4TJ*ID7a!}VGZ(zl)bl&j<v4mvs}GDUd>Z%C?(s;(ws12U^C+G
zo63^dY=bFL66%{Ga96i&pK!Rim|<g(qTBUQp3Z4XI>?BHp&X89eEIVB?$mM_Y<U3M
znRx|#dpd2vFW<a58A%>VArHK}8h6uP)Z6{-{oS%KF=U(uKULe>j<Z(<>6eF_o2zR{
z`1-3a&g)5Q01g0=bP$I9`yagc^bZK$k<O2wfBwzO&ryb0x8<RpYh{rHvylsx55NEY
z@!9+B)z4r5PhTt#i<HZoPrnDd3N+k*{)?}E`tOcsq(n*RyMN<<d3f*rcVGQaUw!kn
zVS>Wn`_X^+@gID5r+44^$>EdFKECEt{q1j$ukqp4c8sAUI$ZDP?|nEvzZnX|2C$ns
znZiVXt%*hWN`z#!^_3zWp!AFmIna~<eLa`bfq>4N^FZWafe;9QEg(5aCIyJIpU;qY
zt_B0;RD#_w36F_SRvUttHPa5gQ5r)MfdP6%L2TrYZWcfl;`Vnx`xpPmKmF5xe1ERP
zJeDU{7xSKX!xMO1DPTzH<~=Ki8`N+K-?YpnvEeZLva+N>NQ{RxOao(ojF`=q1WZCC
zsF;`l2^j`T5dl1nK$7>Dm}e-!1x1MVd*|`~UVrvq|LNcTJ73)Q5zq)TT2I8DJ(t8(
z!aEb26SxytPT~oA19K38ZXUHoB+`Z)+JXWUEdp2|Vi+;7j7TQl8H)Danp4-d)Pb2a
zCqjz${>}LG=IU^ed6$KS!@$EV+z2ozJfb849)t#>Op*w;8i^b-gHr;xMCb;4h;y=l
zk;s4y9UP#$I3Y)5h%G45m>|%#LkVGxfB;C`0?8wRm{0~ZLG7|b5VF;Jb}sG<G6XPz
zA(Mk3Z=eBD0gT)Uk`aUhBS%+C7#0Aj;1)t4;;xJXLxZ3mj8yn<{lSk3gTq2bb(H{@
zWa!ehIRXZubq5R#Bxc68Q3l)0#u3=18Ui!4Ob8AF4wxC6NJSn<oMQ_Zc?ya>kT7WE
zA^7B$BLchOoY35p1av`&-;;)0CpJI`1Lw)x=49c?A%mHF*EDNyr*5{??%}<+0GEsn
z*c}-mPbo5_j+}_ZqYF|Hm^d(!GEaNZ)jKhOB~0Pi!NmiSlSnuf@#dh40gMo>Lyk5v
zRr4T7k%-k)f(WsYxHcH(kzpIf5z#bkqp}0nMv71(h=BBr(~~y6xN!cEFZQAjbbq_8
z&2PSwuM^+@a{DGf8mOuVje^r`6%YK)1D01dK55_H>%-dLygj|E7BTox!uN-vg}1(=
zR;52EjxZFJ!pg9oYNWNBzj@P!t0yz$U132$%(3yjw?+()md9-|i?bmFPhvT;Qgf(Y
z4F{oo^L!=^b6%{_;*dfcY@S1={cfI{4^w&a?6~$f?~ZT2Ia)ohh_%Y=caLwsy{pP)
z+ySCsf)=?(D!CoEwfAyy=%;E;QQfl(maUz~DW$1gK79cpuU`MQHf-3TB!Py3^SB2*
zz4+`83*El^@+&#Kxc&7n-oAS1t|Fra9G*SHp3Hl>%pd=i|6qFW{rb(XU;XMg>#-l+
z|LF3&pTK<V{d_*&z5C5K_qUbPAgO)lkN=bBfAT{-_doyBzpJzZ^cRQAAN)su<Nas5
zr^D^1-+O<Tzc~K+?{2?Y?#@$lFW`sg&xem+>^^)iUnT^gY{ZOa$Y?Hx&CFahJcFa}
z1Q|saiV_tud(S&Cg$7FD2sjX6bIQ@qfE19405OHvek4I~$~YwHPEvpz69B4LKm-s3
z8_+3Gg*y;f0J0GEK!CO=!`bA`&;IG(|3Cb*fBjoJA3;JdpFX>~*zYe7JkKQQ?(W`A
zudc7Jo@P&1eSL_vb*nT84~PD6?5?>u73AF^>*@{;!L`f0qXg)ck{}oI#{i(b=b;0*
z(40nr6gU$S`+zQTcYFVffADwyUw`lQ+hcNAF^`AKYuz?Xl$l0GjY^afA-d))OhTb<
zly-~`0RaYX7SNMqvrfn^ggq*D>B)uiuJrX(WDM#^ELpTcM;hmg8f`x(Y%z@a`O|sc
zrzbBC6p`~pzI7RmAvzIO<z1pp2EsuADV2o1TV_JQmXJ>oJH!e;QoN&-0bRK;q7hJZ
z1acO#9Bu?`XsItQiDHWYtU%c?2A~2+28uM0Z*BuwMcksYgJD3JxiEz}_8bl(-B-#J
zghi6%$wdGS6)|C0XJJ(Ya^(U|frz*uXt)Q4G9oed#{b19-vcuva#PemNj-oW4G37k
zfx>`E0|1cRC{56|kQ9nxVeI|N@#GCK2_;bSfRb3xX&3+zg(wd!PO!uXkP=4&$dG}=
zu_Kxh7&J!gu7;4w(K$gX;3I$`>?uy}=CQNo9-KN2ws>20J8c{0da_=9sgN`JvhsdF
zV6o;#G!AxdIo!09xVK<p4^L#Ok)au}8HSo`0&+0u>?BDf5FsGIkpd!75V5=0&V{k6
z1EF_skp_{JI$>hU&;ltdp?A==@X&)&^gyW7g~S?(A8MNKj=PUEfAS~nxAO4Sy3X|E
zqx6LN@z?bgEj-8L7;%O?h!?py!~Ep<NW=0Pt}fqwb$a#H`Qh#QaP~-Pce9%>avs_S
z*96<<+B*xJ(lB8^cei%7wbk|I<GOnO;6=jNg+b?x-mMQK80PJvW3MV>+d4HJvLsa6
zx;2AgN@Bxq8pNS*JRX#{9+%114g8&4o!&g&KRn9aSx;}~%dyOR-TGbV>jhuEemFI|
zr;O#0hdG!KIw;MVr)fWwi^>{#wlLomvk?u}t3RqyOas0D-5-qm=f}t2y?t|Q8>%Up
z1cLB>x8HHWC(nNT`rH5C^!%@k7y0e4ezWR2r5y>mnrls18rI9F&p!O)pFDj2%f~NX
zZWVV=-@ADKMpA$Go39?fzQ6tE-P><ob#1$0e){QW@BQ^3CfUFGvw!^Um#>wk{U{&3
z_u2RU`X3$U<9A-ngkPWk#c#j8Psh_HS(1#?2QS7Sd^o-TV%XULXNmwK0EE^N#BGZZ
zLc(fc;Zl+=ZX3$n#T<LcfPKa7!P7`tKq~}9C$kzInFRV7WO6%)V#wfy0UJw%BbG#I
z!n%M03_BWzpc3eY>rf`BN+FyAMbyp!HR^VH`@1jy=|BDXe}DhgOLgOklORT{*Edfy
z$B-J}PPcwMpHh<joO!+g+Ut7kX5}!+G?TMahkYqYfH(vA^2pk=uW%4*4F^O8w#p>{
zcE~4`UF0O@P=KaA3<8Iwr`KQp*+2hB|J6VL$N%W=?a56W0V5c!k3lX5B-9NOnSl@k
zN&z)?=;2J^ogfi4w(x138Y%A*8n;tJ+?prCf{5WYBefPhW~2y@t{dRoxyE_JPQmPc
zy1u@+x!mukx$IyhNjbpXqkuYCMBg+I1sji&8H0=DW}PWVAQ~qC2&^bOhy`(QCqU=U
z?k3=92QLZ^EbJ2Kgs93BRYeAfWKcXTU=O~aflK0k(g?=v(LiPrLnNh4vW8BODF>0{
z@C~s<Ys>&0LckF~5djbsP?1Z7q8L@fG%{?F#J3KF%;+#c@06VX<_~^?OeV=WhzJx3
z$XzK7(2XL%$il@aK?{RH2o#fd4Ro||*vD3fl$o3eD5X@2qyQEnpm_vh;=q!KGqC`B
zP;$>mjVuwWLBW(Fjjl?>hzW8>V1N^%5O#M8Z`KDe@m2|`o8We<%{NTj>1+((?4-Ry
zP9Ym$?quX-44ucMZb*Pg5k&wzBe8%vbp!KU5XmEB$z+BZN9?Nus{(~a8L$Oo075TW
zh=mwY0x1-k8Dj8g>cj?WD2c72LoH=kkTZm<ij24{)-e*w8BZ$Dz7xAoK1Cd0S>nkH
z-X%Hyy1#nd_MZ_gacXe4DBQyCS?R=Z+Ye9ejmeYamY&|e+#c_nyX<GYxVbu9%>1x&
z2F$tSp&5`y&`I<Aby>Fx=f~|X2D-qqzc?h{7EM!-bM=7DiPVTzEI<kKhBhU1iH&k;
z+p5G@C2^6qv=j+KnS`fYdw`6@{ml|@ULJq(+n2w2yKd{91P}}l_vfY4LMif$SC^Eg
zC~nIp4u|pL;)AEt{Ndf*tL1!pJiTMb9*Q21w{PldTwoaDgYW<7;yd5><Lh7j#b5LQ
zH;MqlM1kn($$M96clqS`<*VQPe0ctzy?*)aZ$CHUwsmc1t=89mZjT~Fv5mT}cZ+xL
z-pW+k`5W7+#$oICj}Ll!I4ji`fAGCe{<Hsde75hm+n@i#zh5^*)crX8_&@pU-~Ge)
zbAR05e}4M<)th5}tdOKn<K_I`)2q)un%~>cawO_OBS3(Q+iDg{3_fXW<{ls}8KZ)*
zb>sx(Lyi>#`<7rxoD%5jG+|)(irtiEva`e9iC8<80}q9C(+cQGgoxd}2Tjh2*t3CS
zLu3I6uM`QelZe@)-Jh+;`r9|J{`BWx|MYZUb>!h{*g=GW5pr06a<v!3ykEzQ=UnOZ
z=9NXixM7;FFmii*WYY2C%Bx20{q~&EhM8QR^7>dZV{CakAa7&<;$Vc592@X8<^jSa
z?`UG4axeYy&F#<r+kf_7|DS*T^RIb5LPwSBy&V?}!9XTr018Oqf&>W%f{a0AN)T8O
zG^8Y!Ea78-5L7^8YaI|I%~*!4+Ocy4X_u1O4MU@Y<kDMLW=bMckzgsq&GqGO=KaMm
zlp#+^?5sh7%7b?=s!Pcw2@`W9z|>GUKna-8&>>W;8jA)~R3cmeh5$!&Xn+Is=AvMd
zWNncIgB+Tt0VIgRQIW`@bKN-a5ti@~oBKE;8ESOs-~ylt1<}Zv!VpkF0{=ZiAe$jP
zWDG>0jRXTn(ol4^K;D^cC<Gl4$u|O1l8s}q$o#jy|5p&01<(RKD2vZCJ0U7{vxEcz
z!abA-jgbVjH6v1CFC(-q2ez}iL!L8o%1AK~*i0h;ISUgbki-~ZB;rI!45Z*RdnjY_
zP->?TV|OA%kD!K31e<L)v_1rE5R6*=_s(l{>rU06K5lDxZP>P0TBqL|2EaD#uY+4o
zgwc}{0B~4$Ph5~yjq<X%fe%8N#Hnx2)N>#Nfp!Z|01l1{Va5eh7F9#_a7RT-0M><(
z-9m7}h;d*5#{lh8GP5B8R`5v#6^)HMGJ10WuQ*)lyWOX|Ty~M4$w;*R+8<U+AM<tC
zL-vRBLddVr@_h7QJa5CLv}xpfK%n*2?Y0q*C1w2JdghYX1vnhWU1;UHv@|-2yH>|)
z)kAUIU!>t$j-e%M1K7M=d2LN@s2z#J;GhoKbtqyiK#3rc^W~V}a-z;jVwo<)&A`xm
zHGFq}d%j;EkIUO*zdh+a!2OD+lWt307vm@UX_#~Xt9pL<I@L7|hr{I{Dak<hr`ywV
z>dibFkh{Ud+uL(hq+o9!e)?CgK6#G!k6--a&u&jEI1xZ3;xr;z8sy1~r_(sk6aVhZ
zUtV8*clr8v?~YB~Tzkqh0*<@=ZkkG>=kI-ofYzp4Ti+btYO5Aep1dd}ef6u~0E|F$
zzqW_k)Y2&L|Kv~J`(J!&k8u9{XTSP)zcd)<9e?!RcYpGq{mHW@pf7*^<)44O)m`(R
zVcqYa>_7T&|AY5-SNkH5$OGX34Awd5<S`?L$q?Qn(0K-QMhioyMBG^p?2E#rkq9Y>
zDI$m<G*%OEOo`&;D$0)BVR(XeOoeT0m<Pa3Bti~MNSsk9ya5?HF({%$41f*l$pGwx
zzP0vn-j4k4)pp)CRMGCcS$FUM@bW7Asd)m|hnOx2c^HT|czW;j_9%mt{UpQWq5W|k
zB47rV!{gm+N*D7Xk3;Hd9Q2-vGdrL`Rq_Faf(U9!=1{UsgojJK9$SuAzxm?N{!f4U
z5B|aNt7Ge)kW263r)mm}NQJ3P6vR;h#S5~DVTcNWVoGH<xeQ<x+eSD0B&H7?sLKAr
zt+Q*libM^2s)4B>A+S(N(K}6vcrw-=6B>lIi+R`;+Ryalkn*8S17%sP3mUq0Q?~Pc
z_lt4tG>$OmA-l^A%?T)ws2YG6Dlt$>;gw<sV>cU_&AXr&b`1weIaHA%LYM(lf=*}{
z-U1Rv7a1Ldtzn{Yv$PXmz&r?vIW(AoJb(!>x;i>Jcc7j~+&#p?8-{}$f<Y9d0LOri
zfe33d(+FZ#Td|CRC}bEwocVwM$A2v)_N{`XFpp8jTI<?lK+!~EL;yo^MDIWlR-G^u
zkpm1u%z^+DVZe?kp)jpE3)se`Fgg$u2g!(;AT;O@U<4M<uD}3jNw`M}#A_TZd?rO_
zC3lwr8xWjSGh$c|HV=0VkWEh!TkUJ(dTu=@N*EnVgl7z;%p$I?0&~G6LPGllV4c%w
z4T8#b;Tn!X7FZ^O2qFyak`p=@_Fe^8gao!hkX*twGddds!${P_AyLu@WJa%BM(7|7
zEDNm0L9ETipbtoHXaEr4C}HOZp2x4|_n-3Rr?`Fr$NMhN;p#&4i2c0MO*uU-b1qF_
z2(2m4i(kxstNz8C)0Zc9m;LVYs-(kYU5#8lg)LdKG7r8~pXK}jb!p8(Y2q65<#1Mz
zJ`96JLRUy3*cTY2y8uZLSOmbp(Bu#g_c;&4NMi~M_jB`ymyhSi<LmnF_4eww?;c`4
zZmCZPbk2v1i*d|_qZ61*k&F3adpy@3Yt`^TgC`$9Lrss{!{fVCdpth&2X_aG<^0$l
z&JDbziHBi-v3v5~hga`C4Ugaa>Q`GgZ01h6WUs3*l{8&Fndb4D>+#z!KY#woAHDqQ
z=bNrLP?13jUhVR;7nhPoYwP3odVO5qef#Eo|7hNs^7Z@A`EuXy&)@##Z#L`dvA@Jm
z{)4|UUuS!}{+s{j-&s$3ndyU%KKagPfB61~ZTjXfj$gie`^{;U-36U@7w<iJ@nrtN
z)#2$5CCRpe1aS)01_T-k*?nc>hyW494qeH+bxi~)8T4$X?gewVf|!iRn4?v}-jUsV
zL><vKLdj*vxKfNAh_FMF=pkh$><~&agf7exEds}g2im~Nz#3LE+GB{Q?^4R9V`9Xt
zOZ3`XhjAbC)x~ge!J6N`K5nP`a(!Kw3S^La9&TR$`ltKnSMzR<X6v$Sdd}lPmx_UP
zIgb;K62dW9z-oC^<}kiwXgmhc2u!gXWkR|+&_1{2^wmHA|NamE_h0>!KVR+^cODu1
z_wq0g7o)oiU}QsgqMCB%g3$xPqiaCN5O6XH?>3cU2)3;<9QwLO=en$>ttrZw)}>~%
z7D^7*J5h|<jYnWwo1!!ImQd!aLvbu)J|x~>jq^N=In2TvHRvAVm>SVYmhzaB0V(i^
zpdAn?C|^Ww&PGX`85=`v?ubZ`R)b6eZsCZ~8L%@mum?v3K!@-K6a)ehi6X+p-Gsr+
z!2ui`ctC6cVG%(g0No=adw2$M0u8zhuiy&E0f_|3NYjY6fud6;fPfNV5Dq*eh(rfd
zhfLKWLFmT+>kq%zYKP25K!RYXX##IDO>UJ_0x+x=90W<DvuS5ystC-8qb<(K%$O69
zVG%)$oCw3mL&D0!G2|ej0H9!O&_^$Bu9%%Tp*w(*fWZK?ptTSNE)Kz#$d9f_hS0*T
zyMse`>!*fLx6>)M)%ujIQb$cf&^yVPsTrX{o7@qD!J#QS5TSP!k6`uyP9P}3z#?EB
zP)Q1d0TPgrBya=*Z;W|=UTP9+Dmf*H-i@75!<Y#np(+x185uP4&Lvn4EPx=nM|Z%8
zlrh3B%%D2Ktz5kSYI^Sv@%Xj*6<j|}w88O2C-ZUb>6(;bKy00_c0@u6s^|MtyIphb
zcJaZ}1E0}Jy+@x`bOL1u<f%4GV2_v|mKBZTV!wkv&O72QkHtiIpl}JRoU(gSmqA(|
zc8aB_M{609m4Tm(5(-ZJtfw`<zKx~rj>s#|x|`+ZY8WzC9Ww2%Zm6A0nWiC^c^Jp5
zQ+wDpTGtcR$T(a+f08FO?Wg7bbX;nQ^X+lf2U6=xctCh2<YyPpKKk^N-Sct0xY*vl
z{_W3x(bmlam;z`N3J4f>7t@|(dLl``{pQ<?o9A!7_<{nk!}M$#ZPIAR)5CgPf`@iK
z_vULu&C<3yUyWCvd_3*mfB5`&Z{9ps>xTX6+4HA=@&Ulj!_R*Ci+}rXKl<R)AN|2+
zSI=)QfB2nx`%QcGt_JpHaVXP=-?{ky^ZB#)=DCk)MRa3vz<{KIU<y0}Qed~X1!VUg
z2Ck&KQYr>+9jK|d#A71H>=+S%<PIRoaD&$U9005GOtUdL=WB;EdO&pJ0%1@xh9Cnv
zyVvMMX>zKzIVFlU;B@A6Q3{Tja}K7$w(+#X$NRWnwz`;0KW)X|miKP*;eF}d8Qo0+
zY|{oNpa|o`!|TfzGY&H;LGNA@klOi7UDI%o0@JV~!;x0zDTTr$oD!6bInjRLi;4FI
zOJ46@{`w#N^zZ-Qe*Nd4)5E5S2?@(#^tFycr-$`G(V+{M6o3e9!N@sN00A3wS<QwS
zsVDTaR^<JRYxBPN5|Cxx8ak37OcR(vL*j(Oj-H1c29T+$O=-WHmrV2J&4^&!E~ok8
z*~MW8^R6Voj=5PE;sB2dux-PINEvXHJPBk)Bt!u)ZYSp?s<t9vfUtWGTL76n+v*$v
zWN2uN!Wa%@LeL1o!9p@cCngU;TF{CUfMEc0GDZY;^pQfHFNhnkK%Rh*5SY4i;V`s-
z+z}-(SO6n{fx6P@pki1<1ZYFZDtqD+8lX!Mxp(CqQ3FT*&wu<A5JZbm2StLxsnWo{
zQ~;3Hv<Bqli-8aX3x#?jAb_BTU7gnMMO<wV0KlA0BolT?LWxKUCSq744wUD>8aXp?
z<jkG{gpEkHaAB^nDdr5RW3A)?6cOTpz+xOq3AH+oRJU^@wK$uv3--QgZxD=*m@yOK
zkcl9P^o0qO9V{1g$55jIk%$lttVgE<#p;@X139yBtm@!^83B?Q<z$F%h5`l{D4>m*
zK$yCbloBDpF=Yl14{~Qj?{01-Qw#$-tGHka1_ll^0jSE4%?`CrpS&Y}51)U;@Q%*+
zy?I+}vc#7T>6&!IaGnHnpsF8VwdMTqySMu%RDSeX{`#S}fNGefl*EyNMJ+Vax>fC{
zD4t3lOJ<kQOuf@6+X9$P7vxLm)q#l0YDL!D80Lk_v2Cl%{Pg*bFnVj4>X9B8{IK^Y
zPez7|o#Zl4eaZFAw1d+-)S9Mgki@#+Fq*gH-Fj+gmOSR+>LL##YVY-^clYnM-rB>t
zxqE}{9PU<|4)gBm)8~XbjWWz2yAPI!FTVcrB?=YF-Bl<_DU3u;SI^%|Bk<Z^z5V9H
zkAM93%ioz+%&B10BGPSJ-Kaw;%tHqxo&kC;DV2Qv=_e_VuYUc@wzb+^F8Jo=<BN|z
zq_#W#{Le(-kN@fqpMA8?(+%y0*Z<@nD)ElJZ$bO(XMgR7?|*VJ?G|kBKn_6|0nsF&
zdteX+g}K9~;FGhO=0L-!#siW^-Fj#+Mg|59Z_eP`k@6mFq2&~ntZjxQOj>e+fg~d|
zZ(^DuC&^ijnV8bx+MGl~03twXIpeZ;=fIrIhFvNkB*7l+7|pXSR#}@uT{g_o)5D|n
z+c&%S_ZQDTVjLfCU%9n%xM7#2KF+%jZ1HTT-Ssn7a3-)thAEcduIcisP!!q#1X+so
z*rmKL!~;*n`)7G@nyvS{<L$%m{@?%OfAin`<*#1(>HsYSASl>+09DsqcH6d&%v6|(
zDJ4!!0we-~C>%YIBZ+kHK{>`4A+*&5Qb83R0$hr>-qxCv2~UJ-N-3!fJ03$r#^LF@
zo`PA@cr_FbPY#DGJUqK9g5#Jm`l1363|kFUCn=DJDGx#!pa{A<LlBcXp#m1D-2@{e
ztqp)f0-c;P(wU3_3!)JkTBg_lB`~2^q=Es611km#btJ+FA~bhrq{t;yfk*9?956E?
zhgKrcW)K0<7z7Zo8|F;isIfZ$kpV{-a9|LbxpPEkp=8Z~n}-ZN`bc^YkODIQ?H~LJ
zqd0d*2VsYRttCO|)~jKXKywslOQwJ(y0M_403@hOjESR#3`oxIqc8z8RoESpBU5vk
zkb)8h3vmLh*oROh83JpNhYu(n$_4roG@*Cu;Nsz^JW4ymP^hlh4U|C7suKNFZ9>&Y
zhqbzzx6bO-gj|`}&Ao(jgbx$@ibJ6QBzGLRuh4y%DM)}QOg$qTC<dEbI7A07=pLC7
z5CSPex>}&33xLZG1rfp2FsCFEeF@A4L9W3WITTF17&^B`;pP-XP(4&bM14^2VRBk`
zGrT?g_)mDe;m4nPzwC})TNEB>qdntRV$hNp;#?o-^p|$`&wu`SgztQOb@l94x4hjy
zs(XrNoB*?<goks3yw~0^?TFWr7$FfmW4O$2<>-4KC*56JyV?v2%o@1rAp^v!weFNL
zxJWMEJ7Wsw0rZmK!tO|9FFLQs4H^vlCH3Y0><SWq-WKE2>AZA3Z&EII1>y3^&Eax%
zQm2E`>3rUZ@Vqr-@D>5YEEzEXt}b?Ldh_8knMazYuYdY)Zr>bD6?TbYD1|V>HY4tr
zH}9=)UrCYUc0ZPj^?ZLmcLr09Db0kiMX#+=?=dEBcwUZ8eeJs2@2@`meroOU%g594
zouN(pi|_x>{?qZ<O+P;HBYgLR^x^luJC2)8H~seQmp}jI9_{HMwtt@AyLt8p&#v|j
zjxU)mfg-}(!m&9Cg#iOOcqG_1j0HUdR#0;T@=aR?Q2`M#Mh{mY!nC7iDB%?pHn0G?
z_5wirOc6sNawmo~0`-)^TL;NNj*uV#;Y_+R2%Js?NPQZIo@E|{g=Y6hz`=b5H-+rI
zK_H*++|9bdfiw<xx9#rT-Tnsl?>*0Qg+20)1$M1Qg7xaV-#I;;c2^}`%yy~}kw~D7
zkV9Y{h9M0pC6vHnro0~{WgJ8<_i4w#b-O>k{^@Ui`rrTbpZ$~D-#tbL5*oBySSymv
z0umM$TAXqWC9x!Ey~xOH<bp!L2HFY8v3k#C&RaVt=kASyAf+B1A`2q`k_bVJWv^{@
zcSI|>Xm8As;%Zs%$~b~yVYI=8%Ee^+%YB*TVv4*IK!n<Av|h7qo0|`khk3>nd3J~a
zaV0O{J+MFu14NAOg+W09a|g>9N_Dk)av|&s#f-L5>OhwwkC1i{0E7@kcyx4#FbZHN
zcZCEI4gm^A1k}QFL_>sF42fJSYKr1W1th>NazoFE7U7&m3^N44nXm#SM}!KHPzc@0
z7)xMtWhybcAdnOPZ+`Sw4KYCnWI`e6Nut7>Y%>fr3e1MafR5}Dm`k{0sOQAqfY2?4
zAZZ@~fG`pzA_3+?z$lrCCSqqq%$W>HI-t8uVh?0HlFI7t5R{o6&>>wwRK$r*2o_Q|
zoT07;<Q1SR=(hP}+oqc@t5qlU@LX(F%D@541-8uyQgR^nK+77Ck%PO(C{98i2w<*4
zp@f2%t*?Q=C}zk*u!w4xgn(4Uq@++mAy$7>nT5!6gg^*OKx0N^v^*zYvI3)9&{)KQ
zSU`XqQ$Pd*5{iRvR&gBb@ySmp-TW9I-$JhFiQ0w%v5qC5ZG!yhu}3#r-r}pD{_gXa
zR-O#gryq>x+ScuSroaGT?>!PQ*iu2Z)8peAlVsN`#-hCm4dTXoz<H_Iy?Ysl;j#@8
z=}BuFA8z#KJ1Q3??QJM!xIBA*T(Q1A8ep1smahaIw7QsWdiU=5mtWm}{`t$>w{L3o
z09lpp?$`720dd{}B@dV5FvzgKx!Bs74^Knn_KV-x25mDYf^a}$CbS+#6s~>S?+*JH
z@8!!OG`{)yzdPNp>KHvh`))U+JYHO0=iL~+FUxYee=E3iFx@&6OUU&HAMB<*ZjXuf
z9^k|bGNfH^A(AlloA*B0J-I%9`DO3(ww}89`yYSr*^j@E%ej8@yUUx)?|yP|^YpRb
z&Bs)h&)=Tky?OHSlll3R>BEm@KRkVJ9-)ezBPRq=MW+EQfD@3ohdG672#1R$iCBm=
zFh}h~thkdDT$@uOVsgN=QD;}jb|0-o5-vng37e$ZBoK<AV{*{y&SA+?%|ygmTgL#~
zQb5f?C5<DIcuLtQ?*h>wklb@|MvKRQh;6kcTnXBOZSmcca=6k3kFUSHe8T&We@vNE
z0Qh;_Uj`7M0r?)=cy(ntQFpgf9xnln+{=?IAek6T?ztSwp>v+$LRi)w=i~2w_Lu+g
z-~6k8cKc8NZN0s>4Ko=)W@1Lb0CaK|7SjMs;K1y}B}F7agr;VpzJ>xTA$Ew2gkTn_
zBy?2k06}TA+PXWs3mP$jS#%|G%A;gPCS(o>+qSK$Oqoe+i~Z=ki>vv18b{3IM1f#y
zL~2MqAP8yP&AF!i%*i<?bc4i!2@{4o6hgHiMr2b6$PfrBK#8~`I9XyeO#;Rk5y0St
zn_;%_?odDg`iR~D1}G6$0wV;Vvx~C{cg4~}1p<&d(m>?kfGHsuI3oobh9dxKPmoS-
z6e!^<nnSukJvq#fDn=2n2n=k7f;<VVz<Y`l|IP3H5qYq63j!f9Y5~p+9`2AtED=JG
zqX&Ywr~$zVyCI>2X{R727c(3RNzSOAg;IhtrPw4oB*wBQW+cSie3;M?&^>kXJ+Otb
zCBfbaHXw1_s8sYz?hipjaKnvE*fxz&Fwt6DRa@&ha%~>gs*m26U`CPzMrFuk#7twd
z%@`?l6v*Hm5ekWO3M3STZf=BKllM*uQE1&f5AML+BZ+rM)mk8H%Mm(F!NG}}IyeMU
zXA+0%K^zDm-W^4P04NoKFa>~|fVo;t2{WMA&0CK=gLXUH^<kSn9*{qx^V!=wjR8#B
zF6a&^%qC64Q2VZZ`PI8G7Ax}c4}WyA|E4}(+1;`B<UKNM?QluP&5nf0wH)h`hLWaB
zNW;hx#)*ZPHcAz&d*SshaV_U}cKYVjHn&&KUAM<GHzA*rwBzk!<zex6ckdS2X-R#Y
zI$Y`F`Lv`*rFSsCt#sVToTn!@7U{IEN2_`N<S=Ds9519e14eLWD(9EKTMg>6lB<G&
zc`gJUD3WO_(_x%rck{vZCr_5+9d-KZm%n&CRX|KUOt?e2fBz@{(e=&yU;O;fh+?bN
zq+svc!$R(qF6Q~r+j$xfyPFr+m+y_!i>oKkhWW+*$>lt~2kR1EpMLt?-II^jm+#K)
zbdjG`J-z?g_jbFCE1q9{v%j7{x;Z`k?Ct65!}b0_?-kl|^YPPX-~Zml$4{i5cbAt8
z@1hLk?xuzr>VmNbBw+A=&(x7|2@3=Q1H=I~4cb{!88|kyU_gw*=!V@^RdNee&wG{#
zBC$rsOc=mY2DUq=YxIpID`0X*nzSxh8w8Je-jU5Hf}51t-9-Qj6T3-48eA7Q;-+r?
zd$Fxt8*b~`qqi-%mc#qD>D}u$Pd<k6>Um#U*wSh1ZL7y~U(fFz9-dqr%3<nekV+mh
z81#S%JtY|^ry&j3m`d^4z}NNVx8MGoU;g4B{Ps`(hx=c@?iI&%a~?8+gc)F<6aY;I
z<{F&?+Ca?HfKkU`x79~TBc+@?3?m6PZNw=?hNcXmz2}q}eNhiRZ=FOkG|=!2Ml=Nm
z0W+(p1|fi}C&Q8k!c;N}4|yQHxftflaX;sgJ#usxuj*L59)qT#@R)d*jnaV!0tlpx
z$)R>9hA2VF+hZU}sGtWLiUzt8p_c%P=wuRu6B05(L=0g_UXhFff~YwrWFqZBD_A!l
zL2AUvv;YklC1gX&giO>h5|D6oYy#B5ldAx<C|7_Ah^}OyAwk629aBNu$O2Fu3el#`
zz&Z$0Q>+S{_;3Hg_Xv7}EC}F+#K|GpecP}k30JpLYoi3-0vXUSD9Yg0&4R=<2C}FD
zIRQ8kN0v0q$psT^AcaC`#1IvRNFgnI3DWK{iavUpII2S;*8m{Mz@1bmswZM442|m0
zH&p}{s1?*2puuoF)vc|Tvag#-3bUe$T@!nE5CZM3V2YiX8jQgMh9el{k*YbmI1_M!
zu{bDGM(r?6ATFk?=%(bq2Lucrghk1%CiNCvG&6xQXCMy7Xox~Nf|=2XCW;WlT*8~0
zp(Hf)Ad9HcQbt$cvaK?0b$lWDJ3r|gQANscL#~T`+2q>SLn;rz#SqV5)wf^YUhQK4
zS3luj-)`pjZH&6cICxXBb>Yz;!?sgf46^Jc<Qip5<|^5Mn(^h>mrg@kZaodKw8v`w
z)Tt`A7-~i6Ia3sD;Q*XV&r|k`tJ=>;$NSsm&E4a1;Wv+WRpr<=>9!b7GJ(drt<B?b
zc{7j97TdNKWV*NrY$1nwynp@XTa2Ar14uGLk{yBf+AC;2TxIl9^8VR-B|$yDqI|f&
zeS1FFVVVZXPj8;IIF+jliSzAOuevD$wz{gSkvmfvi$cqo^0cSDG{@m;gao-{o*7Bz
zd7O4n(#1YsP3`_2fmtr|Uh3`V7vKH9wY&4ns@vk<e0kjcai?lq*82AH{TDC(=(87}
z4XM8K$m5fOv9g~D4&Lt+Ml%aYfY=QYNW&liHm8K$2>PSD8V7(1B<u?IXj0IF>m5Kc
zC2kAkDNq=Vuo3Q~3F#>Yi7bZbEpu{I55h18K*BCMl`I#9r?iNwrIG7~g;BB*3k4D<
zX_65sn6m0hBLGg=I-D1W(ds$c))6=w2Hr1t{^rYa!{zEb_U`!ZS6^bDUcG#cwEyPY
zSEbl)z6zu84a|Wgc_&T-LdGrcQU>>Owzpp%zj*!f-@W|j|MKo<zpHPnEHL7_g_*0y
z?_sczhYDaGM+El(v5*iH2y#LV9s(>+7g#GZfhXoX?RRX-$N+%Dkjz^IiE+8&6|@~2
zL@|K~NvypI*qHN*JdSB?(Hb&@<+PjTGR{RG5999Q*`W+RiBv)88&x|yA#~_14_6my
z<$0eAa6ytOzz{n14kZz^kObQTa=_l%3{b)eu>fj9azfxh8qh07j;Q3>2`CIYLU$^H
z(ac3X1UKjGFhLjL9z3u$NAy;S(E%wC{OoB@w8ihCSpY;JZ3c<4hd)vW<mxdpdJK+1
zVI~}gh^~Q!B)W5EKo)8W)H)1^mH*cFehh(xYBVTWmJ;CFH=r57v32g8Mr8*q1P+LR
zsi=n41#xsGgcghrnOvEn2-7qn`k)HC9mx)6ic~1FLqmfgfee9$h(?&ybiROoZy5-L
zT+s(03P^|zjF2#T>k;TFG+Y<w(tysVb*+6>Y|B}J`exW%!k~=A#AY$D7|SeB0kDZ9
z8VNE8SW{vGZ48Xwuo*ELVuWW44j;%GhKM*rP(s513jz#6s1XvOVmf4DhXFAZX~f(-
zP;ft12sIOta~P-+BM^n!NW3i_CypSp2tW#2P@X;O`|o{*JL<#JyaLi=U!|b+v}Hf}
z{sV0K`0ATh%gwIMSAP1YdfgD*c=ldZMnuDa74&#S@FUaQ5O>;wB;br(Iuz$@s`dC7
zRUvJ<Q9nJ#+R|9c;W|rPOS{HCvE4*Jz;-Cp)BLU+b{WB!Tjz76>Gb&c`K#mY{dqec
zjne-5x{SltZLMu&nnx)_4{z>U;d#H~vBYMc(wo=64Np<3p4%{^onk)Z+HFgiI->Sr
z#(B76Q9stlw{K~G*&oiQ#|6aaJcYJ=$S*$rk8i*D*T;u7Me?jgfP~1o>Pf9_?Ex-_
z<Nd4U;mv8ii}mh!_vY=}FW-Li8^`10>o=PobIBv{@BZRna~_b$?;oo7!;=?y_U%}$
ztEG$G_uo%t?#BmP&QJf^#~*$_;oVx_RvL$C7l0?$q;Q5RZiaQU?B<2ZJpft@F*0>?
z*w$E-V}%$7Uo6CwA!oG4`WWbLdn_HBKuO^az*wf(wnzcNjsiv;svr<mA(;l-GGG>#
zF%2UDL0}|LKv_@{go^;u1h$|DO9o&9KyZ>dMF2Q$mOw!akJUA-u8*k+9e{RU-uL4d
z-{N>Yy#J$mcU<d|c7w){I)3}|?UT<w9tyLeQwC^hJXl>(QkjOKcYx-v-k!cX-M_m3
z=5@S#*S~ppJ|4ltB1RC0ZkC8rHUn&dS%_RkIFm?5>|qK4loDg8s`S=cH|79E0))*i
zsO01T*h2>Ax>nca)WZN15phN==pC@Q5CaH!Zx)Qj*3AqVu@HuB^Y2aNFvi8j;2txj
zj>(YC0<gt!opi~W%AjeSu|)FKQv@>b)Yl_G4pwkOp21HrWDJI=kaunk1-vUeLnw{{
z*b&u&h0VJ_SQIrA0t<7*jN*<22*Ncu00*GXks=XL!C0MGATUxV0>%J_kVt{rDHQ}o
zU@VBbW86?UIyka;kRSm-5UFwmO9gjRj-a689OyIu7eD@SFa}J(kr=EwVMk)?#%O>n
z(m~x>q8mv@7x2x!OJXAwwD2k#Of>ZF^CSaFDv1MAq(K~Agar+`g9Q(V5I{^p0fa@9
zfs1X>!I`OC5Uv&iL|nHZ2lD{+K7{s&WQ<_u8@8=#_bnWPTcrx#&yK~Zlu*wl;UIv9
z!Q3}VkWy9)0#rA2%mZ=lNaR{`+5ve)lnCpJiCReQK>!HhLJ$-JBqH6Kwt+a%Ga*J3
z$Kc$dLn!6}-H~xPtKlY+yX46$LqRd{@PzC^iIk8#HX$?!O#%JcO*;JOyF6qm=&{4?
zAwYu*CWC&#`CVJD`0LMQ{~pHLA8kAJ2b;xv<B>5()LwSf7HZhr0@D=x>ynI-h<Ylc
z0@p}#`leQ1YSYlIwM6dGa5_A{9Cio4$Vyb&>4=J}_wDU*^RpzLbItC+j2(M_Tpo0*
zCq?o^n8%&*!@Ijym({n-Lnhj4-0K-p%1{X8e0mo!Ea%%`rX=0NnljqCoxG{bKu$<)
zH(xVTN1M`UWzG31Z|mcByf|FX`&s4y93PHfzWnBE@_>}EnT3KHq6acV1P3_ixo*qC
zE1jcuSK#6C{_%Y7ZMCKkUCMr#-oAM`?k^&$woQufZa!#hd;R9~`RT>}(~rg(!&*DO
zn-24b-#N_7?c<$3;;<V%^8*IPvVvvTb9ah7gG{I=%oNeh6B_uY+QF>`kAO9_S3LkY
zjswII*0aM96(R-om6C)^;tN<}uaO~~oroocpB)D9Zk2>0yO!ayls1<!^9oiy74(Rx
ztfTuv!{jDG;7&{#Eg>%u1DJ~417u9eV~Z_lX|OI^MY6uF)S|WI;OVPdy?gt`r$2Zy
z-27pE^*c}oYUObKaB951J=|QB-KFa}g0l$NlIIzcCefbb;Z@wfc{o3w&iP_l^xe^y
z*2&45A|xP!LAWrrurMLArc@oEj|QAMunDu7fEb661doGIM5}~IDKn?7t&TvQ4HPhu
zhk;N)Gz@m$jqWZknAx3&A!$={M;?)Yr`<T_fY|TyPV)Y0nxD<%JQZAP*<sXeWeFpO
zh?0}$LCQSQfQiW@GdpQSJw-k^do~CP4?rB<4b?r51QvqUL6Fo{(89uvh*F9cA%WoN
zK!6Akj;SDazzH$J5JCXK-8smCuqqQ$QouxlfEx!n1Xv(O08#J81<e8iLlFT;um_=s
zhe-1fLdPCK<P=~WjIPi_7*-snh>ict&whfA29D|x!r@lCW0J7o1j)_OJgFl@4UVV?
z-N?1G0QuH{9YYABr;<EL3NNTsrd-6Lr;;sYiU!~u<bqTKQYA+MY3Yt|?0_K@Hl{c^
zW=2F$J!cHSXwf0k0Bs8hUwWAMx*CLcGnLb+8CZ34Xa?+<1<k2hI9qQmA`wF|rtD!3
zGA79sn-Xzm_sAKWHxEgyW{fE%u!h8_CWO#DMA*!WWKbhAL?MDu<Pw}+wO|lHCgccj
z3Pa|I3Nn&d4`kP791bV7B5_g(Lq>CkELg)iJ$b5EKlwD#g1FL9pw19)VY=de_Ck3b
z3|}9cj_-~7?eX|pH#}HBQ+dj%>2MHRN#*pooV##IRCXm4hpBKXZHqJp5}IjB>taM1
ztb!4h-E@%oVjK&jvm8%ac=d5<<f>nPdw2V|UG6091h+H^YdyJgTPu()iNdrzzdCPg
z+gf+5x`vwJ*nQQFHTof6TQs}O-ptkosnBM1scx|Llli!XOJve^*kAA{z!_`Ps_W@E
z?_QL7?DhWU#b@ZQ+j4jN`uyhJ!jnv62~$YyuI|R<8s^{<=75n)-bkTtroBI|JsekU
zUV#b>vd{bVyrj04ESE2yb?e4C)_C>pmo)6>_jY>w3fA)$P!1P<dZ~*adrO?g++{2j
zin@1FU3*Z5GBB!DBp`R9wyAFBXWs_O8QPuOq#N+A;BFMJaoUXbP)KAzAke}^B0RPw
z^5kaGBDhgvBZca&=0r|ILf#$b8ElZ>8&DTCB`ASPlqnD~A$3AV+yI2E83lSWUkLzO
z!ra3vHV<N39>Hwwt#vTQ<-yj6$Kmo)9Nyi_<ClMN_1VqE^{1!%m-AsCOW?ZR-k)zi
zx*4d1D|VL@vOoCaLmp?bQ#;pQTi1oG)^)Wt&bP<A%~XwxAchYBfMH=v=L`{)2|YCD
zL6X4IW6t<{(ufBF;UFiF-m42?YJEcwM&TmO3>j+kt`=LzOxh}vpqdc4_&zyj!n)Zo
zxrLc2L@w!&GB6&5_s@41v+u8-h_6Xfm||Tm%oL#6FpxkWN{(U5x~3xWduj~OK|oYO
z0M6Yxf#L6YMP_#dIaiz6%^(s3Fa}GQ18d+I=wReypikh6!qJoqhC2&l7(@%sL5>#0
z7Q&nq8X^_shEW2Q0|F9ZU|T8gAU5h1rUF}nEM^c;5oX}ZQP2ax5h4spkigXvMNjBX
zJBBlN<Nw{KKlXsJ2n!&Z88NeK55jE5$YL!5nawsp7R(IH<{_EYBiHB{RtE1D5RxDb
zz$tObsjXNTWyk0N2!b)qKm`kdiIP)BawQqb)nj640W8rX0boF@>=~c~OaPnq9r@-x
ztZ%^3T5QHvw-&f9o3FH4sCki~;4%!Po~D^bfsQl|Nt<<74o1%2(vZQtGY#1{$cO=s
zJu?KbIpry|J5w|>CSZh4XwiYuf(RifggLr<1WIPJ;KmRD2w><*=IEvai@49%aISE5
z<=#^gBSAzrZzy)`E|?QOeG=Dy^-oe5_SZyPloEYP`nk7@oEFQ6csLBFiWlwi&F-vg
z+i2v(T)|AV_u*<=Y<+0WdtuDuRgR@_*gQl70PS^aTWtmsgoi`XMyQ8f87|62iewvX
zr=BzNj{4e@JudQY^T*@;`2LewEvTKh2qd)pcsk#`eM_q|%6dE{nNx=9)sd9&R5z{G
ztYYGFIi%sLzI+QH31eF}#ej%;cfD;bVjJ@lPeCH%NSVj|xYO3&-M>px=IiHE$}&C2
zxJwsT*4F#w5wqlS*==>TFmnl@0_XuLGIL6#4r4JhS3^#HTVoulq~05pF#r<qG#@1D
z<)J+P@PqwS^t?qy#QAvtGEL(I2WXjdk9;Tx0zn_r#r`~xWyqEWN8CAV*1#dtFhT^N
zdq8Azt#0Vm%!&78Z4Ezo_cS5RW3HQ4Q!bPcv<C<cfYgv?h&7CpQ0?l&K8P^Fd$aRm
zJg~>%V!A9kWWuDFBoHAWG<k*q6as?;a0Z%@+#w;PfB@;}jL0Z~i6E5Jq9d<&vAUnm
zzHHh%qm@z~k!w7TRLte&L*HKi?(*hIUzhCabrmkR@9w8@8?Qbf?`WB#r8MDyX_y^H
z>L=@%m(#fgth(GEU!Sz4xdmI_f`CZCsC#D&!i?c491w*Emc{m$(i>9lii8ebfdG+#
zkcNF&Cnhwa#I;rR@Xg&U7m2_QoSeG#1i>r>l$#TwwrGI?Ooo9nP9sW4lwnBwX_%jm
zbCERU1nX*<b8l;Qs2<D{&C`G-^PnkDo`AifWD6yChvdvaAcg=5BLX*e0489J9n<PT
zh@==f90LPjWUNlf#{$u)ttd&%4aT4Wx&p0WGcckm_<(@m1lSSgh~~_s0NxZbx`LS#
z5j04Nj3Y6+xH)1YD8LRt=p6$By81|B=9Hyb%CR*TQ7#CLxrI*rH^2X5MCfjY-8Xh}
ziAETjW;4LPKxUPTrj;ZB8MO;|un~|uf`^!zvT5geHxL3zhBko0z#;;Y*%iTr3zEqI
z)Po`j!~w_*Dgi<2&4d8}1ptG)J5pc}C9aPK5Dwk~KzwVqYA3dJT|FK)T5Bkxsk5*M
zN&!eI))s(S9T_AiE+U+<6wyj4qq&qssD#`Bi3BDhC`BBCA^~=D0#8W-vI7uCCJa-H
z%xJ;`8Cs|?O8{r&4%Wj*D#%1pXq0XpdJSnOgD`Z~3S!b40$M<i)EkO9V7k68PyXW{
z)A$T^AU&ao$pOy~kglknD4i<4?GpBjH;-?8yWetdeYzyou$mcxS!wFrgfCK&af*~U
zq6R>;7Hu&cwwmI&luQ@11Lo-<<uV_-V+MFwF_^Zsd4oJBZ#Cp>r<(KO;tH3$+YWJm
zd~-ZKn#OWowl-W{s*Mdo`^nZ-n-Sv3yVL2RZy}UW>NMY!DILFkOT*>%_!c~;RK{Hy
z#{;`5P#HOzf*Z1yd`P?L;qKk>?$jk-eEfYG+PI&Ixt`8;?%&?N!gYh~gaQo2<EZYQ
z01YIgqC!LWjv_Se!CY6>A^;4{FppuvTrVy^c=7D=kmTxScYQVE{kK}1ae?xDJKr9j
zfBO87J{vwfFs{3+i<=M9VLV+<`9j)M7=}YyTBNF82mz;w5=Rc?0?~mWdWU6mn<drc
z<|hj3m_|v8;k`-ADKkQr6oEy61DZrsKX;>ni98{phf32a*41=MyZQ34zo4yX!nQ`t
z$cSV}dyfZ9BU^+ZQ2+)>acKmF7%|9!(Hjm*4CwA^;DBCT$seoss%xx|b+yQosOIDS
z`p_3j`Mk;9Lp_~ee)#cs)`t^LLtAU$w7h$Fbveu75<LLD^feAwsXvraoHQG0U&?uX
z=kWH;^7;&h2#F1nLt__=2Ic{YMoXATHRnVHal|3OKq8Q^ha!*??;Riw09qB(nApJ6
zeul)*!-de0WLmfLly~RzIVZrJAxuF%0224W0I-M^c$!9QwL~dHKU_~&m-}fyURcwN
zv0<|nV8?pG4tbuE@F>B<P@n?u5mpc)029IpNlex-3WS0Kl9>!ajM^y%JU8%0WyS!+
z?xBJbu7nYq5+SsXg$cV017t)3hX4-_G;=N>W)+CHuo>M06K#MQ2zP*tQ4N7YEHQ#v
z@8L+I;6NM!9fAOrGJ!iVgLhR;k%LS0ih;}!d0<CSb^~HcOvw>r!<3nv)UdOZu8shb
zHTKZg3^ti~cT){C@Ub_SaEMh29acujK;1f{#{L=us3b#Tdk6+7V{i`&Z(z*95knvV
z85t&|q>;l65&$JougH)G6c1V<7mPt_bIAY;=0r#djR2uSXqTqOP8jM2=3&IuLd3xx
zogAT|w5{6~=L(&*ww6+lsGg$w&?=#Vf;>F*PKFK^HjIinl?lhvnFbCAH$-6QFp)O5
zpv=-7Fe4a*G$nu*NSF%f+G9X+8FBz3a(G7wR1>De-0=506--IEZox(oC~sj%pbU0Q
z0zMZUwl2HAx|~P+`qkYx=kwi3o8z|f-O+vCe0U_0`z80+2Zr%_NW(NijG_WvyDa8)
z!~0-@iWl=XinnPuY?pJMn4zz9T7BfL<WsrO7;SyH+X&7Zo_cbp+#W5hr|rvk+v!-_
zn}_9Yli`p6q#Wkm7!hqi%BA+@_4019R^e==H}kuP`@3(xs#8{d)Q}dvd9Rx$_Upsd
z)zwUnC1-tj)P{pFHldor9E9d++HP-UH(p#n9SR&ScKdM*63;0VjF3wqb66c*!W>*^
zERe+d649gg4(_&vEvBJcLul*+(vnbbo*dr)<XPPR_I!T?_X+9*dbqg0{NVc8kDk2u
zydC27?AqUd?(bj9RcQkTm!5`hNunm8g1Hcd=Oltel*plqRko6F=y-1Zp#lTsk@LxE
z=N);1Sv0j^4Jb$vux_?iJy~BLD0?J=uHMzOm*ba2^WjN;G2<LcFz9T3oQN!_BMSN>
zD^Pb3Ldp=%z=#GqqE=WNLJg`>fE0aQossg44WTUr0hvTfK8!L1X-Mt1-CtZ^5YO5k
z2kDH%@gd#aeSP`jGL=D9_tQn$Up$^y;MBZAnX321fEv_oXjL)~R?oTARd0{IR`9yA
zR@Eh?5)jdi5Hnj^5wtU))kU|JYx3FwOsoYHQeXhK6-oP}TDQaXV7BJrvY+n#u3}r@
zypb#EnM7*eWFkn6J+|JGXY?QxG9>`FG!EGVOXsq8#$hfgjY{XGmy~_;#DJjPAnhr+
z^qMpCP`G#+#JwTb@a#3H2b7H4LV>QG5Q5Ov2{mBGfxy*CIgN4Jz|Yoq2!ok2QxFiX
zzO9hZTLcPh%`^~SMXXLKA^--4Ed@xNs87rtk|Aju3e;}68L0X|;7DWU2GNrUk(dw>
zAQ9q_6R?GQ0u&HroT+3Q5qb}UR8ofm+c97XBuI_A<Sv<o5Ml!fOoo|Iw08v!X2peQ
zg;dVasn^8fTCsJ?sL<Uwj1mi^X=RT@27+#y5L%#xTM7b$D|5zvro0f&AsY~&gkxb|
zoD2$Q5e@_I2qi%Q+AuPCGTgZ>CPI2f%AgTrYB*O;z%gP1L~InC5@a7HFn4r8%yU9Y
zk~8M6X|zB`w>e|Zxv8Cv)LI%{ST_J99|g!rGOIydGh*oCYZr7!z{T@w4hiasgSumN
zWJ2s!%smf@2?a`1R7%DK<|=I669?syB2wlJ-C%7EMH1-DNMR@ly*n6oM_xl-P~U($
zr{^g-?mA#r>9@Z;p1y7Cve=><44#Ct3JW>CL0UAWK@Pa?XBA&NaKpK3KR?#v+4%7^
z5gZ6EcuhN?Ven?SJXXpLNRYXV1NC>MoIL5n+heV+Z9AS%FJGSS9v&WTS?c|L{o>W~
zhC>c7VE5Egt;?yNm;TrvPj7p8SkIY^lCGQf(9?a5KJTtkfMvOO{-P0P^uSiGUJQcZ
zd4sm1YG%j9e4$xMqCeJ$+po+Da?m`DmwO+_j0Av;fmw)idv{MbQs&6$d7zNG99ugZ
zPJOGZ8x_%0R0R??wCQkhd42fcVi<4AlkY#ryT4q&dGm0;vfISC+sn=K=Z6ul$9i@D
zDiK^2zszkIgLVff6XKp+aC967&JFT_!%h+g3~p{wJex^kw^&ZilB-6VNV~TTa1snq
z_YlvHLyj8S)<|dx`op7YMBRE@tYe&=+nT4{i_xA8mW6oEP>3*LgeNqb9myG8b_mVE
zz^glWcmyJt^3FIQNwE<-Mi9gmnlT(rAb9VpG7}#5#I!3i3bylBf}(GS6bO+*Q{uX8
zU;OeLP2i-%KGlBA<MX#)eD2<a2+^DdzWq`Sz1M)Oy6G_Uz@Xkz!hsu-4h0a%iP2G#
zGeRDk01qiZsibJQtVbUcpeX=2Pzfsg5QFA6pjSBEA88u;`OYJGZblhOvXKu@ZU_ld
zM+NrOU4@t!S(qS@N=1WEt6N*oUPj?ET}{)j45Lh!a~_n^bwFcG;p<xOZMy1$Es<wJ
ziJ*yj0W0Arg9gyCuTDd_l3PRrjKCf>s6V=eTZ16dMq@`O>I=}y(ZYi$z$voO(t*f(
zLryF;(lrf<ppym+XY}l$oWzCzYzXWeK+sd}8r&;@JMNt}w;63B3`m{IgcM{PXwkO7
z5{>}?t0E&AgIS0K4qm(WPKvm(yMkxJUa?m_HV}bL6&40hVU$rsPzaD&Cf5!I#B7iy
z0&>8n*bGDhYBvcWJU6DCD8;fl6foyBB6HF}ctD&CcV1@-MIWGT(Zmx3<$yrymUaN0
zQJ}kfQ0t-QoG?ya4NN2LA_9dXMA|}Nz^SuGVoo9fK&0<hltCj~L3aURONo%1St1;T
zAO~|c!Pw=<S-eMND40M<5@1k<K!|xZQVt42MG-d|g)$R_6yD5K*$g2FiB*AG8^}va
zxJ$09kw8x(EsREHkRBVFp%NPXp5(v;)D6{!Ku8AE6);d&C>1oLzl9Vo&$4c`JhabW
zonQOIMs3+Tv2%hX9tFj=)qpZid#~2IVP6c)Fg5Mgt4Ah~sEemko=m%Zeej2btdZMN
zqpe7@J}!QK)1+L~nozfVSnAfn&!^Mvuin0UynFS{tKWV3?yJ}5cY2b17;X**X6?a1
zwQc(UrwC7*W=YQUK<|DQcaMn7e3!G;0u+D((CCHT&Ed$5)C~WJhSG=8Kb6_o%s1H>
zIif_W8$@HP<y4*RT{1Hw+@D3zb@9&A)Vo=1#3JcE8P1Q}IfmRFj#%w_&daB_8bd$3
z9h&Bk?e=v$^yg2Hx=eNOjy0v)+WT<k`n%u%Q<u5-nsY8M-rT%+{qpnAU3ehfLae(6
zWK45Yce|Wz+bk@-c<;HcFz>d9AUo}=Ys@vSfA!U8uRnh^dyEf1?2q)=o3wU3l-t+y
zi(h^A`7gex8M7fQ$+g`z-!Cyh)LAI1@4jc2n5vkntU^~BEB9yGSBh)BUd#Sy!@P%c
zXPsz#j7WFJW%Z}b%{+l}lhK2a>3Na7KD(3Sko#triMR{#u^w-4r5u%U2x8zuc}@oT
z&fv<)%A|}oQWr#$3xSf;4N1hfNSmax&q4VYp3QSp+d?oyn5hzh3&FZfN3U92Nvhc~
zF3%2PA5BIT-D3LgUw)82Bw2NkZR?QryWi__h<(^~f4Xq05M>$9wJcpkK0Q7^ul)jI
z=TK}m)q-#?T7*jzArwW9p<HNWX(-g<d}S$MSd?jgJucU9I1P8Hi)v~-2S!4vv?UVW
z1D@Z#d++8YstCif)K<&XlzGx^B((UJuyEC4Lb1wH=VP5FY4eel5Xk+KBUxK%(&~}N
zMedFh%uqA~;}T4$NxlFz{g5sxYcO+7poB4Tuu-zpm{FLOV~S++oW07h>PRv3Ef8=T
zz7z-rFW9pNNgBfyNfO)<y1Tg-F0>^U^k<Ax{8{o+Adn!2Y83MvgTf3-UFdQJPcF=^
z5hck4E6I!k&Sxg^o`egOi8oLIGsJ~DT-kJ(4m#H1N$ft@r?zvAQUuNsQ)OQ-DKn<#
zW${|JD7`T1EXo5$OC=8YUg6e_`rt!N%AUJhk-UbK;?QEuQ;3E=Mb8)uMIt>p*#r)b
zD_A3fIG9FqV-B_F1Qd(NLSry7(Zq4Z=<E&$_tb>Y9wfpso!It){W=$tbPQJ(At{c^
zaE7K#zAI5dq*)TAWld=|xCo-q+BrPCRVS)dVWQNN1u@MsaVStrnQKl2pR^^Y00W`i
zyGKtY7tLr%b>RaxBg&eskXD^rSem3Piw;p98atR#4>X?P6L}|glb`bWlkk%_zm6~L
zom~FghYL@a2y>Z=3x@c`<5OQr(=mZFWR{7#%W1`n4#dUj>^!dZMyAt?n;Hu}5NO*d
zV)sU?93XX7`nb3KVdRH*9nOcw$D6b2_aC3WUAI4cSpWQ^{kY{L=?A3G)!MxE?eh5D
z^SgKZ!?Q)$7MW;Mksv0BRuwU3ZZnrK#&mx(y*TchgG>M6iFa({I+vUEx}^$H%N*4g
zsbxOSS<AM?_UZER!?#Gje0WUZ>CI=aU;pa%?j9}{vIUYTMr>CT%6XoNebyS4A$+--
zZ622$RJ~L!`uev1<KO+_i=W@Wd3k3Tk3K(rO0hT|4==v_`HO$`4|l)3Sq@45JXlPo
z)9H0>iE&D@q$DNg(BfG_a-(T>)XZk6J;lX6b?g$|b09=yP<pqW34^RVCO;}kPf=9L
zNOnr-7^OcaOXqs>^W5xug52H6{oDmgA)pN$nJ2`Nn7Pa>oyXuX4l15h7^R|tSh71|
zS5Bd1yC8$dps5ja6=va-JDv^Yfa8+6a0aRQ*c)eeEVvw}CauzCbIrn8Rr-c^-+g%e
z@WCS|F6;SOS)bls%Ix#)%+raNS#R#PC^=P{#P;dOPx*Y!r|zv3JuZVN1<P?37M?Vn
zxtOGP4^I=0q>0$b5UX>O!(97*ne~7+p&pl)H#94^nyuh&7GYaAl2V!SLM;Mgu;|=$
zkQavdmYUfuJ-V5Pr-+i&V<Yvs;y6=Pv|P)fvy_mFhm5|)md*Rp+6=1Gv<Rio!+XY@
zqkG?o*VM>xCd#64gfNjLHJX#7h;|AHO)wCSppodzo|w8cV+jH$5!DRI5mxIE4MRAG
z1@n>u$dojbn@3CEofm?+9XKYAOcEp^EE4cNDPb_R41*IwlSXEOJgKDYBpSgHoLV#{
zqK;CVtSI0rzzf7K|KV5v2BBQ*6eA-WDB+nJwk03oJ9VOpPK@bDmj~F+laO;-PfWJ2
za57~CDZnC3Lr~}WSTtyUCA?AynowkFE$G&6iyFYmD@BRIkRGn!XN&`UM^$77rS#oo
z!r;hrH?UtZ!bf+DvBh=8MQUEJqr;pB284+U##*X+IFn!tIo23EwHc8JP%UG#LT*Ir
zkjPGskz-i25@zZnqp2c2xD@X@bAUB=_~<Nxh%6iwF^ri}2xZ2&b|R69)J2&{HNr!X
zYoQuR$~*T#r3iHi7!?~G5CBPebCWlJ`=Y%14f~a_lOT2%Jd+-Q_2Jz=fB4VOAAj&*
zq~%OWsaYy4_HA&0e4W#riEFF<X{&d8zYI4-l*8>@%jsY|Epi<=&`JlnTx(oFRl}NJ
z*3R4f(qx*(Cslb4efsv#zyId)eD!O}$Y|x*NqS!E@p!y_?UxUa56{lF_wZl`)zO_w
z6x32ib{^9EjtJs@m=9$hB8PtIem=WkJ{?(|%WTpgK0J?|h+S$N4nJv<AAfk;s2Wo;
zq*QU%)A9IXneK1vm6c^`RS_{bAy?~GWnX)eY<(~=)-5uVJ+=8%TjzPAfBla?`SRCa
z{p8F0`1IY!i*<9c>s0vFFTQ;7*Pp-no6nnkdj9s|X^)#%FFdEa`#!%2k0g^oaL$nj
zWQwxu#I=A!h?s+57}fL9hsJKH=FdZ7L#-$i$hnHOBRE9vibld<O2~dOvvA9zb=E!~
z%dObFc*#OTs33hu7Ags?93unp&BIYDxge7rNr5C8#-*|lbYdGQjbPzP(+Qj;$_&q-
z8kxf37%TaR&F%TJtziRxUU!{Lak<()<Y9M^q=h?d?>B9l?i)c-`0})Q%hzArF2~Pu
z{m6pp^=GtRq!tN#H2v{UfBOB8zKSyyn#h^B&VISTY>&}I#ujAWrdfhAF)t7(*NOYe
zQikt7Op3sSIm#ezEiczFu2c1OjTD1-AAC@_kXVkfZ&q8IrVJxS_5ltIbJHsIpsEw&
zH0j;pc%1lj+vbI*J}B#k;!XOKQ&TD_rL;CJ(vt$x!%o<*xF%@>CB;Bl5J^TrRUYY<
zRzV#)K?i{|hsTt*F%HxQ?3i{@hPXKjkHH9BQ)&mDXn%$%N^lDNLUCZ)S@%H8NM;i9
zP{2oMN`OlOoIbnvtP{bQ88jygQ@~abnH*VFkZ=zpPO;pCQ`2hSodAnKbiX3GW=v_(
z(>PQ9r?3AW0ge$Y9;3?wGSXv?=UiS@f7()MI^<;+8{(EEI>^&hb;n=;s&J|UUR+8!
z-OXB9$%~FGt??Gi3#KdALbMZR!k{vfcc_Df1!Mt6X5pAfjJ%}nlv6UHOXf;hi6_T!
z=ja{5MzD-Eu?=kQo3rbj*A<oLqJ|J*Fmcn2y?Uit5d%`)dI`_EP?!`#nN=%@co0oe
zv}6Qn3ELIT+=-x~={_jyT#ZXc<{D5+s@ekeR>qTPOYd5kTqvM6qG(4-Pmg4-1k-d%
zcGJ*|V73h?;%qoAvcCM=oA%|eI8DZTl!H^rI16*U|MBsE`|kbwytoue5lPOd2$^A5
z*J?(hv^!UGkn*0sKa(XuDspVs(^NFP^u8O|JpHM!tG63jCfIfTXcy;t5ZAi7tXYSj
zS6lvcehk8O)R8E?m(zV_KOJ7&AMe+v@8P;$SBHlvxhqVhxP!<FHJzvMu28o~(6PO=
z%kwh7IA5MpqNKt{D*G8q*}H>+>Uj0DU&Olq`1ZR!S4f?7{_ys9+K$t4ndW6S>AM~8
zzgU(Ruio6>zy6w3X!!c+(%mB&O|AO|P^pO}SZr)}clE#jo3DTI_dogiCpy`Ok3ZV`
zcYVBnJl@Fto6kS{%~vme^JbB6<9&aB(fyJGMQ}OYoepg$WhaZikxa1w(uk{SAy(Fv
zLE#d8rMT`-!f?*fahD~9xwu4GBofUho;Z3ebE*=Iy!&vm4!*XellEJdLqstHDJG(5
z0ElZUr3kAdgK|$7ND%^;0E;Iv3uTai(lb-407=>)#@tApLdiUZK|{1e4ob_hp(DFn
zX7ug6+um`BF|zC~PuuxI)y-z%-Na&39=ESv#=b?5%XO=SFFyZ#UNoi~xvVz_(#l#J
z`}p+X{Nvl_?;fA1XJV}99(#;!FO}h_+ALjIq}23Ku@KH3l`>=KQTjIO>6XphI;m6B
z4ENEAOHyZdC*D6j=S&#vdk;<!14P0sq5GiaW<*kYCyJ_qi83l<X?c5pbN6~V+)l@X
z)K&+z%+<YzQ9&x@a^RefRf1Y3@5qV9xhH#5wrAuNUeYeajoa?VoICW$vL+F^6E|`)
z5kWEwG7&@+$&&D#KwJnJNQe?C$I7G`ASs+csRRZwTuFlRKx72A&Qw&KP_|6L-YLSk
z3Jl4_J0gfEWrS*?vnOXyN_U9z7)~TnRIHC<O9w|%cWo-x1Came@BSVNrc5U-POZ3`
zXi2+LVyp%Z_h9$X0s#{ZZOnqafHaC4sM=6LTiRTi$6ltT6qGVCor!1GWC*2!?`fI|
zgjB&5d}KIwrJ|fia42VjkY>cBAW<gHK?qMXk3z#?Hl+FUc^i2>?;FQHf=K(=xc6Ea
z(G|)J0-2444ATp4$UbQ7wao__!-oiZBr~W-+K`DI(#GyhlZ%S(MRW898_eb@Eat8r
zv{Y)s%cN!)2@>A7l%!HgDyOBBvxFNb3uV<buto=Cn7Vp_34<dBHOrk?EcVZSNz2z?
zGbvlA(jkg5`0UUB^8MTY{Qje}MQve4$%uVVy<S{fp}|3=c$GMl1TWjVU)D=(JQYmG
zdQ-|=-1X)%B6;27ZvF6Z{j|25#kj|r$4(cQ(zl5{mieH1VY#4=+ZT@?o`NZQcbXb+
zr^9^rv#+Gk```cR>FL}#XqSw=E;sw;8Q08^v2)6nRzvr&GR-_W+-ogs#%(!XE*Bbo
z6f|vG-1^Kk*2}Xl^P8XiGM=A*c>7&YOl2wa($Ci(`{9=EfAO`Ae*Nyx&yVl=`f0ko
znO{^{+Hv{(vVGr&yKPh@x{<K&=3-Qr(=7k^<rlyF_rL!2Pxx5;`rj_^-ah8wa&vh3
z(^qf)@vmR~{B^th=lo#{!Fko|>X(%=@cEb0mPWD0(@L?3ZZPBH#HyTodKF5<ZkIhT
zW}EXE=-v6Kc9pSF&s3Oqs+x4Cu)LW{QHf{XojmdImM@pq$0>9nr>c|)4v_Nnip$0%
z(1B6~#HEr8+_Dv-5L5yaYf=v?z(!O7qc(9Oxd`n<M?w-Qlu`mrjD<i5VTQZIjgoyE
z=o>yfY)UIjQ`!1{ea5=lHK|OJQz;+`98XfD*dTWX;^xKl;)^d-ItsM{F5IT`-tpnj
z+xLI^{l|;#ID{oMLK`2d?ZLJijES@s8=JJ6VVOqEkv?3V83p_E9#o5>PA5x?&8UC~
z5k9UPg{(9p=unR%*TPjIVz*jL?t5tm_=eiX2$`2bF)y=nt7OaV;qHjjtGl~npq1KK
z={)ut&>XvnM`_1aS)>Y}Oj4qcoS0lZcSs8|DzlF?4(xz;=o~vi5xR(y4@nxF8Wd3?
zU04LXkwa7hPMI`nFoJerK@t!~CADXCrUG0uYR(xQ5{7h2&qm@S#s+GD02!5tt{e-+
zM#_{Bb|;WI;*R;8(p*9$BT6C|Ml^*D5{@l`3Tr}q3{jB$hoAnE#>mzvi8;WnuEWWa
zM7$46EyP)omd;9pIEcjr5|eEvm3Vcc;*_Omf$%ad#fb!^6tZkZ>M>j43~CF@N_NGe
z=2VayNXQ1E=h(7M5SKljiG>LYB-=H!L?n$ZNE{$XZZX_1sr?c=HT!Tq?|VR~2)ei9
zO;!UGY$~-hA|$qr0!&E~Ta-e;9(lOoan-`8q83R`$rxERisbH8-7Fnt)(9dYpd>ob
zA~?DUz>@GvJP@f|GCheUsfl#YMM@!(q7*)Y;Eq&o92}lZ*sm_f0|$cI?X(`geyPV3
z<wE0I;@gn2ZQIlT@#nX{yGA5KgDn^e+NpGzJcnQ^hx9n?5txZ+N@XWZhnsqsYH^8x
z81dMr*OFHp`Qvl{@Ng}+x72y8&ljRBaMV#2{_y^~K6d%M+xhaRKR!HexqFo=;<ty>
zo1c8f^C>Ss{_yROE@^#moyOKFtg6)GsWDU7h&a&fY~k9~>0(>IO7%9q&U_By>%K<1
zIDGg>V=9Kr-R+y{_Ev{{`tT>r_2uaeNnE?B4_?a4UwuVa|M;hW`rSYMv*+{E$M?^d
z_fJ2(eR_U-x%L2oq=z{Zr69mrTU-CTzxmDI{M%pt`t^3qcKMeNk3U{rjdi+tef#A<
z{Pfk&J{S3y{JxLn4%Rv4)5cr2`R*{M*B2$T#!zX7_wR9hqvTwU+1I2^JaNsAW_Izc
zR1eWF!39yA$jjs|Ntvz5iFqo_DK+gPN$2?CJ3JN*I%w0%psKsY+?Wd{A{F)?v@kV7
zP;v?<J0)idD^p4Efy}^^REdI!63h?+5d%p;*EXesSd*1$XJryjW^kwOl%8W`-o(R?
z+ct)KK5Y@rM&HT3=bnGT3P6~gD=D(q<6X0Q-p((N?eyk_=t2V`*<Rezc^`efKJTBt
z`|$q#=Ga9nYY`;Q12m+xQl_+EmaslCT&gq)3ur;H3?|hvV!9LR5|m@#r@4SY({XCe
zE%xZ?>^;qlgGGVhIJWe)2oqzvX?Ahj9bw_sA}V8Q%k2TDW4*n-JxtP0Gsb2+;5~!g
zI3i1(bdges8mkkLyH$xjl{r&l=909p#!N{FCa!6SI46$0ZE%Asi)ZfPEJ5izS3)PQ
zM&Sw(shLKRlpIJ3PFB*MQ7FOW!wYd|mh=@Yl#FDT%CHb-_JBmriAXuZuAG`dfsOel
zQW#0-kRZ%L7nMX7wks(zgD5EH%vI6Agc0Gyx~oeCKwBc#%qV3Hqf{r>5Z1+Gb=D5%
zrk<Ap)o79KS(ZZf7d3tNFpcgy<w$cIb()m1XKhSO{^b4W^TZh(U=7*^)d<rd#zfLJ
zs&L=9W|Ru;WQFj^c4Hx+V6A}**m*j6zp{UzRKY=(0Er)T8tptn*Nq2dU&({nbDp@z
z#!@tRK4E>VwO}>5sgAu$>HV4!l$7V5(n$^}nPwYGdA8a@Cc&=e9aWM5lU;=@DMFOJ
zXD!70Zo=TGBci|};XENB!h@(zoNG!`-8>tSQ?9N}#~5Pmw8XZ%`HuPgN8i5xw%z^&
zC1Hn@C!#}UK7U*tmCWjaJZI%JD@)6#RXE+nImgv!8DZv!c8z`2(&}f2qq%Nd-}{Ev
zKqvmb^L*H@muY!V>-&$FyVJ?&1^Hm^k=xVz>%+sNfS>&Tc{$xZ?p5J5MG+;o!{@Jc
zKC<icb(4D7#ui+xUz3@Msbop>z)(@HbWp5=B~;k9An@adk8OG*<)-NOr`y{wJljrO
zBR0~*vA*bbafi{KP(`L_p^%%Knb#^Ww2ZttlGMv~-Y+ht;9WN=9!}kzo!bG|?(r93
zX;u5*e)ZGe{GWgE_1Dw!<MsL5AD{mG;VJ9Xj<;XFKK}Y=H(%ek^4<7@b1BW9HeT9E
zr-$s@yk9=PJAO4kuB|}o<ma7@;SZ=^hL7kD$+5l9`IoUhqo)o}dV;l_mSK<5l>3_V
zNrHH;@IrDa<>=SQ=ikyt5S9Z*Y0M&0CZj7Txt_dqbWm}Rs#DK0BL&J7fNFB#(Zc~%
zt{#*b$wF8ynTRSSCpjcVDl);uq8T6rjhTdj)QHiUK|!hwo=>>Fqfh(1_H~+4doz?X
zBnIicrx{REo%3>j0?#Q|2^Pr5$MO03Lv2T#G}Iz0Dvk3rwjW%eg`d-ARP;fKUBQN0
zxt83M#@Gv~b8F!?NT#`P)G}Sqy}(I1&J^AwlvKbbn0&il&)c@|XoA0x03o(@6Q&*9
znM$E#nW;a=ihxSelx$O3h-4~Y=;_s6ouw{V#yMo_(TT_{fjEfc&}uNMsu<hqn7Ek^
z9>$VExbU^x;dNe~x=tl_qJxxmY)8#4v57ABylF#%dPH^iQt8p7R_wvz<V?O&3bY^*
z%$YkyxN|8TV<Z+;OA}Cr9K<~JX!~=nJqn|1)FG6K)k8Cq0kD9b_&D6j2N6)fF$O|y
z3ij{}nTU1@2$zgarBG}(T2LXPOjA|^5G3TvOy((z%D#`e=6-IRKCvekUn7@sILdzY
z5zNKlVNuI5a?S233R$8JVgix7vx3Pb5F}Jw!?Gx14?ZwFqM;7m!YNsheCPc`T2p>b
zo;;XJN#2D9EOQi_k>|5(BY#F!-AV1!)O*?8do!lEKJCytS!YqHjFEjqYfTXV#mdUp
z;gzUu8wqW`%W?K=x_h#(Tq<}BUzm62xLJ6;c2d$It>x|k76As6l9^-lTF5Y%L@2V8
zRcV{+qT}VSTL1C<?tvKQXldO&qIu<I+oKANV5)oLa*56<EXCsa$@cF*=HD{NzzbuI
z$auHKvte2+LVYj1GaZEh8#jyg?Fc_NuS0t89$p`Rc&ewH&yOTUSVn*L6^m}`5Rq1a
zyrS)W?Xq0X+lYSIz<S(2e1C;LU&gj|K1`&-=TFoQI(dmQ_T*$PPD+_{`LuQ$z#x;c
zXkFcjD{<p`-Je-S=XpVnuo^milli9m$~!-N_~!2JGnP3pd^OA7>a<<0PW$WoFIRtu
z%h|6Z-FKzs=H~g~Z8|?ZonQR&YEJ`vnW$x^YuVl5R*r4&2{=Of<?K$$8mv=|ufKft
z*Z=y>U;TW#YoFF{KR$lDuPfF2xqkk|%dd{hm&evVjJJ;tgE&!9ua&QJYdc~aOVsh>
zemFcooTkBfQ(|j_D29VZ7Jhi|ep?U0>(-;m(lDCbY*u#dgLp!?h{8IhD6b+ySB!7|
ziQaB}I=1TVG?AOPpowUd5D33R`4ZzfyK^}tCrC0O&Urjx?<rTHFiRq+ZwV)!GJ1+I
z70?bLq+=)w5e+!l(uFuiRss<a8H1-tItXOMpn5KlNyQVSncc;0soE5}UpFXwV6h~e
zO%4NN+j6OMEPs0Y{Z~KUE%%Qa$Fi@dm#gui*Y|q3<Io*cL=!0h*M<8yNEaK&7t=lF
zPtO~ec&b=0;RQ?*r9WLsviF)CMV)J-%EV+(8_$DTW3qy2H`5Y*ODJQ$Asg)=M8Qc(
z#yUGCZ?gF=WbPmz46lcq<@H<(H!&^;(t@#Zk4-S16N0#>N*+s93|97{4EIrbxJ=Ce
z>R<);ZnC7W9?T;%OHYSEl3+b4BPx1lr7WdLTJ1bQ0v|+3Ds$#Wup||yBncLRXOu|i
zCVnLm4tHjRo@9FfDJmf&i!-1JmzuO?E*U$bWP;Ub&zvT+tN7$Jh%DIH#A7d#6m!@X
zWuov*giVY!by6u2UH&g${Vj~hyBy}S?>oe24u?sRaZWiP)Kp4%Pv`Cw(sNG|8pa44
zkz)f9h4pSqnMI2fJ)K$x7m^}OoCMZMNW=prBS0){=a5;dq!GE16l$DQxKrd#e#>$p
z;Sfm-@9JLl`4YVkldim5?$WN;bS57oO978O-jIN<P7;bjY}_+WwWfP=Q;9hPik6eM
zBv1y9Q)Z>im=~ER2E}g7EH)wpMbwN6Q9=-1NTpDYUAU3E6VUn|1O_V|>G<_O-v8^{
z?c3kQ6^)T9VTvZ`MhIzzO?&JiFv}RbK@2JgXg%Ctj=y*#^XE{{tl9Ucx0g@<<y$vc
zo3X8pi)i9FCA}#5oGQiB_C2!jbsgKt`EYkXAC8>~cDZ^Cnr1b#u!o2951&5$FzC<U
zUjFoW`EkV$Zy&ELq=!8S<yO;|<#b#4#p}D<<LP!fqI-C*KIBwhAMeWD7k2sd_M7i}
zhQ)wL19=D6xV`<NR@o)2+s(}#HXH8i7zI@{ZS9IoEtl<Kz)t+;eEBX@3-P6Q+eTaH
zbaRh+&h2`+ZhF+ufAyQEA0Ni!>dHK}b|*J4f0o<$)9?SKiew@O;x9n3kzgmG%v74H
zes;6`>;LhWfBQF|zgo`gKfnL<r}6GuPDeVtyu16w&zGOgdZT#%Dc6C)M9Ee{4c(u1
zy6%-1e)(!H^*loMv-{r5GL=)KwwUeVy<5s{wH~>jm6_<8P7E(dWFrA3Nf8p1xw3%Q
zZTtRT=#OWaZjLodLh48&P;y*S3eZTBv{Su-Z^}wi5Q9=PAB700InFsMO$|t7BXUkn
z@6aG9cV!^UL<TAqoN!CYWF{st%h(u_#DKygvB!qox3t)|F`&3!2m-=ovu?E4;+`SQ
zdr<Ent-8uQsc@@@$EOQtfAiH>I2}%Di70&CI_*1u`1bAFr<IP&QkcZ5^6IE}vOSX_
z^1Rz1qiEA<AA1mU3>uk-ntSqG8ItBg!qN!tLZOU(_-Z>*1V?vK;%vlSMJO_BEdnXX
z<jkGg6rCcoP~|MDes?#&xIf(-d^sJaIt3nh$376@mc8(CnzdAJ*tFfKQqsn;@VG{q
zsfO%*7+8;zql;7+x01c1lL?DTdJQ;9#cD$vfy0Gz7ii>^#T*WBs&k4b1uMsuLsgkV
z!Je66qB6|q7^Xbn5(S(bK|m)~{6$j2xhB%tiHMW;)JEhy21P)eeJ~nfM^v5~*%_?x
z0Gvt<W3XpY1V>oJF8}k-f1{b9H78DOlG)a0v!Y9dI|-qvZkJ?XPEU{0G!3XFht-3O
zUaItn)Z<jwA~LHV#FTwf#o;7rRElV!Qh{wW6;UcF9+;;fhD&M?KWEOF#BMo(k^v$I
zNl+$j30DMpDlA8YkFbl6%NP_}A11nASMreb;(<amK$F%mk{J}fc4WBfjAU?hlR6cN
zVWKv)5HP9jRuo(^O{C={7F5SbsPfeJ-AI%z3^0Sb@5zZ4<jW#jwc_yCzy9pse|`Al
z|1$o0H%R=27OG5Rl$No(AP9)jou`r6DA1;KHipZg2A%G=_VwQo1bm5&_W%0Z=l}Me
znJprF>OxMkG<NqMN{}wRU;Ea5WaT#Be6<|!UhBTm=te>@)w1`SSDQ~i{<!|(Lpa?@
z73Ff6PM^lQs7!ZlW{mOa-G|5X^<vLwZO+5?k01a1)E_TSNGbW*Z~ms0_VCT`F4wW9
zWhkY%sFYR?^UYF~XmhVq%?Jc8=W7ZC!fq_6H}|>keKQ|B6$BBt!*yQ~Ot`kPoNn%<
z)am&0`surNoId~THy{7}+s(XEk==sUmw)|B@#)iV|Jzf)mIALxC-rrYiG;#I+(h`Z
z)AaBD-9P;1-@W{j*8PwDhqvp~nVIZ#_u}-c-yD81S6uSrx6#{fMvxRDQV4fCTwE`k
z>NMTITyL5y*OmIn>2NpI#j=!SN;zri)i_5VTZBj0k$DrMWTYx+Xkq3;rE)*p_P{^>
zz#pZ}G~K;a>MSLuBQvOw>phVWHq<+k24S$sex`$hC-E&yAy3o<RA>i-rBMv>Dba%(
zr!z_DOzMy%R#JsCr6DDSi3_<&BncDE+%K6u49tC4p~3m^G`11GZpN1(9^?6$FT_zK
z3xOk(n6h6J398zr8LO}R<7dD5`NTpwM5}<k(9`>iulDZoeDOR@i?wQNa^ieh>%5eC
zO2^2J+=VrRlCzLd2^s1_oFdGrNu@}EZ&unABI>rMLKdRv;OL2PcO$7y)19k83_2gO
zm%Tr&&P64iyYy47H;3i^i+MgAZ;k<C9)oNf;7Be~wLzQcd~A~mWm41L4S;IeRdCAa
zG%YZ5l43%BAy=;j4EC_t3$`Hj5ng~X*gL>s8NuF%aG@H(%$5vL!c19-)zZ_VLDIa|
z+>?-F&q5I?kzn-_DGQ=!9Le3a!nW87kW|>gG`5TbWY1t0LL~ko9lUrsldJnAHnNCv
zjVO{B0?Nwfp4y5rm&&Eq3OVn%Y#dtv+5+p9BXb{q*{r~NW_m)k1d$?!OVjlkG~9g%
zqdz5tsz$d+0mLcf{20L*9nws8N|mUg9a54<7-?KGRwhXkCU7Cr5yKKeNd!qR)D24T
z%>b^~EXwfQ_p2Z(_Td~%*S#PKRofi<j_@eVG-yNNO<1ZDR~3t4M7fZ1M_5o3<`JMO
z93i5siC?87WhaWrP_2>H*Dj@EQraQeMGP)MG=jPakBw(c|M(AI{hM?9^Z)wvANzBt
zOXjrr*b2x+#9RaxAh~9nv|u5ZDg`E-?Jzg^$A>)s;XB*jWtkE^(7Mxb9T!&(EVH>5
zvd2+oUzejhD7LV{y!(9j;&fYI`gz`d*yw&cTlTni2-=aS`7j?ofBo67KL7mJpTEAJ
zKL6tI^0s_2tMeAmpJaVrAN^^whjD$+`rQ-#;k$?b&xh;t+Cw;jLz#pNv0CA?U$`=J
zVdBiL(5V%IUdPtg>%M{`yKgwQx=d714qB#mTg$e4-?8tx?iUHby9RQM?(^lLKdom`
zT9&)t{KLQf$>%>mym+y+Iv?ke!;>F>`19kZPkGbYtD9kQUV9i{N561mHPn`+l|@gL
z|KZ>K>KFfb|CRRfxPCle#wa>-`}}nG4__}|-^;X)cTYi|MRArn8r#@YnZS0Kl{*WB
z_9f_4{ZK!=hQF-OnS0ul;}X1-qn2b>2`ijC$t-nCD$WI>8AzH`67c}>^?asp&v=AT
zooW@boR+9WK)Qox-HilH6PJpTEW}2x2VRM?Co{r>8VyDmXi9U$hD_)+6<`BqPv`Q?
zFcKkQWKS`#O9&&A4Z-ksN}WL?%0#qqgkWxxoSPm_2kA;uZ182TAyvoWz)j!~8;)c$
zXUgl|FP|RAzAxqQ_Q&h`!&_wOE^F@Dsz@!^YHQQP04`5Y*XKv7zJ;YVvvvLS<il8x
zti`Wa@F-e{O<ARs$^oHrXlzt^z#*;Jc9zSv@11P$e%Z%{G&G_*Gz&@k=16lDORVQL
zkx7drn=w%eLGJ4GVktD07__0-7TLl?!XuTdfKwH$A!SLH;LG3uhQp-PSU<53jYqB<
zatmIqOd!`74oaj34Qj+=WTOpZkR^>BJx7ueXj^x`8a-QfxOpgvZLup>5E^)mNJ<}(
zo&uRw&Ez;ZBaGb0cV%V$L<B;Bg(yNtCaVCo0hhRx<d!fJ2C6uXeUNGLLa`eI(y}`<
z8*N%7fGku5Hk7@F*YF&rWFhxD#Z;-NjXjiIi3CIi+=D8Cw~>Lg)`sm#N?gK=SYe(j
zD7Kvs_rd|<U_@yQEyFRc(j&+v1{sS*YN0$NDl;b>^U<|akJyPLAX}ErX-iwIRu>6L
z!IpzMoKT__7rF?^G(jZl6o!~N&0$=$P#B<ms7azsBBhjxDVh{aq$y&ogJ=XbNg5_g
zP)b9BW^poh<4ATf5^{0)kl|23G-ytPOKV6<lcu+yEkFI)5B9%*|Neh_c;^!}=AgM?
zIUH@QKH@A#5!EQ<Ozl9-+$z<3Qqq~ZirYG5diecm{q6r6$i%A<;BYHbVKNRa*lQX&
zwZI7Q-b*b!ABjT`ZMo%%`)0>@c+2?-(}67)ab#=A8$Ew<YJatiS5LpYfxqFrulCtb
zKKpFBYxU;4OaI|%@2Kf*H#t9_KV8;Q?zZsCF)eNQC3Vf<@qCVT*wx^x6jtV&SBF>2
ztD3fNWA8SB6t$1QW@&{>vz<=M&Ew;Fnp=c3Ja>;W=P=|}idK?t`}yOi2%p46{N>O7
zx|Tk_dbvz9ybCwm%EPzcP{!%?jVhaExI2*(4NT|jVDj5j{kQ-9um0}ee*X0r+jx6j
ze+b)RhTqTa^-sU5U%!y~BmMX`TJf7I<yM*`j-rco%flPGZW%5*2~fh3*J<swZ1up^
zjK~GkEYGq&bUDWV-xUz3ITK=s>_Nyd@y9U9P3Ya*@@{SW^BiNQP2q)HGPpG20-nhu
z3quM`$3!QR1mz^zreP!5lN<3Z7mGTjO0p9;t*0tVp{xQSHcmlr5#Wp@iBN{JBoibh
z38c*Fgn$uQ!i}ILN{vyNW~VlBsS|Q2lE94l>B=4$dk!~?0T~peZD$^Y`E;b()(@Ye
zo|=LN#GlhQQt&a3)6`hqlT8Nk^kR;MH7h2`Nt3q}31JF?jdaPqTMVdX#<gF$jFjdQ
zR7=(x#Nl2T#<+nbdG6_ZNL5f8d6^6QI?oCjqh?>v%7IM8-l`sDS_n%W(@DV`5D)Ba
zwg6*@LYLa6S}YT1p94HO3v>cX0(t;9P8`SqnSxm<LJDN#q9DuKNF%IPKL>SfsUVt&
zi$<rA;S`QJ3<&N{e%{hIi*<NdjP#7*l0qPk&Xj2+yb5g|LQo3x42IIwC@f2m7=qxE
z`)=BTW-dKxMRJ5WPotp7&H?F9P7_TO*AdlYY=|IR)rW?rU6NJ~voJ#`JT<F=9v);I
zGM&<yJS8$ayc|u)mBb4tR3LRqwUK7L39aO-xpN7iBRoLiDmlQp>md{%0!ti~c=D3r
z<h%wbqwsJxVFV9PDMU(MvCkwH>a-f|9_17XB+=Nh4T+?SODC8cF(XpTzL{NPx;t8>
zsjxA5dbFlVk!%C=X-XBCGm^xEJtZB*14Ngi;-H2YL0y6HR>g*gNON=5^saz+M7Xv(
ziG!GwC*JPrSpU=I$KUn$QH@+O%Pi~ed*2YrLTWp>MiuAE5s^s&N{a}M9mC22x{h}b
zasH2Q{roLdr5*9bm$H<hjL|1HQXcz=>m~L+HZSYB<@4e0aR2(|h)rebOy|ofKRs|8
zc6`H4hzhNbd<~6(?$2k6>pE<ofYz@EV%o2dS2Ha-6+LR5SQfp6^z{K5iJ@}x5!X*2
ze7GSplt+35MQKE>Ntx_v@7uo4r(3<5bLQB?ePn-T0SWJyb243?E+`$zC^FT;VbAMI
z%uT=~Wj?<D?whCUnRUeaosY{+d2zG6IlXw@wDrsA=T+X^+~54_i`T!LM7A-8?=jHr
zGPn7@=|6t`=9ho}`sc@OeD~0Q82ftl>t^ky{_M@oU;n(#i672RrPZnOM)LxMParv=
z9dbODn>pJJ;R3N9H9tK2b<lRMwX}OsO=q!Qqu!Qk;hmk*6V@3LsM!aNJ=1cvb+7s9
zp??2<37hLHIUK09EMN{wNOX(`MkZM>m>Y2r2Z@pv4q-OTGxeFcWCXep?V1dg)LR%4
z0!EYo4NnFeX(l6*nL3m+lMP9MbizhF0TG6z6%^hMm=zO4PE?h00v|X%n$()4^>8RG
zfV=iEV9P!%oFq5Hz6Gzm&~bk8%{Pxq8y|0}H8V{o8@oSlJUkG`;~`1HU2i|1PxD5>
zDatI)Jc8*!rG-0HO>NNtPA8`|lcy0@2Doo^H3C{~=Mk~zUPVnpOzX6VA%;jv+q_fk
z9grAs;t{G7x6|=h>+Rjt7Mg3DMwg|4F3?3s7auydxp?8~m9%EoiM*u`PIi(ayTCiM
zvp|JN3lniqUXn7}n;b*9f~vV7H=hcam3Tnnav>jdc$tIPj<DEE>1Nht$DuO~Ydy(!
zr>r2LSQJE1f&l0aU=gG{gPEM1TWpa$XfKva<}IbA3UvmyprjxYBnkD6IYBgNiUjL9
zybv<1J6RIjRX~!q0VzY||NfhQCCom8k`rJKHsofJsgz*HB}v7w&j@VcFouU0LS!O)
z1XnQ(j#{<0dBR=_YYizXWl%sGBe_fyk|hzPOLBU!G7F{b#Es$6w`>QojleM{@5t6T
zg9i|vmkyqsHg*JLM~=hIc3peO*m9t486~o5l~TwNS`-@86o|+WZI%0;wQ%F0EXtW-
zUQ3G&odli@{w$e0k?*9_*v2qbfki?nMpDuMl&J+3rm<qKO-Z6d=f`ubyG^L%J8|jd
zpuSf=@B}6X8qu1`rn8Mrg0m`PaGAjADLE{9jCw3j+wE7gzJ8M`SV#Z;ck9!hb4ijI
z4kk0%_C2Y28519mGau{KWEo+$4aT%aUl7zLuZR7-5{u!(*_X$MhYn84y|>37KmG2z
z4-c2?$B*k88nssU47$0!Kfiz2saPa48kg~O?$}?xeqHZhefs8Kp4Y*JxHTX9bh^=`
z=l#>=!xal^a+~Ju<EIgZNlIx2L`O}-YDQHdMLchBKi{sG=k=VS5mpZT>ecJx;V0fd
zIv?p`-~aCSb*WMhG##5Bj;EV6ee=_wzxsz?NW(JSpMU)0zV<=n3D)JP_=n&8<gfpS
zH@`kDE#E)>;qqa<%gfU^es=oo_3hujtgjAo{sE6?X^Pty7-uh&kEX(0wp?CH9YJ*s
zxvUDwWo}AW&cRxgUBTLBotIQ?RekGm9@x)8Op^+)AaOI>ldMLK_WZG)d$Fjq5F58b
zR7fU>qfMbqQbC+1LQkS6sDT#_PpT~R7o1E&@Eyz~oPHr;0z(+qSp))NN+y93lxgH(
zsx9pb77hgrLV!|mW@iA|BAq!oIMZn79QzoS<H`DNkL%Owmwnfwmpy#Mwqj>nH3b#$
zLcAN5HWkU!;T5g%+&}&DuYY#BJ$UyLXFt4o{O-H;-9t8h{4~V(Jt|w;<)Pf&9xX$7
z5*AhKBiR$I1f_x)I)defNvK(sqvU3#YS?JauWL*VmPxb^D~m=4Y9YjAmbDNd(z;~d
zX(R@5;nK9s%&$IwbGzv2)qHc{QbZ^dEyg8MI8vB$x+@hbsZ*8GnNqo?qHNck<{VTL
z2oSD;BxHK1Z-Wc>vq%hLvNc(WC2hEAVA_2rDV)v6BT)gbX#)&rNO~utjy}M?Cy_f*
zsEzJJQ-f^>4u(up&P-8)B_G2t1Te@6l51KfuK=fSEFesj7kDGvjGH<J6MLd45@JDQ
zBh6D~3?XLjlmltZ!oH{PnMKh9nbrYjuQl1jop#FP=37hio>nRsvQh|c!^XCgg9i`g
z`PFBvjr#CwMuPX0eS|T@;0}thJvi5dOP`S>nQ2C`k{<#W=a%hA`jl9r?67oIMo{i!
zZV8H3BdzQn9$DQT_QQv%X6_xrBUWLSrD9=HBGGUP4+(e9Jho#MgR2%e5N>%L?rEUa
z1JxPsg7Xk`PIj&>-OB+YX_2U9DhMtlD7CSRRPHFL<QVP*dAdY#Y66nXGC7+u1#8At
zdoRi27ogTgMX4#F6|xXFF>A?-U+Yx4xL()GRWASSzuNi)j@s6GS7$*L)y$}pgqS?E
zxosL{zU9h2Ho|(XeLdb6{^?7ZUfE6sqx#O%VYBu6;FtGrcPH$tS#CByY~zPVe|yP~
z;j6p%wH}w3KmVDJ(GdtwTC2{RS(hT^xUH*@*V|huO_#$om7Ck!R^{pW>G6HH^hBen
zlFIHx%%pANf_=1$x8p4%k=80tx2H)5Q_MW|?Rt3}*N<z@r_-AkFYfO5%eOMWx?IP1
zfBy6J`IG2*I-Zp2i_d>@xIeNExNjf-%g5)3=X639)%yv5`^Eh)|JC8E7dNFoJ^c%|
z6{_bxzdF|K=C58&FOK^3XMFR_ML8@{!k3co_)fA3l^b5)hE~XA>jRf6I@YHZIs4{$
zrD1hvg$hY1Vf2T6Tf@BDBw$$)J#p!L-4Q}1mv7!JAKp)3UG9=O^gf+gh3Sketmyz*
z(74dNV|opC>`u961lh*!Oayjt3HAVGa(2!^aA#~(TQWd^5Xc}&uoy;dCU<Z}W-=k2
zG64y7B25A5m9=GAAcITe+(n&AWiFMgECMalveZDU%+mNkSsPQM44GPpK(aI0zHk22
z4)eO%<>6zii%zb!ia*np$U}pVja*oY(NX<*zx?6H^Z6VwH-y+Aq2-W1a)=*_%+nrD
zdR=0a5wTvR2*w6g=I)haA2~*L=i$EYleU5o%fXEzEHW`_)|AM-i(~Jb?G_vp_S2i&
zdUq;^6Hk%>i7i5Q@17pZZK0JRDM7r)irm%uko0Uv&Yf<KuFTU+ax7d!td<kBr4vLf
zo7x~I3Zg8OrO{??T9`rm*jU8Ll7trIAVh>wT(6Oul1b<?nU9{f?M|`Txfo{44XUy4
zNmn?cW9-j_1j!f`mVqf~%Ag3KoifSzVsT?6-kQj8Fzo;&ojG#IGLnGX!bM0S!N`ys
z3Z}wLlhUL}*u?2XlVss4%;8BCj!-wQSf#csQ^9p-^C*J7OKsGsYA|t}-(}xD;FFk_
zUg`)V^TbS<9wFKUoiM}0T|ydk4w{LbNHVX<7FM#ZK8`>TTi6W-#F11vC$Ks#L<%Do
zsi$S{OeAc*2{WRw(8v)kla>i+Oby_OZQItJLVSA~fsSIq#@J`gBAt}}qOt89Y8j(?
zGBPE?35}8v3|4YaS?X}2%}|82@!mxWbV|Dnu32V8Z#*PRTplw#wI&HDmEkhGEA3k#
zDo|n<rj!|QZ6Jw7c#^*FbonRSfB0ugV!UWo<+#Yc5fgia_sVDz0!#~InoX9mTOzG2
zWzD63m|lHJGLQ8+q7QQ0*N>NYzQnd~QR@c!^zp+t=fN*t%IO$BY3S*0dinCz{ru+X
z;oWtNq!6Wby;7}4mVxbJeUF>tXH%O#d-3w~moM)>|FU1UZM#MgL?+QD$IG|xAxWiB
zAI2I}*^T4jfuSHN9JhxzV>XYDN*uyMmgV8;ZE{Un9zJ~+T9%2%vu*qJ-M4@G{cr!5
zfBF6Y>$iXU-+%wl|BvnYlMGxx6_F`Ph+1?1*{hd-_wT>@>5H2echmEquwJeY>w+D0
zQ`+DEV|y|1{Ea=VV?I%G@*&!q_0IWq;iA?JQ(_@Zi5{Et;TEIQ*p1X^GG?x$QiE0x
z_sD%4c<vb&_y!*%H80y>@%gSyI{)x(amaKh!dxvvYwJ-o=SDJRA&V?jsLiA^ypld6
zh4sjtKn(V%l9Z{*;;Az@QyEgh3Km#81;GJ@3#=qhLBiTtKp^G?4&q2jvWcV<0SdU{
zFDQr;#30y0UYgbfm0(q_6U7N3jc3H<Hf2;miGYY0jtu4f+_y_Sou7`cg+_V&r$6-E
zd{h$zrZy=NZr^-<e=3hp<1)5!-KkJ0*V<$R(I_IV2rwcAQ`xS&8|&QCakXvVLDH4#
zT;#f4VvE>mgi+IE0+LclQv{`zB+^o3Vn`VaWS&SBNwu`8<%?IxyEpUP>MhG5SB8xI
z3v{g$lc*+$i?fEdgNFt$A>?ELoJYz@_j4|uEmAvbFl*LJs&WIWq`*{3Ea4`5t!B%i
zsY#?x(8_^i9v$jCz#M@gR7i}487>FU9^et;iav^uOI)|ua_n0`hd1AMqR~@rs2P(Q
z5}6=g$tsOde?mv}C36o0qJw;-W&yZIm}L+;s*;}>Tb7oR8IiGrn+=u!>({@@2xKTZ
z928m1Vz?8PR8LOMC^dC>)>yaD;&7}E%}i#G5coCW>C9T^qONj~nT2SYYU&Wov?wJ+
zX4am}sVz&_-jN8Zqzsyvb_f%kVsRMnL1iUs+#jP(W4~fIW9ME1VbAA%>sPDY!?w|f
zZv!=`vjHSMA;JenZEQ)Exi#)#k;$ZC$<Z7k8nJ^#eB?fGI>3orK@2a%RI>-Oz_-F2
zkugu$I<%s|iNY+Ii!hnbi_mAch65g+21Q~5k$C56vPDUf#vWQ4x<sNjgn^4n1{NgP
zcM0;MEr#o9KwJNA`o-toUS8k7AK(7i2lHVDpcc8?c#PBi&2%Hmk#ZP)A5=>#S(_$E
z4L*(Yb@ZsyZfpJY{`nf+3eM;Kn{OU|`~GTrSl8~6bDQ6M{hQm@_f(GS<DY)`^ccEO
zT2vKnG(xDPH}~u5upHm~qU_sve%i-5p8C}>WE@}JAD5{eUhtgfr>j`*cAfNC)K7Oe
z*QaYZjj^ZNvEJX^-aS2iZ@qDvGpF~Km6vhzlb_0d{PQ3G6A06BDwz6M-+%Mr@p^r_
znyuN-AE%R?KL3rK<HMi+_^|DZ_*eJK-~XR}{<oje=U<-o-~Qq8(<Qm3p-p`J>Q}da
zcPt<NkdMN?Q;R4Eic50WlV-iK=qXHvQWCY~c6s{K!+v<<5AWdni~CbalmockNE4|-
zT64Shr>7i_ZWs-|+t`%|$Ak2b?diGgyYh|l#*2W2qGxHM!m^W|NS&qSV2HCRCgn?_
zKIdsBQrL5r15pr9JS=IaF}Tj)V7sCmC_U2{3@z{k?GcJ}ApwMWcqHd$EE37YAsOie
zAp}x@LIEO6C}BiIc7+4|n!ClmJEvvaFXz2m7TNZ=1O`N^fS5$IkV+e47mk~D-=Cg@
z;wS&=m#t=rVLJKw`NJQ-lUHxfAD-VouMu2T2~3=-Aykl#!7eGnl(N%MChI#Wp#_8>
zAtB#vgWcZTZhMF7yxi{V>cSJXO*ajU;E6hEWg-$FJkD-|9YbU)O{alZua{RZZV$(H
zvyha=(am@d^li{?QW-_cVIg*|gr)^oN>N$YzNiE+k_f5nm6(_i)6A;}qM#Y2NT4$3
zL^P^WiiR_liQ&Q86civmkQ-{-jp0|+C8<d4P7;wC2~vs6>Rb}5sOH$cCez+QeFZeb
z6T}ph6(utR+8E&@vL<<UIb?SBYbtvLJ1CN?X74PN49*gDng|H3@W@PRr4+ZpQrTFI
zRR9V^Y5-ouwQ$HVV(F|bAli4zU>F(dh*T|7!61P;Yjx?CE66rDf?bABNMsSpYzKCb
z6euSbYA2##5?>BcGV-k9u&2lgYC!-GB9VClHpJ>7*f$=an%I0GEfjT95{N6IASt0B
z<CuuBdT^#N?;KB^*m-~=qG%Hw8X-WX4I*-DR@%OKX111=#dqw%U@ijkvP|J2r8>j_
zjGdS?hxCwjM+qYIZU#JBz1^SW#B`izR#cssMbvky-Rl`t77pY<X%$5|D5hdnA{~@F
zG{Ibj+p?6g^}PI^_dons-QG_0NMWV2o5yAM=X~Cm!~HV7K9Ww-x6*oh`Lm|n!NoAL
z_?~osI=}jAtJBq&%ecnNoBf*Ke}DOZ|JM(H{J8fr)%HTAUi<mQPk-_1m%o;@c6$Bs
z{X6W6%L=G;nW$-|W(G!(e0=&t((~!f3yl7FzPx+<(EZ9;xAVh%x_SBP6?W&A5kpZ#
zBQq~g8;!C1o}z{7bbCjN=MUH14K>Hsm$HO{HhTZ=510L$KIZp7{C<qkI*oPPq65Ob
z58t~T{`i0Vwoh?<bN`c{{MF2SJGX!Qx4-zsFYaD^algNP_wXK`LiM=3DDumHxc&91
zy#1rU-(A?HP?0EVa+|x*Fy_vtWC0SN#W^l1&+{bO$MuJB_~C#9qb-;!m74H=H9voh
zA#M~t5|6`%d0=_bo_;)j^KSM{+W~Vzh^NoOGTV{L0fjPdm>g0Qpwcx6)u9qo#Q^Oc
zehw_?J4tem$Sp@B1G!UxN@L#xnKUOrlB5Be8Odp25cddU<=~lx6AnbC&_D))XDS8>
z6bUXYMWP6^OhE|;l8EM69oRS2X+BQ1NGqjbs#U8TGMT#OzGios?;?7JEz{w_w(a=`
z1}Dc<eSZ0>qmQudhj(T8ZX4UKnoF(0ApqLRVd!i@%FL&kjI>Z#`17+1A%=~CY7@7o
zkI&R|zg(~H-*OesY2}qWnRAY9c#}rNk%mZdrmO8rGr40MGK0Y~m17;Xg7Z^`S0V<A
z2si8PMXQsjaG5Ht5}ta1jLrw;oiZ2>XE;lqdDx()ySp$on7B$2uR>F_!n}wu4OJqJ
zPR!ssOcXnh3l}BNLtUqwIoBe4U<5}foC)qdSShcq2whh>*c@ZP5Ifr5eapgbu{*iv
z<s7@xxcauSfM`TOY3Fq4B9Y>7s;=SN5SBo4&Lz*XU!#xoG0ccor_B9K+{4cDAAa_0
zxchR~&6vAHn$$R7cU3uY4i!?SEuFBubnA%WRe5ZdN_(d&xppLsTNPySdC_{?G}>If
zNGG1Sfi;B*%#>YdhZA5-7Yc$dX=e4rI}=w4qf7*|102|mcnaT`%1EJMLYjWP%E_Ou
z>v~x`nc;a04l+u>3RR&YoLEHMS~7qnO535e_n<PdTauSl99td3q;h7Zfk0F(?oLqG
zF?P&{1XJT2p_piju?9~{N)#+pWln?%!26i&c|5rxq^2&)=E8%8k;xiiX@$v)!xEVm
z!BfH)>d=Y=tveSLE`kl|^U&1G_S<*ow^y5sjgp?-hR)^18)^EIw`X&0yGM?!Z)Ed<
zN?a*~IHiE-@ciTV*Y}sT_isMze|m})?$dI6_gUYz=jR8Q$+Fq?liqzX<@o*&-ywwA
z)5qb(sTHy7J~$QZ7O`)ATwcDq8(XexJYJt=dc$$PKiuEE_-sFac>4YmrH`<i+m~V6
z+V@$P^R`K8-L1~$%@@Du&kvXN<Gj3jbNBk`awd(X9o86^AASJybseSH&FMAOTrcN`
zPjAzG7>E#o#D08nJl%fb@A`Ir`>+4%&CmYs)i1wT=+mdC?=I`p1?3>Qwa<TY_uqXj
z4{zz?W3;(MMJRYcC}#z0$PGX{bqP%&MoAJMeSb<+zJB*MWPb74f$2OQPP`mQ61&;9
zj!#c;8P{i8yLBYB(%PKwrw>Eu03se;%bX&-U#V$AMH_K&%ftcLKqBcm7kDRTWF{%B
zFoHOU=WtN*q$A28Oi3H3A*YnVWlCGqch-e+BnkN-;vi)gn1wXX9ULS}0>qhF2%U6-
zfhmy;I0y*`T6lOycEe?h-qG#)bPk7^uOl|n4c@oxGOEh3Bw`y|%SyQ&U)|r|y!r9{
z4`04K-hTFib4=tG`0!8vobB~D-@i4=BwZ$o4bM+2R7ojlXha@Wu7i#f`PSx%c*z_p
zOIV-@YXvKO^sA|&OUYW<&GuDG^TO3VDY;d)o=#-$A#T{Bdp3BVYI${gy8rCO-NMVU
zAdRLe9E#OCK<t^zG|;p(l2%wCDP|)eVS}PZVH$ywK>`P}A&rtpkg6*QwdBH$M`ls%
zp2$#<ok$yoyA>J1P0B>M3lpfiqDI2CqXjWw64lA5w!kh^DMm<E3UI=iY{Rr9#^y!B
zM>=|kTY_OkG!T`xgeZe~nyo{Evw?QcXapl!B0Q6mgrY|al0$k(2yxFW%8>utum2S>
z4<AYfL2<GTGLZ+6%#<|JGeQVP?yD9_0c*|Zjv$^`BX;v5!nqs|#L{%v+G<te&`Cm%
z(nIQzMvue7R7gjPC;df2D#=E0L<qCMB8K?nY{^ao!OFc_ZW&9`?oJ4d>)QMJ91TJ1
zW!pUhyX1sK<fg|H1rvjr!kiZ-F3RkEuQ#VG;RY=N3&7zTHfV0_hFT)r>&(?7gt}8C
zGAW`^Sk{7#WP*DQ3Pg$uDJ)gk%hJ8@#AlbrZ0j!Js>rTfDVarB`ZZPHT}7cm=`u|c
z7MwyF8d`|kv8ZM7E%vc#|1lpw4j(z`*kqA`yBjGBr-jWgp$8j#k+R?abmQe}bw_#b
zZ5_7t>3se4@zeVLig!=@4<F9YX?i@}e(@_AST7%J@5a?HXDjsCPyhb=fBuix%if0>
zaXXdUyPx;Vd)r5y58|}jp5|LzV~o4cf4;u|*t2E-aC3M6*%x0FV!J+H;<8^m+}(Pu
zGF-J{+j}Haj`?PK@%pRhAKpGaf4I5-^5v_$hYxSZa4q%e{6wtlzP>p9)O}lSZ*E@y
z?BT=Pr>A|(F4O`!QVJ`9UcS8J-M_fI`|_*&>tEhZ_3iqtK0dACMQ5R#*RTHer-FCo
z`Fkoehs1u#Nx~ZOnWj7FPVJDfvrQJj+$gtnxHlc)wyobkk(9gFuWLnJ4AYXvwm!yr
z&8_dds}uG$TngLi@jKmGjmQG-NU23iR&k2R8`4lsFo-JrDMC0kRe&T+%o=IZj>KSE
z5L?cLbOM}7;Y@AMI58Y50w-=hEaWJDO&87)QWH*OlpIKKmB^$5PH-WQ)I!vm2?PWs
z457?K(ZQL)5uGkqw-J|N-F&z8y4ry$>tMcyZbS|peH0adozfx>%sJ)D+c($8=i4c6
ze)hGNNyL0FpT7IUr}yLB_jAwJFOTB9#|V`A;((FpqqfP@m`H{pl8WoK%XCohM9Cz8
zQHrOfDje&zXCx9jX{MHlL0(HKwIqSqBDnuWLZWDuLp$ETobSK9dG+Q^J?OD+<yd7O
zG*&hrF8h{sSsIn1NykZjcu1+1Au)&r;6@(Fm8lRqWDa36Go2{|(a})BECCSp4((iK
z;^ayS@=BaBv@kD;Gj{=)G!Pwa3SzF<F4m-_ty^%VaF77VQaMA&qq%j7AQ9^wy;6i1
z3O$<fZk=(V6y#uyBuXOcR}u<d$kxaV5j1G@5MoX$5-A*zky!;{Nr_A%Rki`3sz8ga
z-CFgqaA*d>7~~QW25oGs?8a1z&{#Vy2N^poU_(@QLQbG<uUyN;3h!7JBW25=Yjq6k
za`!^^Pgal0H}Dm1h=r$1BvWS64g4{RQaWU%0FbB$@!{sCh4KR2Fzw-{z7i^>;kvuz
zbS!I3zPe&eq`Yo2)!aH!lR~94Xf@&#@#`uQttu>-jg!618KZ`;DP*9OAn=ydq^t<{
zY{fjyCF+JvR6N4Hsp^z4-C;#VhK+!tM4u0>n<o{@$UO&H-MX#AmPXgXhdu_9&LtBZ
z{_*jtvyh)YJC#XvIhHBuG9KSOy!+VhSF1OZbLQ?B72cl8{f$tkvu6pRL3K)bd6gS~
zcz*0{lvBuv*t53v{g3O@Q{VY1?c>Ax%(d(O;`Z+TG`)NKXJ5y~T~j-@Lp|1I?t9m|
z@HGg}FF*Tt)9wEF$@2DiXg8Pbdt~koeuz)~VQTLWw|6L)?)Jsc|8|*5|M=t6<<nSm
zUq>Nq*@ZE++mRj|JR-$er`pOsw&--(e%x^bV_K#$&M+Bd%XHJGd0!)Gz4`Lx>mPsq
z&7b~-WPlp&gMjtv^8R;!cszYk+y0CH@$X*QpO*UZ`aAl#^5r_F<xuWlm0$mT9v|!b
zkKqBKz(B&>InG!Wb>u`+2-|VUP*{!~1FC3C?4M1`qby&2W~)bYFuP@QAKQ;{#=46z
z(z_3qDs4<le^k`aL)JZWat+2lYSLJl8np)mpa(G_jc79ln6`*Pv``F+q;^c(hz)GW
zC9{`KL$$~jtcWE!l4c4IcA}1UBD-Xkz?M;%*Nh1WP)`xb)wLqJ^pd4Z2*Q#o&>4y}
z6a{6DL>hPr9-KyPD${k8pb|VY`l&^~s5Osb&f#vXBIusj8`y}95kyqt_IAD$<+7>F
zNk)f5))z*XgG}Gy0?aYY_mQsl>EUKB`|X`z)h)pu$^dylhQGp-U2O!FG^-PoWc0z6
z4}vpn8>@6-CMMy`b%bPDV(}aAXILj@k;P%yosvUYrJi0L=Aw5m?o0JLld;a$dj^Jp
z)#*?VCZz_iqBG^7S~V<2BoZt+Qg8_>#JleidQ7?yvXH8sy)2wFiFM<$2Z~E6K^d8v
z9#SHV3Ui1gl2=~E>u?+hPb``+V9-UU+Zp{TC=*S&e`IazFeR!bsf*YZ#5`BeiXsv{
z$|SZTE)dB`L-A<LL!2-MC}EC+re;FnnUp0ESqPNb*<Gwc$Ytbiu$E1@B!+Pif<UY#
zvt|s(#69L0b!JbvMI^CQ5W}e!DTH9q-JNz*n#j>nGO3UfswH>IQIApP*taAAjkMZ)
zOszB?vMjuW@fuZ0W|B2ffkgz!2cnt%N_C2IfCN&Y35=YV+Am4Q=-Qtn2dPHv;X4Wm
znD(L9i%wA1PyxX=8<s|$SS>SATv$jkRh1H#Zp={%sP?gS6&Db*iY8@(N;}aQ(M0AL
zMHwV;iQ+0<nM!fa4AaO_A|t#OA3ZaIr=`aYSz`>XXKLN@nNOQ6!%useVhx9$uW4g?
z7}HZfZaj59j%ySxhgZEG-#=Z>|NO1|@Tvco|MdRy@v$6n9o!%HDtS1yQYBILz45xH
z9xnFsZ1HxRHc;>&1ts<Kxu4f-j_0e7b$slxc4Wli{wqaWuj?f(Sc!G#J}>uDsnime
z4bpF4y?OD~i}(NS|GQnU{jx?}3<k=Cd-`SM4}bjrygnZ8?|%Ld|HJXciLrlt|90QJ
zsR{>(cwt#)QL*z`gDV$N9AACriBHcL16>}k&!0X_r*>*D=CTy!Q@g2Fe)!?fv2C}n
z{;Ga{JlrjZdV(qS&Ed==xo+3}o8SH?t!VP++sU7P`|;Bg_NPxpntbtP`@6ro|CN>f
zVLx+N%>+kAK_&){L&>Z}kK7{V$|)%eD7-ZAXo&+bAC{xW@u)SgQY7o*kB{;FV{Wc)
z$YhdA*S5ypc2NW`_c}M4D%Zi`8JQ608g<6TQ74R!sltrGES^+^Um_LoER9G>2U(4n
zSe?2WSN1(UGA&ay)+A3ZWKP{_JyVY4jmJo{*fRx@TZHm}J0T+-85cN3L?W4c{zZ7A
z1S(}FM*3mwk$%q7Xuc&0ZH?MOff{{mdy7zSGdnRiTm5=9-$`h2ZDO3dzkl${N@w4H
zc-}u=(<w<Lw!8lyMR?M*&2lD&dH0l=RrL(-^i5|zn9)G9MUxImjIcHeue|k2zfL=Q
z;f+IfNVaH)NXaxwlN8v^CV)l*I5^WczT-1gRc79U!RvbUjDz<35i2c6g`9y?P7xiB
zm7yKu>}#`DbF$J_b%V$vi6B<$&fo<J&WqQMF5b{lb*^=;kaBp_nYzt1kTzhzWu475
zpcoioqQK=im!Z>8GJ60KOY=-*7QF%xw4px@aT=HeYwiT<WF1+U0&pOzj)Z9L0!ZYQ
zyN7~WbVy*`Vue-EA`)SXpddoFAj!l)II<6zFu)+xhEQNZm)M34$r*j5&B*f!I5lJO
zIdWp5lw$~LDkcVL$kv;JTMIunf^MM@2o`9J;l6WS+@w}tO$}r%P<z-shblx+8db9=
z@W|2T-nL5C6d{5znV5%agP=@AKm=|Ik%<X(fjne8TX*gqy~)TJ%07cF3Q;5UjQxCs
zVQ^cKP#`p+wX+9lDyV8H@#)~$1*YJH?g%Ha85n|vS4ME`%21sqfbO^&3{cFFUC4C}
z>lj8M+!g@Fc!%kN!ec&LC~1#?$W*vs&Iy&36cVm#i@G<Y-UoNWsI4CtK<~gF6oSlP
z3Z9cWhKm~sV2We`ky0|l0!eE>pF;x$!jR@OSwkjj4G~8Ia0dVkYRar32)^1-ltbAn
zae%`VZ8O?s!K)$dsFo37q_HsXauVI{Q=i6^Qn~TV=d!(4-%m8{<9vAh54XSnV?f@W
zEBO9PZI^&~xVfw+R||^b$h|pGI_u5pmL6ArY%i*e8W8=6z7DBxrBB;*IhNg2*-RCI
z?eg;Zc(LicRxl-?giSj{0dyjqcGu(ciFwGWoIiYXo~?D-4_EGV_2NaIc9)y$l6TG{
z#wbayfA~ur-R%CucYj>x8B3X;4&7tvCs+dzqgHo@1vQ?A%`~Mg!CajT#5KIb{-cjy
ze*B4%zj*n@{`t%5Yt{Swd^djl>BG0b`S#oY6*oj_s;G$Q9Ew`D>E-jw3-;3=AK!ep
zeE;qE=KJH?>;C17&1awQFExGm3h&PZ;4-;!$3d}>NwDj(*LpNfju{Lgizo2eqMK`K
zQISb(PM4d@mnm(wn58fMe6EWQVZe^0p61gq+tdw7Id7y8Wg~U=jwMsxMq80CSOChF
zyMyn-Pd*HwN*EAm(Ev0kIV=b(cn(L@CvQe>KrK>n6$GR>Iqe85Y6~ys6)XS=s(Bg!
zEYb+-qC$qqgbZ$E=1vn}7!HJJFak(mLLjsV0|G{Oh%>~*l5yK;oFFIGRX7~;21&LV
zbfyI4ZZv?9CZx!YZHcH65KS?iI0DwTnzJGf6oeCQ_m~kK>9H=@4Y@-pn`f7KNCmlD
zO=Xi3o|X=jdB_-jttOIb3Iw;h#!65G(411F<VEs8gi&xHKZ6BT;uNj7GE(p00Thz+
z1uBl?Skg2h3<GbXWWpAt%f@Ke9eSYg=p>N~^CY<uvj=($@Pe>Xl)&N^5-Q9T79uDY
zh|CfM3*k-)0xJ{tTp$c3TcQA9cY+02A$1aW6hO*Ap*0LRt$-lOp;k_%ry=DYVvd+t
zhk%NmQbuhM+PDZ*Mv#;UoHH@>aMlhHqx)HLa0nZ+rLr<rMQdvV&>HGNO_5I!4&EB>
zdo<`4oY_in5F=tC_F$tx4rD~P!HpB4L-lM6RyWSVn|N432thisb0R`UMi69_fdNT7
zrO?bttp!^5z!tC>A?*k|0LHYjA;aJr7vSeEGb)94bAUiZ5D)~#Rya-&FTy88P%UAk
z$pQe=#;gL6F@<-Di0XOi)H<{tNPQ|v7^-82gb<dB#V}@vEG)t34q01rh+Y$@6A>DL
zxkPaIy27f&UYnMVKrDph8r55EB1l;xBP3!3Hq+ohUlBc#kh%!ES?}o4P|+O`h<ffw
zrofab_JA8Z?!bqVH|Q74muY!5_)Xbt8(gHaMeX|+r*AL*_BZr+U>wt@U%vSCpIoj#
z`!ZdBGJN{O*bLbRTh^tfm9A@g^@!Ks`?&0r?~L=I`1{gsFW>&hm-_WSmK~fX$JE=?
znUKdkY+nBO$J6$*KOEK$U}}wngGo{;A$c4IRNh}*E%SW$^|x!Qz!%eIyPl4&w7a|-
z#?8&<dK<XEkx&2Xzbe^p|HHrg`rrN9ZPVFx@vbXjP8S!(!N5pay(@D#5a{Ol^T)S;
zOs$Db&a^IlGhK5ogi`g~=7&~i5xe>EKT>%3_J97jZ@+(Nra8%!_AWV1+wHXd;^yXy
zzr4J7^%AIl^Y+8*)1j8#ba_2}{?X-6Zm`eoVO8JwW<YTt5K>1q^J;3Co(usPjXFt@
zWME+_ksVP|L}n2hX?w-f#Yma3FLZim3Xv^v32y0lJ1v$p@sx4e;51+bURIOha)E#d
zr<ise_ZWb*NMtN$Fy^c<j1i7Wh@2uJr62<hgpO)d0kmNUP{rPe1GTx07-w_$0)POp
z2XXJFF5)XHBh~;{9s)aJLp88fB!hdjK&l9iWMKxbK>-jDzyu`5Wk=$e3>^`bB?Sn1
zBA=@WoV|>x839E$7X>CU>V%9=Z0HX2=`jVJXF7iOjv{-jJO~drP$d$TapGZ{Ho&l*
z5`jS(Vz{`XVW8A$C}l4R%-xF-^d6i*xl8h#HH><7GRP7^9HCB-$dGzWW<5xOL@4S3
zFq5((Q?CcieOqXAy~)Wir&1PBmrhQP+Lc$dXe|RI<N!+U0bs`f1P1gufIIa-fNVW)
zOv6in>;Yy;3hD?f^hBM-F?3BPL=b?93^D)_1S2M((Pd8ph#M?94H68AM5!wxNptkE
z9T_5EB33sN&zS*wb-C0)vH)}N3d$Z7%dz{WB}HON4P@8Z!wf=FcWlRq0bsH}69{y7
z@Rg%E*ow3fQj{t6CnO6cvIN*2C2&t>$t(e)%}vRXl2deuHoSo7Eu0d?>Y$p`Dcl@^
zEko_+Lo_#CDB0RmWk^9{QNTUAtLFypV2OMU!W6#ezPMTRy+ew;brlG67w2qIQ8Zuz
zU(qXQ0B?aYJQG-iPz2xv5tg@_m{^P`6{3_31lMM*bq0*kz}lNPwzZAIniHNiC&3_N
zVyAFm7Gr5NC}46YKoItow4`bkns)}0)C%!5RUiui2Mjfe?nqoEC|d*2Iu0n#XpvK|
zmaJBgxkEG34q7;p2S`Yk@JK1TBXSrdhXC`XvTH0|#!@~%9e(m<`uyhQUwrY=^YN3L
z@t1P`>2<sQ-#@?jCoiABx>SX=Vm-wy&z{P~14&FwI8V#%R3C@a{pRpghD14JIrj>O
z+q>U?yM9<)$2?tML(^fmQB+9TJ$I^%Daodvo^U8$bo=b{LCWFj_Bhv6WV_ooeV7-u
zI&a2tb90lX^vUNRef*<;Fzx;D?cc_<yLj~l$-;(i4Y)Xa_RJN#dm50(v14G0>0&<&
z{O;R(&jQxipe6{y{i_$mQ`Gu&f9CA_k3N6?qaXj~pZ$05KOCx6_A+g5%Cw~rUmriY
zN<aI@UwnDF{XD<UcW?CW9!$sGX7lN%+h4q#^t*Wbo;Aw|AY<SbgkUqtRv?T$ktP&E
z)`Ecn-p~X{ywel~i39?8od@P5KtA`HnrzZ`K+JG{px58gkpL9N!NXl4>go(u5=JFA
zmIxjaI1c-80?NHeL<z~U29jC2AOv)DW&nan5g0W<%q9WImKl4ePB<R{3}EqEF$A=S
zIMV6?z(_=HjKbke;OHI!YHYyBgcuR%5CCL=SXW2^%zzkC0kO9h;N+cBqMi{PIwJ#3
zgKX1~00CNScy6#*>ZZCXh4hXMB{3)TO-4y|5S+@nd-v?zcpL(tpy!JW-nVKjx)}#Y
zGz|m}&KbIMvJ*91Ic+$RgRcOx%XZSfE@*&AJfvQ+9V^ZYVY49*FD|VMkftFIlpM&3
z-F*c_ps*AUn`zqbc6q~0;sGNrBn}enD{Jn7XIBCkhg~rv>cJAUnwDq?VMsfMimo&!
z)(K64Mn4fQZ_snom5?kZLuBj-2*AqC^MKVI83EkU$-yv85_O~uWU)4-0jYKf(qRZg
z$xM_XsX|}{+-NRnJ({UfhDUalfg+QKM|f~y2}2}g=h1o#Z!8-~k;oASP@;%nV`1+}
z)z7^xG1gw{EZVzv18t#Xx<FV+#~7`k$AJJUK#d3<3a>3%H8VYTgzgS0T9|QmcjE-)
z0Bd+xa^g_$A!#BSoy9N*I3Y9|6HW=53dJyDC1S-+<Oh%~(3VIk&j@p(N~S4{)?nN?
z0O{g#Fcy$109aTcG?iF2a8|6CGwDj<#FdZ?wINxXp=2yf9-4{~F(xGgf=(*ZV=8JZ
zK!5`gdBIp)28j89%c7BDQ>-$Lkx+nlK4fsIz2gE_4XLpk0tJ%-Vh=W`W||A5;o1jv
zaf&G8C>=&~jUfl3se%Kq)femD1xY~-t)~G*gRs^POiBrY#yDU5;iF%Spa0eN+4B!C
zH;=mx@cOj7fA^6-d_29|e{qq9v1>i@^GBDb<1sRB<+yKsH@_YFF%x1cC2=U6SA2Xr
z{{5TN+ZvsGls%~eLIrA1=e9mrGSLKK2#SorKqME>Uf!KvL!i&Mt=R~Ees`aEb8<R8
zypMj|U+ynH{#ZtP_`AQZtC#)Fa{h2$mUBRF9ST!P8bCXMQd;kXBxO+6kjZ>)R>M{T
zu%<9S;+Va-D4foBsx86!<tINszyH<x7EQ{qyV_1Sq%p5r$gZaC&wu{>N6)TrcHV#U
zjo-rvbhDS2zxeF>uYQp5Xiv9*i8hk~=54^lF<e1JqY|8q8T>2;7+Y{N4kyQM2#_d)
z5px)-mOV`q0Kz;|>B-i9_lNd}U)6vAeY`!|Y4#P`^3YE!*y_!|J!S(VfF6_}&?#aV
znHno&4oDLP0yC&5hzNuNRFG3ALlFqt8CJpxN+zojrhW{TSP8Xb1Uo_pR09P7K~*Ml
z%GggB1dS<{h=#oru0R~o$gA`@qD6qakpuVwp-2+o76bH9<7lgcLnIg&K}oVf0!Rw5
z*!0l3%MhXCSU9p9Ihtz#Lw`D``+9ru$7Oz8<{`(@Ju)ODz_c0bLH9TLG8b2e%xOD?
z#P-Fu5y&v6>#R?0zJ2PwCG)%iUcqU}r8pXP_0}Uom;f=1l8fD)t*^ty&b>nBwBG>+
z<wBWSWuuV&SdtG@+HbbgM5BQFz^y}v01q%^7zJF$2y4USDI*v2%fQ(-8)WXfL=;M6
ztg};y9#Oh%!>O>If-?aCEwK)kR0De##DKOcbgT;ADH)ZF3}h8_WmjUNZ6KZjcG5Yp
z_6Q^rG31`6fvHH$WZH&FV{3eh1TmDgYT#%&v9HA5K}sk&37nDl4#evdb?D~Z11Kf7
zt^km$wTNQQwV!J*G;s8Vy7x94b(P#GV>t9iYiDz@7633#QCnx0$N|0Mb|28(0>WS#
zh%gX=040PG$UR@cdIm;p3o{5thkf{>c}zandDU%BXrUmqqKm^K<Ph&zx9&<#m@FVE
z3*j6Y$&4j=0kl1vAbCI|2oK_Do<V(+nrd{<qv`=@aG7Nx(38-n21l!oA_n3n5kVPy
z!^GfdeW76tWJasxgEFFagFFmGSRL6MHzdF-cIjQ5tXpC_k&|HxQ0p+V9~4Lz-~x_8
z+!uBv!b&n_WdbwjK<?hCYf5gKmZg^hJK^I314o5S-9r0#HOz;+8&lpn?reEFuXn@4
z_NIK>53j%a_mw}Uby$xOS5nlPUTUnH*k6RDxx8qUf_>Cwz@zk@0s6AmXp>BIk(bkO
zr>8%?US7}OC}Y__`}l`T`(3_yfy=|=hxcGS+OSHOaV)5<_rR9&_3`w5eKPNB8ZTf|
z%vLW&oW}4nO#7Qp%BYLpAO84sembHI^Sj@#jD9+M4c7*Pa1FpfL&GPEl11!%$eVom
z{POtvPwuSEL6i_cc2e;8;ZZrNBE&N6pKbS-@BY{SsEMYs8@A8p^>K!Im<H<0Km7TZ
zpZxgd+2>{X-Qmrh_rPJ3ZhrE`tDn6buT#8zACDk~Xp>0Qi-YVzm#6@gp{`l(jRyt~
znObYY7{Dy1<~$AVN(!M|28w=%GI+}U_Tl`8fBE>^m5;<S?q6*9d`LH(&JroyIz@`Y
zG!6<QB7iIN8aUEi!55o0L<7VF6p5ON*AOF2h6P&%jmX}$avUO%w0SWwBiICLV8=Xo
z!f*s=m{NF-sK_PEqA$o}gj0VqA~FOlm<)PQ^dJa~;7kz?2A({SoRQJP0h7=i7;M!c
zkj+qmn~oEZcBqTR`P9$vZ|`tuV6j4#g(-rDK#D%Ije*IRZe`6^ua>Xhwx`*NNY?PF
z$m1|>1<hK&*^2ex=*wf<UGn_q5lDJ(;YOPh<JQ*|ypuy3l0P-v81&W_U_!NOl9T!z
zM6$}5+mo%g_i5Ngf3i8FwRJ@eb61p+yXCP=)0n0~2oqpoQWqjVfg)ZcJ-1XQagjlw
zNf7iWhA_F{(=oPANG*T^(YjMN8Kf<i5>qHJ_~iZG$4lgUA|MX~PSFfE$Q=+v15g|c
zAP~WQ4Cr760c7D)yIam<*NUVj<R-bzo}!cB$`R{kC~HiiKAN70%v-Vv(Mek(>7WQG
z38O8E6Jkag2#%hUP$4@Lj$UU7?9`e5Z0q=BH95o))7Z_dWJ8M6$#+6aC?JXOE&)IT
zr{=!f;^PUSIR&q)F<2xA@nz+RMlxVz#8^z@2`mRxCkl*I7|u_)*{C-NN~xP-qASN_
z^D&hRDkGpfT^d#yh6uuNODD83VkDa)j;_i=*w2tA=oHvu=AeM)PAtq-GR?O=h(d5(
z!Nbj|2aqS#21y~(M5HC4Bc%k?S7ifZbabGk(8@xMd_ZH)(K`{zIN-8Kw+@aiL56NE
zr);EysS&v$X7UAO%CxL7W#E)r6-Wj~gOJfe482oHzG@mQQx3_A)CG~U%>bA{fuaU%
zq(`?3<E8xIC;ah9&-VVCui%5#tgrv=Z~y!M>++VTS07FnSgZK+lCLMH?R5QYlCjjw
zPjqFZJ_t@e5A88ott*)=+%&!1`P1XmacDC<9Ti}tESIl7*==L}u$GHgAO8MV$7OLD
zgBc-&scg14SJ$vEk>vgFf7|;R(x$c3u8cVG7)gk(Z(dGf=MR5+r%%}IxYn|}PIy?J
z=Ec@R2|5@cIgkYwP7I4vz?ylVHl@Q++qmAoUgw5HWI1;0<;Oqjx~A#Vh91VqZFlj*
zBo!ll^zw(}=KB5JpXSGR=MK~4KYw}k`9FB}gOB$9@OQUw;%Fh;>BXnpXTSL2_+pbD
zf8XzdMsUko6}JOf*tFW?43zxoEz)Rbq)o6z_r{QegCK#I#3|>bt3u{7h3gDPk(TrO
zKYabq{{8#^xbRBjly@(-)IgS*BXcNUJ=^q^vjy&9ePn*3GNkS>4K#!efPO=O;Rjp=
zH$a17lRyJW<bev78Q11OE{Vw88&-2QF5UQoY7ccN8=^C$9eQ&S^1bWXlX!IUz)sx~
z<!s3Uf>oTVkpTd8GzzL3g2aqa0;_dFQMUj$QwT<Av*{9N(++lgsK#c^-5!tYkv7#-
zm35@VWVMQ#%K)H|MBB>fyd5`%myXBB+r#?PNrmVLd^rv?zu3~B?67K4FCk6bK!B-N
zmCa7dW?62zDUOI;C=b?q{^VsAY3uj03qRE{?H=n938R~gn_3$(V;WklQ!bR*YN}|4
zKxImS1d@141jBY1M=P7su#JLUX%F$#W~EYap%~nkG>nuYxDpIz2+W}d)rc#(1t*7+
zaK-RIAZN10d2u0#l}O+mAcP3&7*P=gac#W8?i^r630uI9@$5XtDjh;j2?n&jI;fyT
z3$$?93RJU%8BnoV8-yF6XA)%fj#;e_$sant;<6-D?GPkLsv%%=g27?#t{6hTSV`PL
z-Gf{r2)ROMtd(w3F;efXW6Htm4O}qf&ols_IZ|(UCL<><)YO<{wZ>R<B}PXnPNt0+
zbj&PaA_Cdx!+>yHIZ|L<FvRv*A{a8v4^Dd=C$ox{Vrt+mV>1m$kqii<P&GzCgD}KB
z#u*Zj5=c0=1`|56qalP@>ehJ2S}Qo@4jsEB8W!(t(Xfp4w8nXfGPx(=Xdbc^O2m|G
zt~3x>W>HL}oRWuUQY1O4LNpB^F`xam;`5E^3Ko-!j!Y{q6?gz&tw=E&*gBUHdLNKh
z1M15-B_kyaNG|Sb=)PhWB5&OX4=9GgMjH=yBp7k~qh0<#{F7_{TfYD2@1FNI+41}1
zzxb#9Z|Ckje>g$LYA`VP7VFFF{GVM;*W<LLoXVJ%51aFM6Klp;svXaBGn>YAdie18
zetuXE-_3DSo}P~kdH?c@yYK&5e)YpR-afqklctRVpe{LG4oRQge0KBs=ZLmT+r!hN
zhnw}BXuP=a!+W5V#rpc;tyN#4YM?s+w$p)jH&Kc&wNInY6$zb?u6A*%9ev8KfxR};
zRVmi{5A(w#4oS2Cbm6kUd3L`4yXVh7%T+gmyt~{!yF7ikKdw*f`F_3sy|4bX_FVk=
z#j~IPS3mu1<DAa7|9*XVci_$CewQx4_`&6mKEva$)(;K0Y!wI)MuK5uWS|q9EpRd%
zB-oI%MRO-ilru7uv6C_8fIu{G0DTBcYw!1e{onuL|MvcTNS^Yu9qG6^)s9cENuJZ(
zV4bsr5s{3}w@EdPS)_L9qR+V=X&v)4dJ`NAY=RoW#E~2$oDreM2&m#Xaqb8Vje|5O
zM^LAZ;Kn0)M~JXWk;4I^JE#YQ0VoozfeA(lTtQjz9Iy>8w49<0$SFYF70rSjRU8_4
zrAS1*yAe4o=>6=c)gD?q&wX}%cx*?zeK^lu&pd`(kLE&nZfLznis-6<K9{mFY%w44
z*=Ip?JS<2g<79ExF(W-}YMAwT>1U0!#equ^Kb@+A==}#w2bKZAV=?6r86Z)d=M^+>
z_Va1>SX*Bv7DX0uXa<OY6!nQQn{(^u9ukxUKtTFBM@C^COBthY$x^t*WcF08bVIhL
zgaW`rp&Ud2SiL1nh3yb1<^$r&krdoK7d<v9*oj<?fHw)|6$a+o5yZnl5uiG;+dyzY
z+Y%e`VrihG!oWBL%oJuY5GdiefV#s75n;-y1Ul$kDP=e|*`RMIpCK|v3cV${LJjvt
zp_F0E2GSY{OU~f#v`IznsTwol2Fw7}fIT845g60B^txw3mTFRT=^>H<*RBp>$<e8|
zZY0(^0C36T(A^?B!U*j|g>9GyXjlcfr+`+9kauMAZXH>xV~T*883_Rg%$^cQSIF52
z%TT0O7%rh0MI)F190a)|=SCm_8nnYuzzQ}7DFUExT`Gk@WP&r=;6o(SirN!E>yFv6
zgLa|;VbK5vu-&A$k2K_%Hd@zQCJt93n9o*HU`p<-w=5+(h5%RU$drY9I0MSarPe5X
znIlcfs(DTr>sC<JAg~Blkbwyk^n-C`LtUH6K&vJ)k7O+Z8aC4H7?^dLqY$VJfJ=4>
zA+-?3Za}=9;Q9w}^^w_EPp|*qcd!3&@`8W-#=f7u^SF4IP{+|RBJJmP`gC`hfBEIQ
zTuv8P{Jf;$V*Gkczxh{{#137(Rvk*h^Euu>@i(`p?`@!An=oH~`bm*<t#WzwdB6Yu
z-P@-gI?0AOS+7#2>1v-Y06f?Aem<`jxD~>blBA~wL92D~7){k8=PjS^57ThfW%f3Q
z8&M96zyOkQnnvcfAL|*r^&yYu6M_ys62<oN=GZ=<auALs!D8*{6wepR6OWJ1&RO1k
z^P8u6wz{mHTC`EdO<w=%KmYmX`*g9_r{6w)cz1M*&FGgu{pjMSFIZo<_bXM0O~^Fb
zm;<7Wv%!^87!y7<$_W;PP46`3-K!)5Xwp0cx)1|+Fj994^y~S<x9|Qx|LpC5ScI;&
zFA9Uu#QSI#N|2(yKR+#$hjx-vUpnOV@&0CiO`FXoIhSd8xE;1b-W<X~hHbQ&MMw}M
z0R&2+Fz^BTLUg7ys0SGa29}*IchH$gW2li5wHp2bVGHgU7}gz&`-pThMdUzPuzD1s
zzM`iXTCCM2qal!yBQSeZf&ri+GX#b&9t`eLt0UFhv$fvV^ZM??`IsJWZy#$v^gNUI
z=#9e7=me{EBxFjC3PVaLWM+xDc(9I}-SPdoni2!dSGm6x^y!B$E*E?M%_EcdQ{81x
z`?7iQ$*JA9*TA7^1A1tTCR2&SgN&r0fV4iHA*#(`8`K3Mn<IL3QR)&hZr!vq3^9et
z$!1tVC(qNS<h<EZDI-hjf)|1wwqRz6N@L)813MZsIEq6gC32%N>Df5DsTm7{WA|?C
z)e+fGFunj-&=nVR$}~5?LQp-#xp^n*J+N62U<d3-z)N%4BOY*BoQS!G=EAE3GJ&@>
zA{>bVTJth?h-@CxB8sL}P+GSvJ^<Ypo-8;(!6@k|Kst_$D|(PQfNS1hUpu+>;8arX
zfduQChHUk4Az529NK8GLaitZrfUbZgmNjS-)ilI$fImYZx1f?@P;WjA<eG#OHx9kX
zMV#-Xoe>yD$W2$-Mw|o!fU7pbDROu>U=p;RatY8HsWkUUM!=n#!(b2%q(CHsj=-+M
z16Vg09i6eKa6_aZ#ZF>i6a?(+k#rNj0CgbeaB~n-6Q={BH$18FAVhXPv*gIw){bQ`
z&$)9Q*}122f~C5+D0^K@aux~G5Xs)Eo3m|}1t=;YiS&6D<VYqCF_MQ7p<>TOfnfnK
z5({8(ced_D1US(q6gm4M+_jVQ$g&6vSa3#E2WBt<twXwu1rG1lU;jSeKTiMtHy_T0
zE*jsEKC(w_CP}s`_c~o)J^SS4O`Gy|*nLDV{_&o;-Cuoj`mL{8XT_&?%Q=p)`S|p{
zKD2mJM92`){b_)oJ^bPSyZP*Am!Ey|<~RRReAGn4kmlA?PW#Ojb0ljtZrc`!Adm?W
zYU;<gOfmO9md#QthNWRg?KE7zz;-H|?KoY5J^}YreJBHiaS987bvd98ZSJ~DSDy~U
zI+ba=-Ar{^Ht=D%zA`zjv(>Xby?OiMhi!bmdHcSKpm%K#>)~`N<Aykef^78XpMCzx
zC-L%&>*KHg^_#bwxl*3w`IjGE{_^v3F}K(6p`+(58(Aj6EhTUhC;-}Bn=2wVB__N?
zG};K}K;Q~JK|<irHDY!N_u=@ve}4b3-^4q(y#6dFDpU4E=q$eUpgLDvF{K_;v6lGw
zaP0G9>$0@r>0ZXmX<9ezdEVr`TpV1k&s4T~W*QR@1EmGV(OX9hN7J@I9)UI(3qrDW
zK^lP#mli2_b;}7&pm-P{79s<LfMY;LoV^=jLLptx$l0Wz5NHo(-~!MCSB_#-g4_{M
zIdBe37AHH^INzz3dhGqfn`PyC8^`0*<Kx_Id$!e0LdhdAvm}udtkA>O;4BgDrUqq5
z)j@kpN=G~Q-~3vC_@858EE!Xppl=6>jU>bQvGtMS)Fyf2s)wsBG>6)WP=T_63&c3I
z`&p7$JsS#5*cU@UZCX+e9<le7ff)M=iMjViU_;=EC9Ov;yP=@Km=lr!LDpJfP?c@?
zIfHe-uC(m@qU?|*kiuhRsE9;1qcGTlo(RoCFeP(CPZ($JPd4l<9K0a9vU`j{m1Q7y
zv@m6kpwtbLIZ?uZW{T20#2t!bmzF%c*A|fi0^E8NP8>KeDAL%mK|?2kfFzHK8*w*6
zgkeH$D3sltqLf&}sUfIh0TfA<bj6f4OrsK9NjBF;EUiO?dkU{(X48UcF&`#TtL*L)
zumoR<lfj<_E=bBHuz@3ZM4F;61SE`r5GkWcc(bPLEVLSJwarR`OsfyL&Wt%c2-&-X
zWJ*WOmqe>`f(WOG7MMiLqktJ9I6#A*fQ-C&KO2n@XQrLQ=(388q$AWvkiF;5m7FKQ
zW9B$YZgWjdjY_wboRbG|qH+P?dRk>85Euy+F_#d&f_91k=OV;?Gax2xYe)v`T}F=+
zbkv00&~o00dn#e*icrjT&Xf&TQSK3O3f=dOk1ZU*BQO#4?4wX$NpMjhC==Hf5|wgx
zQzT@pAqbK(aY@itefs)TpVE(iWPkUElO&0>pl7m6NC$P+bz7$EA;0+O;@PL4JjQSd
z`qB3L>Gb=0zTL#LW2X73-90StRy!M>-=3-tM>~7oc9dyXf}X(;YssJg^1qD3yYIgK
zZlPi1RL_-|E}z}(Z(d=_y`87exBO;~QecS&ohGiae_GzBVdOM0Fm5(iU;N<y{Wo#<
z5Yx7u?#Hqpu6FSFSfl9bz%9$r%n*gP*5?#B-G6(Z+wt<%XBSt`_m@|u(B}E=)4Q~}
zk$p5>PEY3vCj~<*^9t5X6O{lo8!&#d-TmyZZoarkwm#l|H=gbeQ1aC$FE9V%=bP!#
z-#*!yV3Q=8Z7!&Tm4$}uU;vW7o~$?G60E5s0P$wS;fSH`4ikVtG@u4Sc7oII{>SrQ
zpZCjFUtk)iGUPOj-VVr<#oq03I_+USHqYxSjq6O8JhzzP{hPNDoQg7qFlM;D-lof|
z>&-hXyK(n&kJs1iv#>GV6Q}5e&LLNX4Tz0|uv^R655~x`kycHFnnKw_Un8-Lpk{<K
za6w%qz)dkxs8A~5%CKMyM+pbyim~<R?ur4y5Wx`5>THU(RJ}W{>%ku3>HZjp<FRFZ
zTGp=J@jUnDhG;1u$Xd6ys)i0Rtl;=(67D2I;Ha&ik0%9c;b$KE;X~eiF5`~mb)JMW
z;Blp)fZ)8=p^#|180hi-h?Hp<kiF>~CP1>@AH#U=Yj@JI6v(bM3E0|G*&EgdmgedO
z%5Lvmp@aD$Xe<sqiZv@@axr9ToYM{#$ZQo5n+4#y1`d%+&V*4hlx#;+aK{eSm7om>
z9jFHrL5kif5Q){rWeP{|DaX+xB}RkpjS&=`-C4vSvPT1eZW7*+ks^EP;DTN#dK2W;
z5fWf%2O2L_&l2R$eRZCcdLW2uNMUypzhxc+f!$VP$%>H?A<a(?$vCk9o>WRj2OLH+
zmN2$W>*tUJ#tuA`WmODGsV|MN7<Dk4L!I3t2;4`YgXB;Ea4s-zp|+F&L90flrihHq
z7o~{}Dex@0kfS0<UHZ63U&12-fEq2z19^>jJ}?EQ3^+s@m=~u_NCG`c7QmwDNJz{P
zvjhXrz&o!F&_lLNh!D^gVl=oSzlHcS5vn~H(21r7M@ZSY7%$=r0C`HF-fA16#|WG!
zYSk#Hhl~*&z$!OWEZADNNJH*2<He1i?orhRzzDJH8cHnDW)o^b+_1YP-eSxE)Wmy}
z6z!s<fR%j!@Mg5jVJ228fxsy|K?P(@o}$mqBKEIBB<lNw7SjNxl6n5Hq`&_g|9*XW
zbK#kPe7)~Lcc(L^i}S+@U<XOw)WAQ3^9EOxr`@NY_CNjgn}7DUb#3LVb{d*XGk;u*
zhHYe$SVJV2H1f8eA71?M7h&&rH$O<S{^?)+%Y_EE04x>GSDVY{pMHj@cB*fF_tod0
z{S~$`Ay?GuJalU7UGRtzrHs?F=k<KF_1(0Uhqj0F+z+ti!_~{Z&@|s!rv+Be!B5Mh
ztsX%^!G5>6L2RCG|0ElKb@RX3-n{(!tG{03?&|ZuLP<xr$EP<hx1W?;hUo&9<J>gc
zbky_J)%C@WfAWui`s!oeX87=Tx93G^EW1xWyZPxy<7YQ`_ge2O?I&ao*rSM*%t4rh
z6WQ??OT<%EceES>%H`gr#V|M`n&!Qbt{vEoqTU_;-QW4|ZIG$lc;Uh$=0cE5q#Zg0
z3&M8Xf$6i}tS(LKX`TDMAQ14{+H8m8$u2JH)4YJ5+IYKsyPGbz_x0I4uX7v6VRMxe
z?ujfFWQ4HUVF{VAI(SF(kj&v!GI*uFKqS~#h`@QJ!62nj4q@T!sK^sojZLBkf9499
zBRt3&he84ZiD=LqR(E_ft=gkL)%N{|)3O{t=+o2jab38kMbDY$+IT(Ix~z%AYY6Lg
z;d_VL!)BY=a^l$mL6nJ-P=Ey=&Wo_Ev(-?pTiR36BhxTVbAo{rTf&O5OVb>V$93Ef
zV;;+<G+nG|M#>wCS`AWQCqaqay9J%b-Jo+%iI1mS#K_E!I0z$JTPpF0abXz{&{`cQ
z8Pk~SnqFS-x8{u8H6Q~BwWT3D^)i%BbxH%5Oi*bxFqdNL?0scU?h#=S=zx^iy$@WQ
z7SaV%SS=*(-XszPsR=naGx_W)l)0~#$XYlNVC#^do4ps%R*JiVQzt2X5hAe13<F{#
z5XTjiLIAy{G`f4#Fupc3%n8^MBUl2V%-X#OM?x}hnjG?mU2*MDYe!^8lAKzz?9@EK
zsq9*W(#W`|Ta=x(m23e{6df5bOmYzbn46_B11Zvgu_PuHXU}a}MO^!gh%xMxnQCW4
z9#R}1c}QSu!t9ul7B3mitPwMXJpm{0t6C<g!Q^SfLjrG<CW=)72@2IYY(uG9%HFrB
zogqyPI+H_eNZ%MT$d0;MYY01E-h-x0+;q(ascH;ln*G!wtfU}gPNP>#qhOgBmd-}C
z(^fH1Py<>}Ql_x*00D5yl*HUdR9wx%6f>y0H5kSaAWUly_ti@&kkk#<=46BhoX5i0
zt48Xiq=3Y05z&S)5myY&<Ia}HC>fYM4`nBL?Xc?8cTf3Yj|1$ly#Dg)<zxNR+jDQR
zE@uK$XAte>!jhePZk9Hy4tKwLc!aNC<KyibCB}>4;i!MQwMsGUQyz<Yw7$$0EFIGR
z#gLZu@wofJkM95F|MKCx*F_T3P(}|d`|D4K9hCD{m%F!jZ$I@e&u=IvTsv|JjD-fq
zM%&Bj#jZVm5Gw08PY?6^{PNS`XFpl~-Cwt*weGv^b$R!wr=G6%J%c}~vUocWdD~R9
z)}FOlhIb!w`u+8bAAj<fzqEJ9{SQ8}^TI1Vy#4*FpMH9^eRaQnSPtJVy-jJ8fuNs$
zeEHEQ9}O>WK5lP+cYII}+&<gC`q>wk|KLl8*X`(NMxMx$m(G5Hd9mRtGj`YYJlA<K
zZn116BWy;;Gmn>!9vqOj*+QvXZFczj{I7oR-#um6@VF^OVYpy3WI{igOkoD?A?+`u
z6kDo`+HlbuR5(v8M~nldRd06l<I`y=m&1JSXQy~v@87>4FNXK$arf@&W;b4carLan
zut`osN+T+HXV4?6qk3fFT8%J}4LM?xp<!(x2_Vo2f*gbU86gJ|z&RQ~ZJ0Sk(7<{y
zXqbm?!VS*d_~6cVT&*5XcX#Uvo^J1thdx)ky<7V+Y~tIyhs&I%X-KGfyWj7j_Kp|E
z5fnJ!^7^ojc>prv)F=QTf(AE_Q15CR6i~*lv81iEvp3{mYp{KFUA}t*O1TscMUy%z
z3|n0uxasb4r@k~-@1FBkYcud@J#_5L**VGxVT!~g!7zGXBdE7dAk2G@)_U_6Np|j@
zDHYPq^(OBw5OlOQs~a>`w`M7^B^jt?91S`1Vuskl8No_GP$EYpXdVK%VjU5yM_HVb
zgf|V^TJj_dhN)e-Jd!nr0o1{hNkLx-ut`8TQVwSGv&hiVK#xo*U_)&MB1#;@0HI2y
zcS=c-sB18VLm&_}nt+$yOo9NB0nXS9Ljr~ngXT^H%h8OSNyA{=hn^#Zh&zWXa0b_s
zVC^wboTw!jHgG;_bm0t9gR{G%nW)2m!cYwdXu6^^2dvpKICv<*!5+36xH{Baa)kC^
z+5q%5QVMT?!9reJFoT&@hmjlrbd?Py8v1f%ZNbkXP3&uw&P^e6F9~Bo%GetM2yCh$
zVK9*sDvo5y`^pgk5>cIU0EHTr%-(Q8t(MkkDNz8YXTdABMTi&GZOn~)V4~&J*I^il
zJtM}NcU<Q*4yauux&bsFqB%}|K-9)<4&i{Hus9$4?h-g9>sB(=a|iLF$r6s)K)RhE
zxOxb3vIP;@rnZe?J=-wQqR^DR;j~G+Np_UihGT}>UF+d^ETP-$HOYMc_Vub4p3qD?
zVSqzKHP?Qe%X1rtKDYjx@7K*UU*L?=o7KCyO&6Q7_Ib`k(UxYkv;r<=o1TC96Y=AM
zH}i+@-+c8QQfjM+_jax?e)NN9pMDbUh}(<%uYX(TWx0Rcr_J-r-5;Nho1oskYzHi5
zcXPe@=o+B;8c|#9Uxdf={XTy5j86UL;*}#_?)MMpyTP`g9h@1BWut(ACt*eeU;0Yr
z`)|K#yk2eo;)lQZFKP41{dfNcV9?rDgjYZO;_#af@7^EH{Ma6NC|_=NU;f3*S1&S+
z^D-;+6D4{6<)>FazM0a4fBSW^)Ni((fTtlT;V3X%6qZiO=abDw-PZMLpe4#ylF()c
zn=MFCNsL`nk0q@iemDP5zk;Wdc|__$#7d|SJY7PlN`iW}1I_oho3hD>lrn9$rLDd0
z@>-KHXW!lYw4LtmZ!b32&zIBVm~x(v$fM-m=YE*mLeUO44`<U8-(2$cqVUBK<0O)z
z9)d5d1;@$CfKkHFjOb&fv&98PBgkaaK<5+ifX!kATzdpDCzlcVh`1v^MUrS9(J9P3
zxS#EKuFH9Onon<+H{XAlAJ?ZfDwVPIWu>$qZEnx5J}<pqeR4HLhfShm)c%yvGq!$Q
z&GTKS=1Vo2C=T5aNqenTDHmNlZABcFX&^5J+)<%<_zXa_UJyNy4b#+*HE`}L_-;ri
zF>O!ho*cDz8K>&qZAG&1P?~vQZw--+9W;hq!caLf3(MrXq^pa5=v)c{)Oy&IErIYj
zAfec-;Wb}q9IX!BPLwl6$!RS-kgN$LBBZI0M4iF{y#-9v6-XjR9EchaWbo!R_@8CQ
z)&;|tC3tY_2_+JQX8=TQ3?Q7HJG(jnsu@s9rsqXUF&mLP$^mc)p9z`c#^aV9!i$0k
zRF?uNQ@gjckI_icvZE2W2dxRA7v?&HY|sExB2^(qF;eGD0V{BjXi_%7a1L)y4PoS_
zJ`_{Nb`AmN5u5eA%i1xl`NaRt4}J~-t_IzWbrjTI5Y%(-COl@U-2np?9U&8hL)GG7
z1Wv^yYxgj61y49~1j)2<i6Q4kkZ_zFJINLG6$+Pxm==IbW&;6QI}rwNgH;ThA1DNw
z1S!mr19f&Gv}D@cI-+1()To=Tt*<9t6neMSki4Tq42Th+S`v4#+!-bZ;<O#12bxAo
zv^2_?O&ua55|Q@Rl87g4Jt1&nKz0)nx9-56iIjonvom8x0wh6gO(6sTFnrv@vIa%W
zLFh@1096KT$YYYL{dBP_`!tp$sP4>0d1TOTra@PCug72i;ltaf=x&!glyTZ!P3IQp
z20YQLtE*>Ud`uS7?&9LJr?<DqhhAZGTu!?$ekzxw8d}ZP`u27<8E}(QF1zO+g+5UE
z=-pSp>gQpJPQ$>d@QyzH^5+*HUFgYAci(^a`>z){-hB4u?&>3)?f&*nv!4E}@cMKj
zuiQ-U9vphr-sZYIwfk@1z^o8y`|O6vF0NnLygERg&*xfMNy9Tn1T&F`jXX*w2pE6x
z>7w5x=Bv+sHh=Z|x9{!>&p76b;qv=$e!teGdlyVs*ZF_@SFgVO*|QhVQhmGV<6WDl
z{P{;WKmBC?(aw(Fw>L&SKn_WyoNXvbD^D9kv}n3`JKWia1LVwTbh#54<!XaTh!L4U
z)Ys+w>wod?fBZEcFfN9)*-vZ^X29ajS=7x>>zs<MaX7U8R8w-i{upD$sA$nG>ghgH
zPWzPoczXLVZnm<VWK$AS$w4%1h|{(p49s48rQQY*=R}loB7_k<qc%+B!5Jv95@vAc
zK<I1m77PJ{C<9r5G3|g>g3-+ZJ4%5yBF)ep!J$>}fnMx<SniLf_fHRRPLF^1_S>)4
z+YfW~(i^A!&A<pCm#;3yaeMjlX5{+fYIFT++IVc@Y0zbmuE)N(smATO^^`Cn0@fNq
zKnlu*!3EYSZ<ENfv`?O2eDcKwXKo8ro#!*xQ?u6Z4vRJovs4DBju`uki-)HZcaAXh
zqOKz)B5_yKOl8Dw(cRpL8Lc}wv#@HK21h5NUYjv73WJ$thS-(i@@BZ)Zp%)tP`fPv
zRCT5R*WgrB9%LhlmBtNg!xTW5NGnu#-aE{!3`0Z9pgjmY7;;M5A_xL78k$5ITv@yV
zWe6l#6@g46Z)Lr6E8qs4DLPmogh0aR#1KYcnXCbua9DK@g94DoYMP*vVOR$ta3{k*
zV=zMGfC#EQrZ^iG1~+1_8kEA7EW(U1KuXq%Mr3M0n%Eo$QUz3@u^R#r0eTPT2x70H
zQ7IE8{%=12A-MItCs)d{hKM)@I0TH7XcwVokP@P>Ac{r+s##4^Kzwa+JVl^qA_Wo}
z2ONcDvme_`<CGI28=1gH$tL6qwh1E20Ll;ztpFmyqFji81rg9-4rX#7*^s(>sCRG)
zM^v-gs-iDiNsRKU=rw>SqO--u2oiz-<-#0@X~=mEqX>@)SqlTc{0L$-C-ZKaSV0Ia
z)JX`#92p@9Q~?$vfIv1g2jQq4hdgfkai%QLjazdP)Rn`{8V*P-LEJb|TNQWP=i6yJ
zr3=WiWno0p2(IeP!~^l!zxeogb=_Y-aZ0Phmp}dV%fI-`=bzl@)A>{b>+*cqef+~u
zb`+QGyYkU9J+7+IPv@`i4<#zC^Sk%UhvTvP_SkTBo%YYCF^`v5;X|IL$JehF)<Lc)
zys$j`<dYYF`7=D+KHlEF{^Nf*RNri-B5?WXvweAf_s!q;rs^S{oULTI+I|$eu5~#d
zPQANFPi0%rN7>-T_W5?Z6=d&iIozMu7TPWPu*r<Z6e+w6gZHKProip~v^+f3{va2p
z?fx@AulMz#FHg7czB(^z)<aUF<riPP{K?O+KL7ZlzWJ_w{jNTmzI^rUFTT9^;!|9{
zfp_<ilF0z9oOY2W9#WJYG8-_|#g+rC!87ob9jM$~_U%|o;Q=H!qVVJC@mK%!?w`Mx
z7)R_GV+afN#X7gewwJN2Ft2@ejL_xJ^f$l#e!t`4YE2EWtI@VSeK@>Z`Qk#zm#4$y
zkvBUC*o+g5#EdN9biKhmZFJd8BW<RUM$bAfjkZH#W&rT&o+1{)F*x9~qIE+;W~2c(
zV<g5P0ENVeWAF+<ND+|9S~!MR!pTn!RJ{U3b!ctYr+4e=Pmk|kAKtvJ^UR!uhu!XS
zyt;f&9NR3sjmzzD1r6<V^>RD1!re*h;qiF3)sLLDwGZb$gS91RCImkzVOSH&Q-;;k
zmWJ^XHJ%<He*Dq+{PU0U?rJ$2A8wcPQ(t|Z*W0^CUuOd9wX&2%o&gVcXJi=D&>e|F
zHqu&!@gkm{R!|s+u7GG4*U!3na-dxS#aJ7FfmbZgpdKy62)4skgfBMf>ebC=SN0>N
zQ35T3OjS5B+QdeiG;m1+pjwwe&k#qq<cwyWQ9T!e0qg*&Kr@e2>=4`$OMqZ9Ktew|
z?j%L{$$N@41~7R?1p`DTpx_*2g1m6=loNm<VwiJU4FIwDsxShY00Kp`$_QqIFb2Q|
zwQ>R{LTn~mv~I2<;_8YS5FiHe3P^}Rk`XZ?9Js=msA^&M!XnDfm;q`;IU58)3&dL0
zrHL6#Oe^+=j{JZ8!7mNjOChUaLx@N>AJCFycbykV16qw#Vr{Z^ApcK<xc3gwR^<W?
zp|ufoPCR8wgoQJS^L`4h1c<p%0B#defzikT$Q%hUNG`%3FeXn6C?f@$Lk7e`lEjny
z3RbN-*wS%_cIpP^y0$?bm-C6%zL0tIs;pXL%c7o$12jQ|fou}2K@OaN6Ama|SFDu)
zLc>9kBZwh9j1WB#5EJ>zdBbeN*)tVp>J6quO`DzoiQI)VK_3$41PEl6pbYL1y)z*@
zmR)SGFVYaq4v68^JP10Z39*xtS$p=$CcS#)pyTy*S?AC9`G^1Yk1l&1Te=Xw+Llj$
z^u@*Hm``t~wr(e)9aSFnY5A_N`>PjoU*8^&XG;%r7nYK@V@YWk$Ls6q*)u!d>S-3*
zCZ@F8?JmlAvERO!&bRH`-~IZ-(>X_2kHosanDVrL{Qm2cMoG{#2z9#H@2)O+xH{jy
zJN0Icn9vpSunBAXn~QB3P!NINzW%BzMkW^a)l(QCVHw#yv>Tydw^|dV<*^^%zBY_!
zuYTy~bGxgDrHdp367bJ&cK_(V{>vYJa=|`7{LRCCEjVqje);3;AKav}uHU_l2C$6;
z18|OA;O0sQLy(!xQ>-VtJNt4}xfC!QF8L~r%rG()L{7{7_wWDd|M2189BE!Su@F(O
zy%_+9otx+I!)d8{d^Fb8<@n+K!?*AM!?%aR^HxS2r{0hCQBU99-#$D-$=LDxufO%a
zxwuHQ8Oz1965S<@%on@u&|~K=n{i_{WEsL_lLRmq2J6wr=rfwKHzzQXAVe@kLY}b<
zK}2puLIgwskQfr723H^f+5#&RI;5CqUssLR=9s^|eR_X)xSd-h7{@{O)1J1wbg@a(
z&?y(a|3GWYu{<1)c{|DB{^{=A=fm9tet$eJv$h5&r@HX6gpcj=`lHi29|OnD6@YmQ
z11KEhc01&Jdt5$#cJcBD&!($Ov(q|jEa&sdp5~`qcBfNqu|`fQqKECKX*pL<L8XAs
zBq$uAq4V5K-IxTi_k`lhERJRsG=MD7VHz~pVrge(%$P!Gm<nG^!{y8EG~_8YZ~~&L
z%1%)i^)Ax@gg|@0p4`g-h>VFS6d{qN03#&w5+K;mVMa<p4Zxj}M3_P3f;I;^b%juf
z97Y};B}=Q0!D%p9DFP{o4Eg}jgFJvC8M{R%EQ{NY$imbm2pCgD2ZGgkz)Yw}UNMZo
zj1nknAHo9&Nzf<+P$(D?xmsitF(DCjbMq!ZP|pIuAwWSH*#prMxOS98rf0z<U?xQX
zfg+h33wsEI13=WCGNB(WhTuRiLpVftAi@Il2)&quk|At*He6eCr-+2yYNv4+V1*>a
zxd!Ycbi@J3qcH%2dkVyi>q^5AA_(S$5zJ5^6h#mbdSOo9Rq`C`MBGET)sq7$5a!Wi
zD|%8$P=u^v%-~^)kqWl%0FncN;A}pyw;o`Yg$Vra00YH#hwJLqJz=yJn2DLYS`0q+
zY~FbkoV7<9iM+dccgyOPmx*_(vlXHh5Fu0Xyv6<$sd1o6_SOtNwmU~ha}K7IB%&0?
zp%Hr621a+-@oE687X~b}K++<?SL6JPf0%Cd>iQXtZU0GVzaH`rpFO)Sr%bf{EKYBZ
z@7`^vMz{CHL5|BG-n={J@i%WijDWX~R(AW%xVzl!xojq48Y5p_*<pV3H@`~bbq2`$
z?V^;*Vc3s0&!l=-9w49xCT(?wyZ7&HO}pox!n?oG;LSkm%Juk^zxYvF{R5XgYU|OP
zv!3d@ZgxXmx;{froQCa~wzC~Z$hFS{=I)9jgE%=#$rLj8l?Gb-f~%g|u%3SPc=6`t
zt6#o<_rEN*ij_h<T=Fme;m4nSJYH<u_y6MiyG|i-`Pt?6M=w+9?Za2Dj@vyfb8G>2
z7s!whkcKp5bEP=AnhqO}mMq*eU2(zOl4t2rw#Tpk^v(bMe|YoTzQ5cJdTQ#)Z3VP?
zJgv>rxnNC6J&H(xKCN(m{II-xSC2v8-1e)72Y$81#}5zd<?SE8{dS(e95(ZI`|U$~
z{^ezjc{>j5R_CQJ{?W^A2bwnI!xjsmZP)X{!D0Yy)w{`dftz3+pe>HwEMqw9>bS))
zwymWzl`Gg5ObJ6A0|cXk8&e0=)#V9!FwhXALa~}X*>bMS$z!Ipy9kNRSnS;4c-K`d
zOF!W0_QN0F&(~M``OR_d@Z#n9jQ+U5zQnt;ub$7$nb_v8T_fQJv&3vDvPNC+SM*YZ
zainpizR)l+J8c0_J*9z%wJxr<qRDo<OH%*vodbkJ5O{|1`O9>F@Jt};nF;A!JGAa3
z;eyQT)0)Bq0H+*c?1NE4)0_jkf=crRD1mqE3|xz(%`Oi`%IHZ-3{9(IjHnNQC4q?^
z#{CwD5osmIR?@JV8iEMw8s_K@DH#ah(cnU`iS`D;jHKNIp-n_5PZz#+C`ptc8s)$g
zzVsy7xWllsd2w=K3j|ChdUc@yhMvLCfaWw|aQF(AJdJECIVFv5)zb#8nr{pe>3f5h
zNlpkQEF@HbEg4r06elIGf)}wK!aB${+9{GKARvRSq{b}JiwPD5CBSh<t##sz-Y{6Q
z0ipL6F3|%1Z0c?8iLiqW1U(P9K1RyID}X_D%^d?<<&+I-caRLWQUE(PY(01(VFh#Y
z&=^Z!3=yM*Go^yPqmY;EGDF`JgF_@-To`=jj+*8KlSAX&Jt**?u!Alb9>CN%3=E7k
zkdc?hki`#hHZ0uFYjR(Qk>*a4kQN@F#eCxM=n)K73T&AVryiUWc54b5Kr?WJXHir$
zH*k0GFjyBK25Avts*IaL0kgT&86brv7FFW8fzg>4#?%>%*OXXI%7qT#n?Wuna!>d`
zvw8}W#bL{FaM=Ki(AA-Z6h^euS>|DH=WVXnKTd6)33o$*SI_!2)3MKg{QH0T`oXE{
znI48AzQ6T%$Mk(Q*j?Blhd+I<ShCAdcEffYI8Qf2d3D3-;;X;;f4zTr_-y}tb92?Y
zUW~i<w||;meJW`{xYH#p6j2oH+|B3HoB6AcKl<hHn_nvswi(ivFZO-0GL6vB42i|K
zMk7j6w4|rQ-PQR9co{CoPk-{u_J`lxA0PL-oi)W604dhK5{{+#=`;>QH=`gcl9Dg$
zSoKe(eDvbwkI!HK&cR*mi|xhdKYspfyFb4D?P+l*xw)BM{^CcQONDpu^bTmsxgOhi
z8PF0`NCPAy<{IcS!E&}XfXvAf3@lEG$ui4+M3M8~|Lx!YpZ}-R`-YgyyevcZMMIpK
zDrPPE{rf&zH@DhOn=3w69|yjBb6Sj&%l$dN{^oV<YdoF*^ziym-_?$N{^~=fZ_m2P
zk9VJbHVx1*%k78baCKd_`*OPN`vIz=S}>)HO(thhiz)ON=Zh3a9}|dZuo!@at0S$E
zGvtPS#9enMv<<*L(c}P;1|l}^hyggDE+GY}I)*B@s`zwP5W3u{M(GDBX*r(XzkhdE
z$2`XAe!07I4LIE2I*TJ&JKvVH)Od#;8Dfbk1r9@Wg>W9mQG7l=)&s;K*pNG$>rgzb
z!Z?cSpspw9AnbJ+Qq7xs_$K+yK>cw!CZw7K41Ew1ujdEEV1*Ik#3BLOz>QkB27L(W
z#F9By>uz{zu`9kT%;M0YTm&pob9L*g6K@mX_IenuhmvB<y+?>zKyp2YNfS(od3z1T
zs2!vv!$k8rGLb?+3Nk`*>S`Nk)zUVqX(1#;&xk!J&>iyZD%dTBcW=?ihzwPXO|t6=
z1#vbaA#rl|7#Yv1=s?gN3BeL&p^8-2+OtqgZFMOUYc*qGa)VId+4Bx@&*q3QcwJL6
z9l}=w8NjHkGX)RS4yF{55f?SZC>l#{-AZYlvn`5f9->CMG}8;lC%W){F4lTicC0X@
z!9+HD__N#sxa9%sS$kCmH;AaB66dp^getpxPB>&a_uhI(X2&iJ(483QEV^NKZb4|s
zgdnL0(>?feXXR9Yk_9ky^9j?*NT6X1z>p9+rb<@8(W$|(#ip8pVoUSv2!<=rGfM|B
zjLkOgKG0^5%R{l$<FvTOYyo1XDX8Cnpi&ZxAcn4vlPtB#Hcx=-;t&ju9iz9z2?PRI
zJu<ro00$AtI(r__Dgv+7vk_}w(nQcpcyAn}q9bJO9RnaiE@@}GT?{V=USk8>XB%10
zw!8?q2QEs%uCAFtBO&H1(8SsRq~F(~Z`!G^wc4Q$SI_v9K{p7G^M|{`?|${w-@ZSj
zXD|EVxYpXe)Id!h`9dJ3k3YNm@}tAA-tC^f<VYOGlgs4|@%He$fAjF>gW)vKM>fyb
zUxJ^e{f;tu?}xhw%m^!LppX^jT)w#f?%%cNpI=^FzJ2|DFbXOym4*~yi}uz36GR|a
z$AUH1w!Gs%UVQOvH+@DM98%$&=T;Lj6Cr2oPHPW9=47E$*{<g!PeVT~3hZro^ZMVu
z+W+H3j2NTfFaC=!o=qe6xqf}_y_Q|R`p<rzo{#kK&HT9FI7DRKWj4cd1H1w0kggCh
z(X0)76Q9md$<rjsp=^CK@MK)_!{7eTfAfF(H>W!u&`dS5&&OEL5BH53;qqqchNn61
zkx|6M-@dKPvL075HlsDnG=BYk`u@Ya+BmRu7^=}%k4-?booRj=`fl@=zr6qUn|Ar^
z>Z83(BgX}*BX!Ot*c=PhGXRi~(};L-6>JwoCz5SMSUVDp1OUx!m<SBJQrQ70m_Tzx
zMspw+8k{+t!GXiOitDM@gE#N}1PoZy{p+tMo(^Aq^X~QC(}5Q}M>MFopoADTrT}Hv
zp0RlpVUy%$ltkJRM#>#Ek-@-E@3b{X@F8IhY1Y^%&MRDVE~x@rwKdX>A9+?DcTG_?
zR7#?^{_Oe1`==Thaq4^n!!U3j+j*H?%0}w>WEKD#PANn0r%WXbJsGJ8W37S83Bx*s
zpOm@v?wv77=d?zT8Z4Bjfr`d(-JU)egbb5W#o8q8fgS+%#=?|>P|9E{V(MP@)K2b(
znh{ULd*~0EuCShrfQelY9GT2x0ISH}vZTc<OSbL?fq`;H*_s5kg}iGkmRJD^TJRPj
zEUAS1*5jls*=is;E4y@u5zQzEp+G980Mc_kuYDj|0H~#4t%i<VlBvqb0Br6$N16f*
zt%j1JB&lwoQQX69HA!oLgjOl&HAOzP;B-JKU2{rvrHR()x=L>+00;(mNVRfA%OgF0
z5Grn!9Bm|DAV7>NGNw8kg{MqjyOcN_V8~L}N(HoWNi-xuN?m-z!Oe*qBms?clQHvz
zy|Lt(Iyo8>vRMnpyt5SCW>Dfzjt%HJ*#nqE4(uIu6nop%VFjqSpdS6mD5|Ew>re=3
zAhU->1q*ubjUpuuOqslM2gAp95J@5t2wf6tR|U$>0uJE7U{R8vEXbL0Ib(>YqTK_%
zOeken><%ie<des2hJC}<tN~wM$(Nt!G6~FOkRiVs;NR9|VrH@v(q*!WOOKS%5jwly
zk#{cH0v0OkQ=1R{@q-*c)SH{fi)H7Rc)WvK`r<(EANn%h-X9IU!%5FMZ5LOPxWID!
z{h#Rivy1DSF$tGT@%!oO`SjVd_V(fN+jq+4V!IJcH0`&~pFMr^R-iBmdV-EbI8A%#
zjd(9v6Yg~G$M3$q`uMY_`^Pz$RM%Fuj3k0mxG8{QXGL;rAr~5!*G_NOXFr*4J`u|Q
zh5X&G?;c<GMVr-}6o{peZ1?SaAV7~Jfc4Y4M<80H{bN1Ghrbgb9Lit*<k`nR{q$xW
z*RTKdIFEt&;zutxKlqICFuy%ex(Rnkijg31un;G<M1Ziy6Zz<^8Yl%q$#{7}mppll
z9eKR{|NTGz_W$y$cehc>#WvZ}<NeWe?iGjI-u&$M>r+Z{tkA)P0(z}h1j8BxWXJ?r
z0U!V$mefhDuHXTTX02<gZr!4NvpziiW*FMbFK>3wE(jg`isxfO^t!-q$aU)2u{X{M
zphp)d#chek5Y=EHOd;eFY0PYHBM=IB_mU9?00bx@1UMrDz=TwzW~0T|-p<Q%KF&}2
z{{8*^(-Pf(`}*wM`u$U@U`Qp=LPKl_&W1gJq<Tk)wmR>}Xw@>U=Q)EMFf+nt8fjHP
ze0;a|?l$JcX<1FJAx_a3qTuj^R)CiC85m%xT&{-w16{o6^@xp?W89Yu#@i34%-|+{
zULddBGYL|ywBb0|kfm0Y2Fl0`?pB7at?Oz5)|n~Rh8IJMO^@ycMZI_9bpa{Eei}1F
zbXa51?@x%-taY3ciP!*5oZNUA1$!7e@d_>uiIxve60(nePXyo#B_u!jP^d3K=rt(9
zBm$aj_gEJ(ap~+UK*oFmPwqpA(fSlT01hTX)(RWoR@|H|0cmh*-XpU|GpEt4G3bD-
zZ4y~DGNCo>jZkVN4tEOc1Rf?A$xa|E*aYDr$>S7s1A6pGkOx~-l?^44sz<T2!LHlc
zfqN9P01VA6-l_~k)yOA}k=A2NyYM841^`YwEUO!%R-g^g*>xa_#);6J!=(l=8_vh>
zDf(`c*TnS@0U-%ceQwA@t&IzqdLL=%+GUdi7&Gbu!x(whmQzYV5V$gIk=+B5uLgii
zid3MCsHXr5TRAXdbU3>Y5RV!=hhx|ZB#r>q!y;OX0wrDc`!|aLrJP-HOobssQV3dF
zDM)DwG)z8tM3NvyPUuF679Q&4JzO%Z;1MiA4!A*>5m!tDrD)Bg-4pv9<lgJb8KR1V
zGmM+z)qcGBDCsw8zvCah%zT{xA9v#CJnbQG$UA}rIp8J02TB8IAwMclj5`of@MXMt
z#O1c1-rvgp?$`M7C%%3D?)6_k)nNBqqeOhCA&^hCm)-dCqmOQ$z1-fs@I#&N-i2{t
z^dV25eU>@S5BKBlW;}NS@Z{z4!lEtnlAlYSMo1Y(4o?clh*D7NdZ02}Pv5`&>iOl5
zUcP$uhxgx0%19{{%QgX+5P6Vn2bh=O;HU4EF3(F|&);4A_U}IW@-J>a&cpD(oBs6M
zci;To?9|ONp)Xb+Z@F9BCe{QZy)PI<(>86U_4RoUOmO((;>8dC(HAfFS9Sg4v06mz
zK7G0U@y|BbyY}5{gdx~*8eKaOa0Ud|L>P>uSVe7iHExU^$V^prJH|E%<afXN@Ba0F
z`^Tq4OcHTA-=T#mx*uY&b~G&&p{qo5aCee!8j6Hn=kAg`Ak7QqB-7PAf51&fELgm3
zZsyl-A|=qJk|*B0Tl{u?yx5I5*SEMNANHw8G!0K2iamRKBJ$W>(R`%Kt566%GE88q
zSlG|O8NIVIow{!fX%p%+3sQ@efB@MD8PXU?QWtkMZKl<ar}Z#Dy*nHZ%j<9MfA`&D
zNtUyF+_SJt#!%EoB&g;H78KUdDQDIVn`@%d+f2;vSXV{p(I3)o8yM$CixbtAwnVN;
zI4r=ZNhHiCLm)6iz+~1#A}=QvPItD%Nf6K0S#{u4>r#}~Mgwe~ykc!L^l=zgm^+No
zTV;vfoe3<wgL;qPbbXWTK)!J>_XdvP+MqegfH4w{;w(IE(geNLG=MKMCOvtJlECvs
z<s?!8i3kWHoFIX^p<kiiVOo(l)Sr-e)f6e^YKj|JSmMd3kW8knFHb}XhRn5xH`wIR
z3DKb~2n=b2n1E*Pj1U?t1`~|Y&(0&^F>r&lIM$&sbnijPL>Y1m!!f)EgcgC0V&1U{
zk>w52Z5Z^(pu~t6%`gQdWld`^HS2)6XHKRoctg2b8=0&~k|`j%38GT&O+#^!89Q4J
z=#DIPWmIxxN0z8ry+^o0KOa5vEUIM)%Fw};a7L)u*B(i_6jHT*R?3>ER$*B{psdK&
zytif@LmkM$|Nj)>SF>hYb{N=Ax7OO`Gson~lbJWKzvT@8f-O2lw<4sD=+LWP6#8`v
zy~u^w4^l^nWH*U!0w4eaRmCcluj})sKjw3L?`7tkDvgod5%q)#O$eBn6Ig?iQ3XQc
zrGW{gHL5CsAyTUV18D+pZWQ7Ks+=oX17AX^lsW>Z6x6XXAPIngR}U*=@s=wAN^tEK
z$mRuCXn;UjbHzhF^_ZKMsV&VkC6|F~(-<odqh)8|V4!(MM6=W_2H<8+$cdUx=8&PQ
zvKW*qUeWu|Z&%~h(=pzs_4>&_4Ie%F;vM}%x~b~UbrCIY4aj_QSZ>JzG(!dq11cI}
zfv}X}@bdo6)Lu`^@1NcMkH4RP@~by*pWj)3<$%q!s_!$p0?^Qn-PMD4-udLu9)9?7
z--K%+YzSt4Gi)w7hUb6#7x%Zvc=<<<x)DORN@ox5zWKdL>{gGZ-$0;9Vzru59K<Vt
zLoK{cU3qKwx1Xo;Czr!HC^>;1j&ZwADvMT^UTbx511(6PZMWP~TRr>LFOKtG8%Ww+
zfA{0>{K>z#I)7-@mU+pltwK(MAQB<<U6M{)%XQIXX{JsZyY7>Z&fa-64jVfDmuL51
zzuj)T2OmG^Ke(XzbG&(u>Y|JL(fovQv<ldmK#d|esjE4%MC7FngvoTkFsR%3`Zxdn
zC;!jiEk{G;WpRkO9NPVTdp);(gQdo5m59A@qek6EL0dK;2eJm(3_;66>$do*9I;yu
ziLc&!cl%(p<4x!iyH@tlo$W0jq}D#Y-A%vw<(uFA?2G#^e={Gm55j!_fvT+yD{L8-
z0?2gthNwBMARm+na)dk?v3oKHi|9CjM@wS8I|4z^{gym{5V*5f52-aY)iy7O+xhJu
zKL70JU;OOXZ-4n5Zl#+c%}^l?OW$eg%rOsLw_0Q0RZQsONZdhzh^2`!>Ox_yo3&Z4
zhDwMb_-Q%5xK{&=+O0co3K1kk(1ivgg4w2WSO~D%lBWi<lCZWu5i|)+35PQ+i}m61
z`iz)ctEjCUizG)<=>in>CS|HfQoUkGRI3MV*rF483w7bTLUWy@hAPxtG4h6iu-SSQ
zMR*u6ffpW(ykNnaF^&=`3|*g`V<$s!49bMkdoWl?s*vvq*PsibIgPa-cnstX8yZWz
zud#QpOKEOoFklLP*joV~&TKw`qN6%=)+(0b%j8yBC#=n=XLM^B2+aW1n$Lvjv^Mu<
zq6ppqt*RQxOaV=qOr3g5gdurD#$roE^GF@zQNbL<eNxR(wPLiQp4n<d^nmElD-`Gk
z5(1ClftnF9kt>)px|9?KaYPIfkQs`x<ZJ*SR*eMElnLB(vxZJ0Rtmww6vVRD=n=g_
z)Up&H@^YNi)Hp!33M9dB=oaA!v}BWE=-eDp*%2`2qjf7>W?T)dju=dj)*-Y>9YD?z
z-%?p&D3Aq0B^xLnYEFRSoSQK#H)O00L#O71e`Hn2m!|E$<c5mM9%7jH$JmWM!^|Mn
zmkKP-O*wifUbDH7&lZDIEe^<L4rWAF>+FDxX7$K27zG!_2CZdK={KHFPQJ@Vv;t<L
zFuwD2{iA=G9v8a%=F#`Mi}k_x{^rwT%fcAJ!7W302mDR|3_GAg-oTT$RwdP}(>&kT
zbX(4=?~b0n-o5pPn~UlBt5<f~-|bH`cB*8~7$6E<KK%IM(?{nQJ=v-Hvh41r<+dz$
zPu~4*_w<7|zx?~p|I24`@!;L_b4b+LCbPgiO{dc^O5AK$PcDvs_+}|fvpTD-yVVkq
z<EAY;cQ3X1zP@?$+v_Jk#8+S39q&2CcySfifv3jCKnBsd`GOU2UUg}j?n{Hu|NbwZ
z{^(D(+v{Y+*TYBYze-{A^%uXc04)$MMvHnv<V8!-?dUBmOCGr+@sHoXc>jmj*V}&g
z@>g%)+5-K9cOP%Q`(Y<qzWK@_x+{AEh(dxmLOR$mxc4D&Q$wf>0h$4AJE(|9jid5<
z|Jh&v{QvR!;SNh-pkkDkS$BfgVs0&$$ys8EV98-JN^Y~E;1KHR)Vz`gOO|PfSXx@2
zfB0_Oz4`e4vy1D8%X~GRy?XoN@OIwkiAtT+!#b?Xas=e(7U6kDTi&7l>cZ-<k>TLJ
zmRyWGis0tJog<Mdbb-PJwmXA?O7=J)127=$(H<dGbRg+5Ex;L52MiAEoS+_{$5YeY
zi`#Gh@ypMC^7YqqTxyw|u<uTDbCHvAl1RrTV!B)n+ii#GY=5`k9f~zN&RT6j3}PAs
z!T^w~$w<LCvt?UaYayu~Lk-&0*-)+I9J`GJ1|q4X=+e|cf>-6p#1vdPl>1wwPGun+
z%3QqeH`}vQy^H-&>pY#1y||f`%8J#qBOsAMg%pB2X;#Fz+LrwRqq}$MJT0?YHKtg!
zMvAM1<A7a6cEqNN3qWNJXgae84<y8)I-g^k7*Jb*K<?-f%@$Hq0J5e9-Lrc_D!4cV
z^g1gJh=wV0&5^Uh2r3+wmezROaUU@RJ<WuRaw6=-PYsg+LcmB4utI50LfIi!t^;_3
zI(Y{G<`pQZ)?T<FM{fYus0PQKXjF_=joYGTn9gc7l$8`SS^zdI-fC`+2F_HewpNJ|
zy)`aoU5Crmyko|!D1O3BOX}H-8<g0hl-2?RpaTi=|Ka_Q0nHnLH4sE&Z%eHf0ib3y
zr9`e)nmRCoy8u+xKI-YnDJmFMZ|aVs>d`y`$w<hl6P17|L=s0+2=0kvw+1|-i~tHO
zXa%skR)B?E%!9Xp1PX;=c4PpxHlbJ4+5o}T)nUr{G&PG&O^@Z&tSVNmIHXh^q4gLr
zGPp4baZ1?SF$4sm%*?hRN2dze4Ao<YwXph}h1{G3oPdzb4KOl*d4T{}{Itx9ZY0ig
zqpG&O#z+7A|N4V}q9fjgWx9G=$#b3dxBu~*W2JEwTpBB)R<MS#1D`1pi4c`j#{Dtx
z8csAUW4}M$-@JVO?92OR@$rLIN_F@A^v!L)U)of$VKBoeGH%yD{BQret9Q@a?d$!k
zZ(e`(a`)`TZr(lo@H<!E`N69%{^QH9UrjA|E8W-=25X1ma%)GPrdvtt>mPsA4>Y~J
zxx4x1{!ko>w$_d3<Ip5s*1SI!q?kBZ8n<Vg2m9&u>U#a~`rC(RpT2%`;OJ=>gE%?5
zHtwT)3lbYqsW$CiZyvl8Qt$bw@zf7b&xeg6;G7#aB@kk*<z%jfkq9dxmIr66Kl`)y
zK7RVByYM%E`D}l(haWz=`tf(ichBMYrM^-K7)hL{;g%#>zp#xsF!s?guuqnSM4=eU
z<h>arN~`^6fAtUl=byg%=GapbF`wIHP7!w}o4i&Cn1U*jX1C@B82d8kgl;XPaUm&T
z#Ym`{co-!G@+S|^-u?3*pMU>5tIgT)_(`k_+HA{`v$LamiDb>Z5KjtCV%K*g?h-Q#
zc`cv-P6D%_c^pM6T60D4LLN~BbQW(w1DRqsQV$Rlz>1`!WW)(z0Bf88vAULJKkq*M
z=C}XTPyhOFUOmexGf$OsBX6K70EE<XXWfuiwVWP3esuk3|73msU`TC9(ND`X<!WwP
zn}GneWs&8CX^o`;0x;$bi&pbUqeNB>Ypd3<BL=1v$X5@xy~W%6W!u|FKl;|lOSo98
z<FWbuSI>{FSdn>}?{D^9-<?iJ9zw^_xi&4av70p@Flz>`&M4Lp(YZEt7b0&h^kQL%
z66@*SOfYoNJdvjq&Ng&;_29w7^?BkjXvkH8u<t7hmA!;9akVIvKs*~X1oCEt%~+r@
z1STzL5mTgAklCHo(VdYHy+U=3eSoUiAxc{s3bdI}7BpAtA#_Lu^PqM@B-cRkgw!Fc
zD!W-wYNC#bps5ovGoochu+SQI1Aw}RAPUZgM4i`}JX8lNWEGhK%u96va%2FgYC`6Y
zY$)vN9u2V}Ig=S6Vj%>KrJ)P8j7|_(0SOqino&YGs=QzhRr&w)@ehQ#^<!8vI$8{l
z9wj&nU<tAT1Z^rnDj>wx+<+QJU))pDq6I}k)D2O{HEmMD8qXi}u*4`t4D1XDy##bL
z_QV*4umTHMbVq_7Ven7`1@r*OAla+~il_>j+_NfB6?cP@XYJ5h&7~|QH?IpFYKchb
zz7&mwiI}A^hUmZqf5h7rIzz*NN?jB!=pl9PixY)RXy6)=)gT3mj7vo`^9I~4wQ@0U
zYMV|KxPn(hh{*ZFkK+gbyC1Cc)NXcz)b4?MJMKSwdGpf=qf<D;6u_GIod7{l?^gmw
z4%2DA`KG@5>MdV9h@$ZN`SONwsk<4UeDv->$Im~%zngM(RNGJ*Top~v&eM1Q>|b90
z=-ThzoL=01`m0~;?oM+%rVk#x`{)1b`Q<PF@V7tTy*|wdYL!9PV<PF|YFno{0E}0g
z)wi#(`0eL^`0UreE44@ib%>EBxE<pKQJMC47{wMX`L*VQ&P(j^!FPX@pMCb`?uGCg
z^K4ByG)JMThy+%&HE}SsyxfJZwmW<I<>wwxuJrW#KRUm7e0X)&@Mz$uI1?v?qM&Xu
zwLkm#!*Bny&GqW?{<B}ddBLT(t8agB^~3iCbN%`=l&&_Vp@Y<$A1U<IsKEiohfV{a
z>jD&_VKGZ&RS{quPtShwv;XVQUp~tnhgA?WkdAZe%3;1gO@c{9KpQYR8b|67mRz&~
z^$8mT%Uqm;M)!vwJq?PR!5_Wz;Od9(Kl=E=qfai^kDqjXlGQp&->){h+vQRg18Y{I
zjAp^yv-YvA)^X?=Xa(g2TGfFOp&9Z>Sg;mLmqAb786as~3C$Q2upuz=f-+DDxQU2B
zaiC~GjO=P{&UyFMm!JMGzx~Hw?4K8I*d|Dtt0HJ_;K1W~4|@6Z^6?M<+tsR`_PdY%
zxBq+`Irg2PcSL0=`=Yfj?xL+2QwXC&C9O!S;<dz1t7^#s+&L;JIgquo=)kI8FVD~N
zexFZuT*G^R@>Ehv>s}A1y34m;-yN5`)y3m{JXsWJ5jY2?i|rP$%?BgI#)XWl7F6&M
zu@;QZ9Gws)HZ^3(3W2<~qQ=TdjdRl|QG_0xtsh>Gt99286cVxZDu!9XJR!>YHYP_#
z4h4`Ty0!rB&dFTB|0p0eH**#TWKwWIcDCS{Q4z&B0}vnvYb6jXA+cjyIW=e~P%W!_
za~8lRp;cO;)n;8><{Ho|Q7eFg>O##xz@3<h%mW}8t2kyW1W>_}mzmOl#Ml<Y4e1fp
zfk(m#5gh_J)aKd@&}CVGMzjNPhjjtdMxB{*rBWuxKEkPaOsIuos1=Av>ry0jrj6Z!
zBuZ`YM~o9DF=RCnolQwFH#JS!l-;;O168PQLK>Sac?s_OJ@kEuL2piiBw)^tOQS$|
zyoar~v<`4%bma-r+?^~XgazT~KB6|V0Fj|qq-s8RD+UF5B_&pL^a@6Z=;}@z^rM+M
z1PNu%gbc()=o)LFuAU|_?1zEOO;kJ)8W6IgcEX1V9I<or5F8XDu~`w2O6XBRiC4><
zyAFk5wgxSA$q8GoQHb2E(Q?Q^2{;nfT9KI6LCA5c(n^ghfVxayy}kRXw$M`mM}Vdx
ztf&x$^~CBxL)Ce?PhY>>-{<tb`E-`;COxEK_xf#JosF!=Z=T(syg#)ilWx|GhK#-A
z{@w5W`0|s-^Y4E;ztFFK_S3_`Wq|Q)_0jkLnb_Tn-~MKI$mqj#H}i;X?yT1M_`R;{
zyP{K{D75zKWn1Fz*;lFWXE2VLrwp}9rqaLmmQe}H47{HAx!_KPPdT4X-;595L;M4y
z34(`!9wRRWt&gMWLJg4I>T>tu^|HGS3WwdKZ>V3L&GdYo9>4efZ+-K-Wh$j&1H)_%
zu94tjT7UH6_%Lk>-5q{E<=kIgpI?0I1MVPy{g3rDqKqk3O&vi1bwgAc%+@IdWYuaY
z;N^y8z<zdQvot^Z$4~$7fAjifK3idm?8auLE?&zr=K|1Atx+>vts}N!z>Do@JH35-
z--*Q0qsW^>t|0Si&QYIiyRceM&z|RUOLKqOzqgKO$ljMRy#JuT7>6aY)$`9^eD?AN
zh@Y%i56=6pz<!zw7&XeJb;?l88dy`M3VjNqP(mF=YH9193K?f`uq3V)LNuIt?r0S$
zR8q&ZW?>aXS^&*zJ>5*t{_f|$`?B24Mu5cz%@a669HcIj!Fq8}3R^$>(=+<=&wu$p
z|M$J*VRb&6N>kbIueR|p`MowYa%5i`gd_O?Xb!VbDB1{s)~i;lSG9<t$-Wg5BSj?^
zZ;P`k?oS1#LtIAyLsCLA_g<p68n)-*?W-c0t#7z!^yABgypls)cX}|Dz^ahd&{+~R
zLvci<ApjD!R<nYG0pj8rj;sR^jvLuL=!OW9SfrzNUs7muZP>dtB#8N7T`!>oWA)DR
zy?9@7ArNe}GDV#-g@6+yG^>hW8qu9(W{<TKuXiC~(}-5BDRKi3<WOUi5>&{wIRZsY
znj2E#K^Np)C0U^uG&>a#a7Pc&)EfxeJ`fo&qTrIXLxbQE0u0a=L$3zlg<Y^!f`G_|
z6H;<->IHx@axev*vk2CS6Z>3{u|fo`21R`hqv4Wk0kXVj6v6^wsp{6HU{ete4%Uu~
z3vh!!3Z!9^Ec@=%kenGsa6z{P!nuiw1&WrzoD~*yQpoC*VD?(G3tDc9*oix<i-h0-
z1ea4wOtN<0iIa2Brh)}p=Z4TnvpZidAx4Z1^I$}%g&KPph%&ZnWD3bSVLdh>pEJms
z=X+||tT|$Jqou*Ac?6_@dBzlh9TK2}7@BzT!~+cI#Yza#mx<AdxCQ1zt}S!yuvF`l
zhsg7(c}y`WI~urYc8SoID8Xm+P`nuhU^NYOx?fH&sXlEbz`8BRe)qfe(fXxdR&gO;
z9J#?5%7VQZb!g^L4f|Q~RBUy%#j#sR$5gtn_WRx3bcQ9i77lrCQ6%<bT>Db%aJYK^
zpPqm8?&0&F-M)Hxd#k7WT%wQCz4L>A86&>@$uC~rO+YdOuG4^MUEjqqt{+^-JnfMI
zZJMU>^vIX8dwVx$l3EO^i}#;Ap1yo_czbJsn>Wm!u5dninM{oRIJafqyz}Vt>SFh1
zANoNLM?k1noRiJjt*(dh1n!W@LcN<CwrosC%ctE<+|21MoQ~E#T#d`_<%>k41umsv
zDBpj7b@uUkmD1~9e}1#zxCxsd{>k{x8SKB&yBRhk0t0VYNJ~XZKmif>Vof1gs{n|u
z?nF}2vc>E2=Cfb@>;LfbdAdMPF;r_gmwJNx<#emy!!SFLdhih7xL&UwoUi-TH7Ma&
z`XO9gtgv<Wr{`eVfScQkPe1?ollL9c?|w7g?LU40pM0{Gz-D384UenKbY64%lkdIm
zdthGfXAS!&?|mE@$FHC3G0*edpO3XH*qTz$xHKm6PF<lbJPRbi*?9Fx=VuPh@r(%F
zjd`STKtfCby8)S8h+r{xvoh`V^*{df<?m1Vw#^DEh(XWCv$9MF(}GB5p+C{g_04U{
zFT1qqVzm1#?_G8EVC$`3UTyZv=G0ue+hvg)AzE%Cu!QDKs>cA$T>zGhQ5?OQwjiY9
z=w&+$V+5k1qc=eenp<ZaVQFg7=Q|1Ohpb;5?k}!Cx_$PxNjvNN+kJ_tGe;wC48C7l
z!{}H`b)tY&iXm4xxol%dk`E<}DO>jJg4~#uC4|r?+Vo?jK6WAXg4oOhw-%g;Y@)o9
zXp}HcL{SZ)9Wiy_g{*5eM|8Kqg3=6SXjU|ha40RP#Atw2K-AFs=77%DDPRC72x3|V
zDh_MX3^0h@TayN^97D+ntRW%0TLWVk3)YGg5V`jnLr_r^slLn^eKf6T9I$}Z1{*SF
zKTg(11lJb&HY-sj!K^{ZLs<w2oTNB2_6jGj31amIrUhM%0<?X|*l0vjz!GS5tcx{B
zqfI9TR%q6k5r9Pq8U9F{*J;w+4dXmbnh+3#Jy0xKLdz{AR}cVitfs1s9SzWut0Pr&
zZ_Zr-OB{ParBGw+K*5CDJ#=W%$%~^X1EafTL2PMl?o^vDjqxqu7D!FN>K(^qxWX=g
zEp98Ai`4*W<%h{AATG!PbIs1)Z?H|VPRzuSO`sz-wc1)(cgWC#ky>W9qErr)B%`b}
zo0*Du1s6l7{cK5f4k<9{lASxR$0QL|87Nh?fXZqWooX}B$NJmP{QJLnx%%D}bg@ia
zhI9S+OdjOTS1)Y;)(|lD=H#-*c5+iN0kp=Y+PWX*;Z^tGd>OsnzkuI-cK7w&{r8?c
zl>~P$_P*NisX(sXW$Mn>>FmAD(?{j=-{1b>^Vj=@m>R+Ma=g5Hx9g{`fA+I7qZdJx
zP|eW#Zb)k^lZAm`Y4c$(EKPTGwuP5B*0>9xUDsyPR+i7dL34F%_u0TW#F)!m6;lAS
z`IwKd-d=s@<JCCK`};T~cAa*W9UD@$7Ghs(V+K>^i%3VU9I?#~QnBccTT+;7J}hKa
z*4uX<{UAVhxcxNo^yIsb9z{9be6jobo(eqp-n-X-`j~?2Y46@KF%3m404MH(B2~c9
zulk-59E{v2XOVtO<#is$ynFri|NAfAd<vV92k6VPEH&gq-A&Umt~#~c6qP!XUIGe_
z#M>UjdaO!aYsK+wb(xXgv4{1`J8M<g&sS;v_@i?BTP$#D3mir=Vgqg-cmz5IkB`3d
z&cKik_b&>jTDCWzUj6txr*?Mt<qJEVU|MS;tU;k!o2jn&Vu0m9A}+z9kL|AIBeqTO
zN?oN|slP&T3y~wD5sBlF2%908LOIgzSO4(hH}^TCF^2>v^X)1H(j=G*d1+Hy*m@A#
z7kT#gKilH=)KsUI2xS1m4d2m(r)^?gw8U7@lp{ZR)V_G;D1a^zy@6vWR;fAmK_<sY
z89|Kcb^^8d{wHr=f2ro!YE`Of@JdZYS7+(`H2n5Aw7zP287@|JcRbp%-mK<nuSy`M
zxfzgpQ&kRPwKajTT5<Arzq6y&d;|$ziZ8WksGOa<NQgvTA7eMK*AF335H&RcXr<J?
zgVQ9bGt<~3AwVabO9OHP%tePtO&V0hiW&vgTwU3<c<e}v*NLn%wxE+jhh7^J0hu-L
z2Fwi@(Hu{Ej7T<{_2i4Efefk|f`S98F$FNh)|5KSQ)u9$gnS1U96^Z~6+N(71Aqo*
zTqTH|%zJe(n+&>=F)#vnbE~Ba8A2yMxoksjC=J3IS~d?z6(Bl+S2saliqV0H8ubQb
zO97(R8YAR_01oU*jHna?#()Tc&4Y6@2#$%8EQ<xIjuOx*bRyIU$bf@8G-T))*`s<a
z>VRg^sf8H8#F}wc0#OPeWT>tyaAGLtfGUp7yRJH7HPVSHGO?$Q;2vW@zlD`}CGeTr
zNka$Pj2SyZDNfCuPzAxU*Oj}K1EMsCW-Xh>+8QWX1(8-MOc@zDbUx)FrVzw|lQtJY
zAdb<8PMbPWpE?wb#?n#Z;10~G2E<|QF=zmV04$K0v+wSfXD9n=@3(igo?!m+`5%7$
zcW>YREv;_OKiZDp{;2=>`*d+dGQc10?=g&k=Vqw^X<O-Z+k1U*&OOZggZ}RG+s|%~
zrQ&bD`s%m8{&L9+OeHY)DRe{s!4JOw@WEDZzBzpT=GE=}JXLQ=&fxOuJFD^ho1gw-
zy4iQMU5%G9+S#}sjMiPJ`%@JOA>KcG^XBF=@7JMUaTG#YZ3eIq5M2loU{xT*7`!9w
zbh{ezeu6IP(zIC1dV2LmX?5t&gol-ku#_Q6ZBXjci~xOEN<$6nEjM?~Iu~|OpJ^Nh
zjCIQMRBJmlv<mk6{)^_jvx~D2zW=SW$Cudg{b%KrsekX{{F6tkRgl9MdT(IS75#2+
z9z0m_6C~o$%Zju?ZLT_@RSE*U&)s=l?DjwYPcJ^r7w6me2N`2H?_x+EF4r<_q-N5J
zwGDk?MJT5SQJ6}kuv*9Q!RGS%OiRtEl4!j?d;jtJyAS(!uP-kiUnzV;1)5Li53lvz
z4?RR>YeNTX_AZ1zoj>fa-@kn4gX_x+HtLTWaN2e2Fpg3@)hVkk?)zdzF=lD{=Cz*0
zntE&6s=9bR0#XYM=m0`CF%L-1xdDJTAT0pJ9GdB2e|!78Q{C$+TUy6Q53ho=<#utt
zIXgeQ5~S8vk<SKt{CL}go!-u`KYfj6ZP+NbXwd7iXWk55A3gL#@7|V^8K&3Is+QU&
zD6QV?J<n{OwUI;H9lhuD;p6o21BfhR3Zo?O<{6>H)VJ0s0CT}8$U2Prt63%l5bg#X
z0wlNLavc!!JYg;}NULZV&`_gBCJ1cHeZAeO0(aIRfO^dZV^Bh-KDvvHK`dz=;))Xu
z>?~cN(xmgiyxi%yA$25_Qe6d3V4RGSY2SijV-R+Z?%b#Y3m98VPl1SeLGD070XVx-
z0G~YeWG7r^afZBe_o`71oP=v}XJj!Bs7Gy$SlJX=i+5<PY60A+p-{k(y{v;0Mn@7g
zMIV&48nK=%^~FKj?3Ai#4`5+yu9e2(&_LAz4BHZc9h6N;*}RfhWm^`Nf_+o7+|(9V
zMo{41FsUw3Pi){i`Ef!fs~Hi!>&6~e8xgMte}o-+)qtVvY&y1Du<wIA=E+iEvSpe{
zKmpCrkrU|5fjGMXIY^M=RTW!R?`-A}sCZML2_-s>2#dpjf>H_wa~QE=1rY=w@F=d;
zTmYF6O`br0A>m9lL)l3K^hK~Mnmwx-H3DRl3T8=%9CT#crv;l513E+_Dg<G!G7!)}
zlcudLV??h^gt<WE>?laB&OHVgMa`jB1UKm$Ahr^;8WP8drmdAg!6~E3soMRies%N)
z(v#)QuuuJ~7u_HJZf^AY;-i23;eYed)zcq$Pu|bBTe{i%i%;wJvE`GnnxHq?pRx{{
z`)}^P{LSxT*FOKkKfAG;Dc4A#fkOmOtu5oaAN&5@Kl^dOc6xPl-0kiT*(_<AX&atA
z_+c1ixp`G5s-5^4wqrWXx6ld=;*O;)o3jUcIts;2e?8sa_3sSv;{5p4t9e<V&PzjQ
z#}*W71SB<y<D_DdF%c~@3L`n;cD~Pt!;7ci`Tp_#P|G1(v!*!W=zWFikf{@|xzec{
zN7Nv?<fcpr_w$Koq@?WtJkCwYyv#Q~-+%DVlh|FRef;8!PoI4`onOCq^~rVrcw2Ta
z@>h3t3Vxi*-4Z#{m2x(UTj+z00F(^S(9Aljjp(Qp$=cih^zWblyl@y-nB&l6u{lt0
zC$1LGX*?hEzU^w8@8+fk<ZCncU<w`7bawIXQ?IJu{Pye1_a1N8+f|K@?86Ahy!*+^
z!(H8;b?<-YU#uQ)kpWGF7i7knJP~=Kt!y8!u3lcA4)e>~^62LN{Nj<V2i>>4KX~kh
zCSdA33Rz<T2P;dHMBEx*ggR+53L6bhf!q@!7$bWEVu<2E-b}6OX<44Vs>Sra#;!ZN
zx+)9rZ*C{Rei&1vz8}tqpi}KHW%Xc8&uBWd*ZZ5t?|#y6*Rf`7%B3<PuM(7<Ek?=4
zQz&IV6fi><V09M(w$?1Ap&gIfVN^Z*`VZMSu+*ZR1x7bO0XdY?rXy9Xq}+8q`O-(~
z2g_!blf)X+ZC*Br?gFjV5Z1`n;c<7U+MuE{3Iv6$F-Q}FD$IyV#HP3!(yU8JgVz!n
zWhLEeKr-yOYSGY#df(ujt$IF!Bui4o1EVo2lX$Ma1RA+nGa@$iVD21?HDbLpFQ6-u
z(gMk8r<lkAqj;+XI%Ai#wHhrTaR9qVIyX0J4(Nv3=px|GT54R$Oelk}u#%s^D@t$!
z_BR*;6jM~v1`x3xF?O?wP0%A?6stvs9_mb9i~`o`q>NP9EHl^wEg+$n%GA`vi5f)K
zMGQ(&M^xyb9)z2<hWV(m2S&1%oLv|N`|}lr6lDp4J5D0N08o84jvRuP1%r!KbHK(D
zs2nCZi*e?~6s$28<(ef>B%Rc)Ido=KtEPZq!O+i;2k1Mu3II$UwiEUVnl>|%!L`NK
z)M6Oal!T(w(Zg1BcF4p+ZSO=?D~f5o#CoUPsaHpF7StmFRicK@tgr{F8Jlp&BtljU
z8U^;L#x7`?yH4s5TXwHYi-L(m0BbZru`t$Ju?q&xvwKLwjj+K)+I3`zwy?G~rpT*V
z6+!df52toF+f*F|-`>I9Z~o@$yB~!2KAC><#qHa+(38z<kN-Ds*Z*wYF1KatwtAQ5
z+4qyv$o2K@et7n~moHzv`o-rb=@Tg&!>TEkvGZaXh#^?Qvv_uPdH&$)0nT^F-R^kz
z_SF}+BnUxLq_fK>((}u&ez%RQ%z?yNxj*kaS=G~lL*OLRr`7cp#6ZL4YIU5TTs*$+
z`!J!2^TOULS127yx9w$u>Q&o`a^taI%2GTSfi{b<Xb$=1*Zq_4k3)L<_6@LeN;T&=
z3^u*>EHbb*7O+5JcCB9fkd~s9Ska*_(1_O^Lo;pcVM#V#@o>KV`t<zn_ZiO4)Afcz
ztamy;e=!|Rus=bpW8(GDr!KC#)MH2@7)6U?AS;EHAkQAL!tNjb-PiwV2l4!Ty4#)&
z6vCJ&mWbgjrvucsAwGs>T>Sasr&3xRE5@Pkpp`b=rAOm<xxP9*`or_DU%q_p^%zrN
zUisbOXz^yJ{U&_xPyf~9fA!-{ACQy+20(W}$N*r}6i!3epRK#Av(0jv>rwe)={8+%
z26e%umVkjkd+-b`uwe5vNU1^rpaCF?3m>t+fQBHamd;4g5_2C}+yhtxWvUh1>soIQ
zEynZn5H2?6-FxbP_`_#q+6!-naa@DXV>ZS4?d%+C&646f-~IM_6`)RnRu!B(tJ?J;
z)fqD#qJ|<Z0Hb;H>YA8Ad8tctS)Z@<SaNH^T_bZ84y_-8^<pPz%wlZENjj#QncX-W
zCW=-jwUXUuGZ|xFRXYLiNA|YA+sSJ1B++8kIeShB1VJo8D#RnS3>ws$mR8-6U@4VL
z4N<z)c1U@%>c&(&jI!K10qL9}Xek{vmV}O~8ObQKH)IPIj_5rxRR{D;&h7!NT0S8q
zr+djs`?ZmAm#Q@`1p*jGb#NvW2aW1e!U#}MQj3IT5{v+QlmWc4TVWw?#w+s4niwPw
z#b-83kj<otM6(Uq-Wj~qM9ZOxiim0<k9k>&QR)RJh$&_}GKJzqO>3~=3@)+Af=8PZ
zCxzJKu3D7X>{K~oL(<s!oDt28)IB;C3USpTwl1MS31c4z<dQ`@0sxx<g|Vt5I0?DA
zM{Al>43ZY^9q!RVjFrt&7nh~fj1GkYshfI3B7<WJU9NRRMj$ii&{zUB!^S+gGV%ev
ztLWI7RU_~Q$XdBIhzfEJ_24iWUYQ;V8gq48dpj7pGn%4>Xj4HR*mvlT48jgWsI%g!
z$T~R~n2HmUu}V`HU$iL2NJz3&4cLhlig}qy6>DZe=|bV5ynWNHI*Ji%i_uhB#^~$r
zaDO;GJHDD`fOM*5FI;-pd71X`pa1gk@Q1%09{%~m_ujdDckDj=5pKs<n=-sPtRIVQ
z?x)LtD)(>w%~v|z+~2e>KEL^^pYLAGa{+<Epe(MDq)q1J5jcd%psO`Mdh(=~eE-=O
zr~S?TF!Q+5R-(yjSlMaDz=6(vlrT*UVJWv`c@Tu-*$U$-^&zgdY1Lz(e!bv+J+(I+
z7j3hJHKb9OxwX1Zaak%%2%Mm*htRLKGK!qKU@_a0xT~eMl6P-zhV9O5In764?+`Go
z2^NHi(MqX~6e8EtEG)%43xTj^Rd8b7fZPExat2G#+lT$|^gHjKt@!Sjx0&r?1iE-I
zJ$k1cpS9VfU*mS0y5klwUTvh#xNahAb3?XfggP5EcYsKthBrU?i|2nmdC^U5!zM@<
zqBseuY=-HyJ|8W{<#dqLx)x0H^KaU8oZi&((CfwHO{?p8dwaPV+SAJ>{_LCQuV25a
zEv3j_#e2SZeD(PKZ$121fAZw13^rkFM1*R@)tb}NU^>a^j`1`Mbaj26Us}T1y>wk?
z=lyb9AYunXNm}Y%_cEVoH5w*?U{HV@2%{El9i57IgVTmfgYy;k9rP<VKnGwz*Rnu<
zezTOeT5qB+DK$!=Jbmxo-Sqs`og-{6AJ7;>#1@9-_2F>XTg2~t_n%+?_>&Zjs#7I(
z_tU{DL(S$1+7y+2-PX9C?vD~sjo2(>Q|E+ir>Qo;j_PEMy(h6j`_(z9RWEEwBODe1
zWX~q7>=Df(^~ZeN7KNG!CpL}7%d!WpYPAvLRG~q!!}{WC(Wd)@1gW)<Ryi9ZVO|`x
znrRo(Y!F(u&{fnFqlb=Y1&qQ|l~LMKkenPtAL2~Z1)4D=^N74a45(^2T5E_66)h%P
zDw!#ZlQJ1VG*q1DQbDMNJ~WCn@10VV;1sMiq$~h#KtPHOTeA#ROnPWm(TjVO258dC
z4D5tv)rb}QNZ?w50a|v3TMeVBIeM!YB%bti7AaISKwt%|#DkhGK-k>G7$b?b;#O73
z!HnaObAvc(8_bwpD<D{>Y?x7-LWm-a<iZ$J?ZuE+F|-(%g9ZZJMuB1g5LnDwZPqaX
znju9$wSMbmah<5`8+Dj#YuRuOp;mP0I?$69K&<8%&8$n@Cj?`*T3YMJC<MBIt-ukN
z;zJK8KnVy)XNd^8C!7l<fM#$mI0GTVHJ1~lAjQ<#s)C}!BserPMr#zy{@@A~S)?IT
zP&D??iBCmGmPWb`O=F-5D27g0)XfPDTo}wN5;l&BB2p#KEd!!3jXh=qpOeIzD?M28
zMLInH`q>xl<*PDP<t}TGJewMZ%|;f3ZdjhldhHJ$JnFl4_2-Ax*>;%rexP|f#RvcO
za6PsiPFEp6)7O9X)4%;i%ImAVI0QAdkj7Sy4RVT6n-N9g5+OZ!_<k4WTI&6q`?@s8
zu{*nPgR)F5XMlU5r`-zn`yDU`6qxPA`v>oS(w0-|S1=!3THo{h&9l7ovg}&Jayapv
zV>iTM=$9~0bvZ3Dgt%I@!vP2qnUG+vlvZ}QO=$&fjxm%{mixPJe*O2$%`F+sSjpPh
zugkJ0B!xy*F$JCG)InQy^!8AyS88emwODA9Mr4TLO4Iz5?Aw3-@h1<q%hx}B^Xw3Z
z@Zg>6>py+kjSYUk)6`byAe}=*@*ZH%VZ#m%4pE%EEEWjWQ6!hvoqqFg0AoO$zhD2=
zeQSQP9uij|$1)3V2y`92#z0fS*iGDqNcRhsbts$en_Zc$S(ifB_bJJu0ChDi*N-2^
zus!bY_pc8Gz!#hCdbs}KpRT|6!Q&4%D>Ba}%>@<;2*qJ>u-V#4Zx*<3DZ#}>YIrg2
z-uPjb4TQL+aZ+@|A~85L6Y`EKnk+}$o&}#(2n{Mp03c`{4MQ;-C=p6>?4g^T*ed&!
z%c0H-l^o?<B6iUk*TaJc8<;-CH~riErhf6|xZM<tZJu(4K6Oui@Z+cd<j3cajG<x_
z6Ay0FjMiKmQ!b~HfrD4}dJqW-n|G`RB++aE;D$)Sv4IDUSRIuWg3Y=ZtwvbFn#g^!
z#Z-j6n*+4O68i>ItrU8SJVJI!QcDzK7Yo&^U~;G><(qv2h{I?GD2h%3SR4?lDG<Qa
z6oH^&4BoH3>#6SusQ@PMLE0_g2tD(nEGuFVR*wTvBO)}_YK|S~qT&QCEMg`|lhL3q
zPDLAVXAYg4bTK=3J}vC6hLM$o7_=ZHhslLn9vYZ9I=ITnGS|fb6NwuWlmZrr*j>@H
zP;VNzRQKYT00Xm$K?_RFD)pu%CR6FFsxm`u#DQ8f_e?!faT5kD-k2GR84*}1VGFsK
zR4Yu-fF<YP0m*AXN6oPA*-<)m4grU8m>IhO#yt~uAx%LN1{Ze)0PcdDAY`4FC=r9U
z;trJs8VLrWsd%7T$w9+Xiu7%6Y|O0Kf^(}RI5q5=cH-<7R$gaHYo-O5&;@8AcQY1o
zXyAb=3gP0!0jXILyG2c?Ym7T1LbXcCVHP6A6?-d~Fs-ySCPQ|wH9!}L;#6IX=3LVd
z2ZEAQ45|Qdf|$gcRI?CEQ4p-BnmVo>dZj*~%~Dx~Vl0z;^0r{2NKo0d=DD=PoBE5t
z-|e8!fZf?6o9<3WNHK5-m)rGb?5{rg$UA{cJb!Q&c5g0UOlx_u%&o1Tz@zWPrw>_g
zAyv@*SO56;|N7tWcj?L5c+LCU({yA?fr=YuZ2{Sk29W@M{qeUSY%ce2zENOUot<4@
zH1D{>`R$wIG@+G#waL=2w#{s9m{*&tP)&Uvw-E=fy4>B~ZLdEnb3X2m2$y%qS808v
zv!esA1E-ErdMS>S#+8<1Yidk2*NcsuUcGJ*hVxZDC<mftk#1b(X*teSL8Zf{p`(zz
zOi=0s79t`TQ_!5L)4Hf+o??FqfNgg$i_5$u6x#YW`ukVsPv3bE`S$r2bM529tvvqG
zI}fhJp8c%uChP(SpwvuJ!yMoY(x_Mf3EXQ{VfOMCIo8a_U;cl-`VR-N;c<Y3P{R=R
z#~_O8m>t%07#;<C(azWF*{Z7}<ncP3eRE1Hfmz1U!A3PmeS@$u>MzDVQ69$AIXDJ+
zupQr7Z9crdejFc!33Kp(nv9Tv6&e^AXw&C&xzD=0<6+x{!~Ea_|M{`zIV>%0`x-Ce
z?p~WT4Z7@N-!VC2gGi=}o6vCqacvXf89>H<>rwD5Q936lifGs)y4$`$u=x%7DFE5q
z`(@o1m-Fe(Iz;{Wtj5qS+x^Wx?=v~@&_BL@w0e4d@%;}Tyw|PEtyc507;tl__10hp
z5*DMdt^FJ;W>03#Env|gj3EsHp~`eR0fl%L$U>Qb$rv4EsLQdPCgU+zJKmRi`!;Tc
z073#tiTm@|UT7|Hj8)N*NoZ-)xan@*&SD|Y)uM$c<`9C^a^lvNIwcB~j|tI&xUmL{
zAUd_0h{)r6@3LqZQj7#8qf^5HY>Ii?asfPeM35E50u8*@21yXXGpe~M;;Quvk(*Lg
z5-?>X45lY&?ldb&wCwB-Mi$*?2ZT*uvx#-Nxc4er00}TrLd%YEL|*{Z9XLhvMcasE
zZbS1$Lb5n|4ctoBUT7+TLc<n<l?8{W+CpTgEog6bY5~lJa&Z7cAj)karNtpNX@bxi
zwN@AwZ*1z$9^0`vp~eE0Q$Y4^)kSSx2kspYdEiLc7z9>{Tc((}V?Yp41^~=OL4^aI
zg;JH8p|Ckus6YWr15QXrK!IwZgyzDnw#cN`GFp&OwW^z<tDgd;Nw_v{kpo!vDn9hc
zk;u>sXz!e%I*OR~7VZf*4N6dPoE!pbQ|~1>%9-cAmj%3X-M0ot;69=s38HAxOnE)h
zG{vyh0mJD4<7gzTC*gs(2D9qmt#U$Cz#g<)w_GHV%*PzMjsm)MQHoBvOIg5=hp%4h
zyq~60PV39tQwSk8Ilc4IyD|J|y1S#}eR#Zn@btmh4VMr5%ZIvY_x#?w(`SeM+d~k;
z2ci4%pTtLk`ptB|zxn;&{MG;E@BZOG)EhrP8@ls%ujhPUrUL-hxwtrlq&1QR3ViYK
z$>qhP(DT{&VLoP_@9%au)u+Q3_hPWjMx1?GovC}RtJPKNHAwV%p17ZmFFRl5*N5UN
z37xx!VZFY*x!XS+u7F2bJ+zxo`|<kXyp?>2n~OEH<H3re>yd|*W8qFLYrDAtkM&ez
z2rXCWIt9_PG_mz~HjE?XQp`gaLwAPDUAC$KfgMPU7&O*wswc!AY^luKvq$;<`THMk
z-~C^G>(M3O|Mtx!<M{?2e*1&VZ@tfq`K15?g%w-GY_{EUIcOL-0{GhCWImEjs`pAO
znCF|H{@dq&wM)=n#_AUrX*!TqUlC#?3jj560A6s|q%voVvEK|scdolreDbZ6Q8|Es
z7d^as(Oo=R^^Ig>0s9AMwJVt;3@P?<wwl)GlUM<bCcT+N&B%ek5N5YsdGjW}I>FHq
zd*UUIz2AJjh?#Xw`rxOI)K<Yupwod9VF3!w`p2&K?5YM3Qn0;r7uX7o0}{GBDgsrL
z$Xro6(*h`7s!vM;0?EsiwVZ5rdfJ`czZ%Ym%g5_uUJvK*>#=%_{o^a_<Joz9@W?iJ
zXu#SE5JNuLai+So5^AUnwH_-HQcsuTxGyu+r7A~G)C!O@6OC)MipD6^#Ry<km(U{?
zuuiNvB~+9waU}*_6d$m@W|?B&X`Ygd_j!Nu;BwyG3WKV1<T6*Z0<{CtT5GUi1uA6f
z5{eKmgi*nA%Vxj{APAN!J**BVIa~E)D6HJK<*o5RO`6SJ7l>w{D`=S(vJ{|Bgt0lX
zxJ&Xzj%pgg7ift+w<w~uIf@L1lY0kdh_%3oU`z`jDXF-0-Jts$!Q^v}n1Kf;fW-`B
zl9N|%>CFAuN=1rp6#*(JJGE0`tccKv5Od=dpAI!m*g_&KFi>gImKL~hMMrf($0bHv
z++(1nR9I5ChO!&lSh^&5@VT;A18v<o&HD-w(FscGq9j>~hh)Saf)E#pCfHFx#EBA9
z2p)kDnN5%?0F;W746#5HS4Hq@0@9dL)5-)s7XnKus3I&CV(=&kRZp(f04q?}$s|Br
zG@d#4os$L-%qOQ#x|6}yD6?`-5@2$#)Yd>pVs#tLPXxV%p0Fqs2qZNdDrp5iX*h>c
zI0P#v$K>@bbb}MLas+bD3pxVMtqqtGPm6S(h}Ehcs;z{bN`b)vgb;|AWh5_+&B;UX
zmZ=gV+FE4uD8G68v(I1NRM=%%D$4$dNF<o};_mI|Kl<nYqFj9&>9}6W_MO%FJgx91
z)gct!p5NI!PyP1y({_FNoeyNSKRx@&&1Wxu{y+Zwmw$8m>g8~I4BW@leDlsKUS3{*
z^XlsgO&SO81eim=d2sdg@skH1f0S0FK2N(lz@XFV?&~imP+>WpN^2aVt~Y&ue)ZK)
zf6}G<tM{%0($uP*^l-Z6higx3VR7oin9}wd9P~ui77P@}q2RPS?-(9C+yzcAUpy-f
zLy#pqnU&m1HY?2(O|TCPtEAkQ`<a7#hMv1Fh-=k$?7P*n?AzfGL?m%|w6)`@+{}D_
z9!_^lIR#EdmgchQSIg~J4=?&pK3RPb6izocpWZ+S=TE<N{^M_r>#*FuMXYrGNZ6d*
zLlVrEwiFYP13M`@oD7<p7Aw2|{2xF6%l%sY^2(W0nD&Y!EFca*W)0N{fyiwbxmdzn
zv0Gsd8<KIg8mk`LvP>)w?_VEIw_kqmTOV%afDC<VxVdggB_@GcbmG^qq4yd<){g*B
z780>qRi93}U)r(v?W59;a(ZrMt~aNAoDpM+7CPsWA)P^O5`y*sP?g+(V%a-`p+ncl
zWrq@!CB(I(k+8=gBED1_D#8HnAjubWAoUKQUu^*VbSm76>DzGHPW`Q2T*+Cw+$Dz#
z6bUlsZZ!|FACIIXX_?xz=V>W#UbojTr&ss)4w|Ri`#PgiT92Csr3_sxWSz5Vl@NQ>
zW3GS<p%36?X@Ep7y`<nRdx*f$Q8NPq0ED@U-Q(rCe=`r=>+^MAg1|KOQ3H>!k6+la
zHt2k|M0f(pu5hf9*p`X}08XGVSMF$bU?~P<R&~FGeno-L&-&GB-JhLV8d%-%$k35B
z2h0V=uvj-@XYO6Y9knyC=495Cm5H)Psf)WJkTq2fj$oCDiW&xFw%mxYQqN&F6ZOj2
zyP+K_ta=<rt>o5P>_VHERFNZzYG8@da&<+C3KoFC8a8X<j^IS#j-D}gFdqTU3OR0C
zJtEG^=8C9J43$U0ntcGBrJ_Mq2<nZCR_GOAu5(fX(?%P?sx6z4So64wvB<fPSOm^v
zII{H^a_9yUaViAD;wa8uFbE<94gzrn0Eh)3vSkNAA}#KbNgZ5K6^Y}(y0fFmC<|NX
zSm4lXJHmV<W(QEyWon_L7KExvo@gCavxh{TKvU){n!t%m1&$B~1ThiBOcpE%=E@4z
zAwW;wkYi}s12qH6#TcQTFeb|xFi`BJ9vsX)KtOj@6!%QQ(23m(!~m80m;?8sG=+j?
zkl;jVsX28N%@omu-Dn`YpZNNG`{NHj|G)k6_eVm(1uGJ(qz-XlS&!kt!|MkD=$(ai
zyBTo+vzUBOMSxewF<#bp+3Ml!{cAq{`u6{OeD&L}|J}d;#qXcdy>%@+#ALM{*<U^X
z^kVbj)zzc>x5qfdn8x$X)zt?dZmuuWhJ&11v*Q=PJ4}aR`}p~9esh?X6X@8T=c0iy
z4XdzTb;D-Ao9o4J;hU1};4`wMcyU^E{qUk3nzdtv+wIx8-rP#Z-i2=65vFpx3!973
zjq_c|$635=AAf(n4!`-?Pppgc-GN$brX^qsJTEO~2sFnIRYiM)wjAc%tF*<7?b3P+
zhe=7RP4)9#xVR+La(`oxhkiWFr5H)`YNv6)kFUD(N7v)o=G9;PdYZI<=i&KxAD_K*
zCbjU}-^9>)m|zebwjRu>Ckh||&<y|#z-J&ffYaaq^~?YGm1Nnjw}{Rth}ld)5Yw8D
z_sr3it*~swQGtxegA2H&VB0<^!LB3;#DV&F-Sg1Ka6H}GZV3_BS0}_|>z$A1-a?z+
zz%bzWaofEX8EPEV9i}(dKh<RIG6vMJ?&?d%sm$x574>l2YXM#?&(s!l;#@$f#Ez#E
z$DX<ZFn}#BH=7Eqx~L0K!ItcBWM0*_kP8^Q3NV|I!3cI>q48VqFQ*Y-eiPbQN{v|J
zGW7vDFL-_$3ZQXd!sV8&ZwVJIj0?=Q?RTE<YAt!+c2$qN^6Kq!R~O>6Ue$ByPPVja
z!2&>|*cWpsU=YZg#S)*q*IvESq8!;ovjEI;%qR2>hpM_*9TfId`!3Y3w_E8VLOIlV
zqajl4RA$`Gtv|z&&wPl7=WkFQ*c^Ji-1z-#17n~h*iuK!v4%cG(PK5AhQR5ZS1FAh
zcCI}b1z?eE%06VSiDrl!Qh;Kz&aNvZQ|?2{iU#6lE3zY|h}le%x8jXGBrPDpO(%uq
z+?{eBd&DV_EMiHc2LuDd2+o_XFYP!>Q^4rjS16HdXfelGB%lUE!cv<H)#_bOelqF+
zmqx;#v{Zv6;^3|NguzQqfsd-J3zJMJNlP`FFh;0Wjo4e1zF3KF^9X(fXeSNZs8g<P
zrN-4rLt4|)^+~*vgdjVLfvFKk>WC>jMn*(P2t80^HnL#kf`I@)nwl4{MKMr_ga%Ei
zk0=HcTQe<g!6VgbxL^%)H8VJ#*qET6JcOp05bUI3EDf7A)iq&pOkS(X7|=D4umOWH
zGLqRITQ4paGS>hd(2}inK_-RBc&Z{~3)4WLiy=sWS_#a9o4V!N05oO44_aNyNj>`F
z*bxbGRM9Au>x|Y3_m0#bA;os`u|ou&UE_d+1eRy&`?h+7>Fm|3=X0J)82WayF35Q=
zsf$;ew0?Yb_2JX>Xh;n0KF<gDU~vez9ZNe*Z!8}FvE<n8S9*8;a(DRMFJArifB3_%
zo?+|Ow2(GKpEP@-wCtz!bo>6h-`{Qz+s%V+J%)AY9unhx|J4`w&z?>5y(*qxfA9Xq
zH@oT%62h44+;sy&l-Ras7je@=7poerxE@P@lx?E!0^O&(x0~%loR0Nw9<JZ3_irCR
z{$7995eswmg^JwwvYn<nF9nCq_Pr1HFMrM5h^3IQcZuf(YBg!tF%i$6*Sucx%*1Gp
zj(Kicw4%b@jr1uO8!k4v&W1_n3AGsy%V}Ybk(k*C^22fc-VYwVcfH*|`@`X0(|Wym
zayfqMVd~QK_78Q}V_$&Q9vvN=kcm7dVk1<tTJ>liO2fPV@IQR@H#>1WTcxohQkH=b
z&uE?yATm}-7S{|%BS8gf6%ffH(Bz(^TX&dqls@$W2>p7!j))~xJCy2dd6xbV?oMw{
zzxmDT-4D;lftX}F&FwW!1k%!z>pE$E^)fwt+5^Jd9##+C5w~Z)Eg`DF6y6SaXF8eB
zpcy;_H^pv+$G0%9F|A>K1(tw3h{F1e#Fb1MnIK^^VI)KiPQefW3Asnykx>}Ob(nh$
z<?*{y;Ua|3@8AHL!X`jy2c1hh9doO6z728P+NuR-hk5sMs#Wjj>Gd~rhW$S8PI|1S
zyzWMOcy-yeQ{%x!rwU$M%i0ayiCU;qPCJxQ^nmVxiA!~gdTKV^a4ia;5?d*`P43Ox
zA^@r6@w~%ViKls*m(9@ge3U-4#f)6A-oDzYLKnRCk($x|)(N0M&U2r7FpOyp6>`?5
z;d~f1@{|AWpTU3rqF*ypK&(J#HXW&+8cS*!f>^0ECh6RpgR7Z~-&I-%tnNMdY&KF+
z?F<%bo*)@Ek8D1>OR(l13-%e!sJp<1)qrhr?>!~Q0XE(8>a8=o3u{KI+?w{IGm)xV
zva(0+oIMCQ1;=7lkOLc7n;iRA+!G<52>VW(mIWN<+Jl=3tOJ$OTmm-7-LcS$%Z$i1
zD*0HKBNt;2Ic}&xM%2LFdfnkM4U)zfRH0AOH-s1hku$S88L%=o<Rm()B~CNc2q6PR
zfm#57kSwt4ilTs34=h7MH!BUBsuMCat!B`<R8U~HhO{(C$Jj;ECATDuRl$fwsed3i
zaojL8c7S096K~16f+tYq-Bi{cqdDiM#KK<1kU&pP;E{b|jtF_OK)^1bO3jTC(2)d+
zMW&gtp4hE3pM^LA7bK<(*e%r(0*3^ZAXLKWOU$RZ9uzB?G|Akpv6?bQsm0P`ou(Io
zXl1DsH4icM^7!)n^2uhsPMht;^(VMGd)U7D?X<nT-{H||e0?Ie`f7l6<Ok>QWRt&l
zUgV2^_=|t{^S_>6?p)wv#cLgriXY|!jUj|JDL9?3zWd(x<!!TL!<+k;Z~aR;zI}ar
z*CYuI_~es64MLy);&W^*V--U~5VS6Jsms=>0JdC0w_S1`)?I>iJ$?P=?)PV#Cx#IF
z-TLfox|_})o*ePw(b<d{J-2y+rmsKy^zn!PRTQ?fi}$|wqkMRAeD$W*A`%TMWN*zv
zmxRF~Xq_d+JfC7k#74}BNX;Pv`HF)YEZz2;PIr!p-@FRL`u?~eF6IuVUafLFPyF$B
zuP)CY0Ppi>_lM@P4QC%cOe?JOu72@SC5VmOvo-J)5N<pmrii?4`3;y`q*mhHPydG(
ze=`N^w|!eX2J<H1El5J|R)Nh?gczVvpUAnHkp@iQ!a;Ek3&AA=_RBGBLk6Zm0S1~&
zE{ii9TD&?7r!XFl$D6XHHVl=xwR{`(xx9JvIT381(R_!;!|nF{)z!y;T6zm@4ys|h
zF*^mAX|LYyN|`jXE{iUX#GIiG6^D(44n14HK|n%5>9I_Z-NM+&LI7~1T(POwqw6^u
z{{K-{>M3PGmiajHT%m3O+5}a!Z6jbtN%MmFG@pu4CHU;~;bMC@-Oa9NtIe0ceE!+1
zn*qAXfEpkNB)&Lb4Q?Fp0*>>rUdsV*Pe%nHVQ5|_H7MF@+VomZZ+BReaU=A8NMweg
zmu{O4#47n&04xr%S?ez}K0{#^_H{q)PARUcdOyTi`m)~%MVJ~za&*rkU249gZX0ml
zQ3_o!Qs_42{$!LR1_N1lzWl|1Ip17_fh7%qCt6<4yh+t-olr8u8F21d7=X+wc{K7S
zC=HMi79=vQN{MPiUAnNwl8qI`RZ?Y>TCF<aAZ|!Ubkii*$ksi#8SY+}Q?MC{_DUNE
zBr;5AR(5V`98fdR020Y7SVj{!H&zBMRsl^LbMo5E7qQjc6b<{PfS{raLMoQjHK2lr
z;*Jb0q~ygHRctK56kQsqfXpd$5Yt+g7@3EbA`>7Bc*l%E$e23!6QVjt0z%9l2dG(X
zAX*S2Tanlt02U^yMg$g<EOSA$-Ht$D2o7MivJh(!X>Jf1y)}#k3lKK|vZ(<?@Z6ll
zN>TLVQCA?`fy~;BGFaHK9Nae`CvwVtAZP$;+6iU_;ILHc#J~i@so9{dMeaH}HAAGh
zXzd^(F416tyf8u#1`_Bz7jKKlrtDCyrB!bfu+bdp!rOv18e_vm1Ri>5)rBzeJfk~k
zfT}i9gX7za_4Rf5-R&IysHf|*;qk{8mrovF%C23UU)iKd9X9;UO;bE*cdvbjo4UF0
zy7}zAetUU4|NbX`|8M^A_g~VntX8Yk4-Z;9)iR&vyUn<svPJXxc7Fc5zkdG*|IOyZ
z>-_fY`7i(S?Tc3whnt;>$hf+`{^Y~;!-rq|r+>FUPT4epV2Fmq!hOJ!PmpKyw%I;X
z@p3$DFW=ElN51;v<*T}T_W1EfAi)xJyq;dXKHWYaR~Nu65~lmR=?IqBhnug?o_@6X
z!MB0>7k~M;r^Dh}_ypKREDI(P3}w!-E9xK(5VR4oL>4BnoU=9eVei8wThznekV%(a
zTar03&BlPGU=*@W<in@y>yIBjUgPxJukQDb0j}P;xOj3VnDUEXLFs5sW`rR)1+S4-
z0DuIpb_6&g$5!CYKm6OT{`*f$rpFJ@#Toh$R#6ZnkZ@F}7B)-^tvb_%_}owh7ocbr
zA>`sh^T7R`)0uUvp!XE6sof${%c4VPN4+@5)O(#;91_$Px7=E-n=MwY>tUHcG(Uup
z%6fIk_e|mPY>#gb`gYIbT6C|P4SL(Xur{`(*n*7)3zg=^;;u0!HI4?%r?;4T)gX0o
zDo|I>#nXjbBSrMt%a+*PE4YEFgFtWqG6_f_PliqHkQI@ZgHR;R?k6kQ4EL}1(4*P)
zako@|f46@|6z2F|?(BH-2j`pnyW7;o^UD!POb=KX6}su9$}+cvT`r}W_hTo;X`ZcB
zN}Z5S>fDd!rH`X`=w27EB<mD;$upWc2??hNNw6C>U3^nc<!vJw3CP?t9OnY6)sO&@
zwY5OH_#)Wpbky8@E!N!0>;1hk1n;dZfI*=3+hN>}L((V*hy%9fZic<M<<Nzg5F>UC
zQ&J_N1qu}Dv4KW)1#C*(GaLcfBQeY_SUm=49$HX>SPdT_wNqZCvS2|~RIuqDoScLf
zCcP2t+_IRv#9WU72^^pRl42<4j5a7Lq8kW-Q*CAfBqW+A^Uyp7@`)`qG)4eGh(iSi
zu<EtdK#-MsLh!)z+>AyKoa$jNgTx3}H=B*v%xUJ$N^l=TM}ic9u>cQ5$k3w{qyTP&
zilE>TKoJ_c1Fc>64cGjC{OA)00|cF2tU!nr#0ZVpNQpbErK#7j0x7L^n5#Fd0Tnnm
zH-vIr)Tmes5kv%np9SWCUC$&QLUm$E4+(A&4W$7OD3QS-jBEhZK~TT}U?Pj;M9hEz
zi*u)50F=E})2I?CA1#BI$vo72Txu%0xYlf-6^x0yj;Il$brb|?NIDk`!76r1i#a+u
zc=o8xfQ?-pp`klszi4_O`xkn<udB`KdevP#?%(_1@=8|2Qek&k5@DTBw|@JIOI*ts
zaU?Cz%D(iu(aV?5e){Z}KeyM1?r0J$@k+qv>99ZEyvWT8OoCE8mt#8~7J%vc``?Y1
zE7TssX32{+zqo$)-EV(%{o{|@;pW93evdQ?VK1G5Mg*4BTFG@D*4L}Et5=`?Z2is$
zR;LIEV5q~gKfK*fw(N%Ow(HJ>k7VO=Y7im?UJmyGR?E%nw0?B<^ih94;zu7IfBnCF
z`k#KiSS2P*-5~7bm;}0f@#ur)?yUog0Y*e*@?whG)U2VPJ5WKeg?d7a*;S0y8#y;L
zHAak#cD9v2`?C+<fAp}U`S81&rGjkn;-jbi$B%q|(_S5%J#0GTKq)|Qj+|sr7EHli
ztD!-<{n@|$@?ZaMn)|bL+*s||LR>`xj>Nt|7_bG27I4Du4EKAKzBzDE3cZI0(371o
zM9R(40GKG?04ysEhU8w{!N!q-BdTH3&}}cyL!4R+B~VD>m{ktD?fEzieK!n!iWe7T
zHxoD{H(N5!+KRTN<V-Du#Va(>Wyc(#Ptc=SV4BGpY$11G^u`EP!<waFX)J|eq%<P9
z3@hIZAq^1SoY1|N){eQA-tz(#+OoJ>DG}9ZcPdzm#ik7Tz~echN$Om*kJN<(7^!I~
z%i~8qNXKZ=+SrGrH6l({0*VqXrOxwGIj}^5wm4Q4aEv2$>y<Wwl0#}|>jyp`C~Wq3
zxz3spuh#I+2Op-0<0ds}r=#OiPN(e0^7`%W?7WNI&HK|_rOq|<3;<0C#f3vaGlgZ&
zee5|kNh?gS9NSXOTItftAg{Xa;_-uRs&Re3T?NLd=2!}_sKG!Im{Zp%^ayARvMZsB
z5U|NoGKYvky%C~$bZrI&F(J05&1k~}QJXUYG)4~5xPT)$4bBE2)s?I<4#Z~AFeF3s
zqE(W)OYjPesICFws4ajNYYAd&9DvcanhSMk>fp_pNr#5o00YhoZqbh32;1ZiZj7xu
zI@KNZ1680}i6}%5iB~J`5`{H~;4vlcB}QZELWdlU1RYR(k&YZHdh(#i5v&0=_tB|}
z2X`fQA!GnhW?qx3Ay~v?&3I}Y#LS%#09@xKU~Lv15d;YYNhJ{)cns=-QVSq1P8|U<
zMsZ?q+G>!#13wv}NI(;45wSS|V1Zas&C9|<O;T4}L$etiGj|UVX0+nWM16yOEJIN+
zst`IV2MC;|g#|K1Uoy1@gy4yqS`~&|9b;AgBM0cIl9*W&X96Lm<c@>SM>MAxK;?e2
z)l0wn@H^jzug1+oNicfcgu!jz)z#g-bCqp~{_xCyn7Q8N+dY1B_v!Cm-yT3#akaUg
z-Lz%grmODq_V{dfcL)`0KD8QJGBiXYY&f-IGrfI%`_1RSfA<GJT7CQ^U3}2}dbl56
zT)hAO`khOA^TJ={?dqe=rnzj}>F(K!{}TI)5W2(b+ah9qR67mp%lXA&a~bC)pPxT;
z=`ODx+#RMCGmqP_>XyUtbpO?O{vf0j&L8Dho$g9^{$c8t_^_kZJ8ysWZ+`PX|7>oC
zgyXpFyI7XVz;#}<;l4hHZrq?KTAd~jC?R?iYz0G5b0&pEfIa3J0tJKF+&Mv|Q`rl3
zOc+A@!FNBn{%CvtIG+CT^V<oa!*$oKpT0}&P+opca3L|d5G-h|0QKOjISMm4IOsiE
zy#M9D`R)Jfw^OE-&D)05Zyl79k&IRj-VI(cpaF}AD<;?eoZLK(s0}0pE!v^@SlKlj
zs&bt)bkcS}Tc?f?BkWhJRaz?)7C#`w#MCcds3LTrD))zexP-b2F~xx?1tx=&#SRU7
z*P<#2g!zct$tVqK#7nKWOd_D*TdpVQd(Ns-(4)nKuqdHx&p27@m;o(8y0EHI%!UqK
z8#LAqJMlU*GbQxVX^viI?Ru{6Yll;=G=j4%Gc_Mp(mK|JwPhr=Zrke-2I#Mb3is!0
z=}lkXP^8=^!^j!KI5h}?4!1xk66H|g%uThlpz~r5j9iboEsMm&rK?`7SPZoxxtmg0
z1gkd00%hOf`x5txEyZE#4i#*fmhE_c+%J&pq6(Fhh)*-PVQ(#?&Bln0%1TD^rJdB0
z2}hgad^{#L-aO)_vc60)K%3o5RTINfi@SJNIUuiCGZ5#%1m)Nyv4dJP@z_AQ2&Gz$
ze5NR7L-FE8v6s-6N|^vuBFyTvI5I?Qk_3!Qt*UsT(1R{sS)>`(W+SKBtr2l`C6ES*
z+tFh0;!urfF;#EkkWtXp^_c6hiCEmAcvc9Y#|766M*z}58ZLF32euMBW)zn^3mJkm
zgDxam^(rH=FQMNEpC}a!A)vDupdlCq2WM17t7L-(B}L>3Ik^Xnvu9IUz-O?PG++R5
zV$UaXEk1D}v+QA`Y*rU?v@V9)YE3Y(@0uxC@Mh+Yury@ETEUeTgXB$nWUCdSs12P9
zgR0fa!cGj}WW_<i2wS#b)D%KgR|K|L+&d5gN6YsVI|MYHIRvoFX0MG%R2wTcA!x0l
zsl(w!83TzeuCxe1P7sVkaB_5zHqGEe)Et-yLJgjD30+tY2SbNORn-f(tLyXnX8flg
zd~1BK@Z7B)G6zxOTc%Bj*PGV+a^d6pkZxY=|KZIy&+_wQ*~Rm+UQ&bI{f<3fzyHoE
zoxM3e%dd7?Ej!kwKw#uhoO;3KbYSVavvr;2i@*OBx?X?q{mr-6pr35ZJ88S&u3}!{
zcz^y*!ZRnny8Y!}as}@}+Ei*KO8b|u_2H>Q(J@*sr}_Tk-KVkh&HD2CYQNjR43{g-
z`S9w-3D@UOOv(5zT}XHJi0|Jv0_fxN%}+o5H-C4W-MbJ$S}P|neLN#KYp>n8CwFwU
z0%{(~=cPjEyTQqo8)bwv5DT<2RSlM15|!mR*Fb@#M^6Oho&O(2_|<FOmL`UIZyI6F
zmG<6WI;moJb*dIgFc_9B1NMz|?;HO#Lx3&}8#XKpum#GZEP#e+kz!~z*=%;RyDQhJ
z6TWl4w8L6!&N;?-gG<kIcl+@Vci*31!usa&O%KGnpLXB>Zhm>I?|%mCPUc4Q9bk*{
zEPRpl+$Q5($Xd`Ca(Mi!fBF~y&tJ52-jm+U{9BS|N)Dh=jYk2_Kr7OO=7_5^Qe`Mq
z6Y{xoG<W3)C^K&NG4D}ZI4~%TEPU;Wpcc^r4zSxLUOm?sOfU>2%Z!vL5Kece5qZ9;
zQ%P`<{R@f<I#4;}l&l%r5{pJU;N&PZk<#V00ShGrWE>#E+M{Oo&S?$GFqp%L28;w0
zOzM*$HIVF5ajeiPw9RV~=z$gBN|G6tn2Y5TKV5<Qqz-i+sq*8!OxYa3N|iMr_ef*w
z1s`-@57F&nM<5^LSojh8beL1Fei;Yu_mXxTv7e7EwzRBM&M9lVc(^hJ3pyBtU;+|g
z+unYYo}I#t!U3X~h0jr>n~Z^pQ7WDTaSEc68SfvKyXS7)X}?bx*T<!vD)OZLu^HRa
zaY|r3iOZfQ%um;=y?6DeK2n`!Dm!V7Y{#@91Y)RDqA?5_);+9*Gm)S|n$*&B9B*TH
z8y!1#aJANODm=2Kl~7D7cNbnkIS9~|`i8u-P%@>&VE{c$3R#NrK)by}GZSzg!~`BR
z$Lc|Zq$D9DjmV%YmC3;nH}jb!L339O5DpDRB7_OKt%-e+LaakG#)PO$<g`!gx4<W_
z9>CrQCL5M1mKGvKYS#Hc#ZA(l3lvt$6IDhDG-L`y2ua`-Wk8Hz0SfPfguwUUj<6t1
zm=<&=X3L%b4?p;WfWW{;Kn9&l3hzMZohS_*p&6l#b$DiI4glnRaOT1~EWosj8H{a6
za+>OnxF(vCCoV}4a>7KwJ4W+q;+%j5Ap`|$_a>Mfek&n^fMfEAm{2Z=bM#Gm!#WKI
zxxP1WGg38Ib2B&S#;u1!Yzw<vH{-+#SeX!6$PgKEc%mf0L69g@kH`={){P=aG(wO>
zbqK<Kz23}AJ^tSO`uwadFWKfj!(q33mJc8EZM@%q^=<!~H?M!*-hW8HdAKaQQ{Gje
z)Zab1==0m>^T8h8z3&c>59^b4M37YarX%6=+mA_2ZL@LRPqz^ZEa~#i*U0P5i;poU
z+||0n@o*k-j#7DtF#)fa`!D}ys35v+qgl8h`<M==sZ<k!m6~7Y<FoyAi$c}K(l|Z;
z<ofoPzTvhmjGA{7oLQIqvAvPHONslpU&3<C{G3y``Q7bJyDr$GEswUHQLN0Rb#ra1
z&JirYQk;g3k#ic$Frly*OCwP4JSvTA>msG@2{0@pTAuW8|LyPp;FH_CSJ%tWzd1i5
zh2Or|9sl_6Pp=lczrskgOQ{UVM-*~WJQS%!fgqbjdHUJE`OE+6pI^R&!<26d)tu>6
zr<9TpuGzgXlN3VBA_UXSxsmNjlu8j_t<Jy^6pRVIArU2xQjqt^9*8tnq}j#@9dbC}
z5V9?JN}$L@QVATXA`Gl0$w+BJ3g$zG1tqhjNXX=vIJ)9gU?*HEvBP?`b<nn8O2kal
z9=&5>a3>F-DQ(^9#Cjici?AUifWbI%vX~~SmG`$?D-`C`1FHpl8y>2g5@!wuMP?^Q
zOcnhqNl=}{S%blddJsbIfPpNbYgiF)oOUTdm~CH6sE~tXH^|ssO`BiVtp`a)L+l%@
zND+9*42+brxv_w=O>^}{AD=d(BEa)BzW1Y#rd)SZqk6kOUgG|#v#f95eR#O4_0x15
zrssFpT&Gw%muhi_bW_WL3A(Xl0)Z(f^WGNd+lFcC38$!z%Zt<Pv+wP9hvQt3%p6Q1
zjX=afq-94$OfzuH>>u2a&>}Wc2_`ipg{UEPifGA#68Gj1<V>j9Qymb@A}kzRgd>8u
zk~QTcm`037C)Hk(185kaInYK>!xZzxele+m9>jtb8##1RF+joYhyhOCoT{4|7BpZy
z4@@jYsKo%WZcdTBMD49>5Wzmi3bM!Shq^nQWU5haa>|gVz3f$3NrF>iR$^l+h)m%`
z1c4d`MuCZ7fIA`}qXUs?U?niLBnJPpo&+omgCkbnp^R8A1|gAD+#6b|wsrtVuHl2!
zyBc=tBpyvA^+vYET?Q9$Vn&n#h=Xm1iXi5@J&k~2QIW(o&o~n0h+beCJUCF$o<a`D
zXV((gki#Uq0go|=AriR(u@SpW>=$=M8|)x5v`3l<&@-Y=Q%NHlDtK`igyam^ITzgm
zl5s$g01`wN=uSa}WcUXtuYR-nKX|vi_{^_wz1~c7-kDRoT)*~**WZ5gRG!BCa6Vt3
zuDYA8uX<exVLGI{<LPEPuJ0ba<;&B1n39jt&kcg2$WcyrFFt<zv!7B%PIIZEZ5nN9
zJwJZEK7RTenQmc)<$d2S@44J|cnvJ)U;pCz=Bc$Yd@X6WxQ*-u1UdKZ-aE0E&a^$o
z*FX7-oB#Ez-SgW*na)$Z`?ftjoiBJ(ZomHGXPBfOZ^nnW@#xQA{XyOyzy9l=cZOrR
z`{bkVR+5`%H+P3axAEo|zuF2V3BM{Pmpq}0rGU2du}R4!h>T@lTR4nCiNcJM$A*Sx
zT^%q*5UDZ!;BNlzr_(2|p4;o6Y;T7|(9Y}kzIXgVvJYP+VyjA?%u|3yI`M9w=E@XN
zN7s3K{geOjfBPrzKi5N@4@nb5<|HNvoo9+pgxx6vQXp8G1Qv(FRe%e)yG=>2Lou<%
zZo<b4&Mz1jU;<b?6&x6B!I>0^1GHnf<te%VDw42F;m&|QJ)?Mx{JfyMBp|2h83IBK
z9z9SZ0L3U}LZc+Gu3Kxc1&RWkFb4Ls$%FtT08PQR5e%_}<(LdN$1(uP6M8*LfcM1|
z06QDUhL8YBS;&DfI|zXYn9w?bE`W(_8eo2dtO&F?++lmo!!%_cKDG-7z%-6@P~Fm8
zF@-qg1XK!m1SMA79-krsaw<Wb5{>{?Hb!<yf*Mclvb%ecH2_y_R&SomJ7p0ZY;na%
z#DGMB*r{GGD@=#x1ltBlgHf-SUbGGygcV^|4^P)dB3lYk6CkptntZsD0nNQ_3GznQ
z&t4p`LJAZg>>QXVJo-~Ax3G;m8PEZwfc5~7ND*KIfu~N)I)oiPV>h5H3tA?->X>)Z
z7jkzrYLRyd`;*22VGXZIhA99u!bO}cQl@j`*nH06fEi}upwPvs<aQadCr;QeBOlzK
zj1D}Sy9QUZm3gOqMcjpUGzr~UJUpjh#O`!Q@a^y#7`)gjBgC**J)frn@M4~Dx1Ti6
zNs4fF&LR=Wh&iiEiV?D-SRgt%1roRu5Me>==mnS&46vYg3`PnB2b>91L=ph_b~Pep
za3fjIRs%7&03MsAgy=$U1)?L2jTsF|*X}~Tw9%uF-feZS(|KjiZN+{pmJ7SP4yq;D
z3cmMPA`y_A5pAodK@o7=(;&bNsVGvo0hJ(UjDb6XjLZFy4k*xpQF}tXHUu;sDHEfw
z3QYqhMj>B^GiPgNkdZ*WB@$;Nhg`M|cESe6BnCZ*ed#Gd56FdZ_y+mmk)QqSbAIt*
zyZM;z4r6(;*N^?;Z@ziFk_FGn8X{f#dRYie&Ad!gJxsSBe=pjj>fxgI*XyzxLl2PL
zfnVKC!N>h{yc{xIrs*)xrFK#Garg3jx4YfLPrr%77Xfpfrq)LI=i}ibX?pz4yT|1*
zuCy)JSQ}&IHneb^ZZ2=14ySo|1sZJWdi7ubhd=rLAN>94<Na}WXm39c$S*&Bwl6m~
z@q)9ohjZ_btJUYyAAj~YUw`$D<dHwQ-En)`zV_wv^u;}hTTKFPX5oW0HVA<cjnRW8
z6M$XEAd!%;=8;HbQ%i|GWxED@>S`bv9FGV4(I0;Q)r*rzd-|q5+ym5cJiMqkFR;Ic
z^MtoS>k$PY1M-cefrXGPc+;52yDxw8U;V#-`B&Qy4ynKZfy^mabOtOG;pi*w?x2tl
zMPs(}h*Z)Zb!F@$5>v8Z%;9{bHsa=maB*!SIl={I1;+%20A*)1bGigfM9JnItq4V;
zko6m;AoSRO0(}H4!j8D3V`PlJK`GRUOg)MxLR$jGyFxe25|=9diWNL@N=862uEYf{
z-4Ftj&wIA!(~N+~6UGXo84=78;{w|=s^NLZ;eg^y?5pC8a3;*e$y1L@!)$)Y^0)x9
zi!mhD=2`&O1Qn!-axm?PD25Vk>sk&6SzDi<NxOtyk!098pZ43s#$&^Px?L|@)ROL>
zbgqD=22m)7t&}L1PNyT9ceG*KOgo4?+@ns`7L=__$8EO<wvrg98M7n%Zh!px!&})q
z#8|IbkBkznU9GPPXFz8z-b}-EL!}1DWxw;~Oi0JuN(pQDm=3aTgpo*|csBc{(gb6P
z2`F%6Wlt$BV+gb=d^JdnGv%SZK`SoVubYRYJ}@C}0tB9n6rA=k#HAn@06149BXBKK
z^nol>kH+pJGd6TKE6zwp9V`1{o;oT<A6AP$3`z(teQT1#LozqdrVD2Y5RC3^PxLr)
zv2jT~I#39k1FkEjJoLh~@a|N(O*<~^Wk#MRPAW_RiYS0o*$4q(2|&UWJk(Tx06T#@
zc0vnBfS^!7j4;50(LFj62=)d5<bYgMSHC_*!Dwbuz=j6FdFx>;Nfn5JiO|8(fC7Wj
zgM_5_P8l#Lb0BG9bn<PZISU))09DvF$jWf?z9IukA~plzZGj}Nfhs)bfI`;7l0^|w
zu!U;nYa13Q54t!jI1ynFB~`WX2;@@R^+JI}OnZc`7;X$$h><~Jax^pF)OMLPTEP<J
z+Ocp)u4<cMCzvoEjpJ|n?Gw{yk7cv_C;D)2>$`W?$b~y3-TeA)>+i?4+lkam=8yL`
z`%ga_%8yS^-+b{kbHV)~t*tM(VlPuYZ;u~+|5J+(JS3G`Qa;^$B*nK;PoErOd6;iM
z{o=3w$NQCd&NMb_{nOw7TZN+Rp6{hx?te+Ss!3~40C=-Icps|)<KyAumu~s;=3(^y
z?JwU4{;Ti&&VRDMeO~Gx=9j;d3WeSeOR&bfU;PW^?d9+M@w=b?<u`xvHxs89-~aK;
zzxAK~^uPZv9>01zzwI@J4zLj%0OU+Z%bN^sV=J6^D*b8mBtUuCpahb4%hEDMuDbvY
z%`!zZrJ5mfp0*!8tDk=VbbH6!FaPrLO%F~XIh~%R7q{*GFH=3>aLVlU2Jk9V22@Tv
zNW&ua`|D5thkx|%|M^<xIc1uTQnJKx4_zson){SXAt*D);zXoPX=XKu1!7a1C1-#j
zEa2H|h)8%q45kuf5NZdE762LHrnFUgf$|E+8Ot5X1!zWWfGjr?xY11DVn!kX$Gz&7
zA>y)tA{{0a*2_0BWh{qiON`LAlB#oNMxToiusEga0jy_mG^s@9gD_)gAf7GmXnUf1
zW7Y*Q?AF3%DDWP`LpTD$86XpHq2jzis_1TX<;AIMOhU%gIuh!PXgkybdd50Y4@-qr
zFh}-HGo&FKJgrc6{IuZh9{Yg(<?@JJ%MsVL_rnd?TWSyy2#!{n<;v}aNHm3s^X_2h
zC1L2wShQ~osPZt2Nd^>XlaL|XbTa)aC+o{vpPlTBciVN@-|W`bE5_ppb%ha(*OGE{
z=#~1r#W)*rAaeuEystT-XGxv`b!#BAKDNl#FZsAPl2jpuD`YnFFr$o27M-LfT0#eA
zN1Hqh+oQ<DHk1;o2RdL1pa2ARlpzg3D1r#WSC2%|7Lnk{8cwPZJ_5)B;=&t_`Gl>5
zdE_aE8j09><sG34*q}Yfd!!gjDXsu)gA*kKK<Pc-MTApt`4+v|+H)oY2!R|l-A!dD
z``ODJDPc;?$uK)XBgn`BP{2399_EM?Fv1mR@6ia50)oXMp&>>$aWn;H!sJldHV2WG
z1pxdypkH7(h<IzB%Ye+8{c-a$)v*rn<TQpU6D9z{>T+otqYI#>lmQ4`9brIU`!-F-
zfB<k9$!@e5V?(*?bU`jWgHh!nB#O9$abbQI@W$elnnfaT2DyS2PlDSSU6H`(8S*2v
z4Y0s+CLo1yL~z0!*m-O|Bj8iR<Brh~ki%zm8@OfWi6;k4*|Vd0!yw2bl&1`r)sQK&
zMz~B5BHd5?r7jE)-@b3waoBFRs^#r`{qooE-Yvexe7HH#DElgWn6b#x-k;w*T-VM%
zc89~(9K<G{DvtSZ+{=x(r+Pg3^>W-jo1Q%r;lq5Z_LuXgzuzBT>%wEh<?Xjj`22@2
zb0s}L5y$oG*Y^#&j2R$O+Rx7p`|{!K<9w_gt?Z|0+u>!khq!8d{q^Hm{@EY@@Ba7G
zT~2uhReiYHdO3gn_VW1Ha{qX$@BZ>vva6r{oquricm7WM=Fi{!>b{j7TOS^?q!cbW
z^=-l2Gx(Yc12K^+7)X^e0tmQk+3oV$hAIGhAbPg3f@cXNfjI5(2Y>j{r+dW9hsUqR
zvXLb&hr{j%KO}ia7YIpCOoc&=T_8nH3{%^1wCmxE|KI=RPyb&Zit|f?Jv%~V%4|-E
zlnMrjYDfitqDaPbaFfkI5~8EC3q!j^Jy1^gwB^DOj>MiO^b2`0GSnf3F=dp+>;$Ud
zj?13%MM!K4Pc!uZ?3i~H-4o&j4hi^5p<~0q!D$9+P>h!f+&!5q()vhh5dwtF64Yoq
zAZ}oewyFaNL2+-2EDZ04+$FG3L&AWLc!+t=yNX633b7)PD-$A<V=dG+BlE^k8Vtp$
zI3dLn27rzt$iQQ<f?;a7OXHE#KCDxJvc!R%@WmMS#=Ci2hN5Bz#<q?Wc~JDnYZBDU
z)gw_FIkS{vnB2~FcMH&sD37p*bK5oy8_*zEfU0W`LIc^sP;AqD7}tBbz8i+7=Dx-i
z$Xr>XGX}RnNtx6;0SP(*6SyusANq18$c)zX@mNlUBPBt!1TYpx9vb8+p%Qg3M<!`l
zcNoo7$S=+>S>J)oh%0IyeYhq?PUK28V^EkzV#*i>$T~*Ou1;<eu}IXA(MOJg<R+ON
z#qAm>Xbrdu27+KjOCrm=Mpz>C@xaE!+rR=2Xs%@vyc!nf>bi6Vs0nQ-pn<U)7{a&&
z+|qc80L$Vq3146UosNf7p6XLdd(M!Ar3fZt%)te%119#fbC}IQM&@C|<(Bjf$N^vl
zAdCnf8hd6BcVHoNBSbSKAwq${@LPUmN=igcO{(`m*dmeFV$H_DU?y<n38h8I*o?Ki
zIUu&HGlc=Vg%^N?loFDLkb4CfW|4X!-%>q8Ab9rC2{lRzbAmJCjkAVM2?+D*up?^3
z-Vxb0NOcSbU1@-VyBtBEC|8Rnl#BxBAf>EM<XCHv({=GFv3X&$X|~OB4!vqmz+0pJ
zt;HkCt`n9K=-@aP+q@p}<LC9wn^7kXIA6xOE#XE$^I?y!@MOQbf4W=`FFyX^cc$a+
zlV>%#`{JSRzxdhZ{&HP4G8L@-+C3>WIFxy_3mVZe(srW#BY@9$&!^`nbn4@8e3{@?
z?@z8N_lFOO>*;QO_3`i3dU*KeaeMss;j(oMS6DfwX;&iOzj@%PJiqxd?H}`8=9^bz
zxeldR+QUP<wf7%>{%4>5_z#%afciqO|MK5l-rxY>^epZlOFljS+kfx*fAWLq<C{PI
zmo4E$w1VtFhsRXKXmCzWYJxRHf&1f?i*gY+chlj5Mb|4aAk_>GJ&X|3ZXf4M#T2mo
z?(>^ZULEVJ+w-4)^`Y%CTiVO<)88vUxQpNX1>C*l+@RbIB*7<;yD%F}9z%V8^NWA{
zPyX+(4dhwMl*6Q!;WT@SE6FZVU}8k1lzh3sZo+cFNRTYpJcyZ7bRda-4V+^?ada3f
z%rA+DQ^oBO$`Q0<osh5Sl_%#VIjQe+Gzc2FLtIx(f<!(6&_n?-H5!Z`&X|gKa*}{9
zOVQRtGIkJp2M-1UNEqa}ZXm{3#LrgdfE0QOL>?A~#90&Ll7$UC0rydBRyPm4J~<e@
z{0?A3Rj>}4Af=!|M1a7M!8-UBIQbmRn+f3NKDL0us{_vvs~{nB&<w*7m@<cQz??+A
z=Fmj!x=IT5ws4w5)-nhtqz%+p1dedTiezJ1JmusINzM%b6`~`ABf`=Kp8Wukb$}eI
z`W6H^-^Cbv-knfCAEDjJ&yS%V>y@WL6{h{}{nOb15fH!<Ga{gtO*g<w298!)=2GiK
zyLp;3s+2GQ-y$Hu7$PyU3`{l5fD0DD&Y|})N34)ox+-!(wyI6TV33155`#vND|#}*
z6gDczQ@^BXA8dvcjF}<2B7*iUm|Y`?JYoufSbf^_s#a@mBXSM}OA)4{oa%bzG^AG@
z5eUs643pGEh;Ew^BhgAZZEZk~XwE6r!HIDVka8#U!Rkz@7M`ezAqR;v8gLLIih`>{
zLhHl^{py&3UB>$%dohh*_aGpkAigr1^B(=tWrssS+#<1q^e_NONGmc?_q>a~dU3=<
z^o6uJkxVk&T>~=`<~|gKRd(`tR>M@&*jh#i<TQ!Q`|ysf(<)pWDnJ<IjJW9tDGBWY
zqd;~VTSx(8i1&dL@#JF>VZ@6W157|u;E3%SzC&0;8`K^5_okqX5dv-rL}FPR8IlKX
zV3T!Al@RHeyADBO@)1Pj-k6dOy$02^EF_6A8iyZB+Kg?ZZ{O&&C(m!+FAwMY`S|L<
zUhbaHx9RcA$8}#0LWdtcub;fw-R;!x1zaD-<;lUP%i~uHIAi6I4hyxk<6_N^0E0k$
zzw><a{Mq5~3iaXKTn@;m#HV?D__n_pHy?en{^qSV8|QY~AEeYz{^<7(uU@R*{$g3r
zufKg%QnZq!W01seT@i&#g0vhC-#Jt%cel^Ck2yW;dgSzt5`XdQ-@N_u3rW|zAN)tB
zmmfcW`M=@*`SR{3pT7DLQ#||8-+uPL`oRw4%m3*ge({U*B#4ZsGRat!v)g8tDx*1j
zY@Avo#F2(0C5oX4jtms2?HbcAhPAGQp(O#d?jUV>`J#OCd#C5mU)cJj4};1q-KURV
z9RA=tX*$~gmKL;&G5`-aPB1wj8=Cpy@|!>ZC;!KPI<C`cW=^(C$P$z)rNnNO0CP>c
zdXTdt7F$*+29&^D`hb)p3<QEQhX*j)iUdwVcyXRZ5tTeG3~Xow$XJ5NxrZHr6aoMQ
zm&Q^-3E9J%7yx7d3T)0ZS)xSlS4Pwd*ohg(NYTv80k3Z&B#==Y21DU+gw@a<tOJ@u
zUZ9g31cTL6-rjh59&M1FVSmGq3q-*};7=hZRm2fo!_WZ=;h7}&v66QsqBIC9>cGxc
zZm_*&?*Rsp$TGl{QW`xH4w@LQ*1?mA`w(=cFobf5zNpHawym!O+chIyt6L{x=xcU!
z-;6l{TZN(*;I_n!c_&POZ&t(bK_0Q*cmCw+3Qj#r%;&z4B_3s$huL^s=Lt1@L!Htr
zB<tHt_GG6MEDr(%#ER(QKpCPTFk_<3bN1um0Hy^9(KIFB_L0}H2%b?opaJc2XfJ12
z9d?z@%}15!!v>H9DY|1YU?N`(5IKPX=g@G**>f}Bf%TkeFy}@@J;CZQLAWDOAUkas
zDTQkBeNT&8KeNmu1R1Pf5)zCI2=H|OB;{7++O=RH8(BiIP-)}Jlmo)I0Vx^*I*^cs
z83=jDX-ZX3$28AS3#Ecgfhh|L<c3&)6he^;I0JSMKyxE;HvkXWGhCf#94!#t0%0>e
znJf7SU_=iVU<At+O?X`az+wcluLe5}0C4tYQ5-~qG_(Pf>b%0bSV^#~STQ`i2;gvQ
z9Wc;4#ofr`>7EY4(=n1pREI9@0`<nN&_tjB8Ig$Gw5Hq_oc7Q?GzB~=R`QN)BqX73
z?;sAW7m#fmNEB&nW1yzcRvc5p`&JTM$j}@x#MEp6Ms$v952KK3>6t*8rfv1zB*Vd)
zQpQvQTrrDsC~`eUpN%@;vxmkHO?UN}^FDui^U?QSj!Fx4mgzZhi00drxs7v*ZGF6~
z?_U3UxjsReu`iq^*Ur0I>s(qHzPUHTIq%8O0*L);X}`w-c7AvK{@?1?ua*xN*K+&v
z6J7P$_wSy6`boS0d3(RyKfPZrj|voNW6UXG?|mxK1CsAQeE8CtKuS8K<EtO^{wsaB
ze*E$8D-CVSa#<Sk;eOZhm0!I2=<e^n?0=N+QoYOZyx{iu{xARCfB0vAUeev&{F0K5
zag9D9gZBOX({)iNWOrhm$=g7n#A=Pv0~kpReFP%_g4X@q&R6YSYRZzupT75M|GiJ2
z-8|C||K?YhGcjy(pt`#`eKf)QZ|Hc+5T0gI2fSL@F{db9QI>c2|NKAyhu?l#j@qXH
z$)`RpyD66f6sR=8W=yLWp+zkdDiego5OfUml(0QQ5r=0`8MI*@v8iK43tR)!2n0_%
zMDVN<>R6%ON4g7`Fs<km`yFy%&%qMK-39dum;oJh1xSH`*aL$1H-HNm29R?JU%d~q
zZ-Rbn!E%VzF|L8tDEahpJbWFckYC{Zh_E9xZ0hLHHlI@T2a#|mMaqEy5Q{+!Bnd0-
zgcK;jNE{&23@FUK54NV7c^<yLLl9tN$FPp39%!3M-!LI<)F>fPl;CBc1|etftelmJ
z)*%?IaLU*^F1DVxJnt2?6YkAqzvn3nUe6yc$K!Dj0?XJo&RA(rO`KLq6JrSqSUc`-
zz;)=Ly1#ZOC%TozM*@xz2%UF#TQh;^eZgS{XhH#s1`5E5Fm`0^SmgAq<T|BEVyZ@A
z#!MS@MVK>R3vtkq#@TjzX{dm(0XPTGP2VTMK`d*J#O`1zkzu3~9_-!d1og?`Dh4AO
zN?>qmtBEi&Sb$oLL@2~-z!BhzUIms|$*lMYrXHL<29TiSK01cRw43|;g<c9=qeuiZ
zjWzFcU%^O)z=IrkbhV5^GMdFixH#wIY~?W336c;O<ywh7@gUj&2?5=OIN`Vg9s+w{
zbu|nY+`z@56S=!W0L5$qY$i5<XQBa|%r}S%K9GmQtqd~&;35D(71V}<BPO8k<Nz4}
zB-yHt1>J!pDG_2Bs?kPww&8$2AZH+pfwd~8(J{7AzT!-jD_Vgak{Ccoeu=nvQg$|A
zj!U2bx<e$a;R7+_h{0Fj18u7d!+;cGxOs%<86KX36ukFEp;?3_5@3Y@HX>AnYJ!Q2
z6Nn??0P_~ieFV!`S!`hx!wCRQZo+eq=l1NFr=0`)oc9%LJ<Nql9jSG$NB&;f6X?43
zc4=?!ubj`XKfix_fBxdV8iyh*LnaEhyiXHV4Ak8F)#eiQ?nsHA&JW8t*Zm#z?)~e$
z{|sn-`tplN`{PHSUEaU0ugdgM-roM~{`LF&cMl(4f9rWd>xD!N$954(226~7yR=09
z?Ju^co8BIG&p$rge!9H>YkHP$cQ+boTtA%GZQahxzk9#C{mp0p+5Z;jj|zfOkEK2Q
z)nEVoPyS?<XW#kbAEomn>Czv!z9UMhU%hbs%~#)C!8t_=r@)$dy<S)?4i!R1Up*q_
zxwUJ6_;MZO>geaF>@UA_{Opf@|M~4+KfHc?$4|}YJ>*=DpMB=g>^{=5`Yt)os><9c
zO*D0rx;^!u{&)Z7zxzp@Rp(;PJ8YL+S#m;R6HYpE$`AsBkV#xT5MuMqg4K%%R{{#S
zAz4X}FccR=Mnq*~O$pl)a-ebnei`~EvI0QJ0UZUp#vbuXX$Oo%;iLDY(gRFF8pa$!
zF^qQ=r2q}2%|>9ZV%^#WNH7vm;4t<)z#wZ_0tiu6TYbvBI=F@$f=jdqX2LW<N7y=o
z0#4i)N)5^<Va~+fm&jA_0f{g$1PwWg&|1B1)F03$@R16K1@|1-tX<6~>0Ol~rEr4e
z6s^}I>XOG6Dh7xkB|-0i%#wtng?6bsdU_(BrC6;J2|!`q7gHK;INDVK&}Ay?`Q4OW
zUB`o7nwmK$?dHB1AgzxhA7DJ0Y*9~;?w{|<+xyMNb-ic=1;Tas30rKVH|XqpL@rs9
z5y8433=I^-rK5JrDHl$j%K?dtpPQC}NP@*$1VK(X0Re(v)r0~;G%|%hV4hWa3<A!9
z6m?7HP<Ciaj#`g`4!R#QR9BBoNSSryBln9M3!4+7Q1I$@v=|tyDg3J8bs%t}E{xh5
zh#<HJ;Et%HDM10}5OXC#GF)TCmTmyvi;^NiYy_29+-+zgU-@u~M5)G{yi8p8GedCd
z1Q{48?FrnWv!BuTfHPPNOcdw@;0_i*6KFROKm*_qCpY6l)R0r~1o{-X$7q2QLSQ@!
z&H@18<QVk)nJ*seW}{o?lxN*mh@?y<)F>ltwmo_gp$&7=TIsw-;pkD!EdoNxM3^EP
z(4H$nnw*Ntg*ZC~AxCUv2ux1ENHG?`k02Hh0xuY=GeiRPEYXcCY6o-xN9&q73^5UQ
z3+GKLt2sw|9E2TR2vDYkv{Toh9xK3(!z{N;(-cyz1?=w<bf+BUC7!;U^1<dgW=zj!
z0F0&YPWyetRGgT{<A*OH0lj<s?YsN8-#&cx;pxMNwuT9c%dXxgdA?j;KdxJL#Zd^!
zynCscfZC?Lm77m<X8WdXK1Qa~e6rCxeC)oQ|NO5#<}%-<JstRFzMaN$e|gv6ef93^
zuReEBR>qPferu?jOu^+&uyqO8#`(H+_4IDu*019uo?kvo`%|i|NaAOoO;2Z7QG=K5
z-TQy}ub%z*)iRg$>gS(+{`J58`Q!QV_x|qRdht^B&tK~K;acqR?K=!jN6EQ-{fO4E
zWa(&AqPAQjVVY(I!Z6IarnwJCH3)N*@PG)%0KiXf(r4d2$P^EMeR&!=6~_tW<#)<=
zKZeI&^A2O56$_TdxH3;vM@Te<*Z=yz`EUQ>w=m|UMuqomyF8`iXq%=vPac$}NSW*$
zA%;%aITn%>0R#f3IGRrteFf)$9xjMy^yeTQz;OUg7yu4Qc=4Wm2|OG?H_U{6Cf}m3
zu`5wIObAqB@??}!jBMed#3=_-!p*uv$uU~Q3b6p37&a_!o1nWg3H2D+vOxezCA&fx
zh)0w#*S?T3OcS6RX7Uzg2f>KwyodDy;c-l8rx=NodmoeqYycV)IGZtAXM#Zp5%(bz
zR<x!O2WgG+1lW6X@*ZH35^fK`OdyIgM1xw;Mg|Y+933)I<QNS-e7ioK)i_gdZ>X_W
zMl3XCcyxzUg@GwYyDkm%Sg;paLAM@#LvrgdrDSVQ=Ve#v9yyCCSRY}F>2@BB#az(x
zd@S3;bsceJo{6`$r#)p&jsfmC(YoAZnRfG(4TKmKtRjvyw1XgYkcno=HiW^PB1Jer
zrw9#ZM8}aFvf3G>l7?FeUyxDr4fxd+oDq5#XBGD$Ax3zKD3pUY5U`Oo?C5q?XJVt~
zk#{u5=r!8KLYTO=0fO#;sUgr90a+|sZFv<=<Ltfz1L^Nk*sI2p8m4e&FeKG!a!+LA
z`Ln6co8&545{#%5K|oR#A-@t8cMs&i!6bl0?xAZCu#5AcNQ7sD%tnU6v3X<$Hm(Vm
zFhpkQ;h7L?fDxg1L-;KwPZrxG@vyE+Y^I^jNSN1cOoU<BorVo0K#pd^jL6*u2B<T5
zH=>}_$$WQ{)8-`tgs=y(0|jDk0keZD<b;7>*Pt1R3D$tsZ3e&h%rx976QGVR+0+4S
zXXC+uxuXR{Hx8qcifH2yO(29V0xboG2k;V9P?0fI0c97l$#kcjz`{Lk^|SQiv-HtT
zJ{?Hf7;A!7;^EpH^*q1N@^qd~1NJYzxz~JKzxl;a{^lS5`m1xB^XdKLIVCwAU*3H3
zvM=x7y?xu&iMdcQ$po9&!F@1fo~Bt<<~h~<+LsF8vD^IJ{`9}F?dknb{v7bId-iho
z>X@t7ydT?TdHr~~K3x4urH~b-K9^}MSFg@gVBqz`!<4A6VL&zIrsHw>=K1cs-+uA!
zczg|c{@#y&oU^9dZclqSl<_hh+Rfu~|L^|4|K!7m54OQ*Q)<Sw?R4DMO)M*OXmvXb
zs~!FN;VpYkIh6!X?6#OtIZWHb!pNN<V{Y2QM*vGnzFYw(W{c6MoIm~U&1v3mAAa%S
zY!t$KDH(2m^gC&OuOBWl-*RG&oPwFmb`@*r$RB?C=l{+B=_i*Z9kwgiOcYAq5|$0`
zswW(ovPYMG#<T<B0HS7O3@9|Upc@bm2Sx@{8k=VVNwGEFH4WgDuy5``bisNE9Z+|v
zuii&wC-$f*)(&>Si_%qTcH><DaXesog=`2{m?jJZ9jb^V9*!jes`rA@Q3s+$u4K;Z
zQ+QzB6FON6djy;S)L{WEX>26K)MJ<l3rL6pWpFS?vL2(-jNX+EJUc`t82}J21Swzx
zssxV)Gr|_l!G-%`k~w7}gk1Ri0OH&eM)MF{7Sai_nL5Bm5doY~`!wP90fJo#n|e^%
z%pw^Hh5-j9rVhjdcc)ZBE{o@R=wPG}XENPJtvfes0T1VHgQ!+ZX>@lOwetMx<&=-6
z?-Sn8uCV7Cfcmg*15`9}7Q<nD9b>MsTHDr0i9NH8-JGV|H1iVaRH`c^)(3!;OVV{i
zU_OvIftm6^IFWZB9?7^(1e+6U9}s~#ODvGZ)M2F1Ko~@%(HziW3R-h$=Zw9C6GXFC
ze9nRteNbEhIdf}k0SfS~E*Y<v*z@R{5N)Ys(^SBrF5@xtp8A!f^-N;gjhzcn2Q3WH
z*TlT?HsW;9$46(10FgM|P@P*UQ`tG!fk}`$kP$dII|jrOK<+4>3^oja2$zDf1|w=R
zT!RW=SOgLWHUI@wf(1My1DJbe3^NEoV`S+L|1)puv+7%SCIZudMC@cON}+KXn<UPa
zlCl#KVnhMg<gucWp+g%<cyK4ISO&w*i7jAVAZ0R=wL@kvdu$(K{|vl^G@@gmg$mNp
zbY!}Kg`=|zbc+-K8l8hO(GUp3<m|YfO*ooBS3q;uh6)2V+bW84*y?EvV~a$J!6P_A
z=GxL8TD51l@_bi!Cp!6V`U=_;ef~yu9ycE^{dhgUl?B_}r)jcZzh26Nj{5GGpa0pP
z-G6o8Asx^65Br<li)Wu8!*+f1{+l<tSpZfhNF>Qj6K3pB*T#L9>$JOdOl@dfo3(^8
zIKN1T{O}jQaX#LC?+34b|NHIzYflBo^6uAv^X~1t^Hu%&gjos%%PF)$C3IwTU`973
zK<8AmdH`bIF7IA{{^Is`c>kTt!<S$F^v^&3!H)^D`%=F5EKTM5S6_Vi^?&%)Pu>pi
zJ<5&|98dFHq(L0yW-bfFp+P>JuYUbzc{&r+#3Q!RQxgLhfOc*H>{z+Db&TKQ6oST}
z`#Wq`!g+ocKm7FY;yWLe8-M)CMaxabl=BnTXZc0dr#E>jI16&6l$;jgk_&dE$EW-M
z;s5%NUw@0x$4=N_kW7SFxJyovoooZyNt~TBMiO(Pfk3gFWvsp)@nk;KlSi7ft(Fs_
zqdCYFqfsgj3P~c|BjeU11rl1t7=ct^D1hT6LCL#=Uqfbxj!KSqJRXz;L%}B)!8AZ7
zv<u)Kt$8>Ch6PDdouz$aGC8ABL0laxmEk(ka^{qW1~LK<P_?>8TVx0@fMhZOd~&$+
zV1yt<@<2EX?%h@%3>nb6p)&ztAXvf?OAqg0)!d+U0A&m7=47FaiDM)~Wnz>$qM>aG
zyw4LVDOMb=GKD(t(HfTRe4bCZtZ;pddB)LV3=c~4p4M|{!QIX;#>G-e7-+%SV%sdK
zr!wiX_3fT^CBs!4Z{6{vB>nbzmWLh2_RxQ8Lj-};x6VTm$9^i2GoVYvd25nn?H0IJ
z1gBagr^0iVB%#~F^@iHH8L_F>ROCdq&@2w2iIc1Q#Kb`siFn-%cDbm!DKIKB4#kXo
zg>J?(cp%N}fwH6Vm}YB%sSglHt{B<uG6J##8zFC$5*h}UZI~AhGz4K>I%E}tPU>40
zT&*7@x^fZ%^`3HprtSn?2RNa3a}j*F7?t83sm&>N^E1XvDoQ00DYGDW;7qgtC^$QI
zbVHJ0Kmf-H?F=y>W~7a%1?@u>0RU^n&|Ww@03%I62q2C#K>^q>6~tlz5)uG_tC+V0
zK+{e)2P12Us@kL5$SKE*kM!Hu5gbU2z8QjJc!4swNq6HaDL`{D&!!LC@~j;_rJc>p
zj6)`lVg!;7$%n}RpAm@0MrCjQff0Sn#0|r`6I5i-kfZg-fL-`i6CfZcL<Ni!>tkR?
z^$kn|bJ3xtCyzGLzDD=VNMH<vIN^j~B+m++K9Zg7VBzt3`7(42Tiu6Uce_=eJk34b
zK)E0wUhU~}eE0d6=O=x$wEJtVJT<jf&+eXo`dxtS`t%f=LgaezjfkCLjMmrb?mOf0
zgAbr-PL=n^m+AJnet5XNeZAj*yxTuJe)dCrdMxE;|LW%EhreU(i=d_4p02-q`<tJA
zu_-8#M`Q%R6spjSdQf561wsmPuGN=itUXPWj%L0ea@qRpyN^Fk^B=r_^B?}|PyRpC
zG&{H3FAtx*eD>j&-@gClJ2iC5d*(8gqpml*BiROP!`%e{iHG>0j@S41_YR5D=ueep
zh#@hI_$_uaTye0VHw~0hnA<r3m%x;&?-4%vth~&kZ+>xkJPT#2B--a!w=ffa{T^<g
zOGd*W7#=xI$y1Ca{^q~^fBeOtwM&avU<Q;#C@JrDWtJ3;Bo786mn>v5z(@coxfAsV
zo1VeqXpOW)4h}b}xGjJQ5;<%rCxlK7VG_rRJ`e~nVY!4)OdBwRRY;DFID<tX7C=MW
zpxlBhYyoJ1040#gJs2U9P;_^rVPOQW*o~t>G=e?mOjzN;EpOB~gn=k1Q}m$Ki(uO<
z7J#GYv&1oM0{~wGrIQd^4l0J7U_$C(1rxfd6Mz)&26=#G<joVCG3(+|AyzhtEPQ<k
zW<UU3!#y#1Ou$bN2~+XyiEOXvplfi#?ZR?5+6q}<i0WozaTLaZMRX;G(H}0iFZP!a
zx|x!e!(D$o2N$NnbJcE~0@z0wquIv&nnpNuskR;V<)QBLqxNdtv@n1vN?qG^p67V9
zZCU5YfUy|LT-RkoKqnq5cQ;ca$$9UBcpY46L`dS&mQ6BdN&0B18W3|ywwY$YP8JS^
z!617<3R{Q{*jLIxv;%%LFJ3%ik-BqLL<d0QDOQ(M7&-uOPGen|hr(om7?+@3DwDPd
zZ9q&OeMAQJUTa0A7(F`}T4usUCPpzDC|vbnli6@)zbf5CmL5Hg7)(TKP-T&NtEs|t
z1jz^z0ttvH8UUgzV1h6}2|zRm8UzLu2pRx@6$#A)qmigvVK^IQ%rn3R=>TRP#Nt5~
z%xHm=^C*BJ-r%=FBnkA^LK+#mh6NIYOzZ<XvRvMvkow?mt&PEiVcI;(Df**kfK3&B
z+Mvy7B!G=D8z^f+W<Zy?h)qbQI3tb-CW_dT_6u+luT1JauoTJ$I7K8J_hfrsp1LBt
zz@RZaU_!n028xJGX#_e2F(xBUIwkPIHIrl~Bs3rslrg*HmgJ!>t38~pUpJg%jJ}O>
zIwo;<9`=s9E+fnFbTxgHyKiCTn_BDgGOz1ssU+dNy!$!}%!g;!^S8V?P@8DRq)q!4
z6|W!MaR2I)eVssb{_KbMuYdmSSHGF3=jEBCdMKxI{q^UEkAArS{vEDgZ{J>Rg2!L~
zd%Hj6w7-rkU7j2hR>W;s@91O7bt8w!V_g%A_RUH1Ebg81j)hw1hsO`g_47}E{~vs3
z_ji8%*Z=0-yZ0QruW8Fq8NpMC^SbtMCEe=0d-iM+PkwpKr&%#Vp?6%L-YqhJ`iZ>#
z=3S8j;o~yO37bYLBqeDZgxmx;i*5ZHYC%MZ7#Z>Re)!_k?|fV<J^aEiOFrI~j*U+*
zkAL*{a{n4RL!O2y(}aFchv!jbYvq@J`d|Nd|Mk;Tmy*4(q(H`GYlA}|uy6@32`RG0
z29~Khpbe*REkMrUNGr9;MQC&ih;)bJ!Z{6EIL+uSFd`5MAO;XnIB<9uAPXpj0VIKl
zlHHJ`gAKSSB#{1gz|ny@r|>7aY|afz#Telc+ZHYu4F`ZF@XZYn4(L}G1T&M>QIrD4
zfItWZ*-$f>Yatlq_JM&d5i|`1FSm&=m@Bbk^sqUUb<<QK_z+0J2nexV0SEB_poSZ&
z7$S+nl{|pOe5%kj*r0cq_U;zGKxd+`p~S@p3p^<z17IO_Pp;SHDHy@}dL23-J%us_
z@Q_UGMxKx@3>-a=<F1%`Z@B1^Mh)i?MiJ=fH9F%qVze!7d!9Mp<$9p^=O!F|1q|cu
z%u-g;X|C%s+#C;_T>P?9cmQKhSbDO3ok?ulr$hwg!sCIsdIXkJ0uK(Z8Nkpy7$I0}
zjUsMy($=*ja7c#9p*td|J%l5A0W=061vCh3R6ern+i;4px)%&pUnO(XsUm0$%eQ2Y
zwrznUk%MG1$H7V{^QIv&03}2umW8>xwt%6Q9U-MTJ9TV>PZXUu-3Bz;S9Sy-vg1A`
zo|33wr36C2!Wafb5P+}-mKY1NB212}(-BE<EEWZVfVu-Ck_SZ?VA25cg#^M2;1!ZE
zJH&y31J_`IQGrHyA|K%+hIG4bn*l9qJ`5R#4ia74a3l}foGF+~7XcQ+GD09DpWlZn
z4#HhBDh-3W!x+W42q1K^fzdz_5JSKdBaPtO#2lEhHPMM-cJC1Zd5RRAPOfb%0DQ2q
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%0D|;N;<J*lwi4yXsNUNdt*xTB-@gNS3&V
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
B<?sLi

diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts
index 40d187f5a8..aa9b34b706 100644
--- a/packages/backend/test/unit/FileInfoService.ts
+++ b/packages/backend/test/unit/FileInfoService.ts
@@ -83,21 +83,21 @@ describe('FileInfoService', () => {
 
 	describe('IMAGE', () => {
 		test('Generic JPEG', async () => {
-			const path = `${resources}/Lenna.jpg`;
+			const path = `${resources}/192.jpg`;
 			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
 			delete info.warnings;
 			delete info.blurhash;
 			delete info.sensitive;
 			delete info.porn;
 			assert.deepStrictEqual(info, {
-				size: 25360,
-				md5: '091b3f259662aa31e2ffef4519951168',
+				size: 5131,
+				md5: '8c9ed0677dd2b8f9f7472c3af247e5e3',
 				type: {
 					mime: 'image/jpeg',
 					ext: 'jpg',
 				},
-				width: 512,
-				height: 512,
+				width: 192,
+				height: 192,
 				orientation: undefined,
 			});
 		});
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index aad4ab37c9..bf14d05eca 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -297,7 +297,7 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO
 	body: misskey.entities.DriveFile | null
 }> => {
 	const absPath = path == null
-		? new URL('resources/Lenna.jpg', import.meta.url)
+		? new URL('resources/192.jpg', import.meta.url)
 		: isAbsolute(path.toString())
 			? new URL(path)
 			: new URL(path, new URL('resources/', import.meta.url));
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 72aca4dee2..b59f8dcbe3 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4299,7 +4299,7 @@ export type components = {
       id: string;
       /** Format: date-time */
       createdAt: string;
-      /** @example lenna.jpg */
+      /** @example 192.jpg */
       name: string;
       /** @example image/jpeg */
       type: string;
@@ -6799,7 +6799,7 @@ export type operations = {
              * @example 15eca7fba0480996e2245f5185bf39f2
              */
             md5: string;
-            /** @example lenna.jpg */
+            /** @example 192.jpg */
             name: string;
             /** @example image/jpeg */
             type: string;

From de1fe7cc5a22fed6c677a6b35365c667eece8ab8 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Tue, 2 Jul 2024 14:47:07 +0900
Subject: [PATCH 067/206] Use built-in API (#14095)

---
 packages/backend/test/e2e/move.ts             |  7 +-
 packages/backend/test/e2e/renote-mute.ts      |  9 +-
 packages/backend/test/e2e/timelines.ts        | 93 ++++++++++---------
 packages/backend/test/unit/RoleService.ts     |  8 +-
 .../backend/test/unit/SystemWebhookService.ts | 17 ++--
 packages/backend/test/utils.ts                |  8 --
 6 files changed, 69 insertions(+), 73 deletions(-)

diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index 74cf61a785..35240cd3c8 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -7,12 +7,13 @@ import { INestApplicationContext } from '@nestjs/common';
 
 process.env.NODE_ENV = 'test';
 
+import { setTimeout } from 'node:timers/promises';
 import * as assert from 'assert';
 import { loadConfig } from '@/config.js';
 import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
 import { jobQueue } from '@/boot/common.js';
-import { api, initTestDb, signup, sleep, successfulApiCall, uploadFile } from '../utils.js';
+import { api, initTestDb, signup, successfulApiCall, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Account Move', () => {
@@ -271,7 +272,7 @@ describe('Account Move', () => {
 
 			assert.strictEqual(move.status, 200);
 
-			await sleep(1000 * 3); // wait for jobs to finish
+			await setTimeout(1000 * 3); // wait for jobs to finish
 
 			// Unfollow delayed?
 			const aliceFollowings = await api('users/following', {
@@ -330,7 +331,7 @@ describe('Account Move', () => {
 		});
 
 		test('Unfollowed after 10 sec (24 hours in production).', async () => {
-			await sleep(1000 * 8);
+			await setTimeout(1000 * 8);
 
 			const following = await api('users/following', {
 				userId: alice.id,
diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts
index 1abbb4f044..f6895c43d8 100644
--- a/packages/backend/test/e2e/renote-mute.ts
+++ b/packages/backend/test/e2e/renote-mute.ts
@@ -6,7 +6,8 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { api, post, signup, sleep, waitFire } from '../utils.js';
+import { setTimeout } from 'node:timers/promises';
+import { api, post, signup, waitFire } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Renote Mute', () => {
@@ -35,7 +36,7 @@ describe('Renote Mute', () => {
 		const carolNote = await post(carol, { text: 'hi' });
 
 		// redisに追加されるのを待つ
-		await sleep(100);
+		await setTimeout(100);
 
 		const res = await api('notes/local-timeline', {}, alice);
 
@@ -52,7 +53,7 @@ describe('Renote Mute', () => {
 		const carolNote = await post(carol, { text: 'hi' });
 
 		// redisに追加されるのを待つ
-		await sleep(100);
+		await setTimeout(100);
 
 		const res = await api('notes/local-timeline', {}, alice);
 
@@ -69,7 +70,7 @@ describe('Renote Mute', () => {
 		const bobRenote = await post(bob, { renoteId: carolNote.id });
 
 		// redisに追加されるのを待つ
-		await sleep(100);
+		await setTimeout(100);
 
 		const res = await api('notes/local-timeline', {}, alice);
 
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index f6cc2bac28..fccc052d99 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -7,16 +7,17 @@
 // pnpm jest -- e2e/timelines.ts
 
 import * as assert from 'assert';
+import { setTimeout } from 'node:timers/promises';
 import { Redis } from 'ioredis';
 import { loadConfig } from '@/config.js';
-import { api, post, randomString, sendEnvUpdateRequest, signup, sleep, uploadUrl } from '../utils.js';
+import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js';
 
 function genHost() {
 	return randomString() + '.example.com';
 }
 
 function waitForPushToTl() {
-	return sleep(500);
+	return setTimeout(500);
 }
 
 let redisForTimelines: Redis;
@@ -44,7 +45,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi' });
 			const carolNote = await post(carol, { text: 'hi' });
 
@@ -60,7 +61,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 			const carolNote = await post(carol, { text: 'hi' });
 
@@ -77,7 +78,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -94,7 +95,7 @@ describe('Timelines', () => {
 
 			await api('following/create', { userId: bob.id }, alice);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -111,7 +112,7 @@ describe('Timelines', () => {
 
 			await api('following/create', { userId: bob.id }, alice);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
 
@@ -128,7 +129,7 @@ describe('Timelines', () => {
 
 			await api('following/create', { userId: bob.id }, alice);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -147,7 +148,7 @@ describe('Timelines', () => {
 			await api('following/create', { userId: carol.id }, alice);
 			await api('following/create', { userId: carol.id }, bob);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -166,7 +167,7 @@ describe('Timelines', () => {
 			await api('following/create', { userId: bob.id }, alice);
 			await api('following/create', { userId: carol.id }, alice);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] });
 
@@ -182,7 +183,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
 
@@ -198,7 +199,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
@@ -228,7 +229,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { renoteId: carolNote.id });
 
@@ -244,7 +245,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { renoteId: carolNote.id });
 
@@ -262,7 +263,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
@@ -280,7 +281,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
 
 			await waitForPushToTl();
@@ -295,7 +296,7 @@ describe('Timelines', () => {
 
 			await api('following/create', { userId: bob.id }, alice);
 			await api('mute/create', { userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
@@ -313,7 +314,7 @@ describe('Timelines', () => {
 			await api('following/create', { userId: bob.id }, alice);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await api('mute/create', { userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -359,7 +360,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const [bobFile, carolFile] = await Promise.all([
 				uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
 				uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
@@ -384,7 +385,7 @@ describe('Timelines', () => {
 
 			const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
@@ -411,7 +412,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
 
 			await waitForPushToTl();
@@ -438,7 +439,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
 
 			await waitForPushToTl();
@@ -566,7 +567,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('following/create', { userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi', visibility: 'home' });
 			const bobNote = await post(bob, { text: 'hi' });
 
@@ -582,7 +583,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('mute/create', { userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi' });
 
@@ -599,7 +600,7 @@ describe('Timelines', () => {
 
 			await api('following/create', { userId: bob.id }, alice);
 			await api('mute/create', { userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
@@ -617,7 +618,7 @@ describe('Timelines', () => {
 			await api('following/create', { userId: bob.id }, alice);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await api('mute/create', { userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -633,7 +634,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
@@ -703,7 +704,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
@@ -717,7 +718,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
@@ -820,7 +821,7 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi' });
 
 			await waitForPushToTl();
@@ -835,7 +836,7 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
@@ -850,7 +851,7 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
@@ -865,7 +866,7 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -881,7 +882,7 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
 
@@ -899,7 +900,7 @@ describe('Timelines', () => {
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const aliceNote = await post(alice, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
 
@@ -916,7 +917,7 @@ describe('Timelines', () => {
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -933,7 +934,7 @@ describe('Timelines', () => {
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
 
@@ -950,7 +951,7 @@ describe('Timelines', () => {
 			await api('following/create', { userId: bob.id }, alice);
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'home' });
 
 			await waitForPushToTl();
@@ -966,7 +967,7 @@ describe('Timelines', () => {
 			await api('following/create', { userId: bob.id }, alice);
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
@@ -982,7 +983,7 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: alice.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
@@ -999,7 +1000,7 @@ describe('Timelines', () => {
 			const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body);
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', channelId: channel.id });
 
 			await waitForPushToTl();
@@ -1031,7 +1032,7 @@ describe('Timelines', () => {
 
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
 
 			await waitForPushToTl();
@@ -1048,7 +1049,7 @@ describe('Timelines', () => {
 			const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('users/lists/push', { listId: list.id, userId: bob.id }, alice);
 			await api('users/lists/push', { listId: list.id, userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] });
 
 			await waitForPushToTl();
@@ -1088,7 +1089,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('following/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote = await post(bob, { text: 'hi', visibility: 'followers' });
 
 			await waitForPushToTl();
@@ -1228,7 +1229,7 @@ describe('Timelines', () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
 			await api('mute/create', { userId: carol.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const carolNote = await post(carol, { text: 'hi' });
 			const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id });
 
@@ -1243,7 +1244,7 @@ describe('Timelines', () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
 			await api('mute/create', { userId: bob.id }, alice);
-			await sleep(1000);
+			await setTimeout(1000);
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id });
 			const bobNote3 = await post(bob, { text: 'hi', renoteId: bobNote1.id });
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index 69fa4162fb..b6cbe4c520 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -5,6 +5,7 @@
 
 process.env.NODE_ENV = 'test';
 
+import { setTimeout } from 'node:timers/promises';
 import { jest } from '@jest/globals';
 import { ModuleMocker } from 'jest-mock';
 import { Test } from '@nestjs/testing';
@@ -29,7 +30,6 @@ import { secureRndstr } from '@/misc/secure-rndstr.js';
 import { NotificationService } from '@/core/NotificationService.js';
 import { RoleCondFormulaValue } from '@/models/Role.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import { sleep } from '../utils.js';
 import type { TestingModule } from '@nestjs/testing';
 import type { MockFunctionMetadata } from 'jest-mock';
 
@@ -278,7 +278,7 @@ describe('RoleService', () => {
 
 			// ストリーミング経由で反映されるまでちょっと待つ
 			clock.uninstall();
-			await sleep(100);
+			await setTimeout(100);
 
 			const resultAfter25hAgain = await roleService.getUserPolicies(user.id);
 			expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
@@ -807,7 +807,7 @@ describe('RoleService', () => {
 			await roleService.assign(user.id, role.id);
 
 			clock.uninstall();
-			await sleep(100);
+			await setTimeout(100);
 
 			const assignments = await roleAssignmentsRepository.find({
 				where: {
@@ -835,7 +835,7 @@ describe('RoleService', () => {
 			await roleService.assign(user.id, role.id);
 
 			clock.uninstall();
-			await sleep(100);
+			await setTimeout(100);
 
 			const assignments = await roleAssignmentsRepository.find({
 				where: {
diff --git a/packages/backend/test/unit/SystemWebhookService.ts b/packages/backend/test/unit/SystemWebhookService.ts
index 41b7f977ca..790cd1490e 100644
--- a/packages/backend/test/unit/SystemWebhookService.ts
+++ b/packages/backend/test/unit/SystemWebhookService.ts
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { setTimeout } from 'node:timers/promises';
 import { afterEach, beforeEach, describe, expect, jest } from '@jest/globals';
 import { Test, TestingModule } from '@nestjs/testing';
 import { MiUser } from '@/models/User.js';
@@ -16,7 +17,7 @@ import { DI } from '@/di-symbols.js';
 import { QueueService } from '@/core/QueueService.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { SystemWebhookService } from '@/core/SystemWebhookService.js';
-import { randomString, sleep } from '../utils.js';
+import { randomString } from '../utils.js';
 
 describe('SystemWebhookService', () => {
 	let app: TestingModule;
@@ -358,7 +359,7 @@ describe('SystemWebhookService', () => {
 					);
 
 					// redisでの配信経由で更新されるのでちょっと待つ
-					await sleep(500);
+					await setTimeout(500);
 
 					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
 					expect(fetchedWebhooks).toEqual([webhook]);
@@ -377,7 +378,7 @@ describe('SystemWebhookService', () => {
 					);
 
 					// redisでの配信経由で更新されるのでちょっと待つ
-					await sleep(500);
+					await setTimeout(500);
 
 					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
 					expect(fetchedWebhooks).toEqual([]);
@@ -407,7 +408,7 @@ describe('SystemWebhookService', () => {
 					);
 
 					// redisでの配信経由で更新されるのでちょっと待つ
-					await sleep(500);
+					await setTimeout(500);
 
 					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
 					expect(fetchedWebhooks).toEqual([webhook2]);
@@ -434,7 +435,7 @@ describe('SystemWebhookService', () => {
 					);
 
 					// redisでの配信経由で更新されるのでちょっと待つ
-					await sleep(500);
+					await setTimeout(500);
 
 					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
 					expect(fetchedWebhooks.length).toEqual(0);
@@ -457,7 +458,7 @@ describe('SystemWebhookService', () => {
 					);
 
 					// redisでの配信経由で更新されるのでちょっと待つ
-					await sleep(500);
+					await setTimeout(500);
 
 					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
 					expect(fetchedWebhooks).toEqual([webhook2]);
@@ -481,7 +482,7 @@ describe('SystemWebhookService', () => {
 					);
 
 					// redisでの配信経由で更新されるのでちょっと待つ
-					await sleep(500);
+					await setTimeout(500);
 
 					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
 					expect(fetchedWebhooks.length).toEqual(0);
@@ -504,7 +505,7 @@ describe('SystemWebhookService', () => {
 					);
 
 					// redisでの配信経由で更新されるのでちょっと待つ
-					await sleep(500);
+					await setTimeout(500);
 
 					const fetchedWebhooks = await service.fetchActiveSystemWebhooks();
 					expect(fetchedWebhooks.length).toEqual(0);
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index bf14d05eca..06c3f82601 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -605,14 +605,6 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
 	return db;
 }
 
-export function sleep(msec: number) {
-	return new Promise<void>(res => {
-		setTimeout(() => {
-			res();
-		}, msec);
-	});
-}
-
 export async function sendEnvUpdateRequest(params: { key: string, value?: string }) {
 	const res = await fetch(
 		`http://localhost:${port + 1000}/env`,

From 5d03efa1bb230bf1f22cf4a86a20157cd8aca7c4 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Wed, 3 Jul 2024 06:40:31 +0900
Subject: [PATCH 068/206] dev: fix pnpm dev is broken (#14123)

* dev: pnpm dev is broken

* dev: fix crash pnpm dev because of unhandled promise
---
 packages/backend/scripts/dev.mjs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/backend/scripts/dev.mjs b/packages/backend/scripts/dev.mjs
index 2d0de0f916..a3e0558abd 100644
--- a/packages/backend/scripts/dev.mjs
+++ b/packages/backend/scripts/dev.mjs
@@ -30,6 +30,7 @@ function execStart() {
 
 async function killProc() {
 	if (backendProcess) {
+		backendProcess.catch(() => {}); // backendProcess.kill()によって発生する例外を無視するためにcatch()を呼び出す
 		backendProcess.kill();
 		await new Promise(resolve => backendProcess.on('exit', resolve));
 		backendProcess = undefined;
@@ -46,6 +47,7 @@ async function killProc() {
 		],
 		{
 			stdio: [process.stdin, process.stdout, process.stderr, 'ipc'],
+			serialization: "json",
 		})
 		.on('message', async (message) => {
 			if (message.type === 'exit') {

From fab7d5e484d86dc8c24850dd98ebc1ee3688910e Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 4 Jul 2024 12:33:43 +0900
Subject: [PATCH 069/206] fix(storybook): build skipping even after updating
 impl story files (#14124)

---
 packages/frontend/.storybook/changes.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/frontend/.storybook/changes.ts b/packages/frontend/.storybook/changes.ts
index 7c70972e1e..bc7601441c 100644
--- a/packages/frontend/.storybook/changes.ts
+++ b/packages/frontend/.storybook/changes.ts
@@ -47,7 +47,6 @@ await fs.readFile(
 				)
 			)
 			.map((path) => path.replace(/(?:(?<=\.stories)\.(?:impl|meta)|\.msw)(?=\.ts$)/g, ''))
-			.map((path) => (path.startsWith('.') ? path : `./${path}`))
 	);
 	if (
 		micromatch(Array.from(modules), [

From 6dd2e9fc0b1eeea6b5f04ccac93ccfab658f976d Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Thu, 4 Jul 2024 13:14:49 +0900
Subject: [PATCH 070/206] refactor(frontend): refactor popup api and make sure
 call dispose callback

Close #14122
---
 packages/frontend/src/account.ts              |  16 ++-
 packages/frontend/src/boot/main-boot.ts       |  30 +++--
 .../frontend/src/components/MkClickerGame.vue |   4 +-
 .../src/components/MkDrive.folder.vue         |   5 +-
 .../frontend/src/components/MkEmojiPicker.vue |   4 +-
 packages/frontend/src/components/MkLink.vue   |   6 +-
 packages/frontend/src/components/MkNote.vue   |  16 ++-
 .../src/components/MkNoteDetailed.vue         |  16 ++-
 .../frontend/src/components/MkPostForm.vue    |  13 +-
 .../src/components/MkPostFormAttaches.vue     |   5 +-
 packages/frontend/src/components/MkRange.vue  |  10 +-
 .../src/components/MkReactionIcon.vue         |   6 +-
 .../components/MkReactionsViewer.reaction.vue |  14 +-
 packages/frontend/src/components/MkSignin.vue |   5 +-
 .../components/MkSystemWebhookEditor.impl.ts  |   6 +-
 .../frontend/src/components/MkUrlPreview.vue  |   8 +-
 .../src/components/MkUserSetupDialog.vue      |   6 +-
 .../src/components/MkVisitorDashboard.vue     |  12 +-
 .../src/components/global/MkCustomEmoji.vue   |   4 +-
 .../frontend/src/components/global/MkUrl.vue  |   6 +-
 packages/frontend/src/directives/ripple.ts    |   4 +-
 packages/frontend/src/directives/tooltip.ts   |   6 +-
 .../frontend/src/directives/user-preview.ts   |   5 +-
 packages/frontend/src/os.ts                   | 125 ++++++++++--------
 packages/frontend/src/pages/admin-user.vue    |  12 +-
 .../abuse-report/notification-recipient.vue   |   6 +-
 .../src/pages/custom-emojis-manager.vue       |  10 +-
 .../frontend/src/pages/drive.file.info.vue    |   5 +-
 .../src/pages/drop-and-fusion.game.vue        |  14 +-
 packages/frontend/src/pages/emojis.emoji.vue  |   8 +-
 .../frontend/src/pages/reset-password.vue     |   4 +-
 packages/frontend/src/pages/settings/2fa.vue  |   6 +-
 .../frontend/src/pages/settings/accounts.vue  |  10 +-
 packages/frontend/src/pages/settings/api.vue  |   5 +-
 .../src/pages/settings/avatar-decoration.vue  |   5 +-
 .../src/scripts/get-drive-file-menu.ts        |   5 +-
 .../frontend/src/scripts/get-note-menu.ts     |  18 ++-
 .../frontend/src/scripts/get-user-menu.ts     |   6 +-
 .../frontend/src/scripts/install-plugin.ts    |   5 +-
 packages/frontend/src/scripts/please-login.ts |   5 +-
 .../frontend/src/scripts/use-chart-tooltip.ts |  10 +-
 packages/frontend/src/ui/_common_/common.ts   |   4 +-
 .../src/ui/_common_/navbar-for-mobile.vue     |   5 +-
 packages/frontend/src/ui/_common_/navbar.vue  |   5 +-
 packages/frontend/src/ui/classic.header.vue   |   5 +-
 packages/frontend/src/ui/classic.sidebar.vue  |   6 +-
 .../src/ui/deck/notifications-column.vue      |   5 +-
 packages/frontend/src/ui/visitor.vue          |  12 +-
 .../src/widgets/WidgetNotifications.vue       |   5 +-
 49 files changed, 317 insertions(+), 196 deletions(-)

diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index f99b550a83..4172016f89 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -184,10 +184,12 @@ export async function refreshAccount() {
 
 export async function login(token: Account['token'], redirect?: string) {
 	const showing = ref(true);
-	popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), {
+	const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), {
 		success: false,
 		showing: showing,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 	if (_DEV_) console.log('logging as token ', token);
 	const me = await fetchAccount(token, undefined, true)
 		.catch(reason => {
@@ -223,21 +225,23 @@ export async function openAccountMenu(opts: {
 	if (!$i) return;
 
 	function showSigninDialog() {
-		popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
 			done: res => {
 				addAccount(res.id, res.i);
 				success();
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	}
 
 	function createAccount() {
-		popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
 			done: res => {
 				addAccount(res.id, res.i);
 				switchAccountWithToken(res.i);
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	}
 
 	async function switchAccount(account: Misskey.entities.UserDetailed) {
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 5cb19f388a..faf230a1a2 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -35,7 +35,9 @@ export async function mainBoot() {
 	emojiPicker.init();
 
 	if (isClientUpdated && $i) {
-		popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed');
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {
+			closed: () => dispose(),
+		});
 	}
 
 	const stream = useStream();
@@ -96,7 +98,7 @@ export async function mainBoot() {
 					}).render();
 				}
 			}
-		}	
+		}
 	} catch (error) {
 		// console.error(error);
 		console.error('Failed to initialise the seasonal screen effect canvas context:', error);
@@ -108,22 +110,28 @@ export async function mainBoot() {
 
 		defaultStore.loaded.then(() => {
 			if (defaultStore.state.accountSetupWizard !== -1) {
-				popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {}, 'closed');
+				const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {
+					closed: () => dispose(),
+				});
 			}
 		});
 
 		for (const announcement of ($i.unreadAnnouncements ?? []).filter(x => x.display === 'dialog')) {
-			popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), {
+			const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), {
 				announcement,
-			}, {}, 'closed');
+			}, {
+				closed: () => dispose(),
+			});
 		}
 
 		stream.on('announcementCreated', (ev) => {
 			const announcement = ev.announcement;
 			if (announcement.display === 'dialog') {
-				popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), {
+				const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), {
 					announcement,
-				}, {}, 'closed');
+				}, {
+					closed: () => dispose(),
+				});
 			}
 		});
 
@@ -247,13 +255,17 @@ export async function mainBoot() {
 		const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo');
 		if (neverShowDonationInfo !== 'true' && (createdAt.getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3))) && !location.pathname.startsWith('/miauth')) {
 			if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) {
-				popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {}, 'closed');
+				const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {
+					closed: () => dispose(),
+				});
 			}
 		}
 
 		const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read');
 		if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') {
-			popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed');
+			const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {
+				closed: () => dispose(),
+			});
 		}
 
 		if ('Notification' in window) {
diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue
index b592609e18..00506fb735 100644
--- a/packages/frontend/src/components/MkClickerGame.vue
+++ b/packages/frontend/src/components/MkClickerGame.vue
@@ -35,7 +35,9 @@ const prevCookies = ref(0);
 function onClick(ev: MouseEvent) {
 	const x = ev.clientX;
 	const y = ev.clientY;
-	os.popup(MkPlusOneEffect, { x, y }, {}, 'end');
+	const { dispose } = os.popup(MkPlusOneEffect, { x, y }, {
+		end: () => dispose(),
+	});
 
 	saveData.value!.cookies++;
 	saveData.value!.totalCookies++;
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 8da0d78f35..1cc8b15b73 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -257,10 +257,11 @@ function onContextmenu(ev: MouseEvent) {
 		text: i18n.ts.openInWindow,
 		icon: 'ti ti-app-window',
 		action: () => {
-			os.popup(defineAsyncComponent(() => import('@/components/MkDriveWindow.vue')), {
+			const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkDriveWindow.vue')), {
 				initialFolder: props.folder,
 			}, {
-			}, 'closed');
+				closed: () => dispose(),
+			});
 		},
 	}, { type: 'divider' }, {
 		text: i18n.ts.rename,
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 8a6bef54d8..4bd4bee1e5 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -402,7 +402,9 @@ function chosen(emoji: any, ev?: MouseEvent) {
 		const rect = el.getBoundingClientRect();
 		const x = rect.left + (el.offsetWidth / 2);
 		const y = rect.top + (el.offsetHeight / 2);
-		os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+			end: () => dispose(),
+		});
 	}
 
 	const key = getKey(emoji);
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index 5d54a58e97..e842ec2d6e 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -37,11 +37,13 @@ const el = ref<HTMLElement | { $el: HTMLElement }>();
 
 if (isEnabledUrlPreview.value) {
 	useTooltip(el, (showing) => {
-		os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
 			showing,
 			url: props.url,
 			source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	});
 }
 </script>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 22b1691a86..1313e4c58e 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -335,12 +335,14 @@ if (!props.mock) {
 
 		if (users.length < 1) return;
 
-		os.popup(MkUsersTooltip, {
+		const { dispose } = os.popup(MkUsersTooltip, {
 			showing,
 			users,
 			count: appearNote.value.renoteCount,
 			targetElement: renoteButton.value,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	});
 
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
@@ -355,13 +357,15 @@ if (!props.mock) {
 
 			if (users.length < 1) return;
 
-			os.popup(MkReactionsViewerDetails, {
+			const { dispose } = os.popup(MkReactionsViewerDetails, {
 				showing,
 				reaction: '❤️',
 				users,
 				count: appearNote.value.reactionCount,
 				targetElement: reactButton.value!,
-			}, {}, 'closed');
+			}, {
+				closed: () => dispose(),
+			});
 		});
 	}
 }
@@ -409,7 +413,9 @@ function react(viaKeyboard = false): void {
 			const rect = el.getBoundingClientRect();
 			const x = rect.left + (el.offsetWidth / 2);
 			const y = rect.top + (el.offsetHeight / 2);
-			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+			const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+				end: () => dispose(),
+			});
 		}
 	} else {
 		blur();
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index ed1c0a9e96..bc1f416373 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -346,12 +346,14 @@ useTooltip(renoteButton, async (showing) => {
 
 	if (users.length < 1) return;
 
-	os.popup(MkUsersTooltip, {
+	const { dispose } = os.popup(MkUsersTooltip, {
 		showing,
 		users,
 		count: appearNote.value.renoteCount,
 		targetElement: renoteButton.value,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 });
 
 if (appearNote.value.reactionAcceptance === 'likeOnly') {
@@ -366,13 +368,15 @@ if (appearNote.value.reactionAcceptance === 'likeOnly') {
 
 		if (users.length < 1) return;
 
-		os.popup(MkReactionsViewerDetails, {
+		const { dispose } = os.popup(MkReactionsViewerDetails, {
 			showing,
 			reaction: '❤️',
 			users,
 			count: appearNote.value.reactionCount,
 			targetElement: reactButton.value!,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -413,7 +417,9 @@ function react(viaKeyboard = false): void {
 			const rect = el.getBoundingClientRect();
 			const x = rect.left + (el.offsetWidth / 2);
 			const y = rect.top + (el.offsetHeight / 2);
-			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+			const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+				end: () => dispose(),
+			});
 		}
 	} else {
 		blur();
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 1df9007681..0dc1aa0891 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -463,7 +463,7 @@ function setVisibility() {
 		return;
 	}
 
-	os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
 		currentVisibility: visibility.value,
 		isSilenced: $i.isSilenced,
 		localOnly: localOnly.value,
@@ -476,7 +476,8 @@ function setVisibility() {
 				defaultStore.set('visibility', visibility.value);
 			}
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 async function toggleLocalOnly() {
@@ -624,8 +625,8 @@ async function onPaste(ev: ClipboardEvent) {
 				return;
 			}
 
-			const fileName = formatTimeString(new Date(), defaultStore.state.pastedFileName).replace(/{{number}}/g, "0");
-			const file = new File([paste], `${fileName}.txt`, { type: "text/plain" });
+			const fileName = formatTimeString(new Date(), defaultStore.state.pastedFileName).replace(/{{number}}/g, '0');
+			const file = new File([paste], `${fileName}.txt`, { type: 'text/plain' });
 			upload(file, `${fileName}.txt`);
 		});
 	}
@@ -731,7 +732,9 @@ async function post(ev?: MouseEvent) {
 			const rect = el.getBoundingClientRect();
 			const x = rect.left + (el.offsetWidth / 2);
 			const y = rect.top + (el.offsetHeight / 2);
-			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+			const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+				end: () => dispose(),
+			});
 		}
 	}
 
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index 95eb367318..8854babb6b 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -108,7 +108,7 @@ async function rename(file) {
 async function describe(file) {
 	if (mock) return;
 
-	os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
 		default: file.comment !== null ? file.comment : '',
 		file: file,
 	}, {
@@ -121,7 +121,8 @@ async function describe(file) {
 				file.comment = comment;
 			});
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 async function crop(file: Misskey.entities.DriveFile): Promise<void> {
diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue
index 15f8128e98..1eae642937 100644
--- a/packages/frontend/src/components/MkRange.vue
+++ b/packages/frontend/src/components/MkRange.vue
@@ -101,17 +101,19 @@ const steps = computed(() => {
 	}
 });
 
-const onMousedown = (ev: MouseEvent | TouchEvent) => {
+function onMousedown(ev: MouseEvent | TouchEvent) {
 	ev.preventDefault();
 
 	const tooltipShowing = ref(true);
-	os.popup(defineAsyncComponent(() => import('@/components/MkTooltip.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTooltip.vue')), {
 		showing: tooltipShowing,
 		text: computed(() => {
 			return props.textConverter(finalValue.value);
 		}),
 		targetElement: thumbEl,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 
 	const style = document.createElement('style');
 	style.appendChild(document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }'));
@@ -152,7 +154,7 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => {
 	window.addEventListener('touchmove', onDrag);
 	window.addEventListener('mouseup', onMouseup, { once: true });
 	window.addEventListener('touchend', onMouseup, { once: true });
-};
+}
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/frontend/src/components/MkReactionIcon.vue b/packages/frontend/src/components/MkReactionIcon.vue
index 068a2968db..c0cbd8a65d 100644
--- a/packages/frontend/src/components/MkReactionIcon.vue
+++ b/packages/frontend/src/components/MkReactionIcon.vue
@@ -24,11 +24,13 @@ const elRef = shallowRef();
 
 if (props.withTooltip) {
 	useTooltip(elRef, (showing) => {
-		os.popup(defineAsyncComponent(() => import('@/components/MkReactionTooltip.vue')), {
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkReactionTooltip.vue')), {
 			showing,
 			reaction: props.reaction.replace(/^:(\w+):$/, ':$1@.:'),
 			targetElement: elRef.value.$el,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	});
 }
 </script>
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index c41811febe..26223364ab 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -114,10 +114,12 @@ async function menu(ev) {
 		text: i18n.ts.info,
 		icon: 'ti ti-info-circle',
 		action: async () => {
-			os.popup(MkCustomEmojiDetailedDialog, {
+			const { dispose } = os.popup(MkCustomEmojiDetailedDialog, {
 				emoji: await misskeyApiGet('emoji', {
 					name: props.reaction.replace(/:/g, '').replace(/@\./, ''),
 				}),
+			}, {
+				closed: () => dispose(),
 			});
 		},
 	}], ev.currentTarget ?? ev.target);
@@ -129,7 +131,9 @@ function anime() {
 	const rect = buttonEl.value.getBoundingClientRect();
 	const x = rect.left + 16;
 	const y = rect.top + (buttonEl.value.offsetHeight / 2);
-	os.popup(MkReactionEffect, { reaction: props.reaction, x, y }, {}, 'end');
+	const { dispose } = os.popup(MkReactionEffect, { reaction: props.reaction, x, y }, {
+		end: () => dispose(),
+	});
 }
 
 watch(() => props.count, (newCount, oldCount) => {
@@ -151,13 +155,15 @@ if (!mock) {
 
 		const users = reactions.map(x => x.user);
 
-		os.popup(XDetails, {
+		const { dispose } = os.popup(XDetails, {
 			showing,
 			reaction: props.reaction,
 			users,
 			count: props.count,
 			targetElement: buttonEl.value,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	}, 100);
 }
 </script>
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 970aff825d..db32cdd6a1 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -218,8 +218,9 @@ function loginFailed(err: any): void {
 }
 
 function resetPassword(): void {
-	os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
-	}, 'closed');
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
+		closed: () => dispose(),
+	});
 }
 </script>
 
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
index 1222d3261d..76f54e8d37 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
@@ -25,15 +25,15 @@ export type MkSystemWebhookResult = {
 
 export async function showSystemWebhookEditorDialog(props: MkSystemWebhookEditorProps): Promise<MkSystemWebhookResult | null> {
 	const { dispose, result } = await new Promise<{ dispose: () => void, result: MkSystemWebhookResult | null }>(async resolve => {
-		const res = await os.popup(
+		const { dispose: _dispose } = os.popup(
 			defineAsyncComponent(() => import('@/components/MkSystemWebhookEditor.vue')),
 			props,
 			{
 				submitted: (ev: MkSystemWebhookResult) => {
-					resolve({ dispose: res.dispose, result: ev });
+					resolve({ dispose: _dispose, result: ev });
 				},
 				closed: () => {
-					resolve({ dispose: res.dispose, result: null });
+					resolve({ dispose: _dispose, result: null });
 				},
 			},
 		);
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index 6954f1f6ff..8df5e0fe40 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -188,11 +188,13 @@ function adjustTweetHeight(message: any) {
 	if (height) tweetHeight.value = height;
 }
 
-const openPlayer = (): void => {
-	os.popup(defineAsyncComponent(() => import('@/components/MkYouTubePlayer.vue')), {
+function openPlayer(): void {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkYouTubePlayer.vue')), {
 		url: requestUrl.href,
+	}, {
+		// TODO
 	});
-};
+}
 
 (window as any).addEventListener('message', adjustTweetHeight);
 
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index cab0067813..514350c930 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -176,9 +176,11 @@ function setupComplete() {
 function launchTutorial() {
 	setupComplete();
 	nextTick(() => {
-		os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), {
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), {
 			initialPage: 1,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	});
 }
 
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index f7963f9938..4d81bd0283 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -74,15 +74,19 @@ misskeyApi('stats', {}).then((res) => {
 });
 
 function signin() {
-	os.popup(XSigninDialog, {
+	const { dispose } = os.popup(XSigninDialog, {
 		autoSet: true,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 function signup() {
-	os.popup(XSignupDialog, {
+	const { dispose } = os.popup(XSignupDialog, {
 		autoSet: true,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 function showMenu(ev) {
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index 6123835340..4581908a8a 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -106,12 +106,12 @@ function onClick(ev: MouseEvent) {
 			text: i18n.ts.info,
 			icon: 'ti ti-info-circle',
 			action: async () => {
-				os.popup(MkCustomEmojiDetailedDialog, {
+				const { dispose } = os.popup(MkCustomEmojiDetailedDialog, {
 					emoji: await misskeyApiGet('emoji', {
 						name: customEmojiName.value,
 					}),
 				}, {
-					anchor: ev.target,
+					closed: () => dispose(),
 				});
 			},
 		}], ev.currentTarget ?? ev.target);
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 9d4cd559d9..d2ddd4aa85 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -50,11 +50,13 @@ const el = ref();
 
 if (props.showUrlPreview && isEnabledUrlPreview.value) {
 	useTooltip(el, (showing) => {
-		os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
 			showing,
 			url: props.url,
 			source: el.value instanceof HTMLElement ? el.value : el.value?.$el,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	});
 }
 
diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts
index 2d724f771e..a043ff212d 100644
--- a/packages/frontend/src/directives/ripple.ts
+++ b/packages/frontend/src/directives/ripple.ts
@@ -17,7 +17,9 @@ export default {
 			const x = rect.left + (el.offsetWidth / 2);
 			const y = rect.top + (el.offsetHeight / 2);
 
-			popup(MkRippleEffect, { x, y }, {}, 'end');
+			const { dispose } = popup(MkRippleEffect, { x, y }, {
+				end: () => dispose(),
+			});
 		});
 	},
 };
diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts
index b1c1b19907..251ce5675f 100644
--- a/packages/frontend/src/directives/tooltip.ts
+++ b/packages/frontend/src/directives/tooltip.ts
@@ -51,13 +51,15 @@ export default {
 			if (self.text == null) return;
 
 			const showing = ref(true);
-			popup(defineAsyncComponent(() => import('@/components/MkTooltip.vue')), {
+			const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkTooltip.vue')), {
 				showing,
 				text: self.text,
 				asMfm: binding.modifiers.mfm,
 				direction: binding.modifiers.left ? 'left' : binding.modifiers.right ? 'right' : binding.modifiers.top ? 'top' : binding.modifiers.bottom ? 'bottom' : 'top',
 				targetElement: el,
-			}, {}, 'closed');
+			}, {
+				closed: () => dispose(),
+			});
 
 			self._close = () => {
 				showing.value = false;
diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts
index 7a008a4486..278d842d09 100644
--- a/packages/frontend/src/directives/user-preview.ts
+++ b/packages/frontend/src/directives/user-preview.ts
@@ -35,7 +35,7 @@ export class UserPreview {
 
 		const showing = ref(true);
 
-		popup(defineAsyncComponent(() => import('@/components/MkUserPopup.vue')), {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserPopup.vue')), {
 			showing,
 			q: this.user,
 			source: this.el,
@@ -47,7 +47,8 @@ export class UserPreview {
 				window.clearTimeout(this.showTimer);
 				this.hideTimer = window.setTimeout(this.close, 500);
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 
 		this.promise = {
 			cancel: () => {
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index f656a52371..560f692acf 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -116,11 +116,13 @@ export function promiseDialog<T extends Promise<any>>(
 	});
 
 	// NOTE: dynamic importすると挙動がおかしくなる(showingの変更が伝播しない)
-	popup(MkWaitingDialog, {
+	const { dispose } = popup(MkWaitingDialog, {
 		success: success,
 		showing: showing,
 		text: text,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 
 	return promise;
 }
@@ -166,28 +168,24 @@ type EmitsExtractor<T> = {
 	[K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K];
 };
 
-export async function popup<T extends Component>(
+export function popup<T extends Component>(
 	component: T,
 	props: ComponentProps<T>,
 	events: ComponentEmit<T> = {} as ComponentEmit<T>,
-	disposeEvent?: keyof ComponentEmit<T>,
-): Promise<{ dispose: () => void }> {
+): { dispose: () => void } {
 	markRaw(component);
 
 	const id = ++popupIdCount;
 	const dispose = () => {
 		// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ?
 		window.setTimeout(() => {
-			popups.value = popups.value.filter(popup => popup.id !== id);
+			popups.value = popups.value.filter(p => p.id !== id);
 		}, 0);
 	};
 	const state = {
 		component,
 		props,
-		events: disposeEvent ? {
-			...events,
-			[disposeEvent]: dispose,
-		} : events,
+		events,
 		id,
 	};
 
@@ -199,15 +197,19 @@ export async function popup<T extends Component>(
 }
 
 export function pageWindow(path: string) {
-	popup(MkPageWindow, {
+	const { dispose } = popup(MkPageWindow, {
 		initialPath: path,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 export function toast(message: string) {
-	popup(MkToast, {
+	const { dispose } = popup(MkToast, {
 		message,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 export function alert(props: {
@@ -216,11 +218,12 @@ export function alert(props: {
 	text?: string;
 }): Promise<void> {
 	return new Promise(resolve => {
-		popup(MkDialog, props, {
+		const { dispose } = popup(MkDialog, props, {
 			done: () => {
 				resolve();
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -232,14 +235,15 @@ export function confirm(props: {
 	cancelText?: string;
 }): Promise<{ canceled: boolean }> {
 	return new Promise(resolve => {
-		popup(MkDialog, {
+		const { dispose } = popup(MkDialog, {
 			...props,
 			showCancelButton: true,
 		}, {
 			done: result => {
 				resolve(result ? result : { canceled: true });
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -261,7 +265,7 @@ export function actions<T extends {
 	canceled: false; result: T[number]['value'];
 }> {
 	return new Promise(resolve => {
-		popup(MkDialog, {
+		const { dispose } = popup(MkDialog, {
 			...props,
 			actions: props.actions.map(a => ({
 				text: a.text,
@@ -275,7 +279,8 @@ export function actions<T extends {
 			done: result => {
 				resolve(result ? result : { canceled: true });
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -323,7 +328,7 @@ export function inputText(props: {
 	canceled: false; result: string | null;
 }> {
 	return new Promise(resolve => {
-		popup(MkDialog, {
+		const { dispose } = popup(MkDialog, {
 			title: props.title,
 			text: props.text,
 			input: {
@@ -338,7 +343,8 @@ export function inputText(props: {
 			done: result => {
 				resolve(result ? result : { canceled: true });
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -377,7 +383,7 @@ export function inputNumber(props: {
 	canceled: false; result: number | null;
 }> {
 	return new Promise(resolve => {
-		popup(MkDialog, {
+		const { dispose } = popup(MkDialog, {
 			title: props.title,
 			text: props.text,
 			input: {
@@ -390,7 +396,8 @@ export function inputNumber(props: {
 			done: result => {
 				resolve(result ? result : { canceled: true });
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -405,7 +412,7 @@ export function inputDate(props: {
 	canceled: false; result: Date;
 }> {
 	return new Promise(resolve => {
-		popup(MkDialog, {
+		const { dispose } = popup(MkDialog, {
 			title: props.title,
 			text: props.text,
 			input: {
@@ -417,7 +424,8 @@ export function inputDate(props: {
 			done: result => {
 				resolve(result ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true });
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -427,11 +435,12 @@ export function authenticateDialog(): Promise<{
 	canceled: false; result: { password: string; token: string | null; };
 }> {
 	return new Promise(resolve => {
-		popup(MkPasswordDialog, {}, {
+		const { dispose } = popup(MkPasswordDialog, {}, {
 			done: result => {
 				resolve(result ? { canceled: false, result } : { canceled: true, result: undefined });
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -476,7 +485,7 @@ export function select<C = any>(props: {
 	canceled: false; result: C | null;
 }> {
 	return new Promise(resolve => {
-		popup(MkDialog, {
+		const { dispose } = popup(MkDialog, {
 			title: props.title,
 			text: props.text,
 			select: {
@@ -487,7 +496,8 @@ export function select<C = any>(props: {
 			done: result => {
 				resolve(result ? result : { canceled: true });
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -497,53 +507,57 @@ export function success(): Promise<void> {
 		window.setTimeout(() => {
 			showing.value = false;
 		}, 1000);
-		popup(MkWaitingDialog, {
+		const { dispose } = popup(MkWaitingDialog, {
 			success: true,
 			showing: showing,
 		}, {
 			done: () => resolve(),
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
 export function waiting(): Promise<void> {
 	return new Promise(resolve => {
 		const showing = ref(true);
-		popup(MkWaitingDialog, {
+		const { dispose } = popup(MkWaitingDialog, {
 			success: false,
 			showing: showing,
 		}, {
 			done: () => resolve(),
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
 export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType<F> }> {
 	return new Promise(resolve => {
-		popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, {
 			done: result => {
 				resolve(result);
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
 export async function selectUser(opts: { includeSelf?: boolean; localOnly?: boolean; } = {}): Promise<Misskey.entities.UserDetailed> {
 	return new Promise(resolve => {
-		popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), {
 			includeSelf: opts.includeSelf,
 			localOnly: opts.localOnly,
 		}, {
 			ok: user => {
 				resolve(user);
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
 export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
 	return new Promise(resolve => {
-		popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
 			type: 'file',
 			multiple,
 		}, {
@@ -552,13 +566,14 @@ export async function selectDriveFile(multiple: boolean): Promise<Misskey.entiti
 					resolve(files);
 				}
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
 export async function selectDriveFolder(multiple: boolean): Promise<Misskey.entities.DriveFolder[]> {
 	return new Promise(resolve => {
-		popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
 			type: 'folder',
 			multiple,
 		}, {
@@ -567,20 +582,22 @@ export async function selectDriveFolder(multiple: boolean): Promise<Misskey.enti
 					resolve(folders);
 				}
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
 export async function pickEmoji(src: HTMLElement, opts: ComponentProps<typeof MkEmojiPickerDialog>): Promise<string> {
 	return new Promise(resolve => {
-		popup(MkEmojiPickerDialog, {
+		const { dispose } = popup(MkEmojiPickerDialog, {
 			src,
 			...opts,
 		}, {
 			done: emoji => {
 				resolve(emoji);
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -589,7 +606,7 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: {
 	uploadFolder?: string | null;
 }): Promise<Misskey.entities.DriveFile> {
 	return new Promise(resolve => {
-		popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), {
+		const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), {
 			file: image,
 			aspectRatio: options.aspectRatio,
 			uploadFolder: options.uploadFolder,
@@ -597,7 +614,8 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: {
 			ok: x => {
 				resolve(x);
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 }
 
@@ -608,8 +626,7 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n
 	onClosing?: () => void;
 }): Promise<void> {
 	return new Promise(resolve => {
-		let dispose;
-		popup(MkPopupMenu, {
+		const { dispose } = popup(MkPopupMenu, {
 			items,
 			src,
 			width: options?.width,
@@ -623,8 +640,6 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n
 			closing: () => {
 				if (options?.onClosing) options.onClosing();
 			},
-		}).then(res => {
-			dispose = res.dispose;
 		});
 	});
 }
@@ -632,8 +647,7 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n
 export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
 	ev.preventDefault();
 	return new Promise(resolve => {
-		let dispose;
-		popup(MkContextMenu, {
+		const { dispose } = popup(MkContextMenu, {
 			items,
 			ev,
 		}, {
@@ -641,8 +655,6 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
 				resolve();
 				dispose();
 			},
-		}).then(res => {
-			dispose = res.dispose;
 		});
 	});
 }
@@ -656,14 +668,11 @@ export function post(props: Record<string, any> = {}): Promise<void> {
 		//       Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、
 		//       複数のpost formを開いたときに場合によってはエラーになる
 		//       もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが
-		let dispose;
-		popup(MkPostFormDialog, props, {
+		const { dispose } = popup(MkPostFormDialog, props, {
 			closed: () => {
 				resolve();
 				dispose();
 			},
-		}).then(res => {
-			dispose = res.dispose;
 		});
 	});
 }
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index f57aa51b5b..1459997dcb 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -464,16 +464,20 @@ function toggleRoleItem(role) {
 }
 
 function createAnnouncement() {
-	os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
 		user: user.value,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 function editAnnouncement(announcement) {
-	os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
 		user: user.value,
 		announcement,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 watch(() => props.userId, () => {
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
index a52f8eb7af..93800873f9 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
@@ -109,7 +109,7 @@ async function onDeleteButtonClicked(id: string) {
 
 async function showEditor(mode: 'create' | 'edit', id?: string) {
 	const { dispose, needLoad } = await new Promise<{ dispose: () => void, needLoad: boolean }>(async resolve => {
-		const res = await os.popup(
+		const { dispose: _dispose } = os.popup(
 			defineAsyncComponent(() => import('./notification-recipient.editor.vue')),
 			{
 				mode,
@@ -117,10 +117,10 @@ async function showEditor(mode: 'create' | 'edit', id?: string) {
 			},
 			{
 				submitted: async () => {
-					resolve({ dispose: res.dispose, needLoad: true });
+					resolve({ dispose: _dispose, needLoad: true });
 				},
 				closed: () => {
-					resolve({ dispose: res.dispose, needLoad: false });
+					resolve({ dispose: _dispose, needLoad: false });
 				},
 			},
 		);
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 3e2332e408..eea3f68130 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -129,18 +129,19 @@ const toggleSelect = (emoji) => {
 };
 
 const add = async (ev: MouseEvent) => {
-	os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
 	}, {
 		done: result => {
 			if (result.created) {
 				emojisPaginationComponent.value.prepend(result.created);
 			}
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 };
 
 const edit = (emoji) => {
-	os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), {
 		emoji: emoji,
 	}, {
 		done: result => {
@@ -153,7 +154,8 @@ const edit = (emoji) => {
 				emojisPaginationComponent.value.removeItem(emoji.id);
 			}
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 };
 
 const importEmoji = (emoji) => {
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index 8077edff5f..7a8786d415 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -160,7 +160,7 @@ function rename() {
 function describe() {
 	if (!file.value) return;
 
-	os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
 		default: file.value.comment ?? '',
 		file: file.value,
 	}, {
@@ -172,7 +172,8 @@ function describe() {
 				await fetch();
 			});
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 async function deleteFile() {
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index eba5b92154..10bcfa6d4e 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -1008,8 +1008,18 @@ function attachGameEvents() {
 		const domX = rect.left + (x * viewScale);
 		const domY = rect.top + (y * viewScale);
 		const scoreUnit = getScoreUnit(props.gameMode);
-		os.popup(MkRippleEffect, { x: domX, y: domY }, {}, 'end');
-		os.popup(MkPlusOneEffect, { x: domX, y: domY, value: scoreDelta + (scoreUnit === 'pt' ? '' : scoreUnit) }, {}, 'end');
+
+		{
+			const { dispose } = os.popup(MkRippleEffect, { x: domX, y: domY }, {
+				end: () => dispose(),
+			});
+		}
+
+		{
+			const { dispose } = os.popup(MkPlusOneEffect, { x: domX, y: domY, value: scoreDelta + (scoreUnit === 'pt' ? '' : scoreUnit) }, {
+				end: () => dispose(),
+			});
+		}
 
 		if (nextMono) {
 			const def = monoDefinitions.value.find(x => x.id === nextMono.id)!;
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index 5301a08521..ae3a2c31e3 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import * as os from '@/os.js';
 import * as Misskey from 'misskey-js';
+import * as os from '@/os.js';
 import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { i18n } from '@/i18n.js';
@@ -40,12 +40,12 @@ function menu(ev) {
 		text: i18n.ts.info,
 		icon: 'ti ti-info-circle',
 		action: async () => {
-			os.popup(MkCustomEmojiDetailedDialog, {
+			const { dispose } = os.popup(MkCustomEmojiDetailedDialog, {
 				emoji: await misskeyApiGet('emoji', {
 					name: props.emoji.name,
-				})
+				}),
 			}, {
-				anchor: ev.target,
+				closed: () => dispose(),
 			});
 		},
 	}], ev.currentTarget ?? ev.target);
diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue
index 6b67a9cc87..6d24029535 100644
--- a/packages/frontend/src/pages/reset-password.vue
+++ b/packages/frontend/src/pages/reset-password.vue
@@ -44,7 +44,9 @@ async function save() {
 
 onMounted(() => {
 	if (props.token == null) {
-		os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {}, 'closed');
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
+			closed: () => dispose(),
+		});
 		mainRouter.push('/');
 	}
 });
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index b7d648c1a4..6a9a1e16e2 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -108,9 +108,11 @@ async function registerTOTP(): Promise<void> {
 		token: auth.result.token,
 	});
 
-	os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
 		twoFactorData,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 async function unregisterTOTP(): Promise<void> {
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 1182346de9..08c9261dcf 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -74,22 +74,24 @@ async function removeAccount(account) {
 }
 
 function addExistingAccount() {
-	os.popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
 		done: async res => {
 			await addAccounts(res.id, res.i);
 			os.success();
 			init();
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 function createAccount() {
-	os.popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
 		done: async res => {
 			await addAccounts(res.id, res.i);
 			switchAccountWithToken(res.i);
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 async function switchAccount(account: any) {
diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue
index d9596b4e45..b35d406a98 100644
--- a/packages/frontend/src/pages/settings/api.vue
+++ b/packages/frontend/src/pages/settings/api.vue
@@ -23,7 +23,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 const isDesktop = ref(window.innerWidth >= 1100);
 
 function generateToken() {
-	os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, {
 		done: async result => {
 			const { name, permissions } = result;
 			const { token } = await misskeyApi('miauth/gen-token', {
@@ -38,7 +38,8 @@ function generateToken() {
 				text: token,
 			});
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
index 3cc911c014..77229d3349 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -67,7 +67,7 @@ misskeyApi('get-avatar-decorations').then(_avatarDecorations => {
 });
 
 function openDecoration(avatarDecoration, index?: number) {
-	os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), {
 		decoration: avatarDecoration,
 		usingIndex: index,
 	}, {
@@ -108,7 +108,8 @@ function openDecoration(avatarDecoration, index?: number) {
 			});
 			$i.avatarDecorations = update;
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 function detachAllDecorations() {
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index 7aca5f83b2..14c83ed637 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -27,7 +27,7 @@ function rename(file: Misskey.entities.DriveFile) {
 }
 
 function describe(file: Misskey.entities.DriveFile) {
-	os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
 		default: file.comment ?? '',
 		file: file,
 	}, {
@@ -37,7 +37,8 @@ function describe(file: Misskey.entities.DriveFile) {
 				comment: caption.length === 0 ? null : caption,
 			});
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 function toggleSensitive(file: Misskey.entities.DriveFile) {
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 71ad299f50..418b6abc88 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -136,10 +136,12 @@ export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): Men
 			let noteInfo = '';
 			if (note.url ?? note.uri != null) noteInfo = `Note: ${note.url ?? note.uri}\n`;
 			noteInfo += `Local Note: ${localUrl}\n`;
-			os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+			const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
 				user: note.user,
 				initialComment: `${noteInfo}-----\n`,
-			}, {}, 'closed');
+			}, {
+				closed: () => dispose(),
+			});
 		},
 	};
 }
@@ -530,7 +532,9 @@ export function getRenoteMenu(props: {
 					const rect = el.getBoundingClientRect();
 					const x = rect.left + (el.offsetWidth / 2);
 					const y = rect.top + (el.offsetHeight / 2);
-					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+					const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+						end: () => dispose(),
+					});
 				}
 
 				if (!props.mock) {
@@ -566,7 +570,9 @@ export function getRenoteMenu(props: {
 					const rect = el.getBoundingClientRect();
 					const x = rect.left + (el.offsetWidth / 2);
 					const y = rect.top + (el.offsetHeight / 2);
-					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+					const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+						end: () => dispose(),
+					});
 				}
 
 				const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
@@ -615,7 +621,9 @@ export function getRenoteMenu(props: {
 							const rect = el.getBoundingClientRect();
 							const x = rect.left + (el.offsetWidth / 2);
 							const y = rect.top + (el.offsetHeight / 2);
-							os.popup(MkRippleEffect, { x, y }, {}, 'end');
+							const { dispose } = os.popup(MkRippleEffect, { x, y }, {
+								end: () => dispose(),
+							});
 						}
 
 						if (!props.mock) {
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 3e031d232f..ac8774fad0 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -100,9 +100,11 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 	}
 
 	function reportAbuse() {
-		os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
 			user: user,
-		}, {}, 'closed');
+		}, {
+			closed: () => dispose(),
+		});
 	}
 
 	async function getConfirmed(text: string): Promise<boolean> {
diff --git a/packages/frontend/src/scripts/install-plugin.ts b/packages/frontend/src/scripts/install-plugin.ts
index d0a8675b19..37f473b6de 100644
--- a/packages/frontend/src/scripts/install-plugin.ts
+++ b/packages/frontend/src/scripts/install-plugin.ts
@@ -103,7 +103,7 @@ export async function installPlugin(code: string, meta?: AiScriptPluginMeta) {
 	}
 
 	const token = realMeta.permissions == null || realMeta.permissions.length === 0 ? null : await new Promise((res, rej) => {
-		os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {
 			title: i18n.ts.tokenRequested,
 			information: i18n.ts.pluginTokenRequestedDescription,
 			initialName: realMeta.name,
@@ -118,7 +118,8 @@ export async function installPlugin(code: string, meta?: AiScriptPluginMeta) {
 				});
 				res(token);
 			},
-		}, 'closed');
+			closed: () => dispose(),
+		});
 	});
 
 	savePlugin({
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index 9e51272791..363da5f633 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -11,7 +11,7 @@ import { popup } from '@/os.js';
 export function pleaseLogin(path?: string) {
 	if ($i) return;
 
-	popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {
+	const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {
 		autoSet: true,
 		message: i18n.ts.signinRequired,
 	}, {
@@ -20,7 +20,8 @@ export function pleaseLogin(path?: string) {
 				window.location.href = path;
 			}
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 
 	throw new Error('signin required');
 }
diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts
index bed221a622..bba64fc6ee 100644
--- a/packages/frontend/src/scripts/use-chart-tooltip.ts
+++ b/packages/frontend/src/scripts/use-chart-tooltip.ts
@@ -17,20 +17,16 @@ export function useChartTooltip(opts: { position: 'top' | 'middle' } = { positio
 		borderColor: string;
 		text: string;
 	}[] | null>(null);
-	let disposeTooltipComponent;
-
-	os.popup(MkChartTooltip, {
+	const { dispose: disposeTooltipComponent } = os.popup(MkChartTooltip, {
 		showing: tooltipShowing,
 		x: tooltipX,
 		y: tooltipY,
 		title: tooltipTitle,
 		series: tooltipSeries,
-	}, {}).then(({ dispose }) => {
-		disposeTooltipComponent = dispose;
-	});
+	}, {});
 
 	onUnmounted(() => {
-		if (disposeTooltipComponent) disposeTooltipComponent();
+		disposeTooltipComponent();
 	});
 
 	onDeactivated(() => {
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 839fa5faf8..20a280f681 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -112,7 +112,9 @@ export function openInstanceMenu(ev: MouseEvent) {
 		text: i18n.ts._initialTutorial.launchTutorial,
 		icon: 'ti ti-presentation',
 		action: () => {
-			os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), {}, {}, 'closed');
+			const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTutorialDialog.vue')), {}, {
+				closed: () => dispose(),
+			});
 		},
 	} : undefined, {
 		type: 'link',
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index 5d0e065f09..699aa1e1c8 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -74,8 +74,9 @@ function openAccountMenu(ev: MouseEvent) {
 }
 
 function more() {
-	os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {}, {
-	}, 'closed');
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {}, {
+		closed: () => dispose(),
+	});
 }
 </script>
 
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index fa1f0eb8c7..b029533f28 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -99,10 +99,11 @@ function openAccountMenu(ev: MouseEvent) {
 }
 
 function more(ev: MouseEvent) {
-	os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
 		src: ev.currentTarget ?? ev.target,
 	}, {
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 </script>
 
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index ee5176b558..c03afd6cd6 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -71,11 +71,12 @@ const otherNavItemIndicated = computed<boolean>(() => {
 });
 
 function more(ev: MouseEvent) {
-	os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
 		src: ev.currentTarget ?? ev.target,
 		anchor: { x: 'center', y: 'bottom' },
 	}, {
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 function openAccountMenu(ev: MouseEvent) {
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index 19672ef87f..d8574a915f 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -86,9 +86,11 @@ function calcViewState() {
 }
 
 function more(ev: MouseEvent) {
-	os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
 		src: ev.currentTarget ?? ev.target,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 function openAccountMenu(ev: MouseEvent) {
diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue
index 451cc58791..23b0fd4f7b 100644
--- a/packages/frontend/src/ui/deck/notifications-column.vue
+++ b/packages/frontend/src/ui/deck/notifications-column.vue
@@ -27,7 +27,7 @@ const props = defineProps<{
 const notificationsComponent = shallowRef<InstanceType<typeof XNotifications>>();
 
 function func() {
-	os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
 		excludeTypes: props.column.excludeTypes,
 	}, {
 		done: async (res) => {
@@ -36,7 +36,8 @@ function func() {
 				excludeTypes: excludeTypes,
 			});
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 }
 
 const menu = [{
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 80623083cf..c229946bd4 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -126,15 +126,19 @@ const keymap = computed(() => {
 });
 
 function signin() {
-	os.popup(XSigninDialog, {
+	const { dispose } = os.popup(XSigninDialog, {
 		autoSet: true,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 function signup() {
-	os.popup(XSignupDialog, {
+	const { dispose } = os.popup(XSignupDialog, {
 		autoSet: true,
-	}, {}, 'closed');
+	}, {
+		closed: () => dispose(),
+	});
 }
 
 onMounted(() => {
diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue
index 4b3265dab7..773c078b49 100644
--- a/packages/frontend/src/widgets/WidgetNotifications.vue
+++ b/packages/frontend/src/widgets/WidgetNotifications.vue
@@ -54,7 +54,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
 );
 
 const configureNotification = () => {
-	os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
+	const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
 		excludeTypes: widgetProps.excludeTypes,
 	}, {
 		done: async (res) => {
@@ -62,7 +62,8 @@ const configureNotification = () => {
 			widgetProps.excludeTypes = excludeTypes;
 			save();
 		},
-	}, 'closed');
+		closed: () => dispose(),
+	});
 };
 
 defineExpose<WidgetComponentExpose>({

From b9ed3b2427593a279f129f6ee10064e5955fb5b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 6 Jul 2024 11:46:43 +0900
Subject: [PATCH 071/206] =?UTF-8?q?fix(dev):=20dev=E3=82=B5=E3=83=BC?=
 =?UTF-8?q?=E3=83=90=E3=83=BC=E3=81=A7`/notes/`=E3=81=AB=E7=9B=B4=E3=81=A7?=
 =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=BB=E3=82=B9=E3=81=97=E3=81=9F=E3=82=89?=
 =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E5=81=B4=E3=81=AE=E3=83=AC?=
 =?UTF-8?q?=E3=82=B9=E3=83=9D=E3=83=B3=E3=82=B9=E3=81=8C=E8=BF=94=E3=81=A3?=
 =?UTF-8?q?=E3=81=A6=E3=81=8F=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#14137)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/vite.config.local-dev.ts | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
index f9dff13b15..0a88e489c1 100644
--- a/packages/frontend/vite.config.local-dev.ts
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -1,6 +1,8 @@
 import dns from 'dns';
 import { readFile } from 'node:fs/promises';
+import type { IncomingMessage } from 'node:http';
 import { defineConfig } from 'vite';
+import type { UserConfig } from 'vite';
 import * as yaml from 'js-yaml';
 import locales from '../../locales/index.js';
 import { getConfig } from './vite.config.js';
@@ -14,7 +16,15 @@ const { port } = yaml.load(await readFile('../../.config/default.yml', 'utf-8'))
 const httpUrl = `http://localhost:${port}/`;
 const websocketUrl = `ws://localhost:${port}/`;
 
-const devConfig = {
+// activitypubリクエストはProxyを通し、それ以外はViteの開発サーバーを返す
+function varyHandler(req: IncomingMessage) {
+	if (req.headers.accept?.includes('application/activity+json')) {
+		return null;
+	}
+	return '/index.html';
+}
+
+const devConfig: UserConfig = {
 	// 基本の設定は vite.config.js から引き継ぐ
 	...defaultConfig,
 	root: 'src',
@@ -54,15 +64,11 @@ const devConfig = {
 			'/inbox': httpUrl,
 			'/notes': {
 				target: httpUrl,
-				headers: {
-					'Accept': 'application/activity+json',
-				},
+				bypass: varyHandler,
 			},
 			'/users': {
 				target: httpUrl,
-				headers: {
-					'Accept': 'application/activity+json',
-				},
+				bypass: varyHandler,
 			},
 			'/.well-known': {
 				target: httpUrl,

From 8e1d94c6c70679f041f3f4f9cb97c633deb4f536 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 6 Jul 2024 21:46:19 +0900
Subject: [PATCH 072/206] fix import path

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

diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index 9786f8b8bb..9d175119dd 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -7,13 +7,13 @@ import { URL } from 'node:url';
 import { Inject, Injectable } from '@nestjs/common';
 import * as parse5 from 'parse5';
 import { Window, XMLSerializer } from 'happy-dom';
+import * as TreeAdapter from 'parse5/dist/tree-adapters/default.js';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { intersperse } from '@/misc/prelude/array.js';
 import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 import type { IMentionedRemoteUsers } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
-import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js';
 import type * as mfm from 'mfm-js';
 
 const treeAdapter = TreeAdapter.defaultTreeAdapter;

From 0ea88c07b4e34271ca155d69dfa09b01df96cdd4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 6 Jul 2024 22:52:41 +0900
Subject: [PATCH 073/206] fix changelog

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a28c9ef64..51c008c973 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,16 +14,16 @@
 - Fix: テーマプレビューが見れない問題を修正
 
 ### Server
-- チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
-- Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
-- Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
 - Enhance: エンドポイント`clips/update`の必須項目を`clipId`のみに
 - Enhance: エンドポイント`admin/roles/update`の必須項目を`roleId`のみに
 - Enhance: エンドポイント`pages/update`の必須項目を`pageId`のみに
 - Enhance: エンドポイント`gallery/posts/update`の必須項目を`postId`のみに
 - Enhance: エンドポイント`i/webhook/update`の必須項目を`webhookId`のみに
 - Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに
+- Fix: チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
+- Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
+- Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
 - Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)
 - Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正
 - Fix: 自分以外のクリップ内のノート個数が見えることがあるのを修正

From fe852920c3343df4df50b278c78eddaace4915a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 7 Jul 2024 09:55:06 +0900
Subject: [PATCH 074/206] =?UTF-8?q?fix(backend):=20parse5=E9=96=A2?=
 =?UTF-8?q?=E4=BF=82=E3=81=AE=E5=9E=8B=E3=81=AEimport=E6=96=B9=E6=B3=95?=
 =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4=20(#14146)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/core/MfmService.ts | 44 +++++++++++--------------
 1 file changed, 20 insertions(+), 24 deletions(-)

diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index 9d175119dd..74536c68f5 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -7,16 +7,18 @@ import { URL } from 'node:url';
 import { Inject, Injectable } from '@nestjs/common';
 import * as parse5 from 'parse5';
 import { Window, XMLSerializer } from 'happy-dom';
-import * as TreeAdapter from 'parse5/dist/tree-adapters/default.js';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { intersperse } from '@/misc/prelude/array.js';
 import { normalizeForSearch } from '@/misc/normalize-for-search.js';
 import type { IMentionedRemoteUsers } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
+import type { DefaultTreeAdapterMap } from 'parse5';
 import type * as mfm from 'mfm-js';
 
-const treeAdapter = TreeAdapter.defaultTreeAdapter;
+const treeAdapter = parse5.defaultTreeAdapter;
+type Node = DefaultTreeAdapterMap['node'];
+type ChildNode = DefaultTreeAdapterMap['childNode'];
 
 const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
 const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
@@ -46,7 +48,7 @@ export class MfmService {
 
 		return text.trim();
 
-		function getText(node: TreeAdapter.Node): string {
+		function getText(node: Node): string {
 			if (treeAdapter.isTextNode(node)) return node.value;
 			if (!treeAdapter.isElementNode(node)) return '';
 			if (node.nodeName === 'br') return '\n';
@@ -58,7 +60,7 @@ export class MfmService {
 			return '';
 		}
 
-		function appendChildren(childNodes: TreeAdapter.ChildNode[]): void {
+		function appendChildren(childNodes: ChildNode[]): void {
 			if (childNodes) {
 				for (const n of childNodes) {
 					analyze(n);
@@ -66,14 +68,16 @@ export class MfmService {
 			}
 		}
 
-		function analyze(node: TreeAdapter.Node) {
+		function analyze(node: Node) {
 			if (treeAdapter.isTextNode(node)) {
 				text += node.value;
 				return;
 			}
 
 			// Skip comment or document type node
-			if (!treeAdapter.isElementNode(node)) return;
+			if (!treeAdapter.isElementNode(node)) {
+				return;
+			}
 
 			switch (node.nodeName) {
 				case 'br': {
@@ -81,8 +85,7 @@ export class MfmService {
 					break;
 				}
 
-				case 'a':
-				{
+				case 'a': {
 					const txt = getText(node);
 					const rel = node.attrs.find(x => x.name === 'rel');
 					const href = node.attrs.find(x => x.name === 'href');
@@ -90,7 +93,7 @@ export class MfmService {
 					// ハッシュタグ
 					if (normalizedHashtagNames && href && normalizedHashtagNames.has(normalizeForSearch(txt))) {
 						text += txt;
-					// メンション
+						// メンション
 					} else if (txt.startsWith('@') && !(rel && rel.value.startsWith('me '))) {
 						const part = txt.split('@');
 
@@ -102,7 +105,7 @@ export class MfmService {
 						} else if (part.length === 3) {
 							text += txt;
 						}
-					// その他
+						// その他
 					} else {
 						const generateLink = () => {
 							if (!href && !txt) {
@@ -130,8 +133,7 @@ export class MfmService {
 					break;
 				}
 
-				case 'h1':
-				{
+				case 'h1': {
 					text += '【';
 					appendChildren(node.childNodes);
 					text += '】\n';
@@ -139,16 +141,14 @@ export class MfmService {
 				}
 
 				case 'b':
-				case 'strong':
-				{
+				case 'strong': {
 					text += '**';
 					appendChildren(node.childNodes);
 					text += '**';
 					break;
 				}
 
-				case 'small':
-				{
+				case 'small': {
 					text += '<small>';
 					appendChildren(node.childNodes);
 					text += '</small>';
@@ -156,8 +156,7 @@ export class MfmService {
 				}
 
 				case 's':
-				case 'del':
-				{
+				case 'del': {
 					text += '~~';
 					appendChildren(node.childNodes);
 					text += '~~';
@@ -165,8 +164,7 @@ export class MfmService {
 				}
 
 				case 'i':
-				case 'em':
-				{
+				case 'em': {
 					text += '<i>';
 					appendChildren(node.childNodes);
 					text += '</i>';
@@ -207,8 +205,7 @@ export class MfmService {
 				case 'h3':
 				case 'h4':
 				case 'h5':
-				case 'h6':
-				{
+				case 'h6': {
 					text += '\n\n';
 					appendChildren(node.childNodes);
 					break;
@@ -221,8 +218,7 @@ export class MfmService {
 				case 'article':
 				case 'li':
 				case 'dt':
-				case 'dd':
-				{
+				case 'dd': {
 					text += '\n';
 					appendChildren(node.childNodes);
 					break;

From 984d582796f41f200d417b2d7647e9cb25b8dcec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 7 Jul 2024 09:56:09 +0900
Subject: [PATCH 075/206] =?UTF-8?q?fix(frontend):=20=E3=82=B5=E3=83=BC?=
 =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B5=E3=82=A4=E3=83=89boot=E3=81=A7?=
 =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E7=94=BB=E9=9D=A2=E3=81=AE=E6=8F=8F?=
 =?UTF-8?q?=E7=94=BB=E6=99=82=E3=81=ABDOM=E3=81=8C=E5=88=9D=E6=9C=9F?=
 =?UTF-8?q?=E5=8C=96=E3=81=A7=E3=81=8D=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#14139)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 396536948e..4275dc9527 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -29,7 +29,8 @@
 
 	let forceError = localStorage.getItem('forceError');
 	if (forceError != null) {
-		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
+		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.');
+		return;
 	}
 
 	//#region Detect language & fetch translations
@@ -155,7 +156,12 @@
 		document.head.appendChild(css);
 	}
 
-	function renderError(code, details) {
+	async function renderError(code, details) {
+		// Cannot set property 'innerHTML' of null を回避
+		if (document.readyState === 'loading') {
+			await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
+		}
+
 		let errorsElement = document.getElementById('errors');
 
 		if (!errorsElement) {
@@ -314,6 +320,6 @@
 			#errorInfo {
 				width: 50%;
 			}
-		`)
+		}`)
 	}
 })();

From f119f8c2cc791cec02551bfcd9801616284944e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 7 Jul 2024 14:08:18 +0900
Subject: [PATCH 076/206] =?UTF-8?q?feat(misskey-js):=20multipart/form-data?=
 =?UTF-8?q?=E3=81=AE=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88=E3=81=AB?=
 =?UTF-8?q?=E5=AF=BE=E5=BF=9C=20(#14147)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(misskey-js): multipart/form-dataのリクエストに対応

* lint

* add test

* Update Changelog

* テストを厳しくする

* lint

* multipart/form-dataではnullのプロパティを弾くように
---
 CHANGELOG.md                                  |   3 +
 packages/misskey-js/etc/misskey-js.api.md     |   2 +-
 .../misskey-js/generator/src/generator.ts     |  57 ++-
 packages/misskey-js/src/api.ts                |  51 ++-
 packages/misskey-js/src/autogen/endpoint.ts   | 382 ++++++++++++++++++
 packages/misskey-js/src/autogen/types.ts      |   2 +-
 packages/misskey-js/test/api.ts               |  54 ++-
 7 files changed, 537 insertions(+), 14 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51c008c973..23de5957fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,9 @@
 - Fix: 空文字列のリアクションはフォールバックされるように
 - Fix: リノートにリアクションできないように
 
+### Misskey.js
+- Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
+
 ## 2024.5.0
 
 ### Note
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index bea89f2a7c..be2f510ac2 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1869,7 +1869,7 @@ type FetchExternalResourcesResponse = operations['fetch-external-resources']['re
 // @public (undocumented)
 type FetchLike = (input: string, init?: {
     method?: string;
-    body?: string;
+    body?: Blob | FormData | string;
     credentials?: RequestCredentials;
     cache?: RequestCache;
     headers: {
diff --git a/packages/misskey-js/generator/src/generator.ts b/packages/misskey-js/generator/src/generator.ts
index 78178d7c7e..4ae00a4522 100644
--- a/packages/misskey-js/generator/src/generator.ts
+++ b/packages/misskey-js/generator/src/generator.ts
@@ -20,7 +20,14 @@ async function generateBaseTypes(
 	}
 	lines.push('');
 
-	const generatedTypes = await openapiTS(openApiJsonPath, { exportType: true });
+	const generatedTypes = await openapiTS(openApiJsonPath, {
+		exportType: true,
+		transform(schemaObject) {
+			if ('format' in schemaObject && schemaObject.format === 'binary') {
+				return schemaObject.nullable ? 'Blob | null' : 'Blob';
+			}
+		},
+	});
 	lines.push(generatedTypes);
 	lines.push('');
 
@@ -56,6 +63,8 @@ async function generateEndpoints(
 	endpointOutputPath: string,
 ) {
 	const endpoints: Endpoint[] = [];
+	const endpointReqMediaTypes: EndpointReqMediaType[] = [];
+	const endpointReqMediaTypesSet = new Set<string>();
 
 	// misskey-jsはPOST固定で送っているので、こちらも決め打ちする。別メソッドに対応することがあればこちらも直す必要あり
 	const paths = openApiDocs.paths ?? {};
@@ -78,13 +87,24 @@ async function generateEndpoints(
 			const supportMediaTypes = Object.keys(reqContent);
 			if (supportMediaTypes.length > 0) {
 				// いまのところ複数のメディアタイプをとるエンドポイントは無いので決め打ちする
-				endpoint.request = new OperationTypeAlias(
+				const req = new OperationTypeAlias(
 					operationId,
 					path,
 					supportMediaTypes[0],
 					OperationsAliasType.REQUEST,
 				);
+				endpoint.request = req;
+
+				const reqType = new EndpointReqMediaType(path, req);
+				endpointReqMediaTypesSet.add(reqType.getMediaType());
+				endpointReqMediaTypes.push(reqType);
+			} else {
+				endpointReqMediaTypesSet.add('application/json');
+				endpointReqMediaTypes.push(new EndpointReqMediaType(path, undefined, 'application/json'));
 			}
+		} else {
+			endpointReqMediaTypesSet.add('application/json');
+			endpointReqMediaTypes.push(new EndpointReqMediaType(path, undefined, 'application/json'));
 		}
 
 		if (operation.responses && isResponseObject(operation.responses['200']) && operation.responses['200'].content) {
@@ -137,6 +157,19 @@ async function generateEndpoints(
 	endpointOutputLine.push('}');
 	endpointOutputLine.push('');
 
+	function generateEndpointReqMediaTypesType() {
+		return `Record<keyof Endpoints, ${[...endpointReqMediaTypesSet].map((t) => `'${t}'`).join(' | ')}>`;
+	}
+
+	endpointOutputLine.push(`export const endpointReqTypes: ${generateEndpointReqMediaTypesType()} = {`);
+
+	endpointOutputLine.push(
+		...endpointReqMediaTypes.map(it => '\t' + it.toLine()),
+	);
+
+	endpointOutputLine.push('};');
+	endpointOutputLine.push('');
+
 	await writeFile(endpointOutputPath, endpointOutputLine.join('\n'));
 }
 
@@ -314,6 +347,26 @@ class Endpoint {
 	}
 }
 
+class EndpointReqMediaType {
+	public readonly path: string;
+	public readonly mediaType: string;
+
+	constructor(path: string, request: OperationTypeAlias, mediaType?: undefined);
+	constructor(path: string, request: undefined, mediaType: string);
+	constructor(path: string, request: OperationTypeAlias | undefined, mediaType?: string) {
+		this.path = path;
+		this.mediaType = mediaType ?? request?.mediaType ?? 'application/json';
+	}
+
+	getMediaType(): string {
+		return this.mediaType;
+	}
+
+	toLine(): string {
+		return `'${this.path}': '${this.mediaType}',`;
+	}
+}
+
 async function main() {
 	const generatePath = './built/autogen';
 	await mkdir(generatePath, { recursive: true });
diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts
index 959a634a74..76d055cbe4 100644
--- a/packages/misskey-js/src/api.ts
+++ b/packages/misskey-js/src/api.ts
@@ -1,7 +1,7 @@
 import './autogen/apiClientJSDoc.js';
 
-import { SwitchCaseResponseType } from './api.types.js';
-import type { Endpoints } from './api.types.js';
+import { endpointReqTypes } from './autogen/endpoint.js';
+import type { SwitchCaseResponseType, Endpoints } from './api.types.js';
 
 export type {
 	SwitchCaseResponseType,
@@ -23,7 +23,7 @@ export function isAPIError(reason: Record<PropertyKey, unknown>): reason is APIE
 
 export type FetchLike = (input: string, init?: {
 	method?: string;
-	body?: string;
+	body?: Blob | FormData | string;
 	credentials?: RequestCredentials;
 	cache?: RequestCache;
 	headers: { [key in string]: string }
@@ -49,20 +49,55 @@ export class APIClient {
 		this.fetch = opts.fetch ?? ((...args) => fetch(...args));
 	}
 
+	private assertIsRecord<T>(obj: T): obj is T & Record<string, any> {
+		return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
+	}
+
 	public request<E extends keyof Endpoints, P extends Endpoints[E]['req']>(
 		endpoint: E,
 		params: P = {} as P,
 		credential?: string | null,
 	): Promise<SwitchCaseResponseType<E, P>> {
 		return new Promise((resolve, reject) => {
-			this.fetch(`${this.origin}/api/${endpoint}`, {
-				method: 'POST',
-				body: JSON.stringify({
+			let mediaType = 'application/json';
+			if (endpoint in endpointReqTypes) {
+				mediaType = endpointReqTypes[endpoint];
+			}
+			let payload: FormData | string = '{}';
+
+			if (mediaType === 'application/json') {
+				payload = JSON.stringify({
 					...params,
 					i: credential !== undefined ? credential : this.credential,
-				}),
+				});
+			} else if (mediaType === 'multipart/form-data') {
+				payload = new FormData();
+				const i = credential !== undefined ? credential : this.credential;
+				if (i != null) {
+					payload.append('i', i);
+				}
+				if (this.assertIsRecord(params)) {
+					for (const key in params) {
+						const value = params[key];
+
+						if (value == null) continue;
+
+						if (value instanceof File || value instanceof Blob) {
+							payload.append(key, value);
+						} else if (typeof value === 'object') {
+							payload.append(key, JSON.stringify(value));
+						} else {
+							payload.append(key, value);
+						}
+					}
+				}
+			}
+
+			this.fetch(`${this.origin}/api/${endpoint}`, {
+				method: 'POST',
+				body: payload,
 				headers: {
-					'Content-Type': 'application/json',
+					'Content-Type': endpointReqTypes[endpoint],
 				},
 				credentials: 'omit',
 				cache: 'no-cache',
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 20c8509d4c..be41951e4d 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -954,3 +954,385 @@ export type Endpoints = {
 	'reversi/surrender': { req: ReversiSurrenderRequest; res: EmptyResponse };
 	'reversi/verify': { req: ReversiVerifyRequest; res: ReversiVerifyResponse };
 }
+
+export const endpointReqTypes: Record<keyof Endpoints, 'application/json' | 'multipart/form-data'> = {
+	'admin/meta': 'application/json',
+	'admin/abuse-user-reports': 'application/json',
+	'admin/abuse-report/notification-recipient/list': 'application/json',
+	'admin/abuse-report/notification-recipient/show': 'application/json',
+	'admin/abuse-report/notification-recipient/create': 'application/json',
+	'admin/abuse-report/notification-recipient/update': 'application/json',
+	'admin/abuse-report/notification-recipient/delete': 'application/json',
+	'admin/accounts/create': 'application/json',
+	'admin/accounts/delete': 'application/json',
+	'admin/accounts/find-by-email': 'application/json',
+	'admin/ad/create': 'application/json',
+	'admin/ad/delete': 'application/json',
+	'admin/ad/list': 'application/json',
+	'admin/ad/update': 'application/json',
+	'admin/announcements/create': 'application/json',
+	'admin/announcements/delete': 'application/json',
+	'admin/announcements/list': 'application/json',
+	'admin/announcements/update': 'application/json',
+	'admin/avatar-decorations/create': 'application/json',
+	'admin/avatar-decorations/delete': 'application/json',
+	'admin/avatar-decorations/list': 'application/json',
+	'admin/avatar-decorations/update': 'application/json',
+	'admin/delete-all-files-of-a-user': 'application/json',
+	'admin/unset-user-avatar': 'application/json',
+	'admin/unset-user-banner': 'application/json',
+	'admin/drive/clean-remote-files': 'application/json',
+	'admin/drive/cleanup': 'application/json',
+	'admin/drive/files': 'application/json',
+	'admin/drive/show-file': 'application/json',
+	'admin/emoji/add-aliases-bulk': 'application/json',
+	'admin/emoji/add': 'application/json',
+	'admin/emoji/copy': 'application/json',
+	'admin/emoji/delete-bulk': 'application/json',
+	'admin/emoji/delete': 'application/json',
+	'admin/emoji/import-zip': 'application/json',
+	'admin/emoji/list-remote': 'application/json',
+	'admin/emoji/list': 'application/json',
+	'admin/emoji/remove-aliases-bulk': 'application/json',
+	'admin/emoji/set-aliases-bulk': 'application/json',
+	'admin/emoji/set-category-bulk': 'application/json',
+	'admin/emoji/set-license-bulk': 'application/json',
+	'admin/emoji/update': 'application/json',
+	'admin/federation/delete-all-files': 'application/json',
+	'admin/federation/refresh-remote-instance-metadata': 'application/json',
+	'admin/federation/remove-all-following': 'application/json',
+	'admin/federation/update-instance': 'application/json',
+	'admin/get-index-stats': 'application/json',
+	'admin/get-table-stats': 'application/json',
+	'admin/get-user-ips': 'application/json',
+	'admin/invite/create': 'application/json',
+	'admin/invite/list': 'application/json',
+	'admin/promo/create': 'application/json',
+	'admin/queue/clear': 'application/json',
+	'admin/queue/deliver-delayed': 'application/json',
+	'admin/queue/inbox-delayed': 'application/json',
+	'admin/queue/promote': 'application/json',
+	'admin/queue/stats': 'application/json',
+	'admin/relays/add': 'application/json',
+	'admin/relays/list': 'application/json',
+	'admin/relays/remove': 'application/json',
+	'admin/reset-password': 'application/json',
+	'admin/resolve-abuse-user-report': 'application/json',
+	'admin/send-email': 'application/json',
+	'admin/server-info': 'application/json',
+	'admin/show-moderation-logs': 'application/json',
+	'admin/show-user': 'application/json',
+	'admin/show-users': 'application/json',
+	'admin/suspend-user': 'application/json',
+	'admin/unsuspend-user': 'application/json',
+	'admin/update-meta': 'application/json',
+	'admin/delete-account': 'application/json',
+	'admin/update-user-note': 'application/json',
+	'admin/roles/create': 'application/json',
+	'admin/roles/delete': 'application/json',
+	'admin/roles/list': 'application/json',
+	'admin/roles/show': 'application/json',
+	'admin/roles/update': 'application/json',
+	'admin/roles/assign': 'application/json',
+	'admin/roles/unassign': 'application/json',
+	'admin/roles/update-default-policies': 'application/json',
+	'admin/roles/users': 'application/json',
+	'admin/system-webhook/create': 'application/json',
+	'admin/system-webhook/delete': 'application/json',
+	'admin/system-webhook/list': 'application/json',
+	'admin/system-webhook/show': 'application/json',
+	'admin/system-webhook/update': 'application/json',
+	'announcements': 'application/json',
+	'announcements/show': 'application/json',
+	'antennas/create': 'application/json',
+	'antennas/delete': 'application/json',
+	'antennas/list': 'application/json',
+	'antennas/notes': 'application/json',
+	'antennas/show': 'application/json',
+	'antennas/update': 'application/json',
+	'ap/get': 'application/json',
+	'ap/show': 'application/json',
+	'app/create': 'application/json',
+	'app/show': 'application/json',
+	'auth/accept': 'application/json',
+	'auth/session/generate': 'application/json',
+	'auth/session/show': 'application/json',
+	'auth/session/userkey': 'application/json',
+	'blocking/create': 'application/json',
+	'blocking/delete': 'application/json',
+	'blocking/list': 'application/json',
+	'channels/create': 'application/json',
+	'channels/featured': 'application/json',
+	'channels/follow': 'application/json',
+	'channels/followed': 'application/json',
+	'channels/owned': 'application/json',
+	'channels/show': 'application/json',
+	'channels/timeline': 'application/json',
+	'channels/unfollow': 'application/json',
+	'channels/update': 'application/json',
+	'channels/favorite': 'application/json',
+	'channels/unfavorite': 'application/json',
+	'channels/my-favorites': 'application/json',
+	'channels/search': 'application/json',
+	'charts/active-users': 'application/json',
+	'charts/ap-request': 'application/json',
+	'charts/drive': 'application/json',
+	'charts/federation': 'application/json',
+	'charts/instance': 'application/json',
+	'charts/notes': 'application/json',
+	'charts/user/drive': 'application/json',
+	'charts/user/following': 'application/json',
+	'charts/user/notes': 'application/json',
+	'charts/user/pv': 'application/json',
+	'charts/user/reactions': 'application/json',
+	'charts/users': 'application/json',
+	'clips/add-note': 'application/json',
+	'clips/remove-note': 'application/json',
+	'clips/create': 'application/json',
+	'clips/delete': 'application/json',
+	'clips/list': 'application/json',
+	'clips/notes': 'application/json',
+	'clips/show': 'application/json',
+	'clips/update': 'application/json',
+	'clips/favorite': 'application/json',
+	'clips/unfavorite': 'application/json',
+	'clips/my-favorites': 'application/json',
+	'drive': 'application/json',
+	'drive/files': 'application/json',
+	'drive/files/attached-notes': 'application/json',
+	'drive/files/check-existence': 'application/json',
+	'drive/files/create': 'multipart/form-data',
+	'drive/files/delete': 'application/json',
+	'drive/files/find-by-hash': 'application/json',
+	'drive/files/find': 'application/json',
+	'drive/files/show': 'application/json',
+	'drive/files/update': 'application/json',
+	'drive/files/upload-from-url': 'application/json',
+	'drive/folders': 'application/json',
+	'drive/folders/create': 'application/json',
+	'drive/folders/delete': 'application/json',
+	'drive/folders/find': 'application/json',
+	'drive/folders/show': 'application/json',
+	'drive/folders/update': 'application/json',
+	'drive/stream': 'application/json',
+	'email-address/available': 'application/json',
+	'endpoint': 'application/json',
+	'endpoints': 'application/json',
+	'export-custom-emojis': 'application/json',
+	'federation/followers': 'application/json',
+	'federation/following': 'application/json',
+	'federation/instances': 'application/json',
+	'federation/show-instance': 'application/json',
+	'federation/update-remote-user': 'application/json',
+	'federation/users': 'application/json',
+	'federation/stats': 'application/json',
+	'following/create': 'application/json',
+	'following/delete': 'application/json',
+	'following/update': 'application/json',
+	'following/update-all': 'application/json',
+	'following/invalidate': 'application/json',
+	'following/requests/accept': 'application/json',
+	'following/requests/cancel': 'application/json',
+	'following/requests/list': 'application/json',
+	'following/requests/reject': 'application/json',
+	'gallery/featured': 'application/json',
+	'gallery/popular': 'application/json',
+	'gallery/posts': 'application/json',
+	'gallery/posts/create': 'application/json',
+	'gallery/posts/delete': 'application/json',
+	'gallery/posts/like': 'application/json',
+	'gallery/posts/show': 'application/json',
+	'gallery/posts/unlike': 'application/json',
+	'gallery/posts/update': 'application/json',
+	'get-online-users-count': 'application/json',
+	'get-avatar-decorations': 'application/json',
+	'hashtags/list': 'application/json',
+	'hashtags/search': 'application/json',
+	'hashtags/show': 'application/json',
+	'hashtags/trend': 'application/json',
+	'hashtags/users': 'application/json',
+	'i': 'application/json',
+	'i/2fa/done': 'application/json',
+	'i/2fa/key-done': 'application/json',
+	'i/2fa/password-less': 'application/json',
+	'i/2fa/register-key': 'application/json',
+	'i/2fa/register': 'application/json',
+	'i/2fa/update-key': 'application/json',
+	'i/2fa/remove-key': 'application/json',
+	'i/2fa/unregister': 'application/json',
+	'i/apps': 'application/json',
+	'i/authorized-apps': 'application/json',
+	'i/claim-achievement': 'application/json',
+	'i/change-password': 'application/json',
+	'i/delete-account': 'application/json',
+	'i/export-blocking': 'application/json',
+	'i/export-following': 'application/json',
+	'i/export-mute': 'application/json',
+	'i/export-notes': 'application/json',
+	'i/export-clips': 'application/json',
+	'i/export-favorites': 'application/json',
+	'i/export-user-lists': 'application/json',
+	'i/export-antennas': 'application/json',
+	'i/favorites': 'application/json',
+	'i/gallery/likes': 'application/json',
+	'i/gallery/posts': 'application/json',
+	'i/import-blocking': 'application/json',
+	'i/import-following': 'application/json',
+	'i/import-muting': 'application/json',
+	'i/import-user-lists': 'application/json',
+	'i/import-antennas': 'application/json',
+	'i/notifications': 'application/json',
+	'i/notifications-grouped': 'application/json',
+	'i/page-likes': 'application/json',
+	'i/pages': 'application/json',
+	'i/pin': 'application/json',
+	'i/read-all-unread-notes': 'application/json',
+	'i/read-announcement': 'application/json',
+	'i/regenerate-token': 'application/json',
+	'i/registry/get-all': 'application/json',
+	'i/registry/get-detail': 'application/json',
+	'i/registry/get': 'application/json',
+	'i/registry/keys-with-type': 'application/json',
+	'i/registry/keys': 'application/json',
+	'i/registry/remove': 'application/json',
+	'i/registry/scopes-with-domain': 'application/json',
+	'i/registry/set': 'application/json',
+	'i/revoke-token': 'application/json',
+	'i/signin-history': 'application/json',
+	'i/unpin': 'application/json',
+	'i/update-email': 'application/json',
+	'i/update': 'application/json',
+	'i/move': 'application/json',
+	'i/webhooks/create': 'application/json',
+	'i/webhooks/list': 'application/json',
+	'i/webhooks/show': 'application/json',
+	'i/webhooks/update': 'application/json',
+	'i/webhooks/delete': 'application/json',
+	'invite/create': 'application/json',
+	'invite/delete': 'application/json',
+	'invite/list': 'application/json',
+	'invite/limit': 'application/json',
+	'meta': 'application/json',
+	'emojis': 'application/json',
+	'emoji': 'application/json',
+	'miauth/gen-token': 'application/json',
+	'mute/create': 'application/json',
+	'mute/delete': 'application/json',
+	'mute/list': 'application/json',
+	'renote-mute/create': 'application/json',
+	'renote-mute/delete': 'application/json',
+	'renote-mute/list': 'application/json',
+	'my/apps': 'application/json',
+	'notes': 'application/json',
+	'notes/children': 'application/json',
+	'notes/clips': 'application/json',
+	'notes/conversation': 'application/json',
+	'notes/create': 'application/json',
+	'notes/delete': 'application/json',
+	'notes/favorites/create': 'application/json',
+	'notes/favorites/delete': 'application/json',
+	'notes/featured': 'application/json',
+	'notes/global-timeline': 'application/json',
+	'notes/hybrid-timeline': 'application/json',
+	'notes/local-timeline': 'application/json',
+	'notes/mentions': 'application/json',
+	'notes/polls/recommendation': 'application/json',
+	'notes/polls/vote': 'application/json',
+	'notes/reactions': 'application/json',
+	'notes/reactions/create': 'application/json',
+	'notes/reactions/delete': 'application/json',
+	'notes/renotes': 'application/json',
+	'notes/replies': 'application/json',
+	'notes/search-by-tag': 'application/json',
+	'notes/search': 'application/json',
+	'notes/show': 'application/json',
+	'notes/state': 'application/json',
+	'notes/thread-muting/create': 'application/json',
+	'notes/thread-muting/delete': 'application/json',
+	'notes/timeline': 'application/json',
+	'notes/translate': 'application/json',
+	'notes/unrenote': 'application/json',
+	'notes/user-list-timeline': 'application/json',
+	'notifications/create': 'application/json',
+	'notifications/flush': 'application/json',
+	'notifications/mark-all-as-read': 'application/json',
+	'notifications/test-notification': 'application/json',
+	'page-push': 'application/json',
+	'pages/create': 'application/json',
+	'pages/delete': 'application/json',
+	'pages/featured': 'application/json',
+	'pages/like': 'application/json',
+	'pages/show': 'application/json',
+	'pages/unlike': 'application/json',
+	'pages/update': 'application/json',
+	'flash/create': 'application/json',
+	'flash/delete': 'application/json',
+	'flash/featured': 'application/json',
+	'flash/like': 'application/json',
+	'flash/show': 'application/json',
+	'flash/unlike': 'application/json',
+	'flash/update': 'application/json',
+	'flash/my': 'application/json',
+	'flash/my-likes': 'application/json',
+	'ping': 'application/json',
+	'pinned-users': 'application/json',
+	'promo/read': 'application/json',
+	'roles/list': 'application/json',
+	'roles/show': 'application/json',
+	'roles/users': 'application/json',
+	'roles/notes': 'application/json',
+	'request-reset-password': 'application/json',
+	'reset-db': 'application/json',
+	'reset-password': 'application/json',
+	'server-info': 'application/json',
+	'stats': 'application/json',
+	'sw/show-registration': 'application/json',
+	'sw/update-registration': 'application/json',
+	'sw/register': 'application/json',
+	'sw/unregister': 'application/json',
+	'test': 'application/json',
+	'username/available': 'application/json',
+	'users': 'application/json',
+	'users/clips': 'application/json',
+	'users/followers': 'application/json',
+	'users/following': 'application/json',
+	'users/gallery/posts': 'application/json',
+	'users/get-frequently-replied-users': 'application/json',
+	'users/featured-notes': 'application/json',
+	'users/lists/create': 'application/json',
+	'users/lists/delete': 'application/json',
+	'users/lists/list': 'application/json',
+	'users/lists/pull': 'application/json',
+	'users/lists/push': 'application/json',
+	'users/lists/show': 'application/json',
+	'users/lists/favorite': 'application/json',
+	'users/lists/unfavorite': 'application/json',
+	'users/lists/update': 'application/json',
+	'users/lists/create-from-public': 'application/json',
+	'users/lists/update-membership': 'application/json',
+	'users/lists/get-memberships': 'application/json',
+	'users/notes': 'application/json',
+	'users/pages': 'application/json',
+	'users/flashs': 'application/json',
+	'users/reactions': 'application/json',
+	'users/recommendation': 'application/json',
+	'users/relation': 'application/json',
+	'users/report-abuse': 'application/json',
+	'users/search-by-username-and-host': 'application/json',
+	'users/search': 'application/json',
+	'users/show': 'application/json',
+	'users/achievements': 'application/json',
+	'users/update-memo': 'application/json',
+	'fetch-rss': 'application/json',
+	'fetch-external-resources': 'application/json',
+	'retention': 'application/json',
+	'bubble-game/register': 'application/json',
+	'bubble-game/ranking': 'application/json',
+	'reversi/cancel-match': 'application/json',
+	'reversi/games': 'application/json',
+	'reversi/match': 'application/json',
+	'reversi/invitations': 'application/json',
+	'reversi/show-game': 'application/json',
+	'reversi/surrender': 'application/json',
+	'reversi/verify': 'application/json',
+};
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index b59f8dcbe3..ff731a2fa6 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -13850,7 +13850,7 @@ export type operations = {
            * Format: binary
            * @description The file contents.
            */
-          file: string;
+          file: Blob;
         };
       };
     };
diff --git a/packages/misskey-js/test/api.ts b/packages/misskey-js/test/api.ts
index fa31d23faa..95f1946fa2 100644
--- a/packages/misskey-js/test/api.ts
+++ b/packages/misskey-js/test/api.ts
@@ -5,13 +5,19 @@ enableFetchMocks();
 
 function getFetchCall(call: any[]) {
 	const { body, method } = call[1];
-	if (body != null && typeof body != 'string') {
+	const contentType = call[1].headers['Content-Type'];
+	if (
+		body == null ||
+		(contentType === 'application/json' && typeof body !== 'string') ||
+		(contentType === 'multipart/form-data' && !(body instanceof FormData))
+	) {
 		throw new Error('invalid body');
 	}
 	return {
 		url: call[0],
 		method: method,
-		body: JSON.parse(body as any)
+		contentType: contentType,
+		body: body instanceof FormData ? Object.fromEntries(body.entries()) : JSON.parse(body),
 	};
 }
 
@@ -45,6 +51,7 @@ describe('API', () => {
 		expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
 			url: 'https://misskey.test/api/i',
 			method: 'POST',
+			contentType: 'application/json',
 			body: { i: 'TOKEN' }
 		});
 	});
@@ -78,10 +85,52 @@ describe('API', () => {
 		expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
 			url: 'https://misskey.test/api/notes/show',
 			method: 'POST',
+			contentType: 'application/json',
 			body: { i: 'TOKEN', noteId: 'aaaaa' }
 		});
 	});
 
+	test('multipart/form-data', async () => {
+		fetchMock.resetMocks();
+		fetchMock.mockResponse(async (req) => {
+			if (req.method == 'POST' && req.url == 'https://misskey.test/api/drive/files/create') {
+				if (req.headers.get('Content-Type')?.includes('multipart/form-data')) {
+					return JSON.stringify({ id: 'foo' });
+				} else {
+					return { status: 400 };
+				}
+			} else {
+				return { status: 404 };
+			}
+		});
+
+		const cli = new APIClient({
+			origin: 'https://misskey.test',
+			credential: 'TOKEN',
+		});
+
+		const testFile = new File([], 'foo.txt');
+
+		const res = await cli.request('drive/files/create', {
+			file: testFile,
+			name: null, // nullのパラメータは消える
+		});
+
+		expect(res).toEqual({
+			id: 'foo'
+		});
+
+		expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
+			url: 'https://misskey.test/api/drive/files/create',
+			method: 'POST',
+			contentType: 'multipart/form-data',
+			body: {
+				i: 'TOKEN',
+				file: testFile,
+			}
+		});
+	});
+
 	test('204 No Content で null が返る', async () => {
 		fetchMock.resetMocks();
 		fetchMock.mockResponse(async (req) => {
@@ -104,6 +153,7 @@ describe('API', () => {
 		expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({
 			url: 'https://misskey.test/api/reset-password',
 			method: 'POST',
+			contentType: 'application/json',
 			body: { i: 'TOKEN', token: 'aaa', password: 'aaa' }
 		});
 	});

From 9ef6c4716ca25d8adeb5344c82a433370a086f88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 7 Jul 2024 14:19:00 +0900
Subject: [PATCH 077/206] =?UTF-8?q?fix(backend):=20=E5=90=8D=E5=89=8D?=
 =?UTF-8?q?=E3=82=92=E7=A9=BA=E7=99=BD=E6=96=87=E5=AD=97=E5=88=97=E3=81=A0?=
 =?UTF-8?q?=E3=81=91=E3=81=AB=E3=81=A7=E3=81=8D=E3=82=8B=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#14119)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): 名前を空白文字列だけにできる問題を修正

* Update Changelog

* fix test

* Unicodeを含める

* fix

* ユーザー名がUnicode制御文字とスペースのみで構成される場合はnullに

* Revert "ユーザー名がUnicode制御文字とスペースのみで構成される場合はnullに"

This reverts commit 6c752a69c0d3649072e7e4ed30025183bceb48f9.

* [ci skip] changelog typo
---
 CHANGELOG.md                                        |  2 ++
 .../backend/src/server/api/endpoints/i/update.ts    |  9 ++++++++-
 packages/backend/test/e2e/endpoints.ts              | 13 +++++++++++--
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23de5957fd..f62f966451 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,8 @@
 - Fix: 自分以外のクリップ内のノート個数が見えることがあるのを修正
 - Fix: 空文字列のリアクションはフォールバックされるように
 - Fix: リノートにリアクションできないように
+- Fix: ユーザー名の前後に空白文字列がある場合は省略するように
+- Fix: プロフィール編集時に名前を空白文字列のみにできる問題を修正
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index a8e702f328..b39b52bc41 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -257,7 +257,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
 
-			if (ps.name !== undefined) updates.name = ps.name;
+			if (ps.name !== undefined) {
+				if (ps.name === null) {
+					updates.name = null;
+				} else {
+					const trimmedName = ps.name.trim();
+					updates.name = trimmedName === '' ? null : trimmedName;
+				}
+			}
 			if (ps.description !== undefined) profileUpdates.description = ps.description;
 			if (ps.lang !== undefined) profileUpdates.lang = ps.lang;
 			if (ps.location !== undefined) profileUpdates.location = ps.location;
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index d5583ea8bb..2b2699ecd9 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -117,12 +117,21 @@ describe('Endpoints', () => {
 			assert.strictEqual(res.body.birthday, myBirthday);
 		});
 
-		test('名前を空白にできる', async () => {
+		test('名前を空白のみにした場合nullになる', async () => {
 			const res = await api('i/update', {
 				name: ' ',
 			}, alice);
 			assert.strictEqual(res.status, 200);
-			assert.strictEqual(res.body.name, ' ');
+			assert.strictEqual(res.body.name, null);
+		});
+
+		test('名前の前後に空白(ホワイトスペース)を入れてもトリムされる', async () => {
+			const res = await api('i/update', {
+				// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#white_space
+				name: ' あ い う \u0009\u000b\u000c\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff',
+			}, alice);
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(res.body.name, 'あ い う');
 		});
 
 		test('誕生日の設定を削除できる', async () => {

From 55c990e0d9ed0b5b9df53019c46c8be88a6d9bf5 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Tue, 9 Jul 2024 12:42:02 +0900
Subject: [PATCH 078/206] Fix compose file name (#14153)

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

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 06c2d2f21d..b718f3703f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -165,7 +165,7 @@ cp .github/misskey/test.yml .config/
 ```
 Prepare DB/Redis for testing.
 ```
-docker compose -f packages/backend/test/compose.yaml up
+docker compose -f packages/backend/test/compose.yml up
 ```
 Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`.
 

From b61f270eae18121357748a199ae3044304128a46 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 9 Jul 2024 16:08:49 +0900
Subject: [PATCH 079/206] Bump release actions to v2 (develop-stable(master)
 branches system) (#13941)

---
 .github/workflows/release-edit-with-push.yml | 14 ++++++++------
 .github/workflows/release-with-dispatch.yml  | 20 +++++++++++---------
 .github/workflows/release-with-ready.yml     | 13 ++++++++-----
 3 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml
index 86ee0b3fb5..e1bcb5a665 100644
--- a/.github/workflows/release-edit-with-push.yml
+++ b/.github/workflows/release-edit-with-push.yml
@@ -3,7 +3,7 @@ name: "Release Manager: sync changelog with PR"
 on:
   push:
     branches:
-      - release/**
+      - develop
     paths:
       - 'CHANGELOG.md'
 
@@ -20,17 +20,19 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      # headがrelease/かつopenのPRを1つ取得
+      # headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
       - name: Get PR
         run: |
-          echo "pr_number=$(gh pr list --limit 1 --head "$GITHUB_REF_NAME" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
+          echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number  --jq '.[] | .number')" >> $GITHUB_OUTPUT
         id: get_pr
+        env:
+          STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
       - name: Get target version
-        uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v1
+        uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v2
         id: v
       # CHANGELOG.mdの内容を取得
       - name: Get changelog
-        uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v1
+        uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v2
         with:
           version: ${{ steps.v.outputs.target_version }}
         id: changelog
@@ -39,5 +41,5 @@ jobs:
         run: |
           gh pr edit "$PR_NUMBER" --body "$CHANGELOG"
         env:
-          CHANGELOG: ${{ steps.changelog.outputs.changelog }}
           PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}
+          CHANGELOG: ${{ steps.changelog.outputs.changelog }}
diff --git a/.github/workflows/release-with-dispatch.yml b/.github/workflows/release-with-dispatch.yml
index bc6448cb37..0936bc0ae8 100644
--- a/.github/workflows/release-with-dispatch.yml
+++ b/.github/workflows/release-with-dispatch.yml
@@ -33,18 +33,21 @@ jobs:
       pr_number: ${{ steps.get_pr.outputs.pr_number }}
     steps:
       - uses: actions/checkout@v4
-      # headがrelease/かつopenのPRを1つ取得
+      # headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenのPRを1つ取得
       - name: Get PRs
         run: |
-          echo "pr_number=$(gh pr list --limit 1 --search "head:release/ is:open" --json number  --jq '.[] | .number')" >> $GITHUB_OUTPUT
+          echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number  --jq '.[] | .number')" >> $GITHUB_OUTPUT
         id: get_pr
+        env:
+          STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
 
   merge:
-    uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v1
+    uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v2
     needs: get-pr
     if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }}
     with:
       pr_number: ${{ needs.get-pr.outputs.pr_number }}
+      user: 'github-actions[bot]'
       package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
       # Text to prepend to the changelog
       # The first line must be `## Unreleased`
@@ -65,15 +68,14 @@ jobs:
     secrets:
       RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
       RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
-      RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
-      RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
 
   create-prerelease:
-    uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
+    uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v2
     needs: get-pr
     if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true  }}
     with:
       pr_number: ${{ needs.get-pr.outputs.pr_number }}
+      user: 'github-actions[bot]'
       package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
       use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
       indent: ${{ vars.INDENT }}
@@ -82,10 +84,11 @@ jobs:
       RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
 
   create-target:
-    uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v1
+    uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v2
     needs: get-pr
     if: ${{ needs.get-pr.outputs.pr_number == '' }}
     with:
+      user: 'github-actions[bot]'
       # The script for version increment.
       # process.env.CURRENT_VERSION: The current version.
       #
@@ -118,8 +121,7 @@ jobs:
       package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
       use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
       indent: ${{ vars.INDENT }}
+      stable_branch: ${{ vars.STABLE_BRANCH }}
     secrets:
       RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
       RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
-      RULESET_EDIT_APP_ID: ${{ secrets.RULESET_EDIT_APP_ID }}
-      RULESET_EDIT_APP_PRIVATE_KEY: ${{ secrets.RULESET_EDIT_APP_PRIVATE_KEY }}
diff --git a/.github/workflows/release-with-ready.yml b/.github/workflows/release-with-ready.yml
index a0fad0e336..79b6ade012 100644
--- a/.github/workflows/release-with-ready.yml
+++ b/.github/workflows/release-with-ready.yml
@@ -16,23 +16,26 @@ jobs:
   check:
     runs-on: ubuntu-latest
     outputs:
-      ref: ${{ steps.get_pr.outputs.ref }}
+      head: ${{ steps.get_pr.outputs.head }}
+      base: ${{ steps.get_pr.outputs.base }}
     steps:
       - uses: actions/checkout@v4
       # PR情報を取得
       - name: Get PR
         run: |
-          pr_json=$(gh pr view "$PR_NUMBER" --json isDraft,headRefName)
-          echo "ref=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
+          pr_json=$(gh pr view "$PR_NUMBER" --json isDraft,headRefName,baseRefName)
+          echo "head=$(echo $pr_json | jq -r '.headRefName')" >> $GITHUB_OUTPUT
+          echo "base=$(echo $pr_json | jq -r '.baseRefName')" >> $GITHUB_OUTPUT
         id: get_pr
         env:
           PR_NUMBER: ${{ github.event.pull_request.number }}
   release:
-    uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v1
+    uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v2
     needs: check
-    if: startsWith(needs.check.outputs.ref, 'release/')
+    if: needs.check.outputs.head == github.event.repository.default_branch && needs.check.outputs.base == vars.STABLE_BRANCH
     with:
       pr_number: ${{ github.event.pull_request.number }}
+      user: 'github-actions[bot]'
       package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
       use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
       indent: ${{ vars.INDENT }}

From a5407131d4d15edca924e2718902cefd81e49ee2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 9 Jul 2024 17:59:15 +0900
Subject: [PATCH 080/206] =?UTF-8?q?fix/refactor(frontend):=20hotkey?=
 =?UTF-8?q?=E3=81=AE=E6=94=B9=E4=BF=AE=20(#14157)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* improve(frontend): hotkeyの改修 (#234)

(cherry picked from commit 678be147f4db709dadf25d007cc2e679e98a370e)

* Change path, add missing script

Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>

* fix

* fix

* add missing keycodes

* fix

* update changelog

---------

Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
---
 CHANGELOG.md                                  |   2 +
 packages/frontend/src/boot/main-boot.ts       |  27 +--
 .../frontend/src/components/MkMediaAudio.vue  |  47 +++--
 .../frontend/src/components/MkMediaVideo.vue  |  47 +++--
 packages/frontend/src/components/MkMenu.vue   |  20 +-
 packages/frontend/src/components/MkModal.vue  |   8 +-
 packages/frontend/src/components/MkNote.vue   |  57 +++++-
 .../src/components/MkNoteDetailed.vue         |  26 ++-
 packages/frontend/src/directives/hotkey.ts    |   2 +-
 .../frontend/src/pages/antenna-timeline.vue   |   5 +-
 packages/frontend/src/pages/timeline.vue      |   5 +-
 packages/frontend/src/scripts/hotkey.ts       | 173 +++++++++++-------
 packages/frontend/src/scripts/keycode.ts      |  24 ---
 13 files changed, 273 insertions(+), 170 deletions(-)
 delete mode 100644 packages/frontend/src/scripts/keycode.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f62f966451..1bcf721676 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@
 - Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正 
 - Fix: アンテナの編集画面のボタンに隙間を追加
 - Fix: テーマプレビューが見れない問題を修正
+- Fix: ショートカットキーが連打できる問題を修正  
+  (Cherry-picked from https://github.com/taiyme/misskey/pull/234)
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index faf230a1a2..d327016317 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -13,7 +13,6 @@ import * as sound from '@/scripts/sound.js';
 import { $i, signout, updateAccount } from '@/account.js';
 import { instance } from '@/instance.js';
 import { ColdDeviceStorage, defaultStore } from '@/store.js';
-import { makeHotkey } from '@/scripts/hotkey.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js';
@@ -21,6 +20,7 @@ import { initializeSw } from '@/scripts/initialize-sw.js';
 import { deckStore } from '@/ui/deck/deck-store.js';
 import { emojiPicker } from '@/scripts/emoji-picker.js';
 import { mainRouter } from '@/router/main.js';
+import { type Keymap, makeHotkey } from '@/scripts/hotkey.js';
 
 export async function mainBoot() {
 	const { isClientUpdated } = await common(() => createApp(
@@ -69,14 +69,6 @@ export async function mainBoot() {
 		});
 	}
 
-	const hotkeys = {
-		'd': (): void => {
-			defaultStore.set('darkMode', !defaultStore.state.darkMode);
-		},
-		's': (): void => {
-			mainRouter.push('/search');
-		},
-	};
 	try {
 		if (defaultStore.state.enableSeasonalScreenEffect) {
 			const month = new Date().getMonth() + 1;
@@ -105,9 +97,6 @@ export async function mainBoot() {
 	}
 
 	if ($i) {
-		// only add post shortcuts if logged in
-		hotkeys['p|n'] = post;
-
 		defaultStore.loaded.then(() => {
 			if (defaultStore.state.accountSetupWizard !== -1) {
 				const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSetupDialog.vue')), {}, {
@@ -334,7 +323,19 @@ export async function mainBoot() {
 	}
 
 	// shortcut
-	document.addEventListener('keydown', makeHotkey(hotkeys));
+	const keymap = {
+		'p|n': () => {
+			if ($i == null) return;
+			post();
+		},
+		'd': () => {
+			defaultStore.set('darkMode', !defaultStore.state.darkMode);
+		},
+		's': () => {
+			mainRouter.push('/search');
+		},
+	} as const satisfies Keymap;
+	document.addEventListener('keydown', makeHotkey(keymap), { passive: false });
 
 	initializeSw();
 }
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index ebd4fc9ca4..e8dfcc7768 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -80,6 +80,7 @@ import type { MenuItem } from '@/types/menu.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { type Keymap } from '@/scripts/hotkey.js';
 import bytes from '@/filters/bytes.js';
 import { hms } from '@/filters/hms.js';
 import MkMediaRange from '@/components/MkMediaRange.vue';
@@ -90,32 +91,44 @@ const props = defineProps<{
 }>();
 
 const keymap = {
-	'up': () => {
-		if (hasFocus() && audioEl.value) {
-			volume.value = Math.min(volume.value + 0.1, 1);
-		}
+	'up': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && audioEl.value) {
+				volume.value = Math.min(volume.value + 0.1, 1);
+			}
+		},
 	},
-	'down': () => {
-		if (hasFocus() && audioEl.value) {
-			volume.value = Math.max(volume.value - 0.1, 0);
-		}
+	'down': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && audioEl.value) {
+				volume.value = Math.max(volume.value - 0.1, 0);
+			}
+		},
 	},
-	'left': () => {
-		if (hasFocus() && audioEl.value) {
-			audioEl.value.currentTime = Math.max(audioEl.value.currentTime - 5, 0);
-		}
+	'left': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && audioEl.value) {
+				audioEl.value.currentTime = Math.max(audioEl.value.currentTime - 5, 0);
+			}
+		},
 	},
-	'right': () => {
-		if (hasFocus() && audioEl.value) {
-			audioEl.value.currentTime = Math.min(audioEl.value.currentTime + 5, audioEl.value.duration);
-		}
+	'right': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && audioEl.value) {
+				audioEl.value.currentTime = Math.min(audioEl.value.currentTime + 5, audioEl.value.duration);
+			}
+		},
 	},
 	'space': () => {
 		if (hasFocus()) {
 			togglePlayPause();
 		}
 	},
-};
+} as const satisfies Keymap;
 
 // PlayerElもしくはその子要素にフォーカスがあるかどうか
 function hasFocus() {
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 707d7c1501..7c46084c63 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -112,6 +112,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, shallowRef, computed, watch, onDeactivated, onActivated, onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import type { MenuItem } from '@/types/menu.js';
+import { type Keymap } from '@/scripts/hotkey.js';
 import bytes from '@/filters/bytes.js';
 import { hms } from '@/filters/hms.js';
 import { defaultStore } from '@/store.js';
@@ -127,32 +128,44 @@ const props = defineProps<{
 }>();
 
 const keymap = {
-	'up': () => {
-		if (hasFocus() && videoEl.value) {
-			volume.value = Math.min(volume.value + 0.1, 1);
-		}
+	'up': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && videoEl.value) {
+				volume.value = Math.min(volume.value + 0.1, 1);
+			}
+		},
 	},
-	'down': () => {
-		if (hasFocus() && videoEl.value) {
-			volume.value = Math.max(volume.value - 0.1, 0);
-		}
+	'down': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && videoEl.value) {
+				volume.value = Math.max(volume.value - 0.1, 0);
+			}
+		},
 	},
-	'left': () => {
-		if (hasFocus() && videoEl.value) {
-			videoEl.value.currentTime = Math.max(videoEl.value.currentTime - 5, 0);
-		}
+	'left': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && videoEl.value) {
+				videoEl.value.currentTime = Math.max(videoEl.value.currentTime - 5, 0);
+			}
+		},
 	},
-	'right': () => {
-		if (hasFocus() && videoEl.value) {
-			videoEl.value.currentTime = Math.min(videoEl.value.currentTime + 5, videoEl.value.duration);
-		}
+	'right': {
+		allowRepeat: true,
+		callback: () => {
+			if (hasFocus() && videoEl.value) {
+				videoEl.value.currentTime = Math.min(videoEl.value.currentTime + 5, videoEl.value.duration);
+			}
+		},
 	},
 	'space': () => {
 		if (hasFocus()) {
 			togglePlayPause();
 		}
 	},
-};
+} as const satisfies Keymap;
 
 // PlayerElもしくはその子要素にフォーカスがあるかどうか
 function hasFocus() {
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index d91239b9e2..119504f744 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -98,6 +98,7 @@ import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuRadio
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { isTouchUsing } from '@/scripts/touch.js';
+import { type Keymap } from '@/scripts/hotkey.js';
 
 const childrenCache = new WeakMap<MenuParent, MenuItem[]>();
 </script>
@@ -125,11 +126,20 @@ const items2 = ref<InnerMenuItem[]>();
 
 const child = shallowRef<InstanceType<typeof XChild>>();
 
-const keymap = computed(() => ({
-	'up|k|shift+tab': focusUp,
-	'down|j|tab': focusDown,
-	'esc': close,
-}));
+const keymap = {
+	'up|k|shift+tab': {
+		allowRepeat: true,
+		callback: () => focusUp(),
+	},
+	'down|j|tab': {
+		allowRepeat: true,
+		callback: () => focusDown(),
+	},
+	'esc': {
+		allowRepeat: true,
+		callback: () => close(false),
+	},
+} as const satisfies Keymap;
 
 const childShowingItem = ref<MenuItem | null>();
 
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 9e69ab2207..264d8b6c9c 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -47,6 +47,7 @@ import * as os from '@/os.js';
 import { isTouchUsing } from '@/scripts/touch.js';
 import { defaultStore } from '@/store.js';
 import { deviceKind } from '@/scripts/device-kind.js';
+import { type Keymap } from '@/scripts/hotkey.js';
 
 function getFixedContainer(el: Element | null): Element | null {
 	if (el == null || el.tagName === 'BODY') return null;
@@ -154,8 +155,11 @@ if (type.value === 'drawer') {
 }
 
 const keymap = {
-	'esc': () => emit('esc'),
-};
+	'esc': {
+		allowRepeat: true,
+		callback: () => emit('esc'),
+	},
+} as const satisfies Keymap;
 
 const MARGIN = 16;
 const SCROLLBAR_THICKNESS = 16;
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 1313e4c58e..5f1820a379 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -198,6 +198,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import { shouldCollapsed } from '@/scripts/collapsed.js';
 import { isEnabledUrlPreview } from '@/instance.js';
+import { type Keymap } from '@/scripts/hotkey.js';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -294,15 +295,53 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string
 }
 
 const keymap = {
-	'r': () => reply(true),
-	'e|a|plus': () => react(true),
-	'q': () => renote(true),
-	'up|k|shift+tab': focusBefore,
-	'down|j|tab': focusAfter,
-	'esc': blur,
-	'm|o': () => showMenu(true),
-	's': () => showContent.value !== showContent.value,
-};
+	'r': () => {
+		if (renoteCollapsed.value) return;
+		reply();
+	},
+	'e|a|plus': () => {
+		if (renoteCollapsed.value) return;
+		react();
+	},
+	'q': () => {
+		if (renoteCollapsed.value) return;
+		renote();
+	},
+	'm': () => {
+		if (renoteCollapsed.value) return;
+		showMenu();
+	},
+	'c': () => {
+		if (renoteCollapsed.value) return;
+		if (!defaultStore.state.showClipButtonInNoteFooter) return;
+		clip();
+	},
+	'o': () => {
+		if (renoteCollapsed.value) return;
+		galleryEl.value?.openGallery();
+	},
+	'v|enter': () => {
+		if (renoteCollapsed.value) {
+			renoteCollapsed.value = false;
+		} else if (appearNote.value.cw != null) {
+			showContent.value = !showContent.value;
+		} else if (isLong) {
+			collapsed.value = !collapsed.value;
+		}
+	},
+	'esc': {
+		allowRepeat: true,
+		callback: () => blur(),
+	},
+	'up|k|shift+tab': {
+		allowRepeat: true,
+		callback: () => focusBefore(),
+	},
+	'down|j|tab': {
+		allowRepeat: true,
+		callback: () => focusAfter(),
+	},
+} as const satisfies Keymap;
 
 provide('react', (reaction: string) => {
 	misskeyApi('notes/reactions/create', {
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index bc1f416373..8f65e3b60a 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -233,6 +233,7 @@ import MkPagination, { type Paging } from '@/components/MkPagination.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkButton from '@/components/MkButton.vue';
 import { isEnabledUrlPreview } from '@/instance.js';
+import { type Keymap } from '@/scripts/hotkey.js';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -294,13 +295,24 @@ const replies = ref<Misskey.entities.Note[]>([]);
 const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i?.id);
 
 const keymap = {
-	'r': () => reply(true),
-	'e|a|plus': () => react(true),
-	'q': () => renote(true),
-	'esc': blur,
-	'm|o': () => showMenu(true),
-	's': () => showContent.value !== showContent.value,
-};
+	'r': () => reply(),
+	'e|a|plus': () => react(),
+	'q': () => renote(),
+	'm': () => showMenu(),
+	'c': () => {
+		if (!defaultStore.state.showClipButtonInNoteFooter) return;
+		clip();
+	},
+	'v|enter': () => {
+		if (appearNote.value.cw != null) {
+			showContent.value = !showContent.value;
+		}
+	},
+	'esc': {
+		allowRepeat: true,
+		callback: () => blur(),
+	},
+} as const satisfies Keymap;
 
 provide('react', (reaction: string) => {
 	misskeyApi('notes/reactions/create', {
diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts
index b082b6edf2..0a7d136f18 100644
--- a/packages/frontend/src/directives/hotkey.ts
+++ b/packages/frontend/src/directives/hotkey.ts
@@ -4,7 +4,7 @@
  */
 
 import { Directive } from 'vue';
-import { makeHotkey } from '../scripts/hotkey.js';
+import { makeHotkey } from '@/scripts/hotkey.js';
 
 export default {
 	mounted(el, binding) {
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index 273250d1d0..ea64e457e3 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
 	<MkSpacer :contentMax="800">
-		<div ref="rootEl" v-hotkey.global="keymap">
+		<div ref="rootEl">
 			<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
 			<div :class="$style.tl">
 				<MkTimeline
@@ -44,9 +44,6 @@ const antenna = ref<Misskey.entities.Antenna | null>(null);
 const queue = ref(0);
 const rootEl = shallowRef<HTMLElement>();
 const tlEl = shallowRef<InstanceType<typeof MkTimeline>>();
-const keymap = computed(() => ({
-	't': focus,
-}));
 
 function queueUpdated(q) {
 	queue.value = q;
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 98744c6318..813cc326d0 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template>
 	<MkSpacer :contentMax="800">
 		<MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin">
-			<div :key="src" ref="rootEl" v-hotkey.global="keymap">
+			<div :key="src" ref="rootEl">
 				<MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()">
 					{{ i18n.ts._timelineDescription[src] }}
 				</MkInfo>
@@ -58,9 +58,6 @@ provide('shouldOmitHeaderTitle', true);
 
 const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable);
 const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable);
-const keymap = {
-	't': focus,
-};
 
 const tlComponent = shallowRef<InstanceType<typeof MkTimeline>>();
 const rootEl = shallowRef<HTMLElement>();
diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts
index 0600bff893..fd79baa604 100644
--- a/packages/frontend/src/scripts/hotkey.ts
+++ b/packages/frontend/src/scripts/hotkey.ts
@@ -3,93 +3,132 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import keyCode from './keycode.js';
+//#region types
+export type Keymap = Record<string, CallbackFunction | CallbackObject>;
 
-type Callback = (ev: KeyboardEvent) => void;
+type CallbackFunction = (ev: KeyboardEvent) => unknown;
 
-type Keymap = Record<string, Callback>;
+type CallbackObject = {
+	callback: CallbackFunction;
+	allowRepeat?: boolean;
+};
 
 type Pattern = {
 	which: string[];
-	ctrl?: boolean;
-	shift?: boolean;
-	alt?: boolean;
+	ctrl: boolean;
+	alt: boolean;
+	shift: boolean;
 };
 
 type Action = {
 	patterns: Pattern[];
-	callback: Callback;
-	allowRepeat: boolean;
+	callback: CallbackFunction;
+	options: Required<Omit<CallbackObject, 'callback'>>;
+};
+//#endregion
+
+//#region consts
+const KEY_ALIASES = {
+	'esc': 'Escape',
+	'enter': ['Enter', 'NumpadEnter'],
+	'space': [' ', 'Spacebar'],
+	'up': 'ArrowUp',
+	'down': 'ArrowDown',
+	'left': 'ArrowLeft',
+	'right': 'ArrowRight',
+	'plus': ['+', ';'],
 };
 
-const parseKeymap = (keymap: Keymap) => Object.entries(keymap).map(([patterns, callback]): Action => {
-	const result = {
-		patterns: [],
-		callback,
-		allowRepeat: true,
-	} as Action;
+const MODIFIER_KEYS = ['ctrl', 'alt', 'shift'];
 
-	if (patterns.match(/^\(.*\)$/) !== null) {
-		result.allowRepeat = false;
-		patterns = patterns.slice(1, -1);
-	}
-
-	result.patterns = patterns.split('|').map(part => {
-		const pattern = {
-			which: [],
-			ctrl: false,
-			alt: false,
-			shift: false,
-		} as Pattern;
-
-		const keys = part.trim().split('+').map(x => x.trim().toLowerCase());
-		for (const key of keys) {
-			switch (key) {
-				case 'ctrl': pattern.ctrl = true; break;
-				case 'alt': pattern.alt = true; break;
-				case 'shift': pattern.shift = true; break;
-				default: pattern.which = keyCode(key).map(k => k.toLowerCase());
-			}
-		}
-
-		return pattern;
-	});
-
-	return result;
-});
-
-const ignoreElements = ['input', 'textarea'];
-
-function match(ev: KeyboardEvent, patterns: Action['patterns']): boolean {
-	const key = ev.key.toLowerCase();
-	return patterns.some(pattern => pattern.which.includes(key) &&
-		pattern.ctrl === ev.ctrlKey &&
-		pattern.shift === ev.shiftKey &&
-		pattern.alt === ev.altKey &&
-		!ev.metaKey,
-	);
-}
+const IGNORE_ELEMENTS = ['input', 'textarea'];
+//#endregion
 
+//#region impl
 export const makeHotkey = (keymap: Keymap) => {
 	const actions = parseKeymap(keymap);
-
 	return (ev: KeyboardEvent) => {
-		if (document.activeElement) {
-			if (ignoreElements.some(el => document.activeElement!.matches(el))) return;
-			if (document.activeElement.attributes['contenteditable']) return;
+		if ('pswp' in window && window.pswp != null) return;
+		if (document.activeElement != null) {
+			if (IGNORE_ELEMENTS.includes(document.activeElement.tagName.toLowerCase())) return;
+			if ((document.activeElement as HTMLElement).isContentEditable) return;
 		}
-
-		for (const action of actions) {
-			const matched = match(ev, action.patterns);
-
-			if (matched) {
-				if (!action.allowRepeat && ev.repeat) return;
-
+		for (const { patterns, callback, options } of actions) {
+			if (matchPatterns(ev, patterns, options)) {
 				ev.preventDefault();
 				ev.stopPropagation();
-				action.callback(ev);
-				break;
+				callback(ev);
 			}
 		}
 	};
 };
+
+const parseKeymap = (keymap: Keymap) => {
+	return Object.entries(keymap).map(([rawPatterns, rawCallback]) => {
+		const patterns = parsePatterns(rawPatterns);
+		const callback = parseCallback(rawCallback);
+		const options = parseOptions(rawCallback);
+		return { patterns, callback, options } as const satisfies Action;
+	});
+};
+
+const parsePatterns = (rawPatterns: keyof Keymap) => {
+	return rawPatterns.split('|').map(part => {
+		const keys = part.split('+').map(trimLower);
+		const which = parseKeyCode(keys.findLast(x => !MODIFIER_KEYS.includes(x)));
+		const ctrl = keys.includes('ctrl');
+		const alt = keys.includes('alt');
+		const shift = keys.includes('shift');
+		return { which, ctrl, alt, shift } as const satisfies Pattern;
+	});
+};
+
+const parseCallback = (rawCallback: Keymap[keyof Keymap]) => {
+	if (typeof rawCallback === 'object') {
+		return rawCallback.callback;
+	}
+	return rawCallback;
+};
+
+const parseOptions = (rawCallback: Keymap[keyof Keymap]) => {
+	const defaultOptions = {
+		allowRepeat: false,
+	} as const satisfies Action['options'];
+	if (typeof rawCallback === 'object') {
+		const { callback, ...rawOptions } = rawCallback;
+		const options = { ...defaultOptions, ...rawOptions };
+		return { ...options } as const satisfies Action['options'];
+	}
+	return { ...defaultOptions } as const satisfies Action['options'];
+};
+
+const matchPatterns = (ev: KeyboardEvent, patterns: Action['patterns'], options: Action['options']) => {
+	if (ev.repeat && !options.allowRepeat) return false;
+	const key = ev.key.toLowerCase();
+	return patterns.some(({ which, ctrl, shift, alt }) => {
+		if (!which.includes(key)) return false;
+		if (ctrl !== (ev.ctrlKey || ev.metaKey)) return false;
+		if (alt !== ev.altKey) return false;
+		if (shift !== ev.shiftKey) return false;
+		return true;
+	});
+};
+
+const parseKeyCode = (input?: string | null) => {
+	if (input == null) return [];
+	const raw = getValueByKey(KEY_ALIASES, input);
+	if (raw == null) return [input];
+	if (typeof raw === 'string') return [trimLower(raw)];
+	return raw.map(trimLower);
+};
+
+const getValueByKey = <
+	T extends Record<keyof any, unknown>,
+	K extends keyof T | keyof any,
+	R extends K extends keyof T ? T[K] : T[keyof T] | undefined,
+>(obj: T, key: K) => {
+	return obj[key] as R;
+};
+
+const trimLower = (str: string) => str.trim().toLowerCase();
+//#endregion
diff --git a/packages/frontend/src/scripts/keycode.ts b/packages/frontend/src/scripts/keycode.ts
deleted file mode 100644
index 7ffceafada..0000000000
--- a/packages/frontend/src/scripts/keycode.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export default (input: string): string[] => {
-	if (Object.keys(aliases).some(a => a.toLowerCase() === input.toLowerCase())) {
-		const codes = aliases[input];
-		return Array.isArray(codes) ? codes : [codes];
-	} else {
-		return [input];
-	}
-};
-
-export const aliases = {
-	'esc': 'Escape',
-	'enter': ['Enter', 'NumpadEnter'],
-	'space': [' ', 'Spacebar'],
-	'up': 'ArrowUp',
-	'down': 'ArrowDown',
-	'left': 'ArrowLeft',
-	'right': 'ArrowRight',
-	'plus': ['NumpadAdd', 'Semicolon'],
-};

From 600f16d625d8b4c8e7f8a82fb4dd1a77f80bfcbc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 9 Jul 2024 21:57:19 +0900
Subject: [PATCH 081/206] =?UTF-8?q?fix(backend):=20api-doc=E3=82=92Scalar?=
 =?UTF-8?q?=E3=81=AB=E5=A4=89=E6=9B=B4=20(#14152)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): api-docをScalarに変更

* Update Changelog
---
 CHANGELOG.md                                  |  1 +
 packages/backend/assets/api-doc.html          | 20 ++++++++++++++++
 packages/backend/assets/redoc.html            | 24 -------------------
 .../api/openapi/OpenApiServerService.ts       |  2 +-
 .../src/server/api/openapi/gen-spec.ts        |  1 -
 5 files changed, 22 insertions(+), 26 deletions(-)
 create mode 100644 packages/backend/assets/api-doc.html
 delete mode 100644 packages/backend/assets/redoc.html

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1bcf721676..b99a2cbc53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 
 ### Client
+- Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/packages/backend/assets/api-doc.html b/packages/backend/assets/api-doc.html
new file mode 100644
index 0000000000..19e0349d47
--- /dev/null
+++ b/packages/backend/assets/api-doc.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>Misskey API</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<style>
+			body {
+				margin: 0;
+				padding: 0;
+			}
+		</style>
+	</head>
+	<body>
+		<script
+			id="api-reference"
+			data-url="/api.json"></script>
+		<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
+	</body>
+</html>
diff --git a/packages/backend/assets/redoc.html b/packages/backend/assets/redoc.html
deleted file mode 100644
index 2557b4532e..0000000000
--- a/packages/backend/assets/redoc.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		<title>Misskey API</title>
-		<!-- needed for adaptive design -->
-		<meta charset="utf-8"/>
-		<meta name="viewport" content="width=device-width, initial-scale=1">
-		<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
-
-		<!--
-		ReDoc doesn't change outer page styles
-		-->
-		<style>
-			body {
-				margin: 0;
-				padding: 0;
-			}
-		</style>
-	</head>
-	<body>
-		<redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc>
-		<script src="https://cdn.redoc.ly/redoc/v2.1.3/bundles/redoc.standalone.js" integrity="sha256-u4DgqzYXoArvNF/Ymw3puKexfOC6lYfw0sfmeliBJ1I=" crossorigin="anonymous"></script>
-	</body>
-</html>
diff --git a/packages/backend/src/server/api/openapi/OpenApiServerService.ts b/packages/backend/src/server/api/openapi/OpenApiServerService.ts
index 5210e4d2bc..f124aa9f39 100644
--- a/packages/backend/src/server/api/openapi/OpenApiServerService.ts
+++ b/packages/backend/src/server/api/openapi/OpenApiServerService.ts
@@ -25,7 +25,7 @@ export class OpenApiServerService {
 	public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) {
 		fastify.get('/api-doc', async (_request, reply) => {
 			reply.header('Cache-Control', 'public, max-age=86400');
-			return await reply.sendFile('/redoc.html', staticAssets);
+			return await reply.sendFile('/api-doc.html', staticAssets);
 		});
 		fastify.get('/api.json', (_request, reply) => {
 			reply.header('Cache-Control', 'public, max-age=600');
diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index 2a14270a24..efa47a6986 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -15,7 +15,6 @@ export function genOpenapiSpec(config: Config, includeSelfRef = false) {
 		info: {
 			version: config.version,
 			title: 'Misskey API',
-			'x-logo': { url: '/static-assets/api-doc.png' },
 		},
 
 		externalDocs: {

From 02e0a86b12473265c2fc0f219349d2c662f89f0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 10 Jul 2024 01:00:40 +0900
Subject: [PATCH 082/206] fix(frontend): remove unused statement

fix #14162
---
 packages/frontend/src/components/MkNote.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 5f1820a379..fc72813285 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -318,7 +318,7 @@ const keymap = {
 	},
 	'o': () => {
 		if (renoteCollapsed.value) return;
-		galleryEl.value?.openGallery();
+		showMenu();
 	},
 	'v|enter': () => {
 		if (renoteCollapsed.value) {

From 52d8a54fc72b886fecb30a736b3ccf5057ea2a0c Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Wed, 10 Jul 2024 20:40:04 +0900
Subject: [PATCH 083/206] =?UTF-8?q?feat(misskey-js):=20`POST=20admin/roles?=
 =?UTF-8?q?/create`=E3=81=AE=E5=9E=8B=E3=82=92=E5=85=B7=E8=B1=A1=E5=8C=96?=
 =?UTF-8?q?=20(#14167)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(misskey-js): `POST admin/roles/create`の型を具象化

* fix

* docs: CHANGELOG.md

* test(misskey-js): admin/roles/createの型が合うことを表明

* test(misskey-js): single quote

* test(misskey-js): 無を読もうとして爆発するのを修正

* test(misskey-js): fix comment
---
 CHANGELOG.md                              |  1 +
 packages/misskey-js/etc/misskey-js.api.md | 18 ++++++++++-
 packages/misskey-js/src/api.types.ts      |  7 ++++-
 packages/misskey-js/src/entities.ts       | 15 ++++++++-
 packages/misskey-js/test/api.ts           | 38 +++++++++++++++++++++++
 5 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b99a2cbc53..aec4e1868c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,7 @@
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
+- Feat: `/admin/role/create` のロールポリシーの型を修正
 
 ## 2024.5.0
 
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index be2f510ac2..d11d2a4f06 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1160,6 +1160,12 @@ export type Endpoints = Overwrite<Endpoints_2, {
         req: SigninRequest;
         res: SigninResponse;
     };
+    'admin/roles/create': {
+        req: Overwrite<AdminRolesCreateRequest, {
+            policies: PartialRolePolicyOverride;
+        }>;
+        res: AdminRolesCreateResponse;
+    };
 }>;
 
 // @public (undocumented)
@@ -1185,6 +1191,7 @@ declare namespace entities {
         SignupPendingResponse,
         SigninRequest,
         SigninResponse,
+        PartialRolePolicyOverride,
         EmptyRequest,
         EmptyResponse,
         AdminMetaResponse,
@@ -2725,6 +2732,15 @@ type PagesUpdateRequest = operations['pages___update']['requestBody']['content']
 // @public (undocumented)
 function parse(acct: string): Acct;
 
+// Warning: (ae-forgotten-export) The symbol "Values" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+type PartialRolePolicyOverride = Partial<{
+    [k in keyof RolePolicies]: Omit<Values<Role['policies']>, 'value'> & {
+        value: RolePolicies[k];
+    };
+}>;
+
 // @public (undocumented)
 export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
 
@@ -3213,7 +3229,7 @@ type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['
 
 // Warnings were encountered during analysis:
 //
-// src/entities.ts:25:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/entities.ts:34:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
 
 // (No @packageDocumentation comment for this package)
 
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index af0bade5b3..8c403639b7 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -1,7 +1,8 @@
 import { Endpoints as Gen } from './autogen/endpoint.js';
 import { UserDetailed } from './autogen/models.js';
-import { UsersShowRequest } from './autogen/entities.js';
+import { AdminRolesCreateRequest, AdminRolesCreateResponse, UsersShowRequest } from './autogen/entities.js';
 import {
+	PartialRolePolicyOverride,
 	SigninRequest,
 	SigninResponse,
 	SignupPendingRequest,
@@ -79,5 +80,9 @@ export type Endpoints = Overwrite<
 			req: SigninRequest;
 			res: SigninResponse;
 		},
+		'admin/roles/create': {
+			req: Overwrite<AdminRolesCreateRequest, { policies: PartialRolePolicyOverride }>;
+			res: AdminRolesCreateResponse;
+		}
 	}
 >
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 7a84cb6a1a..7331a55a1c 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -1,5 +1,14 @@
 import { ModerationLogPayloads } from './consts.js';
-import { Announcement, EmojiDetailed, MeDetailed, Page, User, UserDetailedNotMe } from './autogen/models.js';
+import {
+	Announcement,
+	EmojiDetailed,
+	MeDetailed,
+	Page,
+	Role,
+	RolePolicies,
+	User,
+	UserDetailedNotMe
+} from './autogen/models.js';
 
 export * from './autogen/entities.js';
 export * from './autogen/models.js';
@@ -236,3 +245,7 @@ export type SigninResponse = {
 	id: User['id'],
 	i: string,
 };
+
+type Values<T extends Record<PropertyKey, unknown>> = T[keyof T];
+
+export type PartialRolePolicyOverride = Partial<{[k in keyof RolePolicies]: Omit<Values<Role['policies']>, 'value'> & { value: RolePolicies[k] }}>;
diff --git a/packages/misskey-js/test/api.ts b/packages/misskey-js/test/api.ts
index 95f1946fa2..1a7574de25 100644
--- a/packages/misskey-js/test/api.ts
+++ b/packages/misskey-js/test/api.ts
@@ -259,4 +259,42 @@ describe('API', () => {
 			expect(isAPIError(e)).toEqual(false);
 		}
 	});
+
+	test('admin/roles/create の型が合う', async() => {
+		fetchMock.resetMocks();
+		fetchMock.mockResponse(async () => {
+			return {
+				// 本来返すべき値は`Role`型だが、テストなのでお茶を濁す
+				status: 200,
+				body: '{}'
+			};
+		});
+
+		const cli = new APIClient({
+			origin: 'https://misskey.test',
+			credential: 'TOKEN',
+		});
+		await cli.request('admin/roles/create', {
+			name: 'aaa',
+			asBadge: false,
+			canEditMembersByModerator: false,
+			color: '#123456',
+			condFormula: {},
+			description: '',
+			displayOrder: 0,
+			iconUrl: '',
+			isAdministrator: false,
+			isExplorable: false,
+			isModerator: false,
+			isPublic: false,
+			policies: {
+				ltlAvailable: {
+					value: true,
+					priority: 0,
+					useDefault: false,
+				},
+			},
+			target: 'manual',
+		});
+	})
 });

From 679318541afb789842db5f2cbf918b8acf284f1d Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Thu, 11 Jul 2024 16:29:18 +0900
Subject: [PATCH 084/206] Improve background color specification (#14176)

---
 packages/frontend/src/pages/settings/drive-cleaner.vue | 6 +++---
 packages/frontend/src/pages/settings/drive.vue         | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index b20774c4ec..8d2946db63 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script setup lang="ts">
-import { computed, ref, watch } from 'vue';
+import { computed, ref, watch, type StyleValue } from 'vue';
 import tinycolor from 'tinycolor2';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
@@ -102,10 +102,10 @@ function fetchDriveInfo(): void {
 	});
 }
 
-function genUsageBar(fsize: number): object {
+function genUsageBar(fsize: number): StyleValue {
 	return {
 		width: `${fsize / usage.value * 100}%`,
-		background: tinycolor({ h: 180 - (fsize / usage.value * 180), s: 0.7, l: 0.5 }),
+		background: tinycolor({ h: 180 - (fsize / usage.value * 180), s: 0.7, l: 0.5 }).toHslString(),
 	};
 }
 
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 81a8d474d2..0e66b93f1c 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -95,7 +95,7 @@ const meterStyle = computed(() => {
 			h: 180 - (usage.value / capacity.value * 180),
 			s: 0.7,
 			l: 0.5,
-		}),
+		}).toHslString(),
 	};
 });
 

From f8ac3fe343a25e3d58c93650826f7377a029c3cd Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 11 Jul 2024 16:39:06 +0900
Subject: [PATCH 085/206] =?UTF-8?q?=E3=83=AA=E3=83=AA=E3=83=BC=E3=82=B9PR?=
 =?UTF-8?q?=E3=81=8C=E3=81=AA=E3=81=84=E3=81=A8=E3=81=8D=E3=81=ABrelease-e?=
 =?UTF-8?q?dit-with-push.yml=E3=81=8Cfail=E3=81=97=E3=81=A6=E8=A6=8B?=
 =?UTF-8?q?=E6=A0=84=E3=81=88=E3=81=8C=E6=82=AA=E3=81=84=E3=81=AE=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#14160)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .github/workflows/release-edit-with-push.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml
index e1bcb5a665..57657a4ba7 100644
--- a/.github/workflows/release-edit-with-push.yml
+++ b/.github/workflows/release-edit-with-push.yml
@@ -28,16 +28,19 @@ jobs:
         env:
           STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
       - name: Get target version
+        if: steps.get_pr.outputs.pr_number != ''
         uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v2
         id: v
       # CHANGELOG.mdの内容を取得
       - name: Get changelog
+        if: steps.get_pr.outputs.pr_number != ''
         uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v2
         with:
           version: ${{ steps.v.outputs.target_version }}
         id: changelog
       # PRのnotesを更新
       - name: Update PR
+        if: steps.get_pr.outputs.pr_number != ''
         run: |
           gh pr edit "$PR_NUMBER" --body "$CHANGELOG"
         env:

From 6b876da44af0b7e5dd553676d230f68995df148d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 11 Jul 2024 18:41:04 +0900
Subject: [PATCH 086/206] =?UTF-8?q?enhance(frontend):=20=E3=82=A6=E3=82=A7?=
 =?UTF-8?q?=E3=83=AB=E3=82=AB=E3=83=A0=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9?=
 =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=81=AE=E3=83=87=E3=82=B6=E3=82=A4=E3=83=B3?=
 =?UTF-8?q?=E3=82=92=E8=AA=BF=E6=95=B4=20(#14156)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 非ログイン時のハイライトTLのデザイン調整

* Update Changelog

* fix cw handling

* ホバーしてたらスクロールを止めるように

* fix

* lint
---
 CHANGELOG.md                                  |   1 +
 .../src/pages/welcome.timeline.note.vue       | 109 ++++++++++++++++++
 .../frontend/src/pages/welcome.timeline.vue   |  98 ++++++++--------
 3 files changed, 162 insertions(+), 46 deletions(-)
 create mode 100644 packages/frontend/src/pages/welcome.timeline.note.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aec4e1868c..259420a6c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 
 ### Client
 - Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
+- Enhance: 非ログイン時のハイライトTLのデザインを改善
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/packages/frontend/src/pages/welcome.timeline.note.vue b/packages/frontend/src/pages/welcome.timeline.note.vue
new file mode 100644
index 0000000000..f385938343
--- /dev/null
+++ b/packages/frontend/src/pages/welcome.timeline.note.vue
@@ -0,0 +1,109 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :key="note.id" :class="$style.note">
+	<div class="_panel _gaps_s" :class="$style.content">
+		<div v-if="note.cw != null" :class="$style.richcontent">
+			<div><Mfm :text="note.cw" :author="note.user"/></div>
+			<MkCwButton v-model="showContent" :text="note.text" :renote="note.renote" :files="note.files" :poll="note.poll" style="margin: 4px 0;"/>
+			<div v-if="showContent">
+				<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
+				<Mfm v-if="note.text" :text="note.text" :author="note.user"/>
+				<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
+			</div>
+		</div>
+		<div v-else ref="noteTextEl" :class="[$style.text, { [$style.collapsed]: shouldCollapse }]">
+			<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
+			<Mfm v-if="note.text" :text="note.text" :author="note.user"/>
+			<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
+		</div>
+		<div v-if="note.files && note.files.length > 0" :class="$style.richcontent">
+			<MkMediaList :mediaList="note.files.slice(0, 4)"/>
+		</div>
+		<div v-if="note.poll">
+			<MkPoll :noteId="note.id" :poll="note.poll" :readOnly="true"/>
+		</div>
+		<div v-if="note.reactionCount > 0" :class="$style.reactions">
+			<MkReactionsViewer :note="note" :maxNumber="16"/>
+		</div>
+	</div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { ref, shallowRef, onUpdated, onMounted } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
+import MkMediaList from '@/components/MkMediaList.vue';
+import MkPoll from '@/components/MkPoll.vue';
+import MkCwButton from '@/components/MkCwButton.vue';
+
+defineProps<{
+	note: Misskey.entities.Note;
+}>();
+
+const noteTextEl = shallowRef<HTMLDivElement>();
+const shouldCollapse = ref(false);
+const showContent = ref(false);
+
+function calcCollapse() {
+	if (noteTextEl.value) {
+		const height = noteTextEl.value.scrollHeight;
+		if (height > 200) {
+			shouldCollapse.value = true;
+		}
+	}
+}
+
+onMounted(() => {
+	calcCollapse();
+});
+
+onUpdated(() => {
+	calcCollapse();
+});
+</script>
+
+<style lang="scss" module>
+.note {
+	margin-left: auto;
+}
+
+.text {
+	position: relative;
+	max-height: 200px;
+	overflow: hidden;
+
+	&.collapsed::after {
+		content: '';
+		position: absolute;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		height: 64px;
+		background: linear-gradient(0deg, var(--panel), var(--X15));
+	}
+}
+
+.content {
+	padding: 16px;
+	margin: 0 0 0 auto;
+	max-width: max-content;
+	border-radius: 16px;
+}
+
+.reactions {
+	box-sizing: border-box;
+	margin: 8px -16px -8px;
+	padding: 8px 16px 0;
+	width: calc(100% + 32px);
+	border-top: 1px solid var(--divider);
+}
+
+.richcontent {
+	min-width: 250px;
+}
+</style>
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 139b2e0a07..db326f9e6c 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -4,24 +4,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="$style.root">
-	<div ref="scrollEl" :class="[$style.scrollbox, { [$style.scroll]: isScrolling }]">
-		<div v-for="note in notes" :key="note.id" :class="$style.note">
-			<div class="_panel" :class="$style.content">
-				<div>
-					<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA>
-					<Mfm v-if="note.text" :text="note.text" :author="note.user"/>
-					<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
-				</div>
-				<div v-if="note.files.length > 0" :class="$style.richcontent">
-					<MkMediaList :mediaList="note.files"/>
-				</div>
-				<div v-if="note.poll">
-					<MkPoll :noteId="note.id" :poll="note.poll" :readOnly="true"/>
-				</div>
-			</div>
-			<MkReactionsViewer ref="reactionsViewer" :note="note"/>
-		</div>
+<div :class="$style.root" class="_gaps">
+	<div
+		ref="notesMainContainerEl"
+		class="_gaps"
+		:class="[$style.scrollBoxMain, { [$style.scrollIntro]: (scrollState === 'intro'), [$style.scrollLoop]: (scrollState === 'loop') }]"
+		@animationend="changeScrollState"
+	>
+		<XNote v-for="note in notes" :key="`${note.id}_1`" :class="$style.note" :note="note"/>
+	</div>
+	<div v-if="isScrolling" class="_gaps" :class="[$style.scrollBoxSub, { [$style.scrollIntro]: (scrollState === 'intro'), [$style.scrollLoop]: (scrollState === 'loop') }]">
+		<XNote v-for="note in notes" :key="`${note.id}_2`" :class="$style.note" :note="note"/>
 	</div>
 </div>
 </template>
@@ -29,43 +22,54 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
 import { onUpdated, ref, shallowRef } from 'vue';
-import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
-import MkMediaList from '@/components/MkMediaList.vue';
-import MkPoll from '@/components/MkPoll.vue';
+import XNote from '@/pages/welcome.timeline.note.vue';
 import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { getScrollContainer } from '@/scripts/scroll.js';
 
 const notes = ref<Misskey.entities.Note[]>([]);
 const isScrolling = ref(false);
-const scrollEl = shallowRef<HTMLElement>();
+const scrollState = ref<null | 'intro' | 'loop'>(null);
+const notesMainContainerEl = shallowRef<HTMLElement>();
 
 misskeyApiGet('notes/featured').then(_notes => {
 	notes.value = _notes;
 });
 
+function changeScrollState() {
+	if (scrollState.value !== 'loop') {
+		scrollState.value = 'loop';
+	}
+}
+
 onUpdated(() => {
-	if (!scrollEl.value) return;
-	const container = getScrollContainer(scrollEl.value);
+	if (!notesMainContainerEl.value) return;
+	const container = getScrollContainer(notesMainContainerEl.value);
 	const containerHeight = container ? container.clientHeight : window.innerHeight;
-	if (scrollEl.value.offsetHeight > containerHeight) {
+	if (notesMainContainerEl.value.offsetHeight > containerHeight) {
+		if (scrollState.value === null) {
+			scrollState.value = 'intro';
+		}
 		isScrolling.value = true;
 	}
 });
 </script>
 
 <style lang="scss" module>
-@keyframes scroll {
+@keyframes scrollIntro {
 	0% {
 		transform: translate3d(0, 0, 0);
 	}
-	5% {
-		transform: translate3d(0, 0, 0);
+	100% {
+		transform: translate3d(0, calc(calc(-100% - 128px) - var(--margin)), 0);
 	}
-	75% {
-		transform: translate3d(0, calc(-100% + 90vh), 0);
+}
+
+@keyframes scrollConstant {
+	0% {
+		transform: translate3d(0, -128px, 0);
 	}
-	90% {
-		transform: translate3d(0, calc(-100% + 90vh), 0);
+	100% {
+		transform: translate3d(0, calc(calc(-100% - 128px) - var(--margin)), 0);
 	}
 }
 
@@ -73,24 +77,26 @@ onUpdated(() => {
 	text-align: right;
 }
 
-.scrollbox {
-	&.scroll {
-		animation: scroll 45s linear infinite;
+.scrollBoxMain {
+	&.scrollIntro {
+		animation: scrollIntro 30s linear forwards;
+	}
+	&.scrollLoop {
+		animation: scrollConstant 30s linear infinite;
 	}
 }
 
-.note {
-	margin: 16px 0 16px auto;
+.scrollBoxSub {
+	&.scrollIntro {
+		animation: scrollIntro 30s linear forwards;
+	}
+	&.scrollLoop {
+		animation: scrollConstant 30s linear infinite;
+	}
 }
 
-.content {
-	padding: 16px;
-	margin: 0 0 0 auto;
-	max-width: max-content;
-	border-radius: 16px;
-}
-
-.richcontent {
-	min-width: 250px;
+.root:has(.note:hover) .scrollBoxMain,
+.root:has(.note:hover) .scrollBoxSub {
+	animation-play-state: paused;
 }
 </style>

From 121af778a0925d5850e9d88261e9a8e8c6fd968b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 11 Jul 2024 18:44:18 +0900
Subject: [PATCH 087/206] =?UTF-8?q?enhance(frontend):=20=E6=9C=AA=E4=BD=BF?=
 =?UTF-8?q?=E7=94=A8=E3=81=AE=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89=E8=A8=AD?=
 =?UTF-8?q?=E5=AE=9A=E3=82=92=E5=89=8A=E9=99=A4=20(#14116)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 未使用のサウンド設定を削除

* Update Changelog

* Update CHANGELOG.md
---
 CHANGELOG.md                                              | 3 +++
 locales/index.d.ts                                        | 8 --------
 locales/ja-JP.yml                                         | 2 --
 .../frontend/src/pages/settings/preferences-backups.vue   | 2 --
 packages/frontend/src/pages/settings/sounds.vue           | 2 --
 packages/frontend/src/scripts/sound.ts                    | 2 --
 packages/frontend/src/store.ts                            | 8 --------
 7 files changed, 3 insertions(+), 24 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 259420a6c6..cd123c938e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
 ## Unreleased
 
+### Note
+- デッキUIの新着ノートをサウンドで通知する機能の追加(v2024.5.0)に伴い、以前から動作しなくなっていたクライアント設定内の「アンテナ受信」「チャンネル通知」サウンドを削除しました。
+
 ### General
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index ebd980ed85..5089f7802e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -7515,14 +7515,6 @@ export interface Locale extends ILocale {
          * 通知
          */
         "notification": string;
-        /**
-         * アンテナ受信
-         */
-        "antenna": string;
-        /**
-         * チャンネル通知
-         */
-        "channel": string;
         /**
          * リアクション選択時
          */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0d89d33abe..a03d792140 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1971,8 +1971,6 @@ _sfx:
   note: "ノート"
   noteMy: "ノート(自分)"
   notification: "通知"
-  antenna: "アンテナ受信"
-  channel: "チャンネル通知"
   reaction: "リアクション選択時"
 
 _soundSettings:
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index b6f1043154..dace2cd847 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -113,8 +113,6 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
 	'sound_note',
 	'sound_noteMy',
 	'sound_notification',
-	'sound_antenna',
-	'sound_channel',
 ];
 const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
 	'lightTheme',
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 090f0cf14c..0f1b725fae 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -54,8 +54,6 @@ const sounds = ref<Record<OperationType, Ref<SoundStore>>>({
 	note: defaultStore.reactiveState.sound_note,
 	noteMy: defaultStore.reactiveState.sound_noteMy,
 	notification: defaultStore.reactiveState.sound_notification,
-	antenna: defaultStore.reactiveState.sound_antenna,
-	channel: defaultStore.reactiveState.sound_channel,
 	reaction: defaultStore.reactiveState.sound_reaction,
 });
 
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index fcd59510df..bba855cd64 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -74,8 +74,6 @@ export const soundsTypes = [
 export const operationTypes = [
 	'noteMy',
 	'note',
-	'antenna',
-	'channel',
 	'notification',
 	'reaction',
 ] as const;
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index e8eb5a1ed7..9cb2742069 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -479,14 +479,6 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: { type: 'syuilo/n-ea', volume: 1 } as SoundStore,
 	},
-	sound_antenna: {
-		where: 'device',
-		default: { type: 'syuilo/triple', volume: 1 } as SoundStore,
-	},
-	sound_channel: {
-		where: 'device',
-		default: { type: 'syuilo/square-pico', volume: 1 } as SoundStore,
-	},
 	sound_reaction: {
 		where: 'device',
 		default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,

From 385969e9f56a39a1e5547b94901d155e1e811263 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 12 Jul 2024 16:25:44 +0900
Subject: [PATCH 088/206] =?UTF-8?q?fix(frontend):=20=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=BC=E3=82=AB=E3=82=B9=E3=81=AE=E6=8C=99=E5=8B=95=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#14158)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 直前のパターンを記録するように

* fix(frontend): フォーカス/タブ移動に関する挙動を調整 (#226)

Cherry-pick commit e8c030673326871edf3623cf2b8675d68f9e1b13

Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com>

* focusのデザイン修正

* move scripts

* Modalにfocus trapを追加

* 記録するホットキーはレートリミット式にする

* escキーのハンドリングをMkModalに統一

* fix

* enterで子メニューを開けるように

* lint

* fix focus trap

* improve switch accessibility

* 一部のmodalのフォーカストラップが外れない問題を修正

* fix

* fix

* Revert "記録するホットキーはレートリミット式にする"

This reverts commit 40a7509286a87911ad4cc06d9482e8a2e5d0e7e8.

* Revert "fix(frontend): 直前のパターンを記録するように"

This reverts commit 5372b2594023952cff34aa62253ed4efef15b5dd.

* Revert "Revert "fix(frontend): 直前のパターンを記録するように""

This reverts commit a9bb52e799e110927ad92cd8f26af980819334e1.

* Revert "Revert "記録するホットキーはレートリミット式にする""

This reverts commit bdac34273e0bc5f13604c7e2f9fa6b1321a0df3d.

* 試験的にCypressでのFocustrapを無効化

* fix

* fix focus-trap

* Update Changelog

* :v:

* fix focustrap invocation logic

* スクロールがsticky headerを考慮するように

* :art:

* スタイルの微調整

* :art:

* remove deprecated key aliases

* focusElementが足りなかったので修正

* preview系にfocus時スタイルが足りなかったので修正

* `returnFocusElement` -> `returnFocusTo`

* lint

* Update packages/frontend/src/components/MkModalWindow.vue

* Apply suggestions from code review

Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>

* keydownイベントをまとめる

* use correct pesudo-element selector

* fix

* rename

---------

Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   2 +
 .../src/components/MkAchievements.vue         |   4 +-
 packages/frontend/src/components/MkButton.vue |   1 -
 .../src/components/MkChannelFollowButton.vue  |  12 +-
 .../src/components/MkChannelPreview.vue       |  19 +-
 .../frontend/src/components/MkClipPreview.vue |   8 +
 .../frontend/src/components/MkContextMenu.vue |   2 +-
 .../frontend/src/components/MkCwButton.vue    |   4 +-
 packages/frontend/src/components/MkDialog.vue |  16 +-
 .../frontend/src/components/MkDrive.file.vue  |  32 +-
 .../src/components/MkDrive.folder.vue         |   2 +-
 .../frontend/src/components/MkEmojiPicker.vue |  33 +-
 .../src/components/MkEmojiPickerDialog.vue    |   2 +
 .../src/components/MkFlashPreview.vue         |   6 +-
 packages/frontend/src/components/MkFolder.vue |  16 +-
 .../src/components/MkFollowButton.vue         |  12 +-
 .../src/components/MkGalleryPostPreview.vue   |   4 +-
 .../src/components/MkImgWithBlurhash.vue      |   4 +-
 .../frontend/src/components/MkLaunchPad.vue   |   2 +-
 .../frontend/src/components/MkMediaAudio.vue  |  26 +-
 .../frontend/src/components/MkMediaList.vue   |  54 ++-
 .../frontend/src/components/MkMediaVideo.vue  |  10 +-
 .../frontend/src/components/MkMenu.child.vue  |   5 +-
 packages/frontend/src/components/MkMenu.vue   | 367 +++++++++++-------
 packages/frontend/src/components/MkModal.vue  |  28 +-
 .../frontend/src/components/MkModalWindow.vue |  13 +-
 packages/frontend/src/components/MkNote.vue   |  48 +--
 .../src/components/MkNoteDetailed.vue         |  62 +--
 .../frontend/src/components/MkNotePreview.vue |   2 +-
 .../src/components/MkNotification.vue         |   2 +-
 .../frontend/src/components/MkPagePreview.vue |  19 +-
 .../frontend/src/components/MkPopupMenu.vue   |   6 +-
 .../frontend/src/components/MkPostForm.vue    |  10 +
 .../src/components/MkPostFormDialog.vue       |   2 +-
 packages/frontend/src/components/MkRadio.vue  |  10 +-
 packages/frontend/src/components/MkSelect.vue |  27 +-
 .../frontend/src/components/MkSuperMenu.vue   |  10 +-
 packages/frontend/src/components/MkSwitch.vue |  10 +-
 .../components/MkTutorialDialog.PostNote.vue  |   2 +-
 .../components/MkTutorialDialog.Sensitive.vue |   2 +-
 .../components/MkTutorialDialog.Timeline.vue  |   2 +-
 .../src/components/MkVisibilityPicker.vue     |   2 +-
 .../components/global/MkStickyContainer.vue   |   6 +-
 packages/frontend/src/directives/hotkey.ts    |   4 +-
 packages/frontend/src/os.ts                   |  27 +-
 .../frontend/src/pages/drive.file.info.vue    |   1 +
 packages/frontend/src/pages/games.vue         |  11 +-
 packages/frontend/src/pages/page.vue          |   1 +
 .../frontend/src/pages/settings/profile.vue   |   1 +
 .../frontend/src/pages/settings/theme.vue     |   6 +
 packages/frontend/src/scripts/focus-trap.ts   |  65 ++++
 packages/frontend/src/scripts/focus.ts        |  98 +++--
 .../src/scripts/get-dom-node-or-null.ts       |  19 +
 packages/frontend/src/scripts/hotkey.ts       |  51 ++-
 packages/frontend/src/scripts/scroll.ts       |   8 +
 packages/frontend/src/style.scss              |  25 +-
 packages/frontend/src/ui/_common_/common.vue  |   2 +-
 .../src/ui/_common_/navbar-for-mobile.vue     |   6 +-
 packages/frontend/src/ui/_common_/navbar.vue  |  86 +++-
 packages/frontend/src/ui/deck/column.vue      |   4 +-
 .../frontend/src/widgets/WidgetCalendar.vue   |   2 +-
 61 files changed, 932 insertions(+), 391 deletions(-)
 create mode 100644 packages/frontend/src/scripts/focus-trap.ts
 create mode 100644 packages/frontend/src/scripts/get-dom-node-or-null.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd123c938e..c6f48684b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@
 ### Client
 - Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
 - Enhance: 非ログイン時のハイライトTLのデザインを改善
+- Enhance: フロントエンドのアクセシビリティ改善  
+  (Based on https://github.com/taiyme/misskey/pull/226)
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index 5d103fa789..c8134416b5 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -153,7 +153,7 @@ onMounted(() => {
 		background: linear-gradient(0deg, #ffee20, #eb7018);
 	}
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		position: absolute;
@@ -173,7 +173,7 @@ onMounted(() => {
 		background: linear-gradient(0deg, #e1e1e1, #7c7c7c);
 	}
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		position: absolute;
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index 25b003ba5a..9560efb7d9 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -250,7 +250,6 @@ function onMousedown(evt: MouseEvent): void {
 	}
 
 	&:focus-visible {
-		outline: solid 2px var(--focus);
 		outline-offset: 2px;
 	}
 
diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue
index 841d37a568..35dc3ad4bf 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.vue
+++ b/packages/frontend/src/components/MkChannelFollowButton.vue
@@ -87,17 +87,7 @@ async function onClick() {
 	}
 
 	&:focus-visible {
-		&:after {
-			content: "";
-			pointer-events: none;
-			position: absolute;
-			top: -5px;
-			right: -5px;
-			bottom: -5px;
-			left: -5px;
-			border: 2px solid var(--focus);
-			border-radius: 32px;
-		}
+		outline-offset: 2px;
 	}
 
 	&:hover {
diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue
index 4ff64dc4ba..c30cb66c07 100644
--- a/packages/frontend/src/components/MkChannelPreview.vue
+++ b/packages/frontend/src/components/MkChannelPreview.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div style="position: relative;">
-	<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1" @click="updateLastReadedAt">
+	<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" @click="updateLastReadedAt">
 		<div class="banner" :style="bannerStyle">
 			<div class="fade"></div>
 			<div class="name"><i class="ti ti-device-tv"></i> {{ channel.name }}</div>
@@ -80,6 +80,7 @@ const bannerStyle = computed(() => {
 <style lang="scss" scoped>
 .eftoefju {
 	display: block;
+	position: relative;
 	overflow: hidden;
 	width: 100%;
 
@@ -87,6 +88,22 @@ const bannerStyle = computed(() => {
 		text-decoration: none;
 	}
 
+	&:focus-within {
+		outline: none;
+
+		&::after {
+			content: '';
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			height: 100%;
+			border-radius: inherit;
+			pointer-events: none;
+			box-shadow: inset 0 0 0 2px var(--focus);
+		}
+	}
+
 	> .banner {
 		position: relative;
 		width: 100%;
diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue
index 6299a28e9f..2e9a172c23 100644
--- a/packages/frontend/src/components/MkClipPreview.vue
+++ b/packages/frontend/src/components/MkClipPreview.vue
@@ -40,6 +40,14 @@ const remaining = computed(() => {
 .link {
 	display: block;
 
+	&:focus-visible {
+		outline: none;
+
+		.root {
+			box-shadow: inset 0 0 0 2px var(--focus);
+		}
+	}
+
 	&:hover {
 		text-decoration: none;
 		color: var(--accent);
diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index a807742bb9..8ea8fa6cf3 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:leaveToClass="defaultStore.state.animation ? $style.transition_fade_leaveTo : ''"
 >
 	<div ref="rootEl" :class="$style.root" :style="{ zIndex }" @contextmenu.prevent.stop="() => {}">
-		<MkMenu :items="items" :align="'left'" @close="$emit('closed')"/>
+		<MkMenu :items="items" :align="'left'" @close="emit('closed')"/>
 	</div>
 </Transition>
 </template>
diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue
index a2cb3185f4..b5f6e78b6c 100644
--- a/packages/frontend/src/components/MkCwButton.vue
+++ b/packages/frontend/src/components/MkCwButton.vue
@@ -45,11 +45,11 @@ function toggle() {
 .label {
 	margin-left: 4px;
 
-	&:before {
+	&::before {
 		content: '(';
 	}
 
-	&:after {
+	&::after {
 		content: ')';
 	}
 }
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index c52404a319..5c3c6aa51d 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkModal ref="modal" :preferType="'dialog'" :zPriority="'high'" @click="done(true)" @closed="emit('closed')">
+<MkModal ref="modal" :preferType="'dialog'" :zPriority="'high'" @click="done(true)" @closed="emit('closed')" @esc="cancel()">
 	<div :class="$style.root">
 		<div v-if="icon" :class="$style.icon">
 			<i :class="icon"></i>
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from 'vue';
+import { ref, shallowRef, computed } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -156,10 +156,6 @@ function onBgClick() {
 	if (props.cancelableByBgClick) cancel();
 }
 */
-function onKeydown(evt: KeyboardEvent) {
-	if (evt.key === 'Escape') cancel();
-}
-
 function onInputKeydown(evt: KeyboardEvent) {
 	if (evt.key === 'Enter' && okButtonDisabledReason.value === null) {
 		evt.preventDefault();
@@ -167,14 +163,6 @@ function onInputKeydown(evt: KeyboardEvent) {
 		ok();
 	}
 }
-
-onMounted(() => {
-	document.addEventListener('keydown', onKeydown);
-});
-
-onBeforeUnmount(() => {
-	document.removeEventListener('keydown', onKeydown);
-});
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue
index 4106b0a436..90284890a5 100644
--- a/packages/frontend/src/components/MkDrive.file.vue
+++ b/packages/frontend/src/components/MkDrive.file.vue
@@ -115,14 +115,14 @@ function onDragend() {
 		background: rgba(#000, 0.05);
 
 		> .label {
-			&:before,
-			&:after {
+			&::before,
+			&::after {
 				background: #0b65a5;
 			}
 
 			&.red {
-				&:before,
-				&:after {
+				&::before,
+				&::after {
 					background: #c12113;
 				}
 			}
@@ -133,14 +133,14 @@ function onDragend() {
 		background: rgba(#000, 0.1);
 
 		> .label {
-			&:before,
-			&:after {
+			&::before,
+			&::after {
 				background: #0b588c;
 			}
 
 			&.red {
-				&:before,
-				&:after {
+				&::before,
+				&::after {
 					background: #ce2212;
 				}
 			}
@@ -159,8 +159,8 @@ function onDragend() {
 		}
 
 		> .label {
-			&:before,
-			&:after {
+			&::before,
+			&::after {
 				display: none;
 			}
 		}
@@ -181,8 +181,8 @@ function onDragend() {
 	left: 0;
 	pointer-events: none;
 
-	&:before,
-	&:after {
+	&::before,
+	&::after {
 		content: "";
 		display: block;
 		position: absolute;
@@ -190,14 +190,14 @@ function onDragend() {
 		background: #0c7ac9;
 	}
 
-	&:before {
+	&::before {
 		top: 0;
 		left: 57px;
 		width: 28px;
 		height: 8px;
 	}
 
-	&:after {
+	&::after {
 		top: 57px;
 		left: 0;
 		width: 8px;
@@ -205,8 +205,8 @@ function onDragend() {
 	}
 
 	&.red {
-		&:before,
-		&:after {
+		&::before,
+		&::after {
 			background: #c12113;
 		}
 	}
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 1cc8b15b73..1790e57c24 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -296,7 +296,7 @@ function onContextmenu(ev: MouseEvent) {
 	cursor: pointer;
 
 	&.draghover {
-		&:after {
+		&::after {
 			content: "";
 			pointer-events: none;
 			position: absolute;
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 4bd4bee1e5..4a3ed69f47 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -5,7 +5,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }">
-	<input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter">
+	<input
+		ref="searchEl"
+		:value="q"
+		class="search"
+		data-prevent-emoji-insert
+		:class="{ filled: q != null && q != '' }"
+		:placeholder="i18n.ts.search"
+		type="search"
+		autocapitalize="off"
+		@input="input()"
+		@paste.stop="paste"
+		@keydown="onKeydown"
+	>
 	<!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 -->
 	<div ref="emojisEl" class="emojis" tabindex="-1">
 		<section class="result">
@@ -139,6 +151,7 @@ const props = withDefaults(defineProps<{
 
 const emit = defineEmits<{
 	(ev: 'chosen', v: string): void;
+	(ev: 'esc'): void;
 }>();
 
 const searchEl = shallowRef<HTMLInputElement>();
@@ -433,9 +446,18 @@ function paste(event: ClipboardEvent): void {
 	}
 }
 
-function onEnter(ev: KeyboardEvent) {
+function onKeydown(ev: KeyboardEvent) {
 	if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return;
-	done();
+	if (ev.key === 'Enter') {
+		ev.preventDefault();
+		ev.stopPropagation();
+		done();
+	}
+	if (ev.key === 'Escape') {
+		ev.preventDefault();
+		ev.stopPropagation();
+		emit('esc');
+	}
 }
 
 function done(query?: string): boolean | void {
@@ -702,11 +724,6 @@ defineExpose({
 					border-radius: 4px;
 					font-size: 24px;
 
-					&:focus-visible {
-						outline: solid 2px var(--focus);
-						z-index: 1;
-					}
-
 					&:hover {
 						background: rgba(0, 0, 0, 0.05);
 					}
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index adcea839ee..a413b146ba 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:manualShowing="manualShowing"
 	:src="src"
 	@click="modal?.close()"
+	@esc="modal?.close()"
 	@opening="opening"
 	@close="emit('close')"
 	@closed="emit('closed')"
@@ -28,6 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		:asDrawer="type === 'drawer'"
 		:max-height="maxHeight"
 		@chosen="chosen"
+		@esc="modal?.close()"
 	/>
 </MkModal>
 </template>
diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue
index c5dd877971..6783804cc5 100644
--- a/packages/frontend/src/components/MkFlashPreview.vue
+++ b/packages/frontend/src/components/MkFlashPreview.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkA :to="`/play/${flash.id}`" class="vhpxefrk _panel" tabindex="-1">
+<MkA :to="`/play/${flash.id}`" class="vhpxefrk _panel">
 	<article>
 		<header>
 			<h1 :title="flash.title">{{ flash.title }}</h1>
@@ -39,6 +39,10 @@ const props = defineProps<{
 		color: var(--accent);
 	}
 
+	&:focus-visible {
+		outline-offset: -2px;
+	}
+
 	> article {
 		padding: 16px;
 
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index 9b8eb19a11..f805be7b57 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -7,10 +7,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div ref="rootEl" :class="$style.root" role="group" :aria-expanded="opened">
 	<MkStickyContainer>
 		<template #header>
-			<div :class="[$style.header, { [$style.opened]: opened }]" class="_button" role="button" data-cy-folder-header @click="toggle">
+			<button :class="[$style.header, { [$style.opened]: opened }]" class="_button" role="button" data-cy-folder-header @click="toggle">
 				<div :class="$style.headerIcon"><slot name="icon"></slot></div>
 				<div :class="$style.headerText">
-					<div>
+					<div :class="$style.headerTextMain">
 						<MkCondensedLine :minScale="2 / 3"><slot name="label"></slot></MkCondensedLine>
 					</div>
 					<div :class="$style.headerTextSub">
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<i v-if="opened" class="ti ti-chevron-up icon"></i>
 					<i v-else class="ti ti-chevron-down icon"></i>
 				</div>
-			</div>
+			</button>
 		</template>
 
 		<div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : undefined, overflow: maxHeight ? `auto` : undefined }" :aria-hidden="!opened">
@@ -147,6 +147,10 @@ onMounted(() => {
 		background: var(--buttonHoverBg);
 	}
 
+	&:focus-within {
+		outline-offset: 2px;
+	}
+
 	&.active {
 		color: var(--accent);
 		background: var(--buttonHoverBg);
@@ -190,6 +194,12 @@ onMounted(() => {
 	padding-right: 12px;
 }
 
+.headerTextMain,
+.headerTextSub {
+	width: fit-content;
+	max-width: 100%;
+}
+
 .headerTextSub {
 	color: var(--fgTransparentWeak);
 	font-size: .85em;
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index 6a4081079c..ea76950c0d 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -185,17 +185,7 @@ onBeforeUnmount(() => {
 	}
 
 	&:focus-visible {
-		&:after {
-			content: "";
-			pointer-events: none;
-			position: absolute;
-			top: -5px;
-			right: -5px;
-			bottom: -5px;
-			left: -5px;
-			border: 2px solid var(--focus);
-			border-radius: 32px;
-		}
+		outline-offset: 2px;
 	}
 
 	&:hover {
diff --git a/packages/frontend/src/components/MkGalleryPostPreview.vue b/packages/frontend/src/components/MkGalleryPostPreview.vue
index 47cccd9b7c..2bb5b8762a 100644
--- a/packages/frontend/src/components/MkGalleryPostPreview.vue
+++ b/packages/frontend/src/components/MkGalleryPostPreview.vue
@@ -83,7 +83,7 @@ function leaveHover(): void {
 
 		> article {
 			> footer {
-				&:before {
+				&::before {
 					opacity: 1;
 				}
 			}
@@ -139,7 +139,7 @@ function leaveHover(): void {
 			text-shadow: 0 0 8px #000;
 			background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
 
-			&:before {
+			&::before {
 				content: "";
 				display: block;
 				position: absolute;
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index 4e3fafe845..617404f5c4 100644
--- a/packages/frontend/src/components/MkImgWithBlurhash.vue
+++ b/packages/frontend/src/components/MkImgWithBlurhash.vue
@@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		:enterToClass="defaultStore.state.animation && props.transition?.enterToClass || undefined"
 		:leaveFromClass="defaultStore.state.animation && props.transition?.leaveFromClass || undefined"
 	>
-		<canvas v-show="hide" key="canvas" ref="canvas" :class="$style.canvas" :width="canvasWidth" :height="canvasHeight" :title="title ?? undefined"/>
-		<img v-show="!hide" key="img" ref="img" :height="imgHeight" :width="imgWidth" :class="$style.img" :src="src ?? undefined" :title="title ?? undefined" :alt="alt ?? undefined" loading="eager" decoding="async"/>
+		<canvas v-show="hide" key="canvas" ref="canvas" :class="$style.canvas" :width="canvasWidth" :height="canvasHeight" :title="title ?? undefined" tabindex="-1"/>
+		<img v-show="!hide" key="img" ref="img" :height="imgHeight ?? undefined" :width="imgWidth ?? undefined" :class="$style.img" :src="src ?? undefined" :title="title ?? undefined" :alt="alt ?? undefined" loading="eager" decoding="async" tabindex="-1"/>
 	</TransitionGroup>
 </div>
 </template>
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index f9d4334c4c..8e3c19bd12 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkModal ref="modal" v-slot="{ type, maxHeight }" :preferType="preferedModalType" :anchor="anchor" :transparentBg="true" :src="src" @click="modal?.close()" @closed="emit('closed')">
+<MkModal ref="modal" v-slot="{ type, maxHeight }" :preferType="preferedModalType" :anchor="anchor" :transparentBg="true" :src="src" @click="modal?.close()" @closed="emit('closed')" @esc="modal?.close()">
 	<div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }">
 		<div class="main">
 			<template v-for="item in items" :key="item.text">
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index e8dfcc7768..582cf238c0 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -39,23 +39,37 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<audio
 			ref="audioEl"
 			preload="metadata"
+			@keydown.prevent="() => {}"
 		>
 			<source :src="audio.url">
 		</audio>
 		<div :class="[$style.controlsChild, $style.controlsLeft]">
-			<button class="_button" :class="$style.controlButton" @click="togglePlayPause">
+			<button
+				:class="['_button', $style.controlButton]"
+				tabindex="-1"
+				@click.stop="togglePlayPause"
+			>
 				<i v-if="isPlaying" class="ti ti-player-pause-filled"></i>
 				<i v-else class="ti ti-player-play-filled"></i>
 			</button>
 		</div>
 		<div :class="[$style.controlsChild, $style.controlsRight]">
-			<button class="_button" :class="$style.controlButton" @click="showMenu">
+			<button
+				:class="['_button', $style.controlButton]"
+				tabindex="-1"
+				@click.stop="() => {}"
+				@mousedown.prevent.stop="showMenu"
+			>
 				<i class="ti ti-settings"></i>
 			</button>
 		</div>
 		<div :class="[$style.controlsChild, $style.controlsTime]">{{ hms(elapsedTimeMs) }}</div>
 		<div :class="[$style.controlsChild, $style.controlsVolume]">
-			<button class="_button" :class="$style.controlButton" @click="toggleMute">
+			<button
+				:class="['_button', $style.controlButton]"
+				tabindex="-1"
+				@click.stop="toggleMute"
+			>
 				<i v-if="volume === 0" class="ti ti-volume-3"></i>
 				<i v-else class="ti ti-volume"></i>
 			</button>
@@ -371,7 +385,7 @@ onDeactivated(() => {
 	border-radius: var(--radius);
 	overflow: clip;
 
-	&:focus {
+	&:focus-visible {
 		outline: none;
 	}
 }
@@ -437,6 +451,10 @@ onDeactivated(() => {
 			color: var(--accent);
 			background-color: var(--accentedBg);
 		}
+
+		&:focus-visible {
+			outline: none;
+		}
 	}
 }
 
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index b1321a8ef9..24b177d255 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -39,6 +39,7 @@ import XVideo from '@/components/MkMediaVideo.vue';
 import * as os from '@/os.js';
 import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
 import { defaultStore } from '@/store.js';
+import { focusParent } from '@/scripts/focus.js';
 
 const props = defineProps<{
 	mediaList: Misskey.entities.DriveFile[];
@@ -49,7 +50,9 @@ const gallery = shallowRef<HTMLDivElement>();
 const pswpZIndex = os.claimZIndex('middle');
 document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
 const count = computed(() => props.mediaList.filter(media => previewable(media)).length);
-let lightbox: PhotoSwipeLightbox | null;
+let lightbox: PhotoSwipeLightbox | null = null;
+
+let activeEl: HTMLElement | null = null;
 
 const popstateHandler = (): void => {
 	if (lightbox?.pswp && lightbox.pswp.isOpen === true) {
@@ -60,7 +63,7 @@ const popstateHandler = (): void => {
 async function calcAspectRatio() {
 	if (!gallery.value) return;
 
-	let img = props.mediaList[0];
+	const img = props.mediaList[0];
 
 	if (props.mediaList.length !== 1 || !(img.properties.width && img.properties.height)) {
 		gallery.value.style.aspectRatio = '';
@@ -131,6 +134,7 @@ onMounted(() => {
 		bgOpacity: 1,
 		showAnimationDuration: 100,
 		hideAnimationDuration: 100,
+		returnFocus: false,
 		pswpModule: PhotoSwipe,
 	});
 
@@ -159,39 +163,47 @@ onMounted(() => {
 	lightbox.on('uiRegister', () => {
 		lightbox?.pswp?.ui?.registerElement({
 			name: 'altText',
-			className: 'pwsp__alt-text-container',
+			className: 'pswp__alt-text-container',
 			appendTo: 'wrapper',
-			onInit: (el, pwsp) => {
-				let textBox = document.createElement('p');
-				textBox.className = 'pwsp__alt-text _acrylic';
+			onInit: (el, pswp) => {
+				const textBox = document.createElement('p');
+				textBox.className = 'pswp__alt-text _acrylic';
 				el.appendChild(textBox);
 
-				pwsp.on('change', () => {
-					textBox.textContent = pwsp.currSlide?.data.comment;
+				pswp.on('change', () => {
+					textBox.textContent = pswp.currSlide?.data.comment;
 				});
 			},
 		});
 	});
 
-	lightbox.init();
-
-	window.addEventListener('popstate', popstateHandler);
-
-	lightbox.on('beforeOpen', () => {
+	lightbox.on('afterInit', () => {
+		activeEl = document.activeElement instanceof HTMLElement ? document.activeElement : null;
+		focusParent(activeEl, true, true);
+		lightbox?.pswp?.element?.focus({
+			preventScroll: true,
+		});
 		history.pushState(null, '', '#pswp');
 	});
 
-	lightbox.on('close', () => {
+	lightbox.on('destroy', () => {
+		focusParent(activeEl, true, false);
+		activeEl = null;
 		if (window.location.hash === '#pswp') {
 			history.back();
 		}
 	});
+
+	window.addEventListener('popstate', popstateHandler);
+
+	lightbox.init();
 });
 
 onUnmounted(() => {
 	window.removeEventListener('popstate', popstateHandler);
 	lightbox?.destroy();
 	lightbox = null;
+	activeEl = null;
 });
 
 const previewable = (file: Misskey.entities.DriveFile): boolean => {
@@ -199,6 +211,16 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => {
 	// FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切
 	return (file.type.startsWith('video') || file.type.startsWith('image')) && FILE_TYPE_BROWSERSAFE.includes(file.type);
 };
+
+const openGallery = () => {
+	if (props.mediaList.filter(media => previewable(media)).length > 0) {
+		lightbox?.loadAndOpen(0);
+	}
+};
+
+defineExpose({
+	openGallery,
+});
 </script>
 
 <style lang="scss" module>
@@ -298,7 +320,7 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => {
 	backdrop-filter: var(--modalBgFilter);
 }
 
-.pwsp__alt-text-container {
+.pswp__alt-text-container {
 	display: flex;
 	flex-direction: row;
 	align-items: center;
@@ -312,7 +334,7 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => {
 	max-width: 800px;
 }
 
-.pwsp__alt-text {
+.pswp__alt-text {
 	color: var(--fg);
 	margin: 0 auto;
 	text-align: center;
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 7c46084c63..0ec0039df4 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -481,7 +481,7 @@ onDeactivated(() => {
 	position: relative;
 	overflow: clip;
 
-	&:focus {
+	&:focus-visible {
 		outline: none;
 	}
 }
@@ -588,6 +588,10 @@ onDeactivated(() => {
 	border-radius: 99rem;
 
 	font-size: 1.1rem;
+
+	&:focus-visible {
+		outline: none;
+	}
 }
 
 .videoLoading {
@@ -651,6 +655,10 @@ onDeactivated(() => {
 		&:hover {
 			background-color: var(--accent);
 		}
+
+		&:focus-visible {
+			outline: none;
+		}
 	}
 }
 
diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue
index dfb6d34618..235790556c 100644
--- a/packages/frontend/src/components/MkMenu.child.vue
+++ b/packages/frontend/src/components/MkMenu.child.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
+import { nextTick, onMounted, onUnmounted, provide, shallowRef, watch } from 'vue';
 import MkMenu from './MkMenu.vue';
 import { MenuItem } from '@/types/menu.js';
 
@@ -19,7 +19,6 @@ const props = defineProps<{
 	targetElement: HTMLElement;
 	rootElement: HTMLElement;
 	width?: number;
-	viaKeyboard?: boolean;
 }>();
 
 const emit = defineEmits<{
@@ -27,6 +26,8 @@ const emit = defineEmits<{
 	(ev: 'actioned'): void;
 }>();
 
+provide('isNestingMenu', true);
+
 const el = shallowRef<HTMLElement>();
 const align = 'left';
 
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 119504f744..68479989b2 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -4,23 +4,42 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div role="menu">
+<div role="menu" @focusin.passive.stop="() => {}">
 	<div
-		ref="itemsEl" v-hotkey="keymap"
+		ref="itemsEl"
+		v-hotkey="keymap"
+		tabindex="0"
 		class="_popup _shadow"
-		:class="[$style.root, { [$style.center]: align === 'center', [$style.asDrawer]: asDrawer }]"
-		:style="{ width: (width && !asDrawer) ? width + 'px' : '', maxHeight: maxHeight ? maxHeight + 'px' : '' }"
-		@contextmenu.self="e => e.preventDefault()"
+		:class="{
+			[$style.root]: true,
+			[$style.center]: align === 'center',
+			[$style.asDrawer]: asDrawer,
+		}"
+		:style="{
+			width: (width && !asDrawer) ? `${width}px` : '',
+			maxHeight: maxHeight ? `${maxHeight}px` : '',
+		}"
+		@keydown.stop="() => {}"
+		@contextmenu.self.prevent="() => {}"
 	>
-		<template v-for="(item, i) in (items2 ?? [])">
-			<div v-if="item.type === 'divider'" role="separator" :class="$style.divider"></div>
-			<span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]">
+		<template v-for="item in (items2 ?? [])">
+			<div v-if="item.type === 'divider'" role="separator" tabindex="-1" :class="$style.divider"></div>
+			<span v-else-if="item.type === 'label'" role="menuitem" tabindex="-1" :class="[$style.label, $style.item]">
 				<span style="opacity: 0.7;">{{ item.text }}</span>
 			</span>
-			<span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item]">
+			<span v-else-if="item.type === 'pending'" role="menuitem" tabindex="0" :class="[$style.pending, $style.item]">
 				<span><MkEllipsis/></span>
 			</span>
-			<MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+			<MkA
+				v-else-if="item.type === 'link'"
+				role="menuitem"
+				tabindex="0"
+				:class="['_button', $style.item]"
+				:to="item.to"
+				@click.passive="close(true)"
+				@mouseenter.passive="onItemMouseEnter"
+				@mouseleave.passive="onItemMouseLeave"
+			>
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
 				<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
 				<div :class="$style.item_content">
@@ -28,20 +47,48 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
 				</div>
 			</MkA>
-			<a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+			<a
+				v-else-if="item.type === 'a'"
+				role="menuitem"
+				tabindex="0"
+				:class="['_button', $style.item]"
+				:href="item.href"
+				:target="item.target"
+				:download="item.download"
+				@click.passive="close(true)"
+				@mouseenter.passive="onItemMouseEnter"
+				@mouseleave.passive="onItemMouseLeave"
+			>
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
 				<div :class="$style.item_content">
 					<span :class="$style.item_content_text">{{ item.text }}</span>
 					<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
 				</div>
 			</a>
-			<button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+			<button
+				v-else-if="item.type === 'user'"
+				role="menuitem"
+				tabindex="0"
+				:class="['_button', $style.item, { [$style.active]: item.active }]"
+				@click.prevent="item.active ? close(false) : clicked(item.action, $event)"
+				@mouseenter.passive="onItemMouseEnter"
+				@mouseleave.passive="onItemMouseLeave"
+			>
 				<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
 				<div v-if="item.indicate" :class="$style.item_content">
 					<span :class="$style.indicator"><i class="_indicatorCircle"></i></span>
 				</div>
 			</button>
-			<button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } ]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+			<button
+				v-else-if="item.type === 'switch'"
+				role="menuitemcheckbox"
+				tabindex="0"
+				:class="['_button', $style.item]"
+				:disabled="unref(item.disabled)"
+				@click.prevent="switchItem(item)"
+				@mouseenter.passive="onItemMouseEnter"
+				@mouseleave.passive="onItemMouseLeave"
+			>
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
 				<MkSwitchButton v-else :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
 				<div :class="$style.item_content">
@@ -49,29 +96,61 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkSwitchButton v-if="item.icon" :class="[$style.switchButton, $style.caret]" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
 				</div>
 			</button>
-			<button v-else-if="item.type === 'radio'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showRadioOptions(item, $event)" @click="!preferClick ? null : showRadioOptions(item, $event)">
+			<button
+				v-else-if="item.type === 'radio'"
+				role="menuitem"
+				tabindex="0"
+				:class="['_button', $style.item, $style.parent, { [$style.active]: childShowingItem === item }]"
+				:disabled="unref(item.disabled)"
+				@mouseenter.prevent="preferClick ? null : showRadioOptions(item, $event)"
+				@keydown.enter.prevent="preferClick ? null : showRadioOptions(item, $event)"
+				@click.prevent="!preferClick ? null : showRadioOptions(item, $event)"
+			>
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]" style="pointer-events: none;"></i>
 				<div :class="$style.item_content">
 					<span :class="$style.item_content_text" style="pointer-events: none;">{{ item.text }}</span>
 					<span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span>
 				</div>
 			</button>
-			<button v-else-if="item.type === 'radioOption'" :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.radioActive]: item.active }]" @click="clicked(item.action, $event, false)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+			<button
+				v-else-if="item.type === 'radioOption'"
+				role="menuitemradio"
+				tabindex="0"
+				:class="['_button', $style.item, $style.radio, { [$style.active]: unref(item.active) }]"
+				@click.prevent="unref(item.active) ? null : clicked(item.action, $event, false)"
+				@mouseenter.passive="onItemMouseEnter"
+				@mouseleave.passive="onItemMouseLeave"
+			>
 				<div :class="$style.icon">
-					<span :class="[$style.radio, { [$style.radioChecked]: item.active }]"></span>
+					<span :class="[$style.radioIcon, { [$style.radioChecked]: unref(item.active) }]"></span>
 				</div>
 				<div :class="$style.item_content">
 					<span :class="$style.item_content_text">{{ item.text }}</span>
 				</div>
 			</button>
-			<button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)">
+			<button
+				v-else-if="item.type === 'parent'"
+				role="menuitem"
+				tabindex="0"
+				:class="['_button', $style.item, $style.parent, { [$style.active]: childShowingItem === item }]"
+				@mouseenter.prevent="preferClick ? null : showChildren(item, $event)"
+				@keydown.enter.prevent="preferClick ? null : showChildren(item, $event)"
+				@click.prevent="!preferClick ? null : showChildren(item, $event)"
+			>
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]" style="pointer-events: none;"></i>
 				<div :class="$style.item_content">
 					<span :class="$style.item_content_text" style="pointer-events: none;">{{ item.text }}</span>
 					<span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span>
 				</div>
 			</button>
-			<button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: getValue(item.active) }]" :disabled="getValue(item.active)" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
+			<button
+				v-else role="menuitem"
+				tabindex="0"
+				:class="['_button', $style.item, { [$style.danger]: item.danger, [$style.active]: unref(item.active) }]"
+				@click.prevent="unref(item.active) ? close(false) : clicked(item.action, $event)"
+				@mouseenter.passive="onItemMouseEnter"
+				@mouseleave.passive="onItemMouseLeave"
+			>
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
 				<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
 				<div :class="$style.item_content">
@@ -80,25 +159,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</button>
 		</template>
-		<span v-if="items2 == null || items2.length === 0" :class="[$style.none, $style.item]">
+		<span v-if="items2 == null || items2.length === 0" tabindex="-1" :class="[$style.none, $style.item]">
 			<span>{{ i18n.ts.none }}</span>
 		</span>
 	</div>
 	<div v-if="childMenu">
-		<XChild ref="child" :items="childMenu" :targetElement="childTarget!" :rootElement="itemsEl!" showing @actioned="childActioned" @close="close(false)"/>
+		<XChild ref="child" :items="childMenu" :targetElement="childTarget!" :rootElement="itemsEl!" @actioned="childActioned" @closed="closeChild"/>
 	</div>
 </div>
 </template>
 
 <script lang="ts">
-import { ComputedRef, computed, defineAsyncComponent, isRef, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
-import { focusPrev, focusNext } from '@/scripts/focus.js';
+import { computed, defineAsyncComponent, inject, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, unref, watch } from 'vue';
 import MkSwitchButton from '@/components/MkSwitch.button.vue';
 import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuRadio, MenuRadioOption, MenuParent } from '@/types/menu.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { isTouchUsing } from '@/scripts/touch.js';
 import { type Keymap } from '@/scripts/hotkey.js';
+import { isFocusable } from '@/scripts/focus.js';
+import { getNodeOrNull } from '@/scripts/get-dom-node-or-null.js';
 
 const childrenCache = new WeakMap<MenuParent, MenuItem[]>();
 </script>
@@ -108,7 +188,6 @@ const XChild = defineAsyncComponent(() => import('./MkMenu.child.vue'));
 
 const props = defineProps<{
 	items: MenuItem[];
-	viaKeyboard?: boolean;
 	asDrawer?: boolean;
 	align?: 'center' | string;
 	width?: number;
@@ -120,7 +199,9 @@ const emit = defineEmits<{
 	(ev: 'hide'): void;
 }>();
 
-const itemsEl = shallowRef<HTMLDivElement>();
+const isNestingMenu = inject<boolean>('isNestingMenu', false);
+
+const itemsEl = shallowRef<HTMLElement>();
 
 const items2 = ref<InnerMenuItem[]>();
 
@@ -177,25 +258,19 @@ function childActioned() {
 	close(true);
 }
 
-const onGlobalMousedown = (event: MouseEvent) => {
-	if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target as Node))) return;
-	if (child.value && child.value.checkHit(event)) return;
-	closeChild();
-};
-
 let childCloseTimer: null | number = null;
 
-function onItemMouseEnter(item) {
+function onItemMouseEnter() {
 	childCloseTimer = window.setTimeout(() => {
 		closeChild();
 	}, 300);
 }
 
-function onItemMouseLeave(item) {
+function onItemMouseLeave() {
 	if (childCloseTimer) window.clearTimeout(childCloseTimer);
 }
 
-async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
+async function showRadioOptions(item: MenuRadio, ev: Event) {
 	const children: MenuItem[] = Object.keys(item.options).map<MenuRadioOption>(key => {
 		const value = item.options[key];
 		return {
@@ -210,7 +285,7 @@ async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
 
 	if (props.asDrawer) {
 		os.popupMenu(children, ev.currentTarget ?? ev.target).finally(() => {
-			emit('close');
+			close(false);
 		});
 		emit('hide');
 	} else {
@@ -220,7 +295,7 @@ async function showRadioOptions(item: MenuRadio, ev: MouseEvent) {
 	}
 }
 
-async function showChildren(item: MenuParent, ev: MouseEvent) {
+async function showChildren(item: MenuParent, ev: Event) {
 	const children: MenuItem[] = await (async () => {
 		if (childrenCache.has(item)) {
 			return childrenCache.get(item)!;
@@ -237,7 +312,7 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
 
 	if (props.asDrawer) {
 		os.popupMenu(children, ev.currentTarget ?? ev.target).finally(() => {
-			emit('close');
+			close(false);
 		});
 		emit('hide');
 	} else {
@@ -256,15 +331,11 @@ function clicked(fn: MenuAction, ev: MouseEvent, doClose = true) {
 }
 
 function close(actioned = false) {
-	emit('close', actioned);
-}
-
-function focusUp() {
-	focusPrev(document.activeElement);
-}
-
-function focusDown() {
-	focusNext(document.activeElement);
+	disposeHandlers();
+	nextTick(() => {
+		closeChild();
+		emit('close', actioned);
+	});
 }
 
 function switchItem(item: MenuSwitch & { ref: any }) {
@@ -272,25 +343,75 @@ function switchItem(item: MenuSwitch & { ref: any }) {
 	item.ref = !item.ref;
 }
 
-function getValue<T>(item?: ComputedRef<T> | T) {
-	return isRef(item) ? item.value : item;
+function focusUp() {
+	if (disposed) return;
+	if (!itemsEl.value?.contains(document.activeElement)) return;
+
+	const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
+	const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
+	const targetIndex = (activeIndex !== -1 && activeIndex !== 0) ? (activeIndex - 1) : (focusableElements.length - 1);
+	const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
+
+	targetElement.focus();
 }
 
-onMounted(() => {
-	if (props.viaKeyboard) {
-		nextTick(() => {
-			if (itemsEl.value) focusNext(itemsEl.value.children[0], true, false);
-		});
+function focusDown() {
+	if (disposed) return;
+	if (!itemsEl.value?.contains(document.activeElement)) return;
+
+	const focusableElements = Array.from(itemsEl.value.children).filter(isFocusable);
+	const activeIndex = focusableElements.findIndex(el => el === document.activeElement);
+	const targetIndex = (activeIndex !== -1 && activeIndex !== (focusableElements.length - 1)) ? (activeIndex + 1) : 0;
+	const targetElement = focusableElements.at(targetIndex) ?? itemsEl.value;
+
+	targetElement.focus();
+}
+
+const onGlobalFocusin = (ev: FocusEvent) => {
+	if (disposed) return;
+	if (itemsEl.value?.parentElement?.contains(getNodeOrNull(ev.target))) return;
+	nextTick(() => {
+		if (itemsEl.value != null && isFocusable(itemsEl.value)) {
+			itemsEl.value.focus({ preventScroll: true });
+			nextTick(() => focusDown());
+		}
+	});
+};
+
+const onGlobalMousedown = (ev: MouseEvent) => {
+	if (disposed) return;
+	if (childTarget.value?.contains(getNodeOrNull(ev.target))) return;
+	if (child.value?.checkHit(ev)) return;
+	closeChild();
+};
+
+const setupHandlers = () => {
+	if (!isNestingMenu) {
+		document.addEventListener('focusin', onGlobalFocusin, { passive: true });
 	}
-
-	// TODO: アクティブな要素までスクロール
-	//itemsEl.scrollTo();
-
 	document.addEventListener('mousedown', onGlobalMousedown, { passive: true });
+};
+
+let disposed = false;
+
+const disposeHandlers = () => {
+	disposed = true;
+	if (!isNestingMenu) {
+		document.removeEventListener('focusin', onGlobalFocusin);
+	}
+	document.removeEventListener('mousedown', onGlobalMousedown);
+};
+
+onMounted(() => {
+	setupHandlers();
+
+	if (!isNestingMenu) {
+		nextTick(() => itemsEl.value?.focus({ preventScroll: true }));
+	}
 });
 
 onBeforeUnmount(() => {
-	document.removeEventListener('mousedown', onGlobalMousedown);
+	disposeHandlers();
 });
 </script>
 
@@ -303,6 +424,10 @@ onBeforeUnmount(() => {
 	overflow: auto;
 	overscroll-behavior: contain;
 
+	&:focus-visible {
+		outline: none;
+	}
+
 	&.center {
 		> .item {
 			text-align: center;
@@ -320,7 +445,7 @@ onBeforeUnmount(() => {
 			font-size: 1em;
 			padding: 12px 24px;
 
-			&:before {
+			&::before {
 				width: calc(100% - 24px);
 				border-radius: 12px;
 			}
@@ -350,8 +475,10 @@ onBeforeUnmount(() => {
 	text-align: left;
 	overflow: hidden;
 	text-overflow: ellipsis;
+	text-decoration: none !important;
+	color: var(--menuFg, var(--fg));
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		position: absolute;
@@ -365,56 +492,56 @@ onBeforeUnmount(() => {
 		border-radius: 6px;
 	}
 
-	&:not(:disabled):hover {
-		color: var(--accent);
-		text-decoration: none;
+	&:focus-visible {
+		outline: none;
 
-		&:before {
-			background: var(--accentedBg);
+		&:not(:hover):not(:active)::before {
+			outline: var(--focus) solid 2px;
+			outline-offset: -2px;
 		}
 	}
 
+	&:not(:disabled) {
+		&:hover,
+		&:focus-visible:active,
+		&:focus-visible.active {
+			color: var(--menuHoverFg, var(--accent));
+
+			&::before {
+				background-color: var(--menuHoverBg, var(--accentedBg));
+			}
+		}
+
+		&:not(:focus-visible):active,
+		&:not(:focus-visible).active {
+			color: var(--menuActiveFg, var(--fgOnAccent));
+
+			&::before {
+				background-color: var(--menuActiveBg, var(--accent));
+			}
+		}
+	}
+
+	&:disabled {
+		cursor: not-allowed;
+	}
+
 	&.danger {
-		color: #ff2a2a;
-
-		&:hover {
-			color: #fff;
-
-			&:before {
-				background: #ff4242;
-			}
-		}
-
-		&:active {
-			color: #fff;
-
-			&:before {
-				background: #d42e2e !important;
-			}
-		}
+		--menuFg: #ff2a2a;
+		--menuHoverFg: #fff;
+		--menuHoverBg: #ff4242;
+		--menuActiveFg: #fff;
+		--menuActiveBg: #d42e2e;
 	}
 
-	&:active,
-	&.active {
-		color: var(--fgOnAccent) !important;
-		opacity: 1;
-
-		&:before {
-			background: var(--accent) !important;
-		}
+	&.radio {
+		--menuActiveFg: var(--accent);
+		--menuActiveBg: var(--accentedBg);
 	}
 
-	&.radioActive {
-		color: var(--accent) !important;
-		opacity: 1;
-
-		&:before {
-			background-color: var(--accentedBg) !important;
-		}
-	}
-
-	&:not(:active):focus-visible {
-		box-shadow: 0 0 0 2px var(--focus) inset;
+	&.parent {
+		--menuActiveFg: var(--accent);
+		--menuActiveBg: var(--accentedBg);
 	}
 
 	&.label {
@@ -432,22 +559,6 @@ onBeforeUnmount(() => {
 		pointer-events: none;
 		opacity: 0.7;
 	}
-
-	&.parent {
-		pointer-events: auto;
-		display: flex;
-		align-items: center;
-		cursor: default;
-
-		&.childShowing {
-			color: var(--accent);
-			text-decoration: none;
-
-			&:before {
-				background: var(--accentedBg);
-			}
-		}
-	}
 }
 
 .item_content {
@@ -466,18 +577,6 @@ onBeforeUnmount(() => {
 	overflow: hidden;
 }
 
-.switch {
-	position: relative;
-	display: flex;
-	transition: all 0.2s ease;
-	user-select: none;
-	cursor: pointer;
-}
-
-.switchDisabled {
-	cursor: not-allowed;
-}
-
 .switchButton {
 	margin-left: -2px;
 	--height: 1.35em;
@@ -489,14 +588,6 @@ onBeforeUnmount(() => {
 	text-overflow: ellipsis;
 }
 
-.switchInput {
-	position: absolute;
-	width: 0;
-	height: 0;
-	opacity: 0;
-	margin: 0;
-}
-
 .icon {
 	margin-right: 8px;
 	line-height: 1;
@@ -525,12 +616,12 @@ onBeforeUnmount(() => {
 	border-top: solid 0.5px var(--divider);
 }
 
-.radio {
+.radioIcon {
 	display: inline-block;
 	position: relative;
 	width: 1em;
 	height: 1em;
-	vertical-align: -.125em;
+	vertical-align: -0.125em;
 	border-radius: 50%;
 	border: solid 2px var(--divider);
 	background-color: var(--panel);
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 264d8b6c9c..a5fbf8d365 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -30,9 +30,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 		[$style.transition_modal_leaveTo]: transitionName === 'modal',
 		[$style.transition_send_leaveTo]: transitionName === 'send',
 	})"
-	:duration="transitionDuration" appear @afterLeave="emit('closed')" @enter="emit('opening')" @afterEnter="onOpened"
+	:duration="transitionDuration" appear @afterLeave="onClosed" @enter="emit('opening')" @afterEnter="onOpened"
 >
-	<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" :class="[$style.root, { [$style.drawer]: type === 'drawer', [$style.dialog]: type === 'dialog', [$style.popup]: type === 'popup' }]" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
+	<div v-show="manualShowing != null ? manualShowing : showing" ref="modalRootEl" v-hotkey.global="keymap" :class="[$style.root, { [$style.drawer]: type === 'drawer', [$style.dialog]: type === 'dialog', [$style.popup]: type === 'popup' }]" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
 		<div data-cy-bg :data-cy-transparent="isEnableBgTransparent" class="_modalBg" :class="[$style.bg, { [$style.bgTransparent]: isEnableBgTransparent }]" :style="{ zIndex }" @click="onBgClick" @mousedown="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
 		<div ref="content" :class="[$style.content, { [$style.fixed]: fixed }]" :style="{ zIndex }" @click.self="onBgClick">
 			<slot :max-height="maxHeight" :type="type"></slot>
@@ -48,6 +48,8 @@ import { isTouchUsing } from '@/scripts/touch.js';
 import { defaultStore } from '@/store.js';
 import { deviceKind } from '@/scripts/device-kind.js';
 import { type Keymap } from '@/scripts/hotkey.js';
+import { focusTrap } from '@/scripts/focus-trap.js';
+import { focusParent } from '@/scripts/focus.js';
 
 function getFixedContainer(el: Element | null): Element | null {
 	if (el == null || el.tagName === 'BODY') return null;
@@ -69,6 +71,7 @@ const props = withDefaults(defineProps<{
 	zPriority?: 'low' | 'middle' | 'high';
 	noOverlap?: boolean;
 	transparentBg?: boolean;
+	returnFocusTo?: HTMLElement | null;
 }>(), {
 	manualShowing: null,
 	src: null,
@@ -77,6 +80,7 @@ const props = withDefaults(defineProps<{
 	zPriority: 'low',
 	noOverlap: true,
 	transparentBg: false,
+	returnFocusTo: null,
 });
 
 const emit = defineEmits<{
@@ -94,6 +98,7 @@ const maxHeight = ref<number>();
 const fixed = ref(false);
 const transformOrigin = ref('center');
 const showing = ref(true);
+const modalRootEl = shallowRef<HTMLElement>();
 const content = shallowRef<HTMLElement>();
 const zIndex = os.claimZIndex(props.zPriority);
 const useSendAnime = ref(false);
@@ -132,6 +137,7 @@ const transitionDuration = computed((() =>
 					: 0
 ));
 
+let releaseFocusTrap: (() => void) | null = null;
 let contentClicking = false;
 
 function close(opts: { useSendAnimation?: boolean } = {}) {
@@ -296,6 +302,10 @@ const onOpened = () => {
 	}, { passive: true });
 };
 
+const onClosed = () => {
+	emit('closed');
+};
+
 const alignObserver = new ResizeObserver((entries, observer) => {
 	align();
 });
@@ -313,6 +323,20 @@ onMounted(() => {
 		align();
 	}, { immediate: true });
 
+	watch([showing, () => props.manualShowing], ([showing, manualShowing]) => {
+		if (manualShowing === true || (manualShowing == null && showing === true)) {
+			if (modalRootEl.value != null) {
+				const { release } = focusTrap(modalRootEl.value);
+
+				releaseFocusTrap = release;
+				modalRootEl.value.focus();
+			}
+		} else {
+			releaseFocusTrap?.();
+			focusParent(props.returnFocusTo ?? props.src, true, false);
+		}
+	}, { immediate: true });
+
 	nextTick(() => {
 		alignObserver.observe(content.value!);
 	});
diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue
index d3657afa94..78053c8cfd 100644
--- a/packages/frontend/src/components/MkModalWindow.vue
+++ b/packages/frontend/src/components/MkModalWindow.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkModal ref="modal" :preferType="'dialog'" @click="onBgClick" @closed="$emit('closed')">
-	<div ref="rootEl" :class="$style.root" :style="{ width: `${width}px`, height: `min(${height}px, 100%)` }" @keydown="onKeydown">
+<MkModal ref="modal" :preferType="'dialog'" @click="onBgClick" @closed="emit('closed')" @esc="emit('esc')">
+	<div ref="rootEl" :class="$style.root" :style="{ width: `${width}px`, height: `min(${height}px, 100%)` }">
 		<div ref="headerEl" :class="$style.header">
 			<button v-if="withOkButton" :class="$style.headerButton" class="_button" @click="$emit('close')"><i class="ti ti-x"></i></button>
 			<span :class="$style.title">
@@ -42,6 +42,7 @@ const emit = defineEmits<{
 	(event: 'close'): void;
 	(event: 'closed'): void;
 	(event: 'ok'): void;
+	(event: 'esc'): void;
 }>();
 
 const modal = shallowRef<InstanceType<typeof MkModal>>();
@@ -58,14 +59,6 @@ const onBgClick = () => {
 	emit('click');
 };
 
-const onKeydown = (evt) => {
-	if (evt.which === 27) { // Esc
-		evt.preventDefault();
-		evt.stopPropagation();
-		close();
-	}
-};
-
 const ro = new ResizeObserver((entries, observer) => {
 	if (rootEl.value == null || headerEl.value == null) return;
 	bodyWidth.value = rootEl.value.offsetWidth;
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index fc72813285..420ff2c651 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	ref="rootEl"
 	v-hotkey="keymap"
 	:class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]"
-	:tabindex="!isDeleted ? '-1' : undefined"
+	:tabindex="isDeleted ? '-1' : '0'"
 >
 	<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
 	<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</template>
 		</I18n>
 		<div :class="$style.renoteInfo">
-			<button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()">
+			<button ref="renoteTime" :class="$style.renoteTime" class="_button" @mousedown.prevent="showRenoteMenu()">
 				<i class="ti ti-dots" :class="$style.renoteMenu"></i>
 				<MkTime :time="note.createdAt"/>
 			</button>
@@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</div>
 					</div>
 					<div v-if="appearNote.files && appearNote.files.length > 0">
-						<MkMediaList :mediaList="appearNote.files"/>
+						<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
 					</div>
 					<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/>
 					<div v-if="isEnabledUrlPreview">
@@ -110,7 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					ref="renoteButton"
 					:class="$style.footerButton"
 					class="_button"
-					@mousedown="renote()"
+					@mousedown.prevent="renote()"
 				>
 					<i class="ti ti-repeat"></i>
 					<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.renoteCount) }}</p>
@@ -125,10 +125,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<i v-else class="ti ti-plus"></i>
 					<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
 				</button>
-				<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
+				<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown.prevent="clip()">
 					<i class="ti ti-paperclip"></i>
 				</button>
-				<button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="showMenu()">
+				<button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown.prevent="showMenu()">
 					<i class="ti ti-dots"></i>
 				</button>
 			</footer>
@@ -175,7 +175,6 @@ import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
 import { pleaseLogin } from '@/scripts/please-login.js';
-import { focusPrev, focusNext } from '@/scripts/focus.js';
 import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import number from '@/filters/number.js';
@@ -199,6 +198,7 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import { shouldCollapsed } from '@/scripts/collapsed.js';
 import { isEnabledUrlPreview } from '@/instance.js';
 import { type Keymap } from '@/scripts/hotkey.js';
+import { focusPrev, focusNext } from '@/scripts/focus.js';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -257,6 +257,7 @@ const renoteTime = shallowRef<HTMLElement>();
 const reactButton = shallowRef<HTMLElement>();
 const clipButton = shallowRef<HTMLElement>();
 const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
+const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
 const isMyRenote = $i && ($i.id === note.value.userId);
 const showContent = ref(false);
 const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
@@ -318,7 +319,7 @@ const keymap = {
 	},
 	'o': () => {
 		if (renoteCollapsed.value) return;
-		showMenu();
+		galleryEl.value?.openGallery();
 	},
 	'v|enter': () => {
 		if (renoteCollapsed.value) {
@@ -419,7 +420,7 @@ function renote(viaKeyboard = false) {
 	});
 }
 
-function reply(viaKeyboard = false): void {
+function reply(): void {
 	pleaseLogin();
 	if (props.mock) {
 		return;
@@ -427,13 +428,12 @@ function reply(viaKeyboard = false): void {
 	os.post({
 		reply: appearNote.value,
 		channel: appearNote.value.channel,
-		animation: !viaKeyboard,
 	}).then(() => {
 		focus();
 	});
 }
 
-function react(viaKeyboard = false): void {
+function react(): void {
 	pleaseLogin();
 	showMovedDialog();
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
@@ -528,18 +528,16 @@ function onContextmenu(ev: MouseEvent): void {
 	}
 }
 
-function showMenu(viaKeyboard = false): void {
+function showMenu(): void {
 	if (props.mock) {
 		return;
 	}
 
 	const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value });
-	os.popupMenu(menu, menuButton.value, {
-		viaKeyboard,
-	}).then(focus).finally(cleanup);
+	os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
 }
 
-async function clip() {
+async function clip(): Promise<void> {
 	if (props.mock) {
 		return;
 	}
@@ -547,7 +545,7 @@ async function clip() {
 	os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus);
 }
 
-function showRenoteMenu(viaKeyboard = false): void {
+function showRenoteMenu(): void {
 	if (props.mock) {
 		return;
 	}
@@ -572,18 +570,14 @@ function showRenoteMenu(viaKeyboard = false): void {
 			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
 			{ type: 'divider' },
 			getUnrenote(),
-		], renoteTime.value, {
-			viaKeyboard: viaKeyboard,
-		});
+		], renoteTime.value);
 	} else {
 		os.popupMenu([
 			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
 			{ type: 'divider' },
 			getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote),
 			($i?.isModerator || $i?.isAdmin) ? getUnrenote() : undefined,
-		], renoteTime.value, {
-			viaKeyboard: viaKeyboard,
-		});
+		], renoteTime.value);
 	}
 }
 
@@ -596,11 +590,11 @@ function blur() {
 }
 
 function focusBefore() {
-	focusPrev(rootEl.value ?? null);
+	focusPrev(rootEl.value);
 }
 
 function focusAfter() {
-	focusNext(rootEl.value ?? null);
+	focusNext(rootEl.value);
 }
 
 function readPromo() {
@@ -638,7 +632,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	&:focus-visible {
 		outline: none;
 
-		&:after {
+		&::after {
 			content: "";
 			pointer-events: none;
 			display: block;
@@ -651,7 +645,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 			margin: auto;
 			width: calc(100% - 8px);
 			height: calc(100% - 8px);
-			border: dashed 1px var(--focus);
+			border: dashed 2px var(--focus);
 			border-radius: var(--radius);
 			box-sizing: border-box;
 		}
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 8f65e3b60a..a8fed56c39 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	ref="rootEl"
 	v-hotkey="keymap"
 	:class="$style.root"
+	:tabindex="isDeleted ? '-1' : '0'"
 >
 	<div v-if="appearNote.reply && appearNote.reply.replyId">
 		<div v-if="!conversationLoaded" style="padding: 16px">
@@ -31,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</I18n>
 		</span>
 		<div :class="$style.renoteInfo">
-			<button ref="renoteTime" class="_button" :class="$style.renoteTime" @click="showRenoteMenu()">
+			<button ref="renoteTime" class="_button" :class="$style.renoteTime" @mousedown.prevent="showRenoteMenu()">
 				<i v-if="isMyRenote" class="ti ti-dots" style="margin-right: 4px;"></i>
 				<MkTime :time="note.createdAt"/>
 			</button>
@@ -92,7 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</div>
 				</div>
 				<div v-if="appearNote.files && appearNote.files.length > 0">
-					<MkMediaList :mediaList="appearNote.files"/>
+					<MkMediaList ref="galleryEl" :mediaList="appearNote.files"/>
 				</div>
 				<MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/>
 				<div v-if="isEnabledUrlPreview">
@@ -118,7 +119,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				ref="renoteButton"
 				class="_button"
 				:class="$style.noteFooterButton"
-				@mousedown="renote()"
+				@mousedown.prevent="renote()"
 			>
 				<i class="ti ti-repeat"></i>
 				<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
@@ -133,10 +134,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<i v-else class="ti ti-plus"></i>
 				<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
 			</button>
-			<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
+			<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="clip()">
 				<i class="ti ti-paperclip"></i>
 			</button>
-			<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="showMenu()">
+			<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="showMenu()">
 				<i class="ti ti-dots"></i>
 			</button>
 		</footer>
@@ -281,6 +282,7 @@ const renoteTime = shallowRef<HTMLElement>();
 const reactButton = shallowRef<HTMLElement>();
 const clipButton = shallowRef<HTMLElement>();
 const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
+const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
 const isMyRenote = $i && ($i.id === note.value.userId);
 const showContent = ref(false);
 const isDeleted = ref(false);
@@ -303,6 +305,7 @@ const keymap = {
 		if (!defaultStore.state.showClipButtonInNoteFooter) return;
 		clip();
 	},
+	'o': () => galleryEl.value?.openGallery(),
 	'v|enter': () => {
 		if (appearNote.value.cw != null) {
 			showContent.value = !showContent.value;
@@ -392,29 +395,26 @@ if (appearNote.value.reactionAcceptance === 'likeOnly') {
 	});
 }
 
-function renote(viaKeyboard = false) {
+function renote() {
 	pleaseLogin();
 	showMovedDialog();
 
 	const { menu } = getRenoteMenu({ note: note.value, renoteButton });
-	os.popupMenu(menu, renoteButton.value, {
-		viaKeyboard,
-	});
+	os.popupMenu(menu, renoteButton.value);
 }
 
-function reply(viaKeyboard = false): void {
+function reply(): void {
 	pleaseLogin();
 	showMovedDialog();
 	os.post({
 		reply: appearNote.value,
 		channel: appearNote.value.channel,
-		animation: !viaKeyboard,
 	}).then(() => {
 		focus();
 	});
 }
 
-function react(viaKeyboard = false): void {
+function react(): void {
 	pleaseLogin();
 	showMovedDialog();
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
@@ -424,7 +424,7 @@ function react(viaKeyboard = false): void {
 			noteId: appearNote.value.id,
 			reaction: '❤️',
 		});
-		const el = reactButton.value as HTMLElement | null | undefined;
+		const el = reactButton.value;
 		if (el) {
 			const rect = el.getBoundingClientRect();
 			const x = rect.left + (el.offsetWidth / 2);
@@ -488,18 +488,16 @@ function onContextmenu(ev: MouseEvent): void {
 	}
 }
 
-function showMenu(viaKeyboard = false): void {
+function showMenu(): void {
 	const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted });
-	os.popupMenu(menu, menuButton.value, {
-		viaKeyboard,
-	}).then(focus).finally(cleanup);
+	os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup);
 }
 
-async function clip() {
+async function clip(): Promise<void> {
 	os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted }), clipButton.value).then(focus);
 }
 
-function showRenoteMenu(viaKeyboard = false): void {
+function showRenoteMenu(): void {
 	if (!isMyRenote) return;
 	pleaseLogin();
 	os.popupMenu([{
@@ -512,9 +510,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 			});
 			isDeleted.value = true;
 		},
-	}], renoteTime.value, {
-		viaKeyboard: viaKeyboard,
-	});
+	}], renoteTime.value);
 }
 
 function focus() {
@@ -556,6 +552,28 @@ function loadConversation() {
 	transition: box-shadow 0.1s ease;
 	overflow: clip;
 	contain: content;
+
+	&:focus-visible {
+		outline: none;
+
+		&::after {
+			content: "";
+			pointer-events: none;
+			display: block;
+			position: absolute;
+			z-index: 10;
+			top: 0;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			margin: auto;
+			width: calc(100% - 8px);
+			height: calc(100% - 8px);
+			border: dashed 2px var(--focus);
+			border-radius: var(--radius);
+			box-sizing: border-box;
+		}
+	}
 }
 
 .replyTo {
diff --git a/packages/frontend/src/components/MkNotePreview.vue b/packages/frontend/src/components/MkNotePreview.vue
index cc2f770cda..c4479bb0d6 100644
--- a/packages/frontend/src/components/MkNotePreview.vue
+++ b/packages/frontend/src/components/MkNotePreview.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div :class="$style.root">
-	<MkAvatar :class="$style.avatar" :user="user" link preview/>
+	<MkAvatar :class="$style.avatar" :user="user"/>
 	<div :class="$style.main">
 		<div :class="$style.header">
 			<MkUserName :user="user" :nowrap="true"/>
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 3fa2eb254e..ee65743574 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -343,7 +343,7 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
 	margin-right: 4px;
 	position: relative;
 
-	&:before {
+	&::before {
 		position: absolute;
 		transform: rotate(180deg);
 	}
diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue
index f6dc00698c..8559d4b96e 100644
--- a/packages/frontend/src/components/MkPagePreview.vue
+++ b/packages/frontend/src/components/MkPagePreview.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
+<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj">
 	<div v-if="page.eyeCatchingImage" class="thumbnail">
 		<MediaImage
 			:image="page.eyeCatchingImage"
@@ -50,12 +50,29 @@ const props = defineProps<{
 <style lang="scss" scoped>
 .vhpxefrj {
 	display: block;
+	position: relative;
 
 	&:hover {
 		text-decoration: none;
 		color: var(--accent);
 	}
 
+	&:focus-within {
+		outline: none;
+
+		&::after {
+			content: "";
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			height: 100%;
+			border-radius: var(--radius);
+			pointer-events: none;
+			box-shadow: inset 0 0 0 2px var(--focus);
+		}
+	}
+
 	> .thumbnail {
 		& + article {
 			border-radius: 0 0 var(--radius) var(--radius);
diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue
index be0b07612a..8a0c7b1e54 100644
--- a/packages/frontend/src/components/MkPopupMenu.vue
+++ b/packages/frontend/src/components/MkPopupMenu.vue
@@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkModal ref="modal" v-slot="{ type, maxHeight }" :manualShowing="manualShowing" :zPriority="'high'" :src="src" :transparentBg="true" @click="click" @close="onModalClose" @closed="onModalClosed">
-	<MkMenu :items="items" :align="align" :width="width" :max-height="maxHeight" :asDrawer="type === 'drawer'" :class="{ [$style.drawer]: type === 'drawer' }" @close="onMenuClose" @hide="hide"/>
+<MkModal ref="modal" v-slot="{ type, maxHeight }" :manualShowing="manualShowing" :zPriority="'high'" :src="src" :transparentBg="true" :returnFocusTo="returnFocusTo" @click="click" @close="onModalClose" @closed="onModalClosed">
+	<MkMenu :items="items" :align="align" :width="width" :max-height="maxHeight" :asDrawer="type === 'drawer'" :returnFocusTo="returnFocusTo" :class="{ [$style.drawer]: type === 'drawer' }" @close="onMenuClose" @hide="hide"/>
 </MkModal>
 </template>
 
@@ -19,8 +19,8 @@ defineProps<{
 	items: MenuItem[];
 	align?: 'center' | string;
 	width?: number;
-	viaKeyboard?: boolean;
 	src?: any;
+	returnFocusTo?: HTMLElement | null;
 }>();
 
 const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 0dc1aa0891..d057d197ec 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -570,6 +570,7 @@ function clear() {
 
 function onKeydown(ev: KeyboardEvent) {
 	if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
+
 	if (ev.key === 'Escape') emit('esc');
 }
 
@@ -1083,6 +1084,15 @@ defineExpose({
 	margin: 12px 12px 12px 6px;
 	vertical-align: bottom;
 
+	&:focus-visible {
+		outline: none;
+
+		.submitInner {
+			outline: 2px solid var(--fgOnAccent);
+			outline-offset: -4px;
+		}
+	}
+
 	&:disabled {
 		opacity: 0.7;
 	}
diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue
index ac37cb31bc..d6bca29050 100644
--- a/packages/frontend/src/components/MkPostFormDialog.vue
+++ b/packages/frontend/src/components/MkPostFormDialog.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()">
+<MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()" @esc="modal?.close()">
 	<MkPostForm ref="form" :class="$style.form" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="modal?.close()" @esc="modal?.close()"/>
 </MkModal>
 </template>
diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue
index 6676e3bf5b..22fc86723e 100644
--- a/packages/frontend/src/components/MkRadio.vue
+++ b/packages/frontend/src/components/MkRadio.vue
@@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked }]"
 	:aria-checked="checked"
 	:aria-disabled="disabled"
+	role="checkbox"
 	@click="toggle"
 >
 	<input
@@ -69,6 +70,11 @@ function toggle(): void {
 		border-color: var(--inputBorderHover) !important;
 	}
 
+	&:focus-within {
+		outline: none;
+		box-shadow: 0 0 0 2px var(--focus);
+	}
+
 	&.checked {
 		background-color: var(--accentedBg) !important;
 		border-color: var(--accentedBg) !important;
@@ -78,7 +84,7 @@ function toggle(): void {
 		> .button {
 			border-color: var(--accent);
 
-			&:after {
+			&::after {
 				background-color: var(--accent);
 				transform: scale(1);
 				opacity: 1;
@@ -104,7 +110,7 @@ function toggle(): void {
 	border-radius: 100%;
 	transition: inherit;
 
-	&:after {
+	&::after {
 		content: '';
 		display: block;
 		position: absolute;
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index 358d9b1f4b..0eba8d6a9c 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -6,20 +6,29 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div>
 	<div :class="$style.label" @click="focus"><slot name="label"></slot></div>
-	<div ref="container" :class="[$style.input, { [$style.inline]: inline, [$style.disabled]: disabled, [$style.focused]: focused }]" @mousedown.prevent="show">
+	<div
+		ref="container"
+		tabindex="0"
+		:class="[$style.input, { [$style.inline]: inline, [$style.disabled]: disabled, [$style.focused]: focused || opening }]"
+		@focus="focused = true"
+		@blur="focused = false"
+		@mousedown.prevent="show"
+		@keydown.space.enter="show"
+	>
 		<div ref="prefixEl" :class="$style.prefix"><slot name="prefix"></slot></div>
 		<select
 			ref="inputEl"
 			v-model="v"
 			v-adaptive-border
+			tabindex="-1"
 			:class="$style.inputCore"
 			:disabled="disabled"
 			:required="required"
 			:readonly="readonly"
 			:placeholder="placeholder"
-			@focus="focused = true"
-			@blur="focused = false"
 			@input="onInput"
+			@mousedown.prevent="() => {}"
+			@keydown.prevent="() => {}"
 		>
 			<slot></slot>
 		</select>
@@ -75,7 +84,7 @@ const height =
 	props.large ? 39 :
 	36;
 
-const focus = () => inputEl.value?.focus();
+const focus = () => container.value?.focus();
 const onInput = (ev) => {
 	changed.value = true;
 };
@@ -126,7 +135,9 @@ onMounted(() => {
 });
 
 function show() {
-	focused.value = true;
+	if (opening.value) return;
+	focus();
+
 	opening.value = true;
 
 	const menu: MenuItem[] = [];
@@ -173,8 +184,6 @@ function show() {
 		onClosing: () => {
 			opening.value = false;
 		},
-	}).then(() => {
-		focused.value = false;
 	});
 }
 </script>
@@ -225,6 +234,10 @@ function show() {
 		}
 	}
 
+	&:focus {
+		outline: none;
+	}
+
 	&:hover {
 		> .inputCore {
 			border-color: var(--inputBorderHover) !important;
diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue
index 3023f63e5d..1a880170be 100644
--- a/packages/frontend/src/components/MkSuperMenu.vue
+++ b/packages/frontend/src/components/MkSuperMenu.vue
@@ -10,15 +10,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 		<div class="items">
 			<template v-for="(item, i) in group.items">
-				<a v-if="item.type === 'a'" :href="item.href" :target="item.target" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }">
+				<a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }">
 					<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
 					<span class="text">{{ item.text }}</span>
 				</a>
-				<button v-else-if="item.type === 'button'" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)">
+				<button v-else-if="item.type === 'button'" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)">
 					<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
 					<span class="text">{{ item.text }}</span>
 				</button>
-				<MkA v-else :to="item.to" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }">
+				<MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }">
 					<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span>
 					<span class="text">{{ item.text }}</span>
 				</MkA>
@@ -67,6 +67,10 @@ defineProps<{
 					background: var(--panelHighlight);
 				}
 
+				&:focus-visible {
+					outline-offset: -2px;
+				}
+
 				&.active {
 					color: var(--accent);
 					background: var(--accentedBg);
diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue
index 721ac357f4..a0994d9cc9 100644
--- a/packages/frontend/src/components/MkSwitch.vue
+++ b/packages/frontend/src/components/MkSwitch.vue
@@ -10,9 +10,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 		type="checkbox"
 		:disabled="disabled"
 		:class="$style.input"
-		@keydown.enter="toggle"
+		@click="toggle"
 	>
-	<XButton :checked="checked" :disabled="disabled" @toggle="toggle"/>
+	<XButton :class="$style.toggle" :checked="checked" :disabled="disabled" @toggle="toggle"/>
 	<span v-if="!noBody" :class="$style.body">
 		<!-- TODO: 無名slotの方は廃止 -->
 		<span :class="$style.label">
@@ -75,7 +75,13 @@ const toggle = () => {
 	height: 0;
 	opacity: 0;
 	margin: 0;
+
+	&:focus-visible ~ .toggle {
+		outline: 2px solid var(--focus);
+		outline-offset: 2px;
+	}
 }
+
 .body {
 	margin-left: 12px;
 	margin-top: 2px;
diff --git a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
index e1d88b5e5c..27483cc7c2 100644
--- a/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.PostNote.vue
@@ -105,7 +105,7 @@ const exampleCWNote = reactive<Misskey.entities.Note>({
 	font-weight: bold;
 	text-align: left;
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		width: calc(100% - 38px);
diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
index 7ae48dcd15..d8d4b5aab7 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
@@ -115,7 +115,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
 	font-weight: bold;
 	text-align: left;
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		width: calc(100% - 38px);
diff --git a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
index 57f26e86a7..6f2930ebc9 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
@@ -56,7 +56,7 @@ import { i18n } from '@/i18n.js';
 	font-weight: bold;
 	text-align: left;
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		width: calc(100% - 38px);
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index 5ecd41bfdf..75066bbc32 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkModal ref="modal" v-slot="{ type }" :zPriority="'high'" :src="src" @click="modal?.close()" @closed="emit('closed')">
+<MkModal ref="modal" v-slot="{ type }" :zPriority="'high'" :src="src" @click="modal?.close()" @closed="emit('closed')" @esc="modal?.close()">
 	<div class="_popup" :class="{ [$style.root]: true, [$style.asDrawer]: type === 'drawer' }">
 		<div :class="[$style.label, $style.item]">
 			{{ i18n.ts.visibility }}
diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue
index 89993e1b8e..b12dc8cb31 100644
--- a/packages/frontend/src/components/global/MkStickyContainer.vue
+++ b/packages/frontend/src/components/global/MkStickyContainer.vue
@@ -8,7 +8,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div ref="headerEl">
 		<slot name="header"></slot>
 	</div>
-	<div ref="bodyEl" :data-sticky-container-header-height="headerHeight">
+	<div
+		ref="bodyEl"
+		:data-sticky-container-header-height="headerHeight"
+		:data-sticky-container-footer-height="footerHeight"
+	>
 		<slot></slot>
 	</div>
 	<div ref="footerEl">
diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts
index 0a7d136f18..0e5c7ede24 100644
--- a/packages/frontend/src/directives/hotkey.ts
+++ b/packages/frontend/src/directives/hotkey.ts
@@ -13,9 +13,9 @@ export default {
 		el._keyHandler = makeHotkey(binding.value);
 
 		if (el._hotkey_global) {
-			document.addEventListener('keydown', el._keyHandler);
+			document.addEventListener('keydown', el._keyHandler, { passive: false });
 		} else {
-			el.addEventListener('keydown', el._keyHandler);
+			el.addEventListener('keydown', el._keyHandler, { passive: false });
 		}
 	},
 
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 560f692acf..17ee6e294a 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -5,7 +5,7 @@
 
 // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
 
-import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
+import { Component, markRaw, Ref, ref, defineAsyncComponent, nextTick } from 'vue';
 import { EventEmitter } from 'eventemitter3';
 import * as Misskey from 'misskey-js';
 import type { ComponentProps as CP } from 'vue-component-type-helpers';
@@ -24,6 +24,8 @@ import MkContextMenu from '@/components/MkContextMenu.vue';
 import { MenuItem } from '@/types/menu.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
+import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
+import { focusParent } from './scripts/focus.js';
 
 export const openingWindowsCount = ref(0);
 
@@ -622,31 +624,33 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: {
 export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | null, options?: {
 	align?: string;
 	width?: number;
-	viaKeyboard?: boolean;
 	onClosing?: () => void;
 }): Promise<void> {
-	return new Promise(resolve => {
+	let returnFocusTo = getHTMLElementOrNull(src) ?? getHTMLElementOrNull(document.activeElement);
+	return new Promise(resolve => nextTick(() => {
 		const { dispose } = popup(MkPopupMenu, {
 			items,
 			src,
 			width: options?.width,
 			align: options?.align,
-			viaKeyboard: options?.viaKeyboard,
+			returnFocusTo,
 		}, {
 			closed: () => {
 				resolve();
 				dispose();
+				returnFocusTo = null;
 			},
 			closing: () => {
-				if (options?.onClosing) options.onClosing();
+				options?.onClosing?.();
 			},
 		});
-	});
+	}));
 }
 
 export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
+	let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(document.activeElement);
 	ev.preventDefault();
-	return new Promise(resolve => {
+	return new Promise(resolve => nextTick(() => {
 		const { dispose } = popup(MkContextMenu, {
 			items,
 			ev,
@@ -654,14 +658,19 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
 			closed: () => {
 				resolve();
 				dispose();
+
+				// MkModalを通していないのでここでフォーカスを戻す処理を行う
+				if (returnFocusTo != null) {
+					focusParent(returnFocusTo, true, false);
+					returnFocusTo = null;
+				}
 			},
 		});
-	});
+	}));
 }
 
 export function post(props: Record<string, any> = {}): Promise<void> {
 	showMovedDialog();
-
 	return new Promise(resolve => {
 		// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない
 		// NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index 7a8786d415..a774412f83 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -234,6 +234,7 @@ onMounted(async () => {
 			background-color: var(--accentedBg);
 			color: var(--accent);
 			text-decoration: none;
+			outline: none;
 		}
 
 		&.danger {
diff --git a/packages/frontend/src/pages/games.vue b/packages/frontend/src/pages/games.vue
index afd6df1ad9..b52f4decaa 100644
--- a/packages/frontend/src/pages/games.vue
+++ b/packages/frontend/src/pages/games.vue
@@ -8,12 +8,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #header><MkPageHeader/></template>
 	<MkSpacer :contentMax="800">
 		<div class="_gaps">
-			<div class="_panel">
+			<div class="_panel" :class="$style.link">
 				<MkA to="/bubble-game">
 					<img src="/client-assets/drop-and-fusion/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
 				</MkA>
 			</div>
-			<div class="_panel">
+			<div class="_panel" :class="$style.link">
 				<MkA to="/reversi">
 					<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
 				</MkA>
@@ -32,3 +32,10 @@ definePageMetadata(() => ({
 	icon: 'ti ti-device-gamepad',
 }));
 </script>
+
+<style module>
+.link:focus-within {
+	outline: 2px solid var(--focus);
+	outline-offset: -2px;
+}
+</style>
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index e73d032000..e2f04eb764 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -286,6 +286,7 @@ definePageMetadata(() => ({
 		background-color: var(--accentedBg);
 		color: var(--accent);
 		text-decoration: none;
+		outline: none;
 	}
 }
 
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 60bf9b4d3d..a328933686 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -342,6 +342,7 @@ definePageMetadata(() => ({
 	&:hover, &:focus {
 		opacity: .7;
 	}
+
 	&:active {
 		cursor: pointer;
 	}
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index 0a4bd4b826..7d192bcbea 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -213,12 +213,18 @@ definePageMetadata(() => ({
 			}
 		}
 
+		.dn:focus-visible ~ .toggle {
+			outline: 2px solid var(--focus);
+			outline-offset: 2px;
+		}
+
 		.toggle {
 			cursor: pointer;
 			display: inline-block;
 			position: relative;
 			width: 90px;
 			height: 50px;
+			margin: 4px; // focus用のアウトライン
 			background-color: #83D8FF;
 			border-radius: 90px - 6;
 			transition: background-color 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
diff --git a/packages/frontend/src/scripts/focus-trap.ts b/packages/frontend/src/scripts/focus-trap.ts
new file mode 100644
index 0000000000..734c73652f
--- /dev/null
+++ b/packages/frontend/src/scripts/focus-trap.ts
@@ -0,0 +1,65 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
+
+const focusTrapElements = new Set<HTMLElement>();
+const ignoreElements = [
+	'script',
+	'style',
+];
+
+function containsFocusTrappedElements(el: HTMLElement): boolean {
+	return Array.from(focusTrapElements).some((focusTrapElement) => {
+		return el.contains(focusTrapElement);
+	});
+}
+
+function releaseFocusTrap(el: HTMLElement): void {
+	focusTrapElements.delete(el);
+	if (el.parentElement != null && el !== document.body) {
+		el.parentElement.childNodes.forEach((siblingNode) => {
+			const siblingEl = getHTMLElementOrNull(siblingNode);
+			if (!siblingEl) return;
+			if (siblingEl !== el && (focusTrapElements.has(siblingEl) || containsFocusTrappedElements(siblingEl) || focusTrapElements.size === 0)) {
+				siblingEl.inert = false;
+			} else if (
+				focusTrapElements.size > 0 &&
+				!containsFocusTrappedElements(siblingEl) &&
+				!focusTrapElements.has(siblingEl) &&
+				!ignoreElements.includes(siblingEl.tagName.toLowerCase())
+			) {
+				siblingEl.inert = true;
+			} else {
+				siblingEl.inert = false;
+			}
+		});
+		releaseFocusTrap(el.parentElement);
+	}
+}
+
+export function focusTrap(el: HTMLElement, parent: true): void;
+export function focusTrap(el: HTMLElement, parent?: false): { release: () => void; };
+export function focusTrap(el: HTMLElement, parent = false): { release: () => void; } | void {
+	if (el.parentElement != null && el !== document.body) {
+		el.parentElement.childNodes.forEach((siblingNode) => {
+			const siblingEl = getHTMLElementOrNull(siblingNode);
+			if (!siblingEl) return;
+			if (siblingEl !== el && !ignoreElements.includes(siblingEl.tagName.toLowerCase())) {
+				siblingEl.inert = true;
+			}
+		});
+		focusTrap(el.parentElement, true);
+	}
+
+	if (!parent) {
+		focusTrapElements.add(el);
+
+		return {
+			release: () => {
+				releaseFocusTrap(el);
+			},
+		};
+	}
+}
diff --git a/packages/frontend/src/scripts/focus.ts b/packages/frontend/src/scripts/focus.ts
index ea6ee61c88..eb2da5ad86 100644
--- a/packages/frontend/src/scripts/focus.ts
+++ b/packages/frontend/src/scripts/focus.ts
@@ -3,30 +3,78 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-export function focusPrev(el: Element | null, self = false, scroll = true) {
-	if (el == null) return;
-	if (!self) el = el.previousElementSibling;
-	if (el) {
-		if (el.hasAttribute('tabindex')) {
-			(el as HTMLElement).focus({
-				preventScroll: !scroll,
-			});
-		} else {
-			focusPrev(el.previousElementSibling, true);
-		}
-	}
-}
+import { getScrollPosition, getScrollContainer, getStickyBottom, getStickyTop } from '@/scripts/scroll.js';
+import { getElementOrNull, getNodeOrNull } from '@/scripts/get-dom-node-or-null.js';
 
-export function focusNext(el: Element | null, self = false, scroll = true) {
-	if (el == null) return;
-	if (!self) el = el.nextElementSibling;
-	if (el) {
-		if (el.hasAttribute('tabindex')) {
-			(el as HTMLElement).focus({
-				preventScroll: !scroll,
-			});
-		} else {
-			focusPrev(el.nextElementSibling, true);
-		}
+type MaybeHTMLElement = EventTarget | Node | Element | HTMLElement;
+
+export const isFocusable = (input: MaybeHTMLElement | null | undefined): input is HTMLElement => {
+	if (input == null || !(input instanceof HTMLElement)) return false;
+
+	if (input.tabIndex < 0) return false;
+	if ('disabled' in input && input.disabled === true) return false;
+	if ('readonly' in input && input.readonly === true) return false;
+
+	if (!input.ownerDocument.contains(input)) return false;
+
+	const style = window.getComputedStyle(input);
+	if (style.display === 'none') return false;
+	if (style.visibility === 'hidden') return false;
+	if (style.opacity === '0') return false;
+	if (style.pointerEvents === 'none') return false;
+
+	return true;
+};
+
+export const focusPrev = (input: MaybeHTMLElement | null | undefined, self = false, scroll = true) => {
+	const element = self ? input : getElementOrNull(input)?.previousElementSibling;
+	if (element == null) return;
+	if (isFocusable(element)) {
+		focusOrScroll(element, scroll);
+	} else {
+		focusPrev(element, false, scroll);
 	}
-}
+};
+
+export const focusNext = (input: MaybeHTMLElement | null | undefined, self = false, scroll = true) => {
+	const element = self ? input : getElementOrNull(input)?.nextElementSibling;
+	if (element == null) return;
+	if (isFocusable(element)) {
+		focusOrScroll(element, scroll);
+	} else {
+		focusNext(element, false, scroll);
+	}
+};
+
+export const focusParent = (input: MaybeHTMLElement | null | undefined, self = false, scroll = true) => {
+	const element = self ? input : getNodeOrNull(input)?.parentElement;
+	if (element == null) return;
+	if (isFocusable(element)) {
+		focusOrScroll(element, scroll);
+	} else {
+		focusParent(element, false, scroll);
+	}
+};
+
+const focusOrScroll = (element: HTMLElement, scroll: boolean) => {
+	if (scroll) {
+		const scrollContainer = getScrollContainer(element) ?? document.documentElement;
+		const scrollContainerTop = getScrollPosition(scrollContainer);
+		const stickyTop = getStickyTop(element, scrollContainer);
+		const stickyBottom = getStickyBottom(element, scrollContainer);
+		const top = element.getBoundingClientRect().top;
+		const bottom = element.getBoundingClientRect().bottom;
+
+		let scrollTo = scrollContainerTop;
+		if (top < stickyTop) {
+			scrollTo += top - stickyTop;
+		} else if (bottom > window.innerHeight - stickyBottom) {
+			scrollTo += bottom - window.innerHeight + stickyBottom;
+		}
+		scrollContainer.scrollTo({ top: scrollTo, behavior: 'instant' });
+	}
+
+	if (document.activeElement !== element) {
+		element.focus({ preventScroll: true });
+	}
+};
diff --git a/packages/frontend/src/scripts/get-dom-node-or-null.ts b/packages/frontend/src/scripts/get-dom-node-or-null.ts
new file mode 100644
index 0000000000..fbf54675fd
--- /dev/null
+++ b/packages/frontend/src/scripts/get-dom-node-or-null.ts
@@ -0,0 +1,19 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const getNodeOrNull = (input: unknown): Node | null => {
+	if (input instanceof Node) return input;
+	return null;
+};
+
+export const getElementOrNull = (input: unknown): Element | null => {
+	if (input instanceof Element) return input;
+	return null;
+};
+
+export const getHTMLElementOrNull = (input: unknown): HTMLElement | null => {
+	if (input instanceof HTMLElement) return input;
+	return null;
+};
diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts
index fd79baa604..ff3cbe98ac 100644
--- a/packages/frontend/src/scripts/hotkey.ts
+++ b/packages/frontend/src/scripts/hotkey.ts
@@ -2,6 +2,7 @@
  * SPDX-FileCopyrightText: syuilo and misskey-project
  * SPDX-License-Identifier: AGPL-3.0-only
  */
+import { getHTMLElementOrNull } from "@/scripts/get-dom-node-or-null.js";
 
 //#region types
 export type Keymap = Record<string, CallbackFunction | CallbackObject>;
@@ -30,8 +31,8 @@ type Action = {
 //#region consts
 const KEY_ALIASES = {
 	'esc': 'Escape',
-	'enter': ['Enter', 'NumpadEnter'],
-	'space': [' ', 'Spacebar'],
+	'enter': 'Enter',
+	'space': ' ',
 	'up': 'ArrowUp',
 	'down': 'ArrowDown',
 	'left': 'ArrowLeft',
@@ -44,6 +45,10 @@ const MODIFIER_KEYS = ['ctrl', 'alt', 'shift'];
 const IGNORE_ELEMENTS = ['input', 'textarea'];
 //#endregion
 
+//#region store
+let latestHotkey: Pattern & { callback: CallbackFunction } | null = null;
+//#endregion
+
 //#region impl
 export const makeHotkey = (keymap: Keymap) => {
 	const actions = parseKeymap(keymap);
@@ -51,13 +56,14 @@ export const makeHotkey = (keymap: Keymap) => {
 		if ('pswp' in window && window.pswp != null) return;
 		if (document.activeElement != null) {
 			if (IGNORE_ELEMENTS.includes(document.activeElement.tagName.toLowerCase())) return;
-			if ((document.activeElement as HTMLElement).isContentEditable) return;
+			if (getHTMLElementOrNull(document.activeElement)?.isContentEditable) return;
 		}
-		for (const { patterns, callback, options } of actions) {
-			if (matchPatterns(ev, patterns, options)) {
+		for (const action of actions) {
+			if (matchPatterns(ev, action)) {
 				ev.preventDefault();
 				ev.stopPropagation();
-				callback(ev);
+				action.callback(ev);
+				storePattern(ev, action.callback);
 			}
 		}
 	};
@@ -102,10 +108,21 @@ const parseOptions = (rawCallback: Keymap[keyof Keymap]) => {
 	return { ...defaultOptions } as const satisfies Action['options'];
 };
 
-const matchPatterns = (ev: KeyboardEvent, patterns: Action['patterns'], options: Action['options']) => {
+const matchPatterns = (ev: KeyboardEvent, action: Action) => {
+	const { patterns, options, callback } = action;
 	if (ev.repeat && !options.allowRepeat) return false;
 	const key = ev.key.toLowerCase();
 	return patterns.some(({ which, ctrl, shift, alt }) => {
+		if (
+			latestHotkey != null &&
+			latestHotkey.which.includes(key) &&
+			latestHotkey.ctrl === ctrl &&
+			latestHotkey.alt === alt &&
+			latestHotkey.shift === shift &&
+			latestHotkey.callback === callback
+		) {
+			return false;
+		}
 		if (!which.includes(key)) return false;
 		if (ctrl !== (ev.ctrlKey || ev.metaKey)) return false;
 		if (alt !== ev.altKey) return false;
@@ -114,6 +131,26 @@ const matchPatterns = (ev: KeyboardEvent, patterns: Action['patterns'], options:
 	});
 };
 
+let lastHotKeyStoreTimer: number | null = null;
+
+const storePattern = (ev: KeyboardEvent, callback: CallbackFunction) => {
+	if (lastHotKeyStoreTimer != null) {
+		clearTimeout(lastHotKeyStoreTimer);
+	}
+
+	latestHotkey = {
+		which: [ev.key.toLowerCase()],
+		ctrl: ev.ctrlKey || ev.metaKey,
+		alt: ev.altKey,
+		shift: ev.shiftKey,
+		callback,
+	};
+
+	lastHotKeyStoreTimer = window.setTimeout(() => {
+		latestHotkey = null;
+	}, 500);
+};
+
 const parseKeyCode = (input?: string | null) => {
 	if (input == null) return [];
 	const raw = getValueByKey(KEY_ALIASES, input);
diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts
index 8edb6fca05..f0274034b5 100644
--- a/packages/frontend/src/scripts/scroll.ts
+++ b/packages/frontend/src/scripts/scroll.ts
@@ -23,6 +23,14 @@ export function getStickyTop(el: HTMLElement, container: HTMLElement | null = nu
 	return getStickyTop(el.parentElement, container, newTop);
 }
 
+export function getStickyBottom(el: HTMLElement, container: HTMLElement | null = null, bottom = 0) {
+	if (!el.parentElement) return bottom;
+	const data = el.dataset.stickyContainerFooterHeight;
+	const newBottom = data ? Number(data) + bottom : bottom;
+	if (el === container) return newBottom;
+	return getStickyBottom(el.parentElement, container, newBottom);
+}
+
 export function getScrollPosition(el: HTMLElement | null): number {
 	const container = getScrollContainer(el);
 	return container == null ? window.scrollY : container.scrollTop;
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 250a2616a7..2feb79ef81 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -113,6 +113,10 @@ a {
 	-webkit-tap-highlight-color: transparent;
 	-webkit-touch-callout: none;
 
+	&:focus-visible {
+		outline-offset: 2px;
+	}
+
 	&:hover {
 		text-decoration: underline;
 	}
@@ -143,12 +147,21 @@ rt {
 	white-space: initial;
 }
 
+:focus-visible {
+	outline: var(--focus) solid 2px;
+	outline-offset: -2px;
+
+	&:hover {
+		text-decoration: none;
+	}
+}
+
 .ti {
 	width: 1.28em;
 	vertical-align: -12%;
 	line-height: 1em;
 
-	&:before {
+	&::before {
 		font-size: 128%;
 	}
 }
@@ -230,10 +243,6 @@ rt {
 	line-height: inherit;
 	max-width: 100%;
 
-	&:focus-visible {
-		outline: none;
-	}
-
 	&:disabled {
 		opacity: 0.5;
 		cursor: default;
@@ -270,13 +279,17 @@ rt {
 
 ._help {
 	color: var(--accent);
-	cursor: help
+	cursor: help;
 }
 
 ._textButton {
 	@extend ._button;
 	color: var(--accent);
 
+	&:focus-visible {
+		outline-offset: 2px;
+	}
+
 	&:not(:disabled):hover {
 		text-decoration: underline;
 	}
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 822b552837..d7df2d10f9 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -227,7 +227,7 @@ if ($i) {
 	right: 15px;
 	pointer-events: none;
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		width: 18px;
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index 699aa1e1c8..87e9e45e63 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -139,7 +139,7 @@ function more() {
 	font-weight: bold;
 	text-align: left;
 
-	&:before {
+	&::before {
 		content: "";
 		display: block;
 		width: calc(100% - 38px);
@@ -155,7 +155,7 @@ function more() {
 	}
 
 	&:hover, &.active {
-		&:before {
+		&::before {
 			background: var(--accentLighten);
 		}
 	}
@@ -226,7 +226,7 @@ function more() {
 	}
 
 	&:hover, &.active {
-		&:before {
+		&::before {
 			content: "";
 			display: block;
 			width: calc(100% - 24px);
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index b029533f28..8307da0d42 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -166,6 +166,15 @@ function more(ev: MouseEvent) {
 		display: block;
 		text-align: center;
 		width: 100%;
+
+		&:focus-visible {
+			outline: none;
+
+			> .instanceIcon {
+				outline: 2px solid var(--focus);
+				outline-offset: 2px;
+			}
+		}
 	}
 
 	.instanceIcon {
@@ -192,7 +201,7 @@ function more(ev: MouseEvent) {
 		font-weight: bold;
 		text-align: left;
 
-		&:before {
+		&::before {
 			content: "";
 			display: block;
 			width: calc(100% - 38px);
@@ -207,8 +216,17 @@ function more(ev: MouseEvent) {
 			background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
 		}
 
+		&:focus-visible {
+			outline: none;
+
+			&::before {
+				outline: 2px solid var(--fgOnAccent);
+				outline-offset: -4px;
+			}
+		}
+
 		&:hover, &.active {
-			&:before {
+			&::before {
 				background: var(--accentLighten);
 			}
 		}
@@ -234,6 +252,14 @@ function more(ev: MouseEvent) {
 		text-align: left;
 		box-sizing: border-box;
 		overflow: clip;
+
+		&:focus-visible {
+			outline: none;
+
+			> .avatar {
+				box-shadow: 0 0 0 4px var(--focus);
+			}
+		}
 	}
 
 	.avatar {
@@ -282,10 +308,19 @@ function more(ev: MouseEvent) {
 			color: var(--navActive);
 		}
 
-		&:hover, &.active {
+		&:focus-visible {
+			outline: none;
+
+			&::before {
+				outline: 2px solid var(--focus);
+				outline-offset: -2px;
+			}
+		}
+
+		&:hover, &.active, &:focus {
 			color: var(--accent);
 
-			&:before {
+			&::before {
 				content: "";
 				display: block;
 				width: calc(100% - 34px);
@@ -352,6 +387,15 @@ function more(ev: MouseEvent) {
 		display: block;
 		text-align: center;
 		width: 100%;
+
+		&:focus-visible {
+			outline: none;
+
+			> .instanceIcon {
+				outline: 2px solid var(--focus);
+				outline-offset: 2px;
+			}
+		}
 	}
 
 	.instanceIcon {
@@ -376,7 +420,7 @@ function more(ev: MouseEvent) {
 		height: 52px;
 		text-align: center;
 
-		&:before {
+		&::before {
 			content: "";
 			display: block;
 			position: absolute;
@@ -391,8 +435,17 @@ function more(ev: MouseEvent) {
 			background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
 		}
 
+		&:focus-visible {
+			outline: none;
+
+			&::before {
+				outline: 2px solid var(--fgOnAccent);
+				outline-offset: -4px;
+			}
+		}
+
 		&:hover, &.active {
-			&:before {
+			&::before {
 				background: var(--accentLighten);
 			}
 		}
@@ -413,6 +466,14 @@ function more(ev: MouseEvent) {
 		padding: 20px 0;
 		width: 100%;
 		overflow: clip;
+
+		&:focus-visible {
+			outline: none;
+
+			> .avatar {
+				box-shadow: 0 0 0 4px var(--focus);
+			}
+		}
 	}
 
 	.avatar {
@@ -442,11 +503,20 @@ function more(ev: MouseEvent) {
 		width: 100%;
 		text-align: center;
 
-		&:hover, &.active {
+		&:focus-visible {
+			outline: none;
+
+			&::before {
+				outline: 2px solid var(--focus);
+				outline-offset: -2px;
+			}
+		}
+
+		&:hover, &.active, &:focus {
 			text-decoration: none;
 			color: var(--accent);
 
-			&:before {
+			&::before {
 				content: "";
 				display: block;
 				height: 100%;
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 07845bacbb..e96402d13b 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -271,7 +271,7 @@ function onDrop(ev) {
 	border-radius: 10px;
 
 	&.draghover {
-		&:after {
+		&::after {
 			content: "";
 			display: block;
 			position: absolute;
@@ -285,7 +285,7 @@ function onDrop(ev) {
 	}
 
 	&.dragging {
-		&:after {
+		&::after {
 			content: "";
 			display: block;
 			position: absolute;
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index c688e8a0b1..6ece33eff3 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -121,7 +121,7 @@ defineExpose<WidgetComponentExpose>({
 .root {
 	padding: 16px 0;
 
-	&:after {
+	&::after {
 		content: "";
 		display: block;
 		clear: both;

From 76b1c74a375dc3de5588995803d445a2951aea32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 12 Jul 2024 16:39:09 +0900
Subject: [PATCH 089/206] fix(frontend): use proper import path

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

diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 17ee6e294a..5e332533ef 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -25,7 +25,7 @@ import { MenuItem } from '@/types/menu.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
-import { focusParent } from './scripts/focus.js';
+import { focusParent } from '@/scripts/focus.js';
 
 export const openingWindowsCount = ref(0);
 

From 6cd15275bbcd17498166cda3455370d5f009e0bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 12 Jul 2024 21:14:09 +0900
Subject: [PATCH 090/206] =?UTF-8?q?fix:=20=E3=82=B5=E3=82=B8=E3=82=A7?=
 =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=95=E3=82=8C=E3=82=8B=E3=83=A6=E3=83=BC?=
 =?UTF-8?q?=E3=82=B6=E3=81=AE=E3=83=AA=E3=82=B9=E3=83=88=E3=82=A2=E3=83=83?=
 =?UTF-8?q?=E3=83=97=E6=96=B9=E6=B3=95=E3=82=92=E8=A6=8B=E7=9B=B4=E3=81=97?=
 =?UTF-8?q?=20(#14180)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: サジェストされるユーザのリストアップ方法を見直し

* fix comment

* fix CHANGELOG.md

* ノートの無いユーザ(updatedAtが無いユーザ)は含めないらしい

* fix test
---
 CHANGELOG.md                                  |   5 +
 packages/backend/src/core/CoreModule.ts       |   6 +
 .../backend/src/core/UserSearchService.ts     | 205 ++++++++++++++
 .../users/search-by-username-and-host.ts      | 101 +------
 .../backend/test/unit/UserSearchService.ts    | 265 ++++++++++++++++++
 5 files changed, 492 insertions(+), 90 deletions(-)
 create mode 100644 packages/backend/src/core/UserSearchService.ts
 create mode 100644 packages/backend/test/unit/UserSearchService.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6f48684b9..060ddd0e81 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,11 @@
 - Fix: リノートにリアクションできないように
 - Fix: ユーザー名の前後に空白文字列がある場合は省略するように
 - Fix: プロフィール編集時に名前を空白文字列のみにできる問題を修正
+- Fix: ユーザ名のサジェスト時に表示される内容と順番を調整(以下の順番になります) #14149
+  1. フォロー中かつアクティブなユーザ
+  2. フォロー中かつ非アクティブなユーザ
+  3. フォローしていないアクティブなユーザ
+  4. フォローしていない非アクティブなユーザ
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index b5b34487ec..0208540afa 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -12,6 +12,7 @@ import {
 } from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
 import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
 import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+import { UserSearchService } from '@/core/UserSearchService.js';
 import { AccountMoveService } from './AccountMoveService.js';
 import { AccountUpdateService } from './AccountUpdateService.js';
 import { AiService } from './AiService.js';
@@ -202,6 +203,7 @@ const $UserFollowingService: Provider = { provide: 'UserFollowingService', useEx
 const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisting: UserKeypairService };
 const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService };
 const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService };
+const $UserSearchService: Provider = { provide: 'UserSearchService', useExisting: UserSearchService };
 const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService };
 const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService };
 const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService };
@@ -348,6 +350,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserKeypairService,
 		UserListService,
 		UserMutingService,
+		UserSearchService,
 		UserSuspendService,
 		UserAuthService,
 		VideoProcessingService,
@@ -490,6 +493,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserKeypairService,
 		$UserListService,
 		$UserMutingService,
+		$UserSearchService,
 		$UserSuspendService,
 		$UserAuthService,
 		$VideoProcessingService,
@@ -633,6 +637,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserKeypairService,
 		UserListService,
 		UserMutingService,
+		UserSearchService,
 		UserSuspendService,
 		UserAuthService,
 		VideoProcessingService,
@@ -774,6 +779,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserKeypairService,
 		$UserListService,
 		$UserMutingService,
+		$UserSearchService,
 		$UserSuspendService,
 		$UserAuthService,
 		$VideoProcessingService,
diff --git a/packages/backend/src/core/UserSearchService.ts b/packages/backend/src/core/UserSearchService.ts
new file mode 100644
index 0000000000..0d03cf6ee0
--- /dev/null
+++ b/packages/backend/src/core/UserSearchService.ts
@@ -0,0 +1,205 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Brackets, SelectQueryBuilder } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import { type FollowingsRepository, MiUser, type UsersRepository } from '@/models/_.js';
+import { bindThis } from '@/decorators.js';
+import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
+import type { Config } from '@/config.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { Packed } from '@/misc/json-schema.js';
+
+function defaultActiveThreshold() {
+	return new Date(Date.now() - 1000 * 60 * 60 * 24 * 30);
+}
+
+@Injectable()
+export class UserSearchService {
+	constructor(
+		@Inject(DI.config)
+		private config: Config,
+		@Inject(DI.usersRepository)
+		private usersRepository: UsersRepository,
+		@Inject(DI.followingsRepository)
+		private followingsRepository: FollowingsRepository,
+		private userEntityService: UserEntityService,
+	) {
+	}
+
+	/**
+	 * ユーザ名とホスト名によるユーザ検索を行う.
+	 *
+	 * - 検索結果には優先順位がつけられており、以下の順序で検索が行われる.
+	 *   1. フォローしているユーザのうち、一定期間以内(※)に更新されたユーザ
+	 *   2. フォローしているユーザのうち、一定期間以内に更新されていないユーザ
+	 *   3. フォローしていないユーザのうち、一定期間以内に更新されたユーザ
+	 *   4. フォローしていないユーザのうち、一定期間以内に更新されていないユーザ
+	 * - ログインしていない場合は、以下の順序で検索が行われる.
+	 *   1. 一定期間以内に更新されたユーザ
+	 *   2. 一定期間以内に更新されていないユーザ
+	 * - それぞれの検索結果はユーザ名の昇順でソートされる.
+	 * - 動作的には先に登場した検索結果の登場位置が優先される(条件的にユーザIDが重複することはないが).
+	 *   (1で既にヒットしていた場合、2, 3, 4でヒットしても無視される)
+	 * - ユーザ名とホスト名の検索条件はそれぞれ前方一致で検索される.
+	 * - ユーザ名の検索は大文字小文字を区別しない.
+	 * - ホスト名の検索は大文字小文字を区別しない.
+	 * - 検索結果は最大で {@link opts.limit} 件までとなる.
+	 *
+	 * ※一定期間とは {@link params.activeThreshold} で指定された日時から現在までの期間を指す.
+	 *
+	 * @param params 検索条件.
+	 * @param opts 関数の動作を制御するオプション.
+	 * @param me 検索を実行するユーザの情報. 未ログインの場合は指定しない.
+	 * @see {@link UserSearchService#buildSearchUserQueries}
+	 * @see {@link UserSearchService#buildSearchUserNoLoginQueries}
+	 */
+	@bindThis
+	public async search(
+		params: {
+			username?: string | null,
+			host?: string | null,
+			activeThreshold?: Date,
+		},
+		opts?: {
+			limit?: number,
+			detail?: boolean,
+		},
+		me?: MiUser | null,
+	): Promise<Packed<'User'>[]> {
+		const queries = me ? this.buildSearchUserQueries(me, params) : this.buildSearchUserNoLoginQueries(params);
+
+		let resultSet = new Set<MiUser['id']>();
+		const limit = opts?.limit ?? 10;
+		for (const query of queries) {
+			const ids = await query
+				.select('user.id')
+				.limit(limit - resultSet.size)
+				.orderBy('user.usernameLower', 'ASC')
+				.getRawMany<{ user_id: MiUser['id'] }>()
+				.then(res => res.map(x => x.user_id));
+
+			resultSet = new Set([...resultSet, ...ids]);
+			if (resultSet.size >= limit) {
+				break;
+			}
+		}
+
+		return this.userEntityService.packMany<'UserLite' | 'UserDetailed'>(
+			[...resultSet].slice(0, limit),
+			me,
+			{ schema: opts?.detail ? 'UserDetailed' : 'UserLite' },
+		);
+	}
+
+	/**
+	 * ログイン済みユーザによる検索実行時のクエリ一覧を構築する.
+	 * @param me
+	 * @param params
+	 * @private
+	 */
+	@bindThis
+	private buildSearchUserQueries(
+		me: MiUser,
+		params: {
+			username?: string | null,
+			host?: string | null,
+			activeThreshold?: Date,
+		},
+	) {
+		// デフォルト30日以内に更新されたユーザーをアクティブユーザーとする
+		const activeThreshold = params.activeThreshold ?? defaultActiveThreshold();
+
+		const followingUserQuery = this.followingsRepository.createQueryBuilder('following')
+			.select('following.followeeId')
+			.where('following.followerId = :followerId', { followerId: me.id });
+
+		const activeFollowingUsersQuery = this.generateUserQueryBuilder(params)
+			.andWhere(`user.id IN (${followingUserQuery.getQuery()})`)
+			.andWhere('user.updatedAt > :activeThreshold', { activeThreshold });
+		activeFollowingUsersQuery.setParameters(followingUserQuery.getParameters());
+
+		const inactiveFollowingUsersQuery = this.generateUserQueryBuilder(params)
+			.andWhere(`user.id IN (${followingUserQuery.getQuery()})`)
+			.andWhere(new Brackets(qb => {
+				qb
+					.where('user.updatedAt IS NULL')
+					.orWhere('user.updatedAt <= :activeThreshold', { activeThreshold });
+			}));
+		inactiveFollowingUsersQuery.setParameters(followingUserQuery.getParameters());
+
+		// 自分自身がヒットするとしたらここ
+		const activeUserQuery = this.generateUserQueryBuilder(params)
+			.andWhere(`user.id NOT IN (${followingUserQuery.getQuery()})`)
+			.andWhere('user.updatedAt > :activeThreshold', { activeThreshold });
+		activeUserQuery.setParameters(followingUserQuery.getParameters());
+
+		const inactiveUserQuery = this.generateUserQueryBuilder(params)
+			.andWhere(`user.id NOT IN (${followingUserQuery.getQuery()})`)
+			.andWhere('user.updatedAt <= :activeThreshold', { activeThreshold });
+		inactiveUserQuery.setParameters(followingUserQuery.getParameters());
+
+		return [activeFollowingUsersQuery, inactiveFollowingUsersQuery, activeUserQuery, inactiveUserQuery];
+	}
+
+	/**
+	 * ログインしていないユーザによる検索実行時のクエリ一覧を構築する.
+	 * @param params
+	 * @private
+	 */
+	@bindThis
+	private buildSearchUserNoLoginQueries(params: {
+		username?: string | null,
+		host?: string | null,
+		activeThreshold?: Date,
+	}) {
+		// デフォルト30日以内に更新されたユーザーをアクティブユーザーとする
+		const activeThreshold = params.activeThreshold ?? defaultActiveThreshold();
+
+		const activeUserQuery = this.generateUserQueryBuilder(params)
+			.andWhere(new Brackets(qb => {
+				qb
+					.where('user.updatedAt IS NULL')
+					.orWhere('user.updatedAt > :activeThreshold', { activeThreshold });
+			}));
+
+		const inactiveUserQuery = this.generateUserQueryBuilder(params)
+			.andWhere('user.updatedAt <= :activeThreshold', { activeThreshold });
+
+		return [activeUserQuery, inactiveUserQuery];
+	}
+
+	/**
+	 * ユーザ検索クエリで共通する抽出条件をあらかじめ設定したクエリビルダを生成する.
+	 * @param params
+	 * @private
+	 */
+	@bindThis
+	private generateUserQueryBuilder(params: {
+		username?: string | null,
+		host?: string | null,
+	}): SelectQueryBuilder<MiUser> {
+		const userQuery = this.usersRepository.createQueryBuilder('user');
+
+		if (params.username) {
+			userQuery.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(params.username.toLowerCase()) + '%' });
+		}
+
+		if (params.host) {
+			if (params.host === this.config.hostname || params.host === '.') {
+				userQuery.andWhere('user.host IS NULL');
+			} else {
+				userQuery.andWhere('user.host LIKE :host', {
+					host: sqlLikeEscape(params.host.toLowerCase()) + '%',
+				});
+			}
+		}
+
+		userQuery.andWhere('user.isSuspended = FALSE');
+
+		return userQuery;
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
index 7b3bdab327..8ff952dcb5 100644
--- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
+++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
@@ -3,15 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Brackets } from 'typeorm';
-import { Inject, Injectable } from '@nestjs/common';
-import type { UsersRepository, FollowingsRepository } from '@/models/_.js';
-import type { Config } from '@/config.js';
-import type { MiUser } from '@/models/User.js';
+import { Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import { DI } from '@/di-symbols.js';
-import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
+import { UserSearchService } from '@/core/UserSearchService.js';
 
 export const meta = {
 	tags: ['users'],
@@ -49,89 +43,16 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
-		@Inject(DI.config)
-		private config: Config,
-
-		@Inject(DI.usersRepository)
-		private usersRepository: UsersRepository,
-
-		@Inject(DI.followingsRepository)
-		private followingsRepository: FollowingsRepository,
-
-		private userEntityService: UserEntityService,
+		private userSearchService: UserSearchService,
 	) {
-		super(meta, paramDef, async (ps, me) => {
-			const setUsernameAndHostQuery = (query = this.usersRepository.createQueryBuilder('user')) => {
-				if (ps.username) {
-					query.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' });
-				}
-
-				if (ps.host) {
-					if (ps.host === this.config.hostname || ps.host === '.') {
-						query.andWhere('user.host IS NULL');
-					} else {
-						query.andWhere('user.host LIKE :host', {
-							host: sqlLikeEscape(ps.host.toLowerCase()) + '%',
-						});
-					}
-				}
-
-				return query;
-			};
-
-			const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
-
-			let users: MiUser[] = [];
-
-			if (me) {
-				const followingQuery = this.followingsRepository.createQueryBuilder('following')
-					.select('following.followeeId')
-					.where('following.followerId = :followerId', { followerId: me.id });
-
-				const query = setUsernameAndHostQuery()
-					.andWhere(`user.id IN (${ followingQuery.getQuery() })`)
-					.andWhere('user.id != :meId', { meId: me.id })
-					.andWhere('user.isSuspended = FALSE')
-					.andWhere(new Brackets(qb => {
-						qb
-							.where('user.updatedAt IS NULL')
-							.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
-					}));
-
-				query.setParameters(followingQuery.getParameters());
-
-				users = await query
-					.orderBy('user.usernameLower', 'ASC')
-					.limit(ps.limit)
-					.getMany();
-
-				if (users.length < ps.limit) {
-					const otherQuery = setUsernameAndHostQuery()
-						.andWhere(`user.id NOT IN (${ followingQuery.getQuery() })`)
-						.andWhere('user.isSuspended = FALSE')
-						.andWhere('user.updatedAt IS NOT NULL');
-
-					otherQuery.setParameters(followingQuery.getParameters());
-
-					const otherUsers = await otherQuery
-						.orderBy('user.updatedAt', 'DESC')
-						.limit(ps.limit - users.length)
-						.getMany();
-
-					users = users.concat(otherUsers);
-				}
-			} else {
-				const query = setUsernameAndHostQuery()
-					.andWhere('user.isSuspended = FALSE')
-					.andWhere('user.updatedAt IS NOT NULL');
-
-				users = await query
-					.orderBy('user.updatedAt', 'DESC')
-					.limit(ps.limit - users.length)
-					.getMany();
-			}
-
-			return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' });
+		super(meta, paramDef, (ps, me) => {
+			return this.userSearchService.search({
+				username: ps.username,
+				host: ps.host,
+			}, {
+				limit: ps.limit,
+				detail: ps.detail,
+			}, me);
 		});
 	}
 }
diff --git a/packages/backend/test/unit/UserSearchService.ts b/packages/backend/test/unit/UserSearchService.ts
new file mode 100644
index 0000000000..7ea325d420
--- /dev/null
+++ b/packages/backend/test/unit/UserSearchService.ts
@@ -0,0 +1,265 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Test, TestingModule } from '@nestjs/testing';
+import { describe, jest, test } from '@jest/globals';
+import { In } from 'typeorm';
+import { UserSearchService } from '@/core/UserSearchService.js';
+import { FollowingsRepository, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';
+import { IdService } from '@/core/IdService.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { DI } from '@/di-symbols.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+
+describe('UserSearchService', () => {
+	let app: TestingModule;
+	let service: UserSearchService;
+
+	let usersRepository: UsersRepository;
+	let followingsRepository: FollowingsRepository;
+	let idService: IdService;
+	let userProfilesRepository: UserProfilesRepository;
+
+	let root: MiUser;
+	let alice: MiUser;
+	let alyce: MiUser;
+	let alycia: MiUser;
+	let alysha: MiUser;
+	let alyson: MiUser;
+	let alyssa: MiUser;
+	let bob: MiUser;
+	let bobbi: MiUser;
+	let bobbie: MiUser;
+	let bobby: MiUser;
+
+	async function createUser(data: Partial<MiUser> = {}) {
+		const user = await usersRepository
+			.insert({
+				id: idService.gen(),
+				...data,
+			})
+			.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
+
+		await userProfilesRepository.insert({
+			userId: user.id,
+		});
+
+		return user;
+	}
+
+	async function createFollowings(follower: MiUser, followees: MiUser[]) {
+		for (const followee of followees) {
+			await followingsRepository.insert({
+				id: idService.gen(),
+				followerId: follower.id,
+				followeeId: followee.id,
+			});
+		}
+	}
+
+	async function setActive(users: MiUser[]) {
+		for (const user of users) {
+			await usersRepository.update(user.id, {
+				updatedAt: new Date(),
+			});
+		}
+	}
+
+	async function setInactive(users: MiUser[]) {
+		for (const user of users) {
+			await usersRepository.update(user.id, {
+				updatedAt: new Date(0),
+			});
+		}
+	}
+
+	async function setSuspended(users: MiUser[]) {
+		for (const user of users) {
+			await usersRepository.update(user.id, {
+				isSuspended: true,
+			});
+		}
+	}
+
+	beforeAll(async () => {
+		app = await Test
+			.createTestingModule({
+				imports: [
+					GlobalModule,
+				],
+				providers: [
+					UserSearchService,
+					{
+						provide: UserEntityService, useFactory: jest.fn(() => ({
+							// とりあえずIDが返れば確認が出来るので
+							packMany: (value: any) => value,
+						})),
+					},
+					IdService,
+				],
+			})
+			.compile();
+
+		await app.init();
+
+		usersRepository = app.get(DI.usersRepository);
+		userProfilesRepository = app.get(DI.userProfilesRepository);
+		followingsRepository = app.get(DI.followingsRepository);
+
+		service = app.get(UserSearchService);
+		idService = app.get(IdService);
+	});
+
+	beforeEach(async () => {
+		root = await createUser({ username: 'root', usernameLower: 'root', isRoot: true });
+		alice = await createUser({ username: 'Alice', usernameLower: 'alice' });
+		alyce = await createUser({ username: 'Alyce', usernameLower: 'alyce' });
+		alycia = await createUser({ username: 'Alycia', usernameLower: 'alycia' });
+		alysha = await createUser({ username: 'Alysha', usernameLower: 'alysha' });
+		alyson = await createUser({ username: 'Alyson', usernameLower: 'alyson', host: 'example.com' });
+		alyssa = await createUser({ username: 'Alyssa', usernameLower: 'alyssa', host: 'example.com' });
+		bob = await createUser({ username: 'Bob', usernameLower: 'bob' });
+		bobbi = await createUser({ username: 'Bobbi', usernameLower: 'bobbi' });
+		bobbie = await createUser({ username: 'Bobbie', usernameLower: 'bobbie', host: 'example.com' });
+		bobby = await createUser({ username: 'Bobby', usernameLower: 'bobby', host: 'example.com' });
+	});
+
+	afterEach(async () => {
+		await usersRepository.delete({});
+	});
+
+	afterAll(async () => {
+		await app.close();
+	});
+
+	describe('search', () => {
+		test('フォロー中のアクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
+			await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+			await setActive([alice, alyce, alyssa, bob, bobbi, bobbie, bobby]);
+			await setInactive([alycia, alysha, alyson]);
+
+			const result = await service.search(
+				{ username: 'al' },
+				{ limit: 100 },
+				root,
+			);
+
+			// alycia, alysha, alysonは非アクティブなので後ろに行く
+			expect(result).toEqual([alice, alyce, alyssa, alycia, alysha, alyson].map(x => x.id));
+		});
+
+		test('フォロー中の非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
+			await createFollowings(root, [alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+			await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+
+			const result = await service.search(
+				{ username: 'al' },
+				{ limit: 100 },
+				root,
+			);
+
+			// alice, alyceはフォローしていないので後ろに行く
+			expect(result).toEqual([alycia, alysha, alyson, alyssa, alice, alyce].map(x => x.id));
+		});
+
+		test('フォローしていないアクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
+			await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+			await setInactive([alice, alyce, alycia]);
+
+			const result = await service.search(
+				{ username: 'al' },
+				{ limit: 100 },
+				root,
+			);
+
+			// alice, alyce, alyciaは非アクティブなので後ろに行く
+			expect(result).toEqual([alysha, alyson, alyssa, alice, alyce, alycia].map(x => x.id));
+		});
+
+		test('フォローしていない非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
+			await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+
+			const result = await service.search(
+				{ username: 'al' },
+				{ limit: 100 },
+				root,
+			);
+
+			expect(result).toEqual([alice, alyce, alycia, alysha, alyson, alyssa].map(x => x.id));
+		});
+
+		test('フォロー(アクティブ)、フォロー(非アクティブ)、非フォロー(アクティブ)、非フォロー(非アクティブ)混在時の優先順位度確認', async () => {
+			await createFollowings(root, [alyson, alyssa, bob, bobbi, bobbie]);
+			await setActive([root, alyssa, bob, bobbi, alyce, alycia]);
+			await setInactive([alyson, alice, alysha, bobbie, bobby]);
+
+			const result = await service.search(
+				{ },
+				{ limit: 100 },
+				root,
+			);
+
+			// 見る用
+			// const users = await usersRepository.findBy({ id: In(result) }).then(it => new Map(it.map(x => [x.id, x])));
+			// console.log(result.map(x => users.get(x as any)).map(it => it?.username));
+
+			// フォローしててアクティブなので先頭: alyssa, bob, bobbi
+			// フォローしてて非アクティブなので次: alyson, bobbie
+			// フォローしてないけどアクティブなので次: alyce, alycia, root(アルファベット順的にここになる)
+			// フォローしてないし非アクティブなので最後: alice, alysha, bobby
+			expect(result).toEqual([alyssa, bob, bobbi, alyson, bobbie, alyce, alycia, root, alice, alysha, bobby].map(x => x.id));
+		});
+
+		test('[非ログイン] アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
+			await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+			await setInactive([alice, alyce, alycia]);
+
+			const result = await service.search(
+				{ username: 'al' },
+				{ limit: 100 },
+			);
+
+			// alice, alyce, alyciaは非アクティブなので後ろに行く
+			expect(result).toEqual([alysha, alyson, alyssa, alice, alyce, alycia].map(x => x.id));
+		});
+
+		test('[非ログイン] 非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => {
+			await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+
+			const result = await service.search(
+				{ username: 'al' },
+				{ limit: 100 },
+			);
+
+			expect(result).toEqual([alice, alyce, alycia, alysha, alyson, alyssa].map(x => x.id));
+		});
+
+		test('フォロー中のアクティブユーザのうち、"al"から始まり"example.com"にいる人が全員ヒットする', async () => {
+			await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+			await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+
+			const result = await service.search(
+				{ username: 'al', host: 'exam' },
+				{ limit: 100 },
+				root,
+			);
+
+			expect(result).toEqual([alyson, alyssa].map(x => x.id));
+		});
+
+		test('サスペンド済みユーザは出ない', async () => {
+			await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]);
+			await setSuspended([alice, alyce, alycia]);
+
+			const result = await service.search(
+				{ username: 'al' },
+				{ limit: 100 },
+				root,
+			);
+
+			expect(result).toEqual([alysha, alyson, alyssa].map(x => x.id));
+		});
+	});
+});

From 91de35ecdf94bb9ada06eb0c34a597bc50064681 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 13 Jul 2024 10:30:28 +0900
Subject: [PATCH 091/206] =?UTF-8?q?fix(backend):=20=E3=83=87=E3=83=95?=
 =?UTF-8?q?=E3=82=A9=E3=83=AB=E3=83=88=E3=83=86=E3=83=BC=E3=83=9E=E3=81=AB?=
 =?UTF-8?q?=E7=84=A1=E5=8A=B9=E3=81=AA=E3=83=86=E3=83=BC=E3=83=9E=E3=82=B3?=
 =?UTF-8?q?=E3=83=BC=E3=83=89=E3=82=92=E5=85=A5=E5=8A=9B=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=81=A8UI=E3=81=8C=E4=BD=BF=E7=94=A8=E3=81=A7=E3=81=8D?=
 =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #13955
---
 CHANGELOG.md                                  |  1 +
 .../src/core/entities/MetaEntityService.ts    | 21 ++++++++++++++++---
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 060ddd0e81..5533e3b518 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
+- Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
 
 ### Client
 - Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index 5dfec589e1..09641ce485 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -50,6 +50,22 @@ export class MetaEntityService {
 			}))
 			.getMany();
 
+		// クライアントの手間を減らすためあらかじめJSONに変換しておく
+		let defaultLightTheme = null;
+		let defaultDarkTheme = null;
+		if (instance.defaultLightTheme) {
+			try {
+				defaultLightTheme = JSON.stringify(JSON5.parse(instance.defaultLightTheme));
+			} catch (e) {
+			}
+		}
+		if (instance.defaultDarkTheme) {
+			try {
+				defaultDarkTheme = JSON.stringify(JSON5.parse(instance.defaultDarkTheme));
+			} catch (e) {
+			}
+		}
+
 		const packed: Packed<'MetaLite'> = {
 			maintainerName: instance.maintainerName,
 			maintainerEmail: instance.maintainerEmail,
@@ -90,9 +106,8 @@ export class MetaEntityService {
 			backgroundImageUrl: instance.backgroundImageUrl,
 			logoImageUrl: instance.logoImageUrl,
 			maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
-			// クライアントの手間を減らすためあらかじめJSONに変換しておく
-			defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null,
-			defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null,
+			defaultLightTheme,
+			defaultDarkTheme,
 			ads: ads.map(ad => ({
 				id: ad.id,
 				url: ad.url,

From 1b175ea759ffdb0e70b66f1958ef114ecd00a50f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 13 Jul 2024 13:02:27 +0900
Subject: [PATCH 092/206] =?UTF-8?q?fix(frontend):=20=E3=81=99=E3=81=A7?=
 =?UTF-8?q?=E3=81=ABfocus=20trap=E5=AF=BE=E8=B1=A1=E3=81=AE=E8=A6=81?=
 =?UTF-8?q?=E7=B4=A0=E3=81=ABinert=E3=81=8C=E3=81=8B=E3=81=8B=E3=81=A3?=
 =?UTF-8?q?=E3=81=A6=E3=81=84=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AF=E8=A7=A3?=
 =?UTF-8?q?=E9=99=A4=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1418?=
 =?UTF-8?q?9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): すでにfocus trap対象の要素にinertがかかっている場合は解除するように

* 他のfocus-trapped要素とのインタラクションがある場合の動作を変更

* typo
---
 .../src/components/MkEmojiPickerDialog.vue    |  1 +
 packages/frontend/src/components/MkModal.vue  |  4 +++-
 packages/frontend/src/scripts/focus-trap.ts   | 23 +++++++++++++++----
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index a413b146ba..7e1ffbfa9e 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	v-slot="{ type, maxHeight }"
 	:zPriority="'middle'"
 	:preferType="defaultStore.state.emojiPickerUseDrawerForMobile === false ? 'popup' : 'auto'"
+	:hasInteractionWithOtherFocusTrappedEls="true"
 	:transparentBg="true"
 	:manualShowing="manualShowing"
 	:src="src"
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index a5fbf8d365..f8032f9b43 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -71,6 +71,7 @@ const props = withDefaults(defineProps<{
 	zPriority?: 'low' | 'middle' | 'high';
 	noOverlap?: boolean;
 	transparentBg?: boolean;
+	hasInteractionWithOtherFocusTrappedEls?: boolean;
 	returnFocusTo?: HTMLElement | null;
 }>(), {
 	manualShowing: null,
@@ -80,6 +81,7 @@ const props = withDefaults(defineProps<{
 	zPriority: 'low',
 	noOverlap: true,
 	transparentBg: false,
+	hasInteractionWithOtherFocusTrappedEls: false,
 	returnFocusTo: null,
 });
 
@@ -326,7 +328,7 @@ onMounted(() => {
 	watch([showing, () => props.manualShowing], ([showing, manualShowing]) => {
 		if (manualShowing === true || (manualShowing == null && showing === true)) {
 			if (modalRootEl.value != null) {
-				const { release } = focusTrap(modalRootEl.value);
+				const { release } = focusTrap(modalRootEl.value, props.hasInteractionWithOtherFocusTrappedEls);
 
 				releaseFocusTrap = release;
 				modalRootEl.value.focus();
diff --git a/packages/frontend/src/scripts/focus-trap.ts b/packages/frontend/src/scripts/focus-trap.ts
index 734c73652f..a5df36f520 100644
--- a/packages/frontend/src/scripts/focus-trap.ts
+++ b/packages/frontend/src/scripts/focus-trap.ts
@@ -18,6 +18,9 @@ function containsFocusTrappedElements(el: HTMLElement): boolean {
 
 function releaseFocusTrap(el: HTMLElement): void {
 	focusTrapElements.delete(el);
+	if (el.inert === true) {
+		el.inert = false;
+	}
 	if (el.parentElement != null && el !== document.body) {
 		el.parentElement.childNodes.forEach((siblingNode) => {
 			const siblingEl = getHTMLElementOrNull(siblingNode);
@@ -39,18 +42,28 @@ function releaseFocusTrap(el: HTMLElement): void {
 	}
 }
 
-export function focusTrap(el: HTMLElement, parent: true): void;
-export function focusTrap(el: HTMLElement, parent?: false): { release: () => void; };
-export function focusTrap(el: HTMLElement, parent = false): { release: () => void; } | void {
+export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEls: boolean, parent: true): void;
+export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEls?: boolean, parent?: false): { release: () => void; };
+export function focusTrap(el: HTMLElement, hasInteractionWithOtherFocusTrappedEls = false, parent = false): { release: () => void; } | void {
+	if (el.inert === true) {
+		el.inert = false;
+	}
 	if (el.parentElement != null && el !== document.body) {
 		el.parentElement.childNodes.forEach((siblingNode) => {
 			const siblingEl = getHTMLElementOrNull(siblingNode);
 			if (!siblingEl) return;
-			if (siblingEl !== el && !ignoreElements.includes(siblingEl.tagName.toLowerCase())) {
+			if (
+				siblingEl !== el &&
+				(
+					hasInteractionWithOtherFocusTrappedEls === false ||
+					(!focusTrapElements.has(siblingEl) && !containsFocusTrappedElements(siblingEl))
+				) &&
+				!ignoreElements.includes(siblingEl.tagName.toLowerCase())
+			) {
 				siblingEl.inert = true;
 			}
 		});
-		focusTrap(el.parentElement, true);
+		focusTrap(el.parentElement, hasInteractionWithOtherFocusTrappedEls, true);
 	}
 
 	if (!parent) {

From bcc92d546fde7a75a48074dc96096ae2083b5025 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 13 Jul 2024 16:15:25 +0900
Subject: [PATCH 093/206] =?UTF-8?q?fix(frontend):=20=E3=83=9B=E3=83=83?=
 =?UTF-8?q?=E3=83=88=E3=82=AD=E3=83=BC=E3=81=AE=E3=83=AC=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E3=83=AA=E3=83=9F=E3=83=83=E3=83=88=E3=81=8CallowRepeat?=
 =?UTF-8?q?=E3=82=92=E8=80=83=E6=85=AE=E3=81=97=E3=81=AA=E3=81=84=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#14192)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts
index ff3cbe98ac..04fb235694 100644
--- a/packages/frontend/src/scripts/hotkey.ts
+++ b/packages/frontend/src/scripts/hotkey.ts
@@ -114,6 +114,7 @@ const matchPatterns = (ev: KeyboardEvent, action: Action) => {
 	const key = ev.key.toLowerCase();
 	return patterns.some(({ which, ctrl, shift, alt }) => {
 		if (
+			options.allowRepeat === false &&
 			latestHotkey != null &&
 			latestHotkey.which.includes(key) &&
 			latestHotkey.ctrl === ctrl &&

From 9fcae7d9b287eda113363780094257614e06242d Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sat, 13 Jul 2024 16:59:08 +0900
Subject: [PATCH 094/206] refactor(sw): enable noImplicitAny (#14191)

---
 packages/sw/tsconfig.json | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/sw/tsconfig.json b/packages/sw/tsconfig.json
index f3f3543013..50d4aae19d 100644
--- a/packages/sw/tsconfig.json
+++ b/packages/sw/tsconfig.json
@@ -2,7 +2,6 @@
 	"compilerOptions": {
 		"allowJs": true,
 		"noEmitOnError": false,
-		"noImplicitAny": false,
 		"noImplicitReturns": true,
 		"noUnusedParameters": false,
 		"noUnusedLocals": true,

From c83c831c53ab2367e2323737ee0f2ae89ab2de6d Mon Sep 17 00:00:00 2001
From: Gianni Ceccarelli <dakkar@thenautilus.net>
Date: Sat, 13 Jul 2024 12:26:48 +0100
Subject: [PATCH 095/206] parse `notRespondingSince` from redis instance cache
 (#14079)

if we don't do this, we'll get a string, and `DeliverProcessorService`
will error out `i.notRespondingSince.getTime is not a function`
---
 packages/backend/src/core/FederatedInstanceService.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts
index 6799f2c5bb..7aeeb78178 100644
--- a/packages/backend/src/core/FederatedInstanceService.ts
+++ b/packages/backend/src/core/FederatedInstanceService.ts
@@ -40,6 +40,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
 					firstRetrievedAt: new Date(parsed.firstRetrievedAt),
 					latestRequestReceivedAt: parsed.latestRequestReceivedAt ? new Date(parsed.latestRequestReceivedAt) : null,
 					infoUpdatedAt: parsed.infoUpdatedAt ? new Date(parsed.infoUpdatedAt) : null,
+					notRespondingSince: parsed.notRespondingSince ? new Date(parsed.notRespondingSince) : null,
 				};
 			},
 		});

From b5fd6183d20e8a93f05aaf85a9b1e586e42464c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Jul 2024 08:00:27 +0900
Subject: [PATCH 096/206] =?UTF-8?q?deps(frontend):=20AiScript=20VSCode?=
 =?UTF-8?q?=E3=81=AE=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92?=
 =?UTF-8?q?=E4=B8=8A=E3=81=92=E3=82=8B=20(#14199)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 743722c231..a0c1c69883 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -29,7 +29,7 @@
 		"@twemoji/parser": "15.1.1",
 		"@vitejs/plugin-vue": "5.0.5",
 		"@vue/compiler-sfc": "3.4.31",
-		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.9",
+		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.11",
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
 		"buraha": "0.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 10968f3e82..6e3ddfc5d4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -725,8 +725,8 @@ importers:
         specifier: 3.4.31
         version: 3.4.31
       aiscript-vscode:
-        specifier: github:aiscript-dev/aiscript-vscode#v0.1.9
-        version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02
+        specifier: github:aiscript-dev/aiscript-vscode#v0.1.11
+        version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9
       astring:
         specifier: 1.8.6
         version: 1.8.6
@@ -919,7 +919,7 @@ importers:
         version: 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
       '@testing-library/vue':
         specifier: 8.1.0
-        version: 8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
+        version: 8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -5438,9 +5438,6 @@ packages:
   '@vue/compiler-sfc@3.4.31':
     resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==}
 
-  '@vue/compiler-ssr@3.4.29':
-    resolution: {integrity: sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==}
-
   '@vue/compiler-ssr@3.4.31':
     resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==}
 
@@ -5472,11 +5469,6 @@ packages:
   '@vue/runtime-dom@3.4.31':
     resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==}
 
-  '@vue/server-renderer@3.4.29':
-    resolution: {integrity: sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==}
-    peerDependencies:
-      vue: 3.4.29
-
   '@vue/server-renderer@3.4.31':
     resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==}
     peerDependencies:
@@ -5593,9 +5585,9 @@ packages:
     resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==}
     engines: {node: '>=18'}
 
-  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02:
-    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02}
-    version: 0.1.9
+  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9:
+    resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9}
+    version: 0.1.11
     engines: {vscode: ^1.83.0}
 
   ajv-draft-04@1.0.0:
@@ -11716,6 +11708,9 @@ packages:
   vue-component-type-helpers@2.0.24:
     resolution: {integrity: sha512-Jr5N8QVYEcbQuMN1LRgvg61758G8HTnzUlQsAFOxx6Y6X8kmhJ7C+jOvWsQruYxi3uHhhS6BghyRlyiwO99DBg==}
 
+  vue-component-type-helpers@2.0.26:
+    resolution: {integrity: sha512-sO9qQ8oC520SW6kqlls0iqDak53gsTVSrYylajgjmkt1c0vcgjsGSy1KzlDrbEx8pm02IEYhlUkU5hCYf8rwtg==}
+
   vue-demi@0.14.7:
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
     engines: {node: '>=12'}
@@ -16525,7 +16520,7 @@ snapshots:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.4.31(typescript@5.5.3)
-      vue-component-type-helpers: 2.0.24
+      vue-component-type-helpers: 2.0.26
     transitivePeerDependencies:
       - encoding
       - prettier
@@ -16789,11 +16784,11 @@ snapshots:
     dependencies:
       '@testing-library/dom': 10.1.0
 
-  '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
+  '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
       '@babel/runtime': 7.23.4
       '@testing-library/dom': 9.3.4
-      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
+      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
       vue: 3.4.31(typescript@5.5.3)
     optionalDependencies:
       '@vue/compiler-sfc': 3.4.31
@@ -17599,12 +17594,6 @@ snapshots:
       postcss: 8.4.38
       source-map-js: 1.2.0
 
-  '@vue/compiler-ssr@3.4.29':
-    dependencies:
-      '@vue/compiler-dom': 3.4.29
-      '@vue/shared': 3.4.29
-    optional: true
-
   '@vue/compiler-ssr@3.4.31':
     dependencies:
       '@vue/compiler-dom': 3.4.31
@@ -17653,13 +17642,6 @@ snapshots:
       '@vue/shared': 3.4.31
       csstype: 3.1.3
 
-  '@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3))':
-    dependencies:
-      '@vue/compiler-ssr': 3.4.29
-      '@vue/shared': 3.4.29
-      vue: 3.4.31(typescript@5.5.3)
-    optional: true
-
   '@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3))':
     dependencies:
       '@vue/compiler-ssr': 3.4.31
@@ -17670,13 +17652,13 @@ snapshots:
 
   '@vue/shared@3.4.31': {}
 
-  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
+  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
       js-beautify: 1.14.9
       vue: 3.4.31(typescript@5.5.3)
       vue-component-type-helpers: 1.8.4
     optionalDependencies:
-      '@vue/server-renderer': 3.4.29(vue@3.4.31(typescript@5.5.3))
+      '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3))
 
   '@webgpu/types@0.1.30': {}
 
@@ -17767,7 +17749,7 @@ snapshots:
       clean-stack: 5.2.0
       indent-string: 5.0.0
 
-  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02:
+  aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9:
     dependencies:
       '@aiscript-dev/aiscript-languageserver': https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz
       vscode-languageclient: 9.0.1
@@ -24875,6 +24857,8 @@ snapshots:
 
   vue-component-type-helpers@2.0.24: {}
 
+  vue-component-type-helpers@2.0.26: {}
+
   vue-demi@0.14.7(vue@3.4.31(typescript@5.5.3)):
     dependencies:
       vue: 3.4.31(typescript@5.5.3)

From 58c596cacf8d2bd2572075051e4361500e791bdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Jul 2024 09:26:25 +0900
Subject: [PATCH 097/206] =?UTF-8?q?fix(backend):=20=E4=B8=80=E8=88=AC?=
 =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=8B=E3=82=89=E8=A6=8B?=
 =?UTF-8?q?=E3=81=9F=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E3=83=90?=
 =?UTF-8?q?=E3=83=83=E3=82=B8=E3=81=AE=E4=B8=80=E8=A6=A7=E3=81=AB=E5=85=AC?=
 =?UTF-8?q?=E9=96=8B=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=82=82=E3=81=AE=E3=81=8C=E5=90=AB=E3=81=BE=E3=82=8C=E3=82=8B?=
 =?UTF-8?q?=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#14195)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(backend): 公開バッジのみをpackするように (MisskeyIO#652)

(cherry picked from commit b8a90659f35fef49d1d00fb2f9b152226c97643c)

* Update Changelog

* fix

* Update UserEntityService.ts

---------

Co-authored-by: CyberRex <26585194+CyberRex0@users.noreply.github.com>
---
 CHANGELOG.md                                       |  2 ++
 .../backend/src/core/entities/UserEntityService.ts | 14 +++++++++-----
 packages/backend/test/e2e/users.ts                 | 13 +++++++++++--
 3 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5533e3b518..bd2a9e32e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -46,6 +46,8 @@
   2. フォロー中かつ非アクティブなユーザ
   3. フォローしていないアクティブなユーザ
   4. フォローしていない非アクティブなユーザ
+- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正  
+  (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index da96878713..7fd093c191 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -501,11 +501,15 @@ export class UserEntityService implements OnModuleInit {
 			emojis: this.customEmojiService.populateEmojis(user.emojis, user.host),
 			onlineStatus: this.getOnlineStatus(user),
 			// パフォーマンス上の理由でローカルユーザーのみ
-			badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then(rs => rs.sort((a, b) => b.displayOrder - a.displayOrder).map(r => ({
-				name: r.name,
-				iconUrl: r.iconUrl,
-				displayOrder: r.displayOrder,
-			}))) : undefined,
+			badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then((rs) => rs
+				.filter((r) => r.isPublic || iAmModerator)
+				.sort((a, b) => b.displayOrder - a.displayOrder)
+				.map((r) => ({
+					name: r.name,
+					iconUrl: r.iconUrl,
+					displayOrder: r.displayOrder,
+				}))
+			) : undefined,
 
 			...(isDetailed ? {
 				url: profile!.url,
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 3458e06384..61fd759932 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -231,7 +231,7 @@ describe('ユーザー', () => {
 		rolePublic = await role(root, { isPublic: true, name: 'Public Role' });
 		await api('admin/roles/assign', { userId: userRolePublic.id, roleId: rolePublic.id }, root);
 		userRoleBadge = await signup({ username: 'userRoleBadge' });
-		roleBadge = await role(root, { asBadge: true, name: 'Badge Role' });
+		roleBadge = await role(root, { asBadge: true, name: 'Badge Role', isPublic: true });
 		await api('admin/roles/assign', { userId: userRoleBadge.id, roleId: roleBadge.id }, root);
 		userSilenced = await signup({ username: 'userSilenced' });
 		await post(userSilenced, { text: 'test' });
@@ -655,7 +655,16 @@ describe('ユーザー', () => {
 			iconUrl: roleBadge.iconUrl,
 			displayOrder: roleBadge.displayOrder,
 		}]);
-		assert.deepStrictEqual(response.roles, []); // バッヂだからといってrolesが取れるとは限らない
+		assert.deepStrictEqual(response.roles, [{
+			id: roleBadge.id,
+			name: roleBadge.name,
+			color: roleBadge.color,
+			iconUrl: roleBadge.iconUrl,
+			description: roleBadge.description,
+			isModerator: roleBadge.isModerator,
+			isAdministrator: roleBadge.isAdministrator,
+			displayOrder: roleBadge.displayOrder,
+		}]);
 	});
 	test('をID指定のリスト形式で取得することができる(空)', async () => {
 		const parameters = { userIds: [] };

From 7afa593d114335368d9031b6e1d5b1edbc4ea9c9 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Sun, 14 Jul 2024 09:31:05 +0900
Subject: [PATCH 098/206] =?UTF-8?q?Feat:=20=E3=83=A6=E3=83=BC=E3=82=B6?=
 =?UTF-8?q?=E3=83=BC=E3=81=AE=E3=82=A2=E3=82=A4=E3=82=B3=E3=83=B3/?=
 =?UTF-8?q?=E3=83=90=E3=83=8A=E3=83=BC=E3=81=AE=E5=A4=89=E6=9B=B4=E5=8F=AF?=
 =?UTF-8?q?=E5=90=A6=E3=82=92=E3=83=AD=E3=83=BC=E3=83=AB=E3=81=A7=E8=A8=AD?=
 =?UTF-8?q?=E5=AE=9A=E5=8F=AF=E8=83=BD=E3=81=AB=20(#14078)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: implement role policy "canUpdateBioMedia"

* docs(changelog): update changelog

* docs(changelog): update changelog

* chore: regenerate misskey-js type definitions

* chore: Apply suggestion from code review

Co-authored-by: anatawa12 <anatawa12@icloud.com>

* chore: fix unnecessarily strict inequality check

* chore: policies should be gotten only once

---------

Co-authored-by: anatawa12 <anatawa12@icloud.com>
---
 CHANGELOG.md                                  |  2 ++
 locales/index.d.ts                            |  4 ++++
 locales/ja-JP.yml                             |  1 +
 packages/backend/src/core/RoleService.ts      |  3 +++
 .../activitypub/models/ApPersonService.ts     |  8 +++++++
 .../backend/src/models/json-schema/role.ts    |  4 ++++
 .../src/server/api/endpoints/i/update.ts      | 23 ++++++++++++++-----
 packages/frontend/src/const.ts                |  1 +
 .../frontend/src/pages/admin/roles.editor.vue | 20 ++++++++++++++++
 packages/frontend/src/pages/admin/roles.vue   |  8 +++++++
 packages/misskey-js/src/autogen/types.ts      |  1 +
 11 files changed, 69 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd2a9e32e5..bcc2aa29c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
 
 ### General
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
+- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
+  - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 5089f7802e..c2f8e944dd 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -6594,6 +6594,10 @@ export interface Locale extends ILocale {
              * ファイルにNSFWを常に付与
              */
             "alwaysMarkNsfw": string;
+            /**
+             * アイコンとバナーの更新を許可
+             */
+            "canUpdateBioMedia": string;
             /**
              * ノートのピン留めの最大数
              */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index a03d792140..8d117e6dc8 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1705,6 +1705,7 @@ _role:
     canManageAvatarDecorations: "アバターデコレーションの管理"
     driveCapacity: "ドライブ容量"
     alwaysMarkNsfw: "ファイルにNSFWを常に付与"
+    canUpdateBioMedia: "アイコンとバナーの更新を許可"
     pinMax: "ノートのピン留めの最大数"
     antennaMax: "アンテナの作成可能数"
     wordMuteMax: "ワードミュートの最大文字数"
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index e2ebecb99f..94026fd503 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -47,6 +47,7 @@ export type RolePolicies = {
 	canHideAds: boolean;
 	driveCapacityMb: number;
 	alwaysMarkNsfw: boolean;
+	canUpdateBioMedia: boolean;
 	pinLimit: number;
 	antennaLimit: number;
 	wordMuteLimit: number;
@@ -75,6 +76,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
 	canHideAds: false,
 	driveCapacityMb: 100,
 	alwaysMarkNsfw: false,
+	canUpdateBioMedia: true,
 	pinLimit: 5,
 	antennaLimit: 5,
 	wordMuteLimit: 200,
@@ -376,6 +378,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 			canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
 			driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
 			alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
+			canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)),
 			pinLimit: calc('pinLimit', vs => Math.max(...vs)),
 			antennaLimit: calc('antennaLimit', vs => Math.max(...vs)),
 			wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)),
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index 398c8695d2..457205e023 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -34,6 +34,7 @@ import { StatusError } from '@/misc/status-error.js';
 import type { UtilityService } from '@/core/UtilityService.js';
 import type { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
+import { RoleService } from '@/core/RoleService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
 import type { AccountMoveService } from '@/core/AccountMoveService.js';
@@ -100,6 +101,8 @@ export class ApPersonService implements OnModuleInit {
 
 		@Inject(DI.followingsRepository)
 		private followingsRepository: FollowingsRepository,
+
+		private roleService: RoleService,
 	) {
 	}
 
@@ -238,6 +241,11 @@ export class ApPersonService implements OnModuleInit {
 			return this.apImageService.resolveImage(user, img).catch(() => null);
 		}));
 
+		if (((avatar != null && avatar.id != null) || (banner != null && banner.id != null))
+				&& !(await this.roleService.getUserPolicies(user.id)).canUpdateBioMedia) {
+			return {};
+		}
+
 		/*
 			we don't want to return nulls on errors! if the database fields
 			are already null, nothing changes; if the database has old
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index d9987a70c3..7366f05356 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -228,6 +228,10 @@ export const packedRolePoliciesSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		canUpdateBioMedia: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
 		pinLimit: {
 			type: 'integer',
 			optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index b39b52bc41..a1e2fa5e4c 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -25,7 +25,7 @@ import { UserFollowingService } from '@/core/UserFollowingService.js';
 import { AccountUpdateService } from '@/core/AccountUpdateService.js';
 import { HashtagService } from '@/core/HashtagService.js';
 import { DI } from '@/di-symbols.js';
-import { RoleService } from '@/core/RoleService.js';
+import { RolePolicies, RoleService } from '@/core/RoleService.js';
 import { CacheService } from '@/core/CacheService.js';
 import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
@@ -256,6 +256,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const profileUpdates = {} as Partial<MiUserProfile>;
 
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
+			let policies: RolePolicies | null = null;
 
 			if (ps.name !== undefined) {
 				if (ps.name === null) {
@@ -296,14 +297,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			if (ps.mutedWords !== undefined) {
-				checkMuteWordCount(ps.mutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit);
+				policies ??= await this.roleService.getUserPolicies(user.id);
+				checkMuteWordCount(ps.mutedWords, policies.wordMuteLimit);
 				validateMuteWordRegex(ps.mutedWords);
 
 				profileUpdates.mutedWords = ps.mutedWords;
 				profileUpdates.enableWordMute = ps.mutedWords.length > 0;
 			}
 			if (ps.hardMutedWords !== undefined) {
-				checkMuteWordCount(ps.hardMutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit);
+				policies ??= await this.roleService.getUserPolicies(user.id);
+				checkMuteWordCount(ps.hardMutedWords, policies.wordMuteLimit);
 				validateMuteWordRegex(ps.hardMutedWords);
 				profileUpdates.hardMutedWords = ps.hardMutedWords;
 			}
@@ -322,13 +325,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
 			if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;
 			if (typeof ps.alwaysMarkNsfw === 'boolean') {
-				if ((await roleService.getUserPolicies(user.id)).alwaysMarkNsfw) throw new ApiError(meta.errors.restrictedByRole);
+				policies ??= await this.roleService.getUserPolicies(user.id);
+				if (policies.alwaysMarkNsfw) throw new ApiError(meta.errors.restrictedByRole);
 				profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
 			}
 			if (typeof ps.autoSensitive === 'boolean') profileUpdates.autoSensitive = ps.autoSensitive;
 			if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes;
 
 			if (ps.avatarId) {
+				policies ??= await this.roleService.getUserPolicies(user.id);
+				if (!policies.canUpdateBioMedia) throw new ApiError(meta.errors.restrictedByRole);
+
 				const avatar = await this.driveFilesRepository.findOneBy({ id: ps.avatarId });
 
 				if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar);
@@ -344,6 +351,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			if (ps.bannerId) {
+				policies ??= await this.roleService.getUserPolicies(user.id);
+				if (!policies.canUpdateBioMedia) throw new ApiError(meta.errors.restrictedByRole);
+
 				const banner = await this.driveFilesRepository.findOneBy({ id: ps.bannerId });
 
 				if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner);
@@ -359,14 +369,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			if (ps.avatarDecorations) {
+				policies ??= await this.roleService.getUserPolicies(user.id);
 				const decorations = await this.avatarDecorationService.getAll(true);
-				const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]);
+				const myRoles = await this.roleService.getUserRoles(user.id);
 				const allRoles = await this.roleService.getRoles();
 				const decorationIds = decorations
 					.filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))
 					.map(d => d.id);
 
-				if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole);
+				if (ps.avatarDecorations.length > policies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole);
 
 				updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({
 					id: d.id,
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 9e41926a97..e135bc69a0 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -87,6 +87,7 @@ export const ROLE_POLICIES = [
 	'canHideAds',
 	'driveCapacityMb',
 	'alwaysMarkNsfw',
+	'canUpdateBioMedia',
 	'pinLimit',
 	'antennaLimit',
 	'wordMuteLimit',
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index eb8a59b34f..3e948abdf1 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -378,6 +378,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</MkFolder>
 
+			<MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])">
+				<template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template>
+				<template #suffix>
+					<span v-if="role.policies.canUpdateBioMedia.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+					<span v-else>{{ role.policies.canUpdateBioMedia.value ? i18n.ts.yes : i18n.ts.no }}</span>
+					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canUpdateBioMedia)"></i></span>
+				</template>
+				<div class="_gaps">
+					<MkSwitch v-model="role.policies.canUpdateBioMedia.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
+					</MkSwitch>
+					<MkSwitch v-model="role.policies.canUpdateBioMedia.value" :disabled="role.policies.canUpdateBioMedia.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts.enable }}</template>
+					</MkSwitch>
+					<MkRange v-model="role.policies.canUpdateBioMedia.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+						<template #label>{{ i18n.ts._role.priority }}</template>
+					</MkRange>
+				</div>
+			</MkFolder>
+
 			<MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])">
 				<template #label>{{ i18n.ts._role._options.pinMax }}</template>
 				<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 50323e3de5..6fb950494b 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -134,6 +134,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</MkSwitch>
 						</MkFolder>
 
+						<MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])">
+							<template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template>
+							<template #suffix>{{ policies.canUpdateBioMedia ? i18n.ts.yes : i18n.ts.no }}</template>
+							<MkSwitch v-model="policies.canUpdateBioMedia">
+								<template #label>{{ i18n.ts.enable }}</template>
+							</MkSwitch>
+						</MkFolder>
+
 						<MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])">
 							<template #label>{{ i18n.ts._role._options.pinMax }}</template>
 							<template #suffix>{{ policies.pinLimit }}</template>
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index ff731a2fa6..d3c857219b 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4786,6 +4786,7 @@ export type components = {
       canHideAds: boolean;
       driveCapacityMb: number;
       alwaysMarkNsfw: boolean;
+      canUpdateBioMedia: boolean;
       pinLimit: number;
       antennaLimit: number;
       wordMuteLimit: number;

From 31e82fc29a335ffc55e57ac89713a4d00de1fe93 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sun, 14 Jul 2024 09:33:16 +0900
Subject: [PATCH 099/206] test(backend): kill many `any` in backend test
 (partial) (#14054)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* kill any on utils:api

* kill any on timeline test

* use optional chain to kill TS2532 on timeline test
変更前: 該当ノートが見つからなければundefinedに対するプロパティアクセスとしてテストがクラッシュ
変更後: 該当ノートが見つからなければoptional chainがundefinedとして評価されるが、strictEqualの右辺がnon-nullableなためアサーションに失敗しテストがクラッシュ

* kill `as any` for ApMfmService

* kill argument any for api-visibility

* kill argument any across a few tests

* do not return value that has yielded from `await`-ing `Promise<void>`

* force cast

* runtime non-null assertion to coerce

* rewrite `assert.notEqual(expr, null)` to `assert.ok(expr)`
こうすることでassertion type扱いになり、non-nullableになる

* change return type of `failedApiCall` to `void`
戻り値がどこにも使われていない

* split bindings for exports.ts
型が合わなくて文句を言ってくるので適切に分割

* runtime non-null assertion

* runtime non-null assertion

* 何故かうまく行かないので、とりあえずXORしてみる

* Revert "何故かうまく行かないので、とりあえずXORしてみる"

This reverts commit 48cf32c930924840d0892af92d71b9437acb5844.

* castAsErrorで安全ではないキャストを隠蔽

* 型アサーションの追加

* 型アサーションの追加

* 型アサーションの追加

* voidで値を返さない

* castAsError

* assert.ok => kill nullability

* もはや明示的な型の指定は必要ない

* castAsError

* castAsError

* 型アサーションの追加

* nullableを一旦抑止

* 変数を分離して型エラーを排除

* 不要なプロパティを削除する処理を隠蔽してanyを排除

* Repository type

* simple type

* assert.ok => kill nullability

* revert `as any` drop
reverts fe95c05b3f53266108128680d9358a3796844232 partialy

* test: fix invalid assertion
partially revert b99b7b5392d9d20c81dfee1346ba8b33ff9e1fbb

* test: 52d8a54fc72b886fecb30a736b3ccf5057ea2a0c により型が合うようになった部分の`as any`を除去

* format

* test: apply https://github.com/misskey-dev/misskey/pull/14054#discussion_r1672369526 (part 1)

* test: use non-null assertion to suppress too many error

* Update packages/backend/test/utils.ts

Co-authored-by: anatawa12 <anatawa12@icloud.com>

---------

Co-authored-by: anatawa12 <anatawa12@icloud.com>
---
 .../src/core/activitypub/ApMfmService.ts      |   2 +-
 packages/backend/test/e2e/2fa.ts              |  30 +-
 packages/backend/test/e2e/api-visibility.ts   |  16 +-
 packages/backend/test/e2e/block.ts            |  12 +-
 packages/backend/test/e2e/clips.ts            |  12 +-
 packages/backend/test/e2e/endpoints.ts        |  17 +-
 packages/backend/test/e2e/exports.ts          |  70 ++---
 packages/backend/test/e2e/move.ts             |  68 +++--
 packages/backend/test/e2e/mute.ts             |  72 ++---
 packages/backend/test/e2e/note.ts             |  75 +++--
 packages/backend/test/e2e/renote-mute.ts      |  16 +-
 packages/backend/test/e2e/thread-mute.ts      |  10 +-
 packages/backend/test/e2e/timelines.ts        | 266 +++++++++---------
 packages/backend/test/unit/ApMfmService.ts    |   8 +-
 packages/backend/test/unit/FileInfoService.ts | 101 ++-----
 packages/backend/test/utils.ts                |  32 ++-
 16 files changed, 403 insertions(+), 404 deletions(-)

diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts
index ab75b9abbd..4036d2794a 100644
--- a/packages/backend/src/core/activitypub/ApMfmService.ts
+++ b/packages/backend/src/core/activitypub/ApMfmService.ts
@@ -25,7 +25,7 @@ export class ApMfmService {
 	}
 
 	@bindThis
-	public getNoteHtml(note: MiNote, apAppend?: string) {
+	public getNoteHtml(note: Pick<MiNote, 'text' | 'mentionedRemoteUsers'>, apAppend?: string) {
 		let noMisskeyContent = false;
 		const srcMfm = (note.text ?? '') + (apAppend ?? '');
 
diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts
index 13c56b88a6..06548fa7da 100644
--- a/packages/backend/test/e2e/2fa.ts
+++ b/packages/backend/test/e2e/2fa.ts
@@ -206,7 +206,7 @@ describe('2要素認証', () => {
 			username,
 		}, alice);
 		assert.strictEqual(usersShowResponse.status, 200);
-		assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
+		assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
 
 		const signinResponse = await api('signin', {
 			...signinParam(),
@@ -248,7 +248,7 @@ describe('2要素認証', () => {
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}) as any, alice);
+		} as any) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 		assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
 		assert.strictEqual(keyDoneResponse.body.name, keyName);
@@ -257,22 +257,22 @@ describe('2要素認証', () => {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
-		assert.strictEqual(usersShowResponse.body.securityKeys, true);
+		assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, true);
 
 		const signinResponse = await api('signin', {
 			...signinParam(),
 		});
 		assert.strictEqual(signinResponse.status, 200);
 		assert.strictEqual(signinResponse.body.i, undefined);
-		assert.notEqual(signinResponse.body.challenge, undefined);
-		assert.notEqual(signinResponse.body.allowCredentials, undefined);
-		assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url'));
+		assert.notEqual((signinResponse.body as unknown as { challenge: unknown | undefined }).challenge, undefined);
+		assert.notEqual((signinResponse.body as unknown as { allowCredentials: unknown | undefined }).allowCredentials, undefined);
+		assert.strictEqual((signinResponse.body as unknown as { allowCredentials: {id: string}[] }).allowCredentials[0].id, credentialId.toString('base64url'));
 
 		const signinResponse2 = await api('signin', signinWithSecurityKeyParam({
 			keyName,
 			credentialId,
 			requestOptions: signinResponse.body,
-		}));
+		} as any));
 		assert.strictEqual(signinResponse2.status, 200);
 		assert.notEqual(signinResponse2.body.i, undefined);
 
@@ -307,7 +307,7 @@ describe('2要素認証', () => {
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}) as any, alice);
+		} as any) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 
 		const passwordLessResponse = await api('i/2fa/password-less', {
@@ -319,7 +319,7 @@ describe('2要素認証', () => {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
-		assert.strictEqual(usersShowResponse.body.usePasswordLessLogin, true);
+		assert.strictEqual((usersShowResponse.body as unknown as { usePasswordLessLogin: boolean }).usePasswordLessLogin, true);
 
 		const signinResponse = await api('signin', {
 			...signinParam(),
@@ -333,7 +333,7 @@ describe('2要素認証', () => {
 				keyName,
 				credentialId,
 				requestOptions: signinResponse.body,
-			}),
+			} as any),
 			password: '',
 		});
 		assert.strictEqual(signinResponse2.status, 200);
@@ -370,7 +370,7 @@ describe('2要素認証', () => {
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}) as any, alice);
+		} as any) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 
 		const renamedKey = 'other-key';
@@ -383,6 +383,7 @@ describe('2要素認証', () => {
 		const iResponse = await api('i', {
 		}, alice);
 		assert.strictEqual(iResponse.status, 200);
+		assert.ok(iResponse.body.securityKeysList);
 		const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url'));
 		assert.strictEqual(securityKeys.length, 1);
 		assert.strictEqual(securityKeys[0].name, renamedKey);
@@ -419,13 +420,14 @@ describe('2要素認証', () => {
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
-		}) as any, alice);
+		} as any) as any, alice);
 		assert.strictEqual(keyDoneResponse.status, 200);
 
 		// テストの実行順によっては複数残ってるので全部消す
 		const iResponse = await api('i', {
 		}, alice);
 		assert.strictEqual(iResponse.status, 200);
+		assert.ok(iResponse.body.securityKeysList);
 		for (const key of iResponse.body.securityKeysList) {
 			const removeKeyResponse = await api('i/2fa/remove-key', {
 				token: otpToken(registerResponse.body.secret),
@@ -439,7 +441,7 @@ describe('2要素認証', () => {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
-		assert.strictEqual(usersShowResponse.body.securityKeys, false);
+		assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, false);
 
 		const signinResponse = await api('signin', {
 			...signinParam(),
@@ -470,7 +472,7 @@ describe('2要素認証', () => {
 			username,
 		});
 		assert.strictEqual(usersShowResponse.status, 200);
-		assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
+		assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
 
 		const unregisterResponse = await api('i/2fa/unregister', {
 			token: otpToken(registerResponse.body.secret),
diff --git a/packages/backend/test/e2e/api-visibility.ts b/packages/backend/test/e2e/api-visibility.ts
index c61b0c2a86..2dd645d97a 100644
--- a/packages/backend/test/e2e/api-visibility.ts
+++ b/packages/backend/test/e2e/api-visibility.ts
@@ -410,21 +410,21 @@ describe('API visibility', () => {
 		test('[HTL] public-post が 自分が見れる', async () => {
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === pub.id);
+			const notes = res.body.filter(n => n.id === pub.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 
 		test('[HTL] public-post が 非フォロワーから見れない', async () => {
 			const res = await api('notes/timeline', { limit: 100 }, other);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === pub.id);
+			const notes = res.body.filter(n => n.id === pub.id);
 			assert.strictEqual(notes.length, 0);
 		});
 
 		test('[HTL] followers-post が フォロワーから見れる', async () => {
 			const res = await api('notes/timeline', { limit: 100 }, follower);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === fol.id);
+			const notes = res.body.filter(n => n.id === fol.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 		//#endregion
@@ -433,21 +433,21 @@ describe('API visibility', () => {
 		test('[replies] followers-reply が フォロワーから見れる', async () => {
 			const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, follower);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === folR.id);
+			const notes = res.body.filter(n => n.id === folR.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 
 		test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
 			const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, other);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === folR.id);
+			const notes = res.body.filter(n => n.id === folR.id);
 			assert.strictEqual(notes.length, 0);
 		});
 
 		test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
 			const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, target);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === folR.id);
+			const notes = res.body.filter(n => n.id === folR.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 		//#endregion
@@ -456,14 +456,14 @@ describe('API visibility', () => {
 		test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
 			const res = await api('notes/mentions', { limit: 100 }, target);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === folR.id);
+			const notes = res.body.filter(n => n.id === folR.id);
 			assert.strictEqual(notes[0].text, 'x');
 		});
 
 		test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
 			const res = await api('notes/mentions', { limit: 100 }, target);
 			assert.strictEqual(res.status, 200);
-			const notes = res.body.filter((n: any) => n.id === folM.id);
+			const notes = res.body.filter(n => n.id === folM.id);
 			assert.strictEqual(notes[0].text, '@target x');
 		});
 		//#endregion
diff --git a/packages/backend/test/e2e/block.ts b/packages/backend/test/e2e/block.ts
index e4f798498f..35b0e59383 100644
--- a/packages/backend/test/e2e/block.ts
+++ b/packages/backend/test/e2e/block.ts
@@ -6,7 +6,7 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
-import { api, post, signup } from '../utils.js';
+import { api, castAsError, post, signup } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Block', () => {
@@ -33,7 +33,7 @@ describe('Block', () => {
 		const res = await api('following/create', { userId: alice.id }, bob);
 
 		assert.strictEqual(res.status, 400);
-		assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
+		assert.strictEqual(castAsError(res.body).error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
 	});
 
 	test('ブロックされているユーザーにリアクションできない', async () => {
@@ -42,7 +42,8 @@ describe('Block', () => {
 		const res = await api('notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
 
 		assert.strictEqual(res.status, 400);
-		assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
+		assert.ok(res.body);
+		assert.strictEqual(castAsError(res.body).error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
 	});
 
 	test('ブロックされているユーザーに返信できない', async () => {
@@ -51,7 +52,8 @@ describe('Block', () => {
 		const res = await api('notes/create', { replyId: note.id, text: 'yo' }, bob);
 
 		assert.strictEqual(res.status, 400);
-		assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
+		assert.ok(res.body);
+		assert.strictEqual(castAsError(res.body).error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
 	});
 
 	test('ブロックされているユーザーのノートをRenoteできない', async () => {
@@ -60,7 +62,7 @@ describe('Block', () => {
 		const res = await api('notes/create', { renoteId: note.id, text: 'yo' }, bob);
 
 		assert.strictEqual(res.status, 400);
-		assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
+		assert.strictEqual(castAsError(res.body).error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
 	});
 
 	// TODO: ユーザーリストに入れられないテスト
diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts
index a229ec06f9..a130c3698d 100644
--- a/packages/backend/test/e2e/clips.ts
+++ b/packages/backend/test/e2e/clips.ts
@@ -79,14 +79,14 @@ describe('クリップ', () => {
 	};
 
 	const deleteClip = async (parameters: Misskey.entities.ClipsDeleteRequest, request: Partial<ApiRequest<'clips/delete'>> = {}): Promise<void> => {
-		return await successfulApiCall({
+		await successfulApiCall({
 			endpoint: 'clips/delete',
 			parameters,
 			user: alice,
 			...request,
 		}, {
 			status: 204,
-		}) as any as void;
+		});
 	};
 
 	const show = async (parameters: Misskey.entities.ClipsShowRequest, request: Partial<ApiRequest<'clips/show'>> = {}): Promise<Misskey.entities.Clip> => {
@@ -454,25 +454,25 @@ describe('クリップ', () => {
 		let aliceClip: Misskey.entities.Clip;
 
 		const favorite = async (parameters: Misskey.entities.ClipsFavoriteRequest, request: Partial<ApiRequest<'clips/favorite'>> = {}): Promise<void> => {
-			return successfulApiCall({
+			await successfulApiCall({
 				endpoint: 'clips/favorite',
 				parameters,
 				user: alice,
 				...request,
 			}, {
 				status: 204,
-			}) as any as void;
+			});
 		};
 
 		const unfavorite = async (parameters: Misskey.entities.ClipsUnfavoriteRequest, request: Partial<ApiRequest<'clips/unfavorite'>> = {}): Promise<void> => {
-			return successfulApiCall({
+			await successfulApiCall({
 				endpoint: 'clips/unfavorite',
 				parameters,
 				user: alice,
 				...request,
 			}, {
 				status: 204,
-			}) as any as void;
+			});
 		};
 
 		const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => {
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index 2b2699ecd9..5aaec7f6f9 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -10,7 +10,7 @@ import * as assert from 'assert';
 // https://github.com/node-fetch/node-fetch/pull/1664
 import { Blob } from 'node-fetch';
 import { MiUser } from '@/models/_.js';
-import { api, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js';
+import { api, castAsError, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Endpoints', () => {
@@ -164,7 +164,7 @@ describe('Endpoints', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
-			assert.strictEqual(res.body.id, alice.id);
+			assert.strictEqual((res.body as unknown as { id: string }).id, alice.id);
 		});
 
 		test('ユーザーが存在しなかったら怒る', async () => {
@@ -285,7 +285,8 @@ describe('Endpoints', () => {
 			}, alice);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'CANNOT_REACT_TO_RENOTE');
+			assert.ok(res.body);
+			assert.strictEqual(castAsError(res.body).error.code, 'CANNOT_REACT_TO_RENOTE');
 		});
 
 		test('引用にリアクションできる', async () => {
@@ -1063,7 +1064,7 @@ describe('Endpoints', () => {
 				userId: bob.id,
 			}, alice);
 			assert.strictEqual(res1.status, 204);
-			assert.strictEqual(res2.body?.memo, memo);
+			assert.strictEqual((res2.body as unknown as { memo: string })?.memo, memo);
 		});
 
 		test('自分に関するメモを更新できる', async () => {
@@ -1078,7 +1079,7 @@ describe('Endpoints', () => {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(res1.status, 204);
-			assert.strictEqual(res2.body?.memo, memo);
+			assert.strictEqual((res2.body as unknown as { memo: string })?.memo, memo);
 		});
 
 		test('メモを削除できる', async () => {
@@ -1099,7 +1100,7 @@ describe('Endpoints', () => {
 			}, alice);
 
 			// memoには常に文字列かnullが入っている(5cac151)
-			assert.strictEqual(res.body.memo, null);
+			assert.strictEqual((res.body as unknown as { memo: string | null }).memo, null);
 		});
 
 		test('メモは個人ごとに独立して保存される', async () => {
@@ -1126,8 +1127,8 @@ describe('Endpoints', () => {
 				}, carol),
 			]);
 
-			assert.strictEqual(resAlice.body.memo, memoAliceToBob);
-			assert.strictEqual(resCarol.body.memo, memoCarolToBob);
+			assert.strictEqual((resAlice.body as unknown as { memo: string }).memo, memoAliceToBob);
+			assert.strictEqual((resCarol.body as unknown as { memo: string }).memo, memoCarolToBob);
 		});
 	});
 });
diff --git a/packages/backend/test/e2e/exports.ts b/packages/backend/test/e2e/exports.ts
index 80a5331a6d..4bcecc9716 100644
--- a/packages/backend/test/e2e/exports.ts
+++ b/packages/backend/test/e2e/exports.ts
@@ -61,14 +61,14 @@ describe('export-clips', () => {
 	});
 
 	test('basic export', async () => {
-		let res = await api('clips/create', {
+		const res1 = await api('clips/create', {
 			name: 'foo',
 			description: 'bar',
 		}, alice);
-		assert.strictEqual(res.status, 200);
+		assert.strictEqual(res1.status, 200);
 
-		res = await api('i/export-clips', {}, alice);
-		assert.strictEqual(res.status, 204);
+		const res2 = await api('i/export-clips', {}, alice);
+		assert.strictEqual(res2.status, 204);
 
 		const exported = await pollFirstDriveFile();
 		assert.strictEqual(exported[0].name, 'foo');
@@ -77,7 +77,7 @@ describe('export-clips', () => {
 	});
 
 	test('export with notes', async () => {
-		let res = await api('clips/create', {
+		const res = await api('clips/create', {
 			name: 'foo',
 			description: 'bar',
 		}, alice);
@@ -96,15 +96,15 @@ describe('export-clips', () => {
 		});
 
 		for (const note of [note1, note2]) {
-			res = await api('clips/add-note', {
+			const res2 = await api('clips/add-note', {
 				clipId: clip.id,
 				noteId: note.id,
 			}, alice);
-			assert.strictEqual(res.status, 204);
+			assert.strictEqual(res2.status, 204);
 		}
 
-		res = await api('i/export-clips', {}, alice);
-		assert.strictEqual(res.status, 204);
+		const res3 = await api('i/export-clips', {}, alice);
+		assert.strictEqual(res3.status, 204);
 
 		const exported = await pollFirstDriveFile();
 		assert.strictEqual(exported[0].name, 'foo');
@@ -116,19 +116,19 @@ describe('export-clips', () => {
 	});
 
 	test('multiple clips', async () => {
-		let res = await api('clips/create', {
+		const res1 = await api('clips/create', {
 			name: 'kawaii',
 			description: 'kawaii',
 		}, alice);
-		assert.strictEqual(res.status, 200);
-		const clip1 = res.body;
+		assert.strictEqual(res1.status, 200);
+		const clip1 = res1.body;
 
-		res = await api('clips/create', {
+		const res2 = await api('clips/create', {
 			name: 'yuri',
 			description: 'yuri',
 		}, alice);
-		assert.strictEqual(res.status, 200);
-		const clip2 = res.body;
+		assert.strictEqual(res2.status, 200);
+		const clip2 = res2.body;
 
 		const note1 = await post(alice, {
 			text: 'baz1',
@@ -138,20 +138,26 @@ describe('export-clips', () => {
 			text: 'baz2',
 		});
 
-		res = await api('clips/add-note', {
-			clipId: clip1.id,
-			noteId: note1.id,
-		}, alice);
-		assert.strictEqual(res.status, 204);
+		{
+			const res = await api('clips/add-note', {
+				clipId: clip1.id,
+				noteId: note1.id,
+			}, alice);
+			assert.strictEqual(res.status, 204);
+		}
 
-		res = await api('clips/add-note', {
-			clipId: clip2.id,
-			noteId: note2.id,
-		}, alice);
-		assert.strictEqual(res.status, 204);
+		{
+			const res = await api('clips/add-note', {
+				clipId: clip2.id,
+				noteId: note2.id,
+			}, alice);
+			assert.strictEqual(res.status, 204);
+		}
 
-		res = await api('i/export-clips', {}, alice);
-		assert.strictEqual(res.status, 204);
+		{
+			const res = await api('i/export-clips', {}, alice);
+			assert.strictEqual(res.status, 204);
+		}
 
 		const exported = await pollFirstDriveFile();
 		assert.strictEqual(exported[0].name, 'kawaii');
@@ -163,7 +169,7 @@ describe('export-clips', () => {
 	});
 
 	test('Clipping other user\'s note', async () => {
-		let res = await api('clips/create', {
+		const res = await api('clips/create', {
 			name: 'kawaii',
 			description: 'kawaii',
 		}, alice);
@@ -175,14 +181,14 @@ describe('export-clips', () => {
 			visibility: 'followers',
 		});
 
-		res = await api('clips/add-note', {
+		const res2 = await api('clips/add-note', {
 			clipId: clip.id,
 			noteId: note.id,
 		}, alice);
-		assert.strictEqual(res.status, 204);
+		assert.strictEqual(res2.status, 204);
 
-		res = await api('i/export-clips', {}, alice);
-		assert.strictEqual(res.status, 204);
+		const res3 = await api('i/export-clips', {}, alice);
+		assert.strictEqual(res3.status, 204);
 
 		const exported = await pollFirstDriveFile();
 		assert.strictEqual(exported[0].name, 'kawaii');
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index 35240cd3c8..fd798bdb25 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -13,14 +13,14 @@ import { loadConfig } from '@/config.js';
 import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
 import { jobQueue } from '@/boot/common.js';
-import { api, initTestDb, signup, successfulApiCall, uploadFile } from '../utils.js';
+import { api, castAsError, initTestDb, signup, successfulApiCall, uploadFile } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Account Move', () => {
 	let jq: INestApplicationContext;
 	let url: URL;
 
-	let root: any;
+	let root: misskey.entities.SignupResponse;
 	let alice: misskey.entities.SignupResponse;
 	let bob: misskey.entities.SignupResponse;
 	let carol: misskey.entities.SignupResponse;
@@ -93,8 +93,8 @@ describe('Account Move', () => {
 			}, bob);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'NO_SUCH_USER');
-			assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+			assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_USER');
+			assert.strictEqual(castAsError(res.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
 		});
 
 		test('Unable to add duplicated aliases to alsoKnownAs', async () => {
@@ -103,8 +103,8 @@ describe('Account Move', () => {
 			}, bob);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'INVALID_PARAM');
-			assert.strictEqual(res.body.error.id, '3d81ceae-475f-4600-b2a8-2bc116157532');
+			assert.strictEqual(castAsError(res.body).error.code, 'INVALID_PARAM');
+			assert.strictEqual(castAsError(res.body).error.id, '3d81ceae-475f-4600-b2a8-2bc116157532');
 		});
 
 		test('Unable to add itself', async () => {
@@ -113,8 +113,8 @@ describe('Account Move', () => {
 			}, bob);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'FORBIDDEN_TO_SET_YOURSELF');
-			assert.strictEqual(res.body.error.id, '25c90186-4ab0-49c8-9bba-a1fa6c202ba4');
+			assert.strictEqual(castAsError(res.body).error.code, 'FORBIDDEN_TO_SET_YOURSELF');
+			assert.strictEqual(castAsError(res.body).error.id, '25c90186-4ab0-49c8-9bba-a1fa6c202ba4');
 		});
 
 		test('Unable to add a nonexisting local account to alsoKnownAs', async () => {
@@ -123,16 +123,16 @@ describe('Account Move', () => {
 			}, bob);
 
 			assert.strictEqual(res1.status, 400);
-			assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER');
-			assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+			assert.strictEqual(castAsError(res1.body).error.code, 'NO_SUCH_USER');
+			assert.strictEqual(castAsError(res1.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
 
 			const res2 = await api('i/update', {
 				alsoKnownAs: ['@alice', 'nonexist'],
 			}, bob);
 
 			assert.strictEqual(res2.status, 400);
-			assert.strictEqual(res2.body.error.code, 'NO_SUCH_USER');
-			assert.strictEqual(res2.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+			assert.strictEqual(castAsError(res2.body).error.code, 'NO_SUCH_USER');
+			assert.strictEqual(castAsError(res2.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
 		});
 
 		test('Able to add two existing local account to alsoKnownAs', async () => {
@@ -241,8 +241,8 @@ describe('Account Move', () => {
 			}, root);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'NOT_ROOT_FORBIDDEN');
-			assert.strictEqual(res.body.error.id, '4362e8dc-731f-4ad8-a694-be2a88922a24');
+			assert.strictEqual(castAsError(res.body).error.code, 'NOT_ROOT_FORBIDDEN');
+			assert.strictEqual(castAsError(res.body).error.id, '4362e8dc-731f-4ad8-a694-be2a88922a24');
 		});
 
 		test('Unable to move to a nonexisting local account', async () => {
@@ -251,8 +251,8 @@ describe('Account Move', () => {
 			}, alice);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'NO_SUCH_USER');
-			assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
+			assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_USER');
+			assert.strictEqual(castAsError(res.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
 		});
 
 		test('Unable to move if alsoKnownAs is invalid', async () => {
@@ -261,8 +261,8 @@ describe('Account Move', () => {
 			}, alice);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS');
-			assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
+			assert.strictEqual(castAsError(res.body).error.code, 'DESTINATION_ACCOUNT_FORBIDS');
+			assert.strictEqual(castAsError(res.body).error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
 		});
 
 		test('Relationships have been properly migrated', async () => {
@@ -279,36 +279,44 @@ describe('Account Move', () => {
 				userId: alice.id,
 			}, alice);
 			assert.strictEqual(aliceFollowings.status, 200);
+			assert.ok(aliceFollowings);
 			assert.strictEqual(aliceFollowings.body.length, 3);
 
 			const carolFollowings = await api('users/following', {
 				userId: carol.id,
 			}, carol);
 			assert.strictEqual(carolFollowings.status, 200);
+			assert.ok(carolFollowings);
 			assert.strictEqual(carolFollowings.body.length, 2);
 			assert.strictEqual(carolFollowings.body[0].followeeId, bob.id);
 			assert.strictEqual(carolFollowings.body[1].followeeId, alice.id);
 
 			const blockings = await api('blocking/list', {}, dave);
 			assert.strictEqual(blockings.status, 200);
+			assert.ok(blockings);
 			assert.strictEqual(blockings.body.length, 2);
 			assert.strictEqual(blockings.body[0].blockeeId, bob.id);
 			assert.strictEqual(blockings.body[1].blockeeId, alice.id);
 
 			const mutings = await api('mute/list', {}, dave);
 			assert.strictEqual(mutings.status, 200);
+			assert.ok(mutings);
 			assert.strictEqual(mutings.body.length, 2);
 			assert.strictEqual(mutings.body[0].muteeId, bob.id);
 			assert.strictEqual(mutings.body[1].muteeId, alice.id);
 
 			const rootLists = await api('users/lists/list', {}, root);
 			assert.strictEqual(rootLists.status, 200);
+			assert.ok(rootLists);
+			assert.ok(rootLists.body[0].userIds);
 			assert.strictEqual(rootLists.body[0].userIds.length, 2);
 			assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id));
 			assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id));
 
 			const eveLists = await api('users/lists/list', {}, eve);
 			assert.strictEqual(eveLists.status, 200);
+			assert.ok(eveLists);
+			assert.ok(eveLists.body[0].userIds);
 			assert.strictEqual(eveLists.body[0].userIds.length, 1);
 			assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
 		});
@@ -347,8 +355,8 @@ describe('Account Move', () => {
 			}, bob);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS');
-			assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
+			assert.strictEqual(castAsError(res.body).error.code, 'DESTINATION_ACCOUNT_FORBIDS');
+			assert.strictEqual(castAsError(res.body).error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
 		});
 
 		test('Follow and follower counts are properly adjusted', async () => {
@@ -419,8 +427,9 @@ describe('Account Move', () => {
 		] as const)('Prohibit access after moving: %s', async (endpoint) => {
 			const res = await api(endpoint, {}, alice);
 			assert.strictEqual(res.status, 403);
-			assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
-			assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+			assert.ok(res.body);
+			assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+			assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
 		});
 
 		test('Prohibit access after moving: /antennas/update', async () => {
@@ -438,16 +447,19 @@ describe('Account Move', () => {
 			}, alice);
 
 			assert.strictEqual(res.status, 403);
-			assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
-			assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+			assert.ok(res.body);
+			assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+			assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
 		});
 
 		test('Prohibit access after moving: /drive/files/create', async () => {
+			// FIXME: 一旦逃げておく
 			const res = await uploadFile(alice);
 
 			assert.strictEqual(res.status, 403);
-			assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.code, 'YOUR_ACCOUNT_MOVED');
-			assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+			assert.ok(res.body);
+			assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+			assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
 		});
 
 		test('Prohibit updating alsoKnownAs after moving', async () => {
@@ -456,8 +468,8 @@ describe('Account Move', () => {
 			}, alice);
 
 			assert.strictEqual(res.status, 403);
-			assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED');
-			assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
+			assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
+			assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
 		});
 	});
 });
diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts
index 0e52c5decc..f37da288b7 100644
--- a/packages/backend/test/e2e/mute.ts
+++ b/packages/backend/test/e2e/mute.ts
@@ -47,8 +47,8 @@ describe('Mute', () => {
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+		assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 	});
 
 	test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
@@ -92,9 +92,9 @@ describe('Mute', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => {
@@ -108,9 +108,9 @@ describe('Mute', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 	});
 
@@ -124,8 +124,8 @@ describe('Mute', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@@ -138,8 +138,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@@ -152,8 +152,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
@@ -166,8 +166,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
@@ -180,8 +180,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
@@ -193,8 +193,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 
 			await api('following/delete', { userId: alice.id }, bob);
 			await api('following/delete', { userId: alice.id }, carol);
@@ -210,8 +210,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 
 			await api('following/delete', { userId: alice.id }, bob);
 			await api('following/delete', { userId: alice.id }, carol);
@@ -228,8 +228,8 @@ describe('Mute', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
 			const aliceNote = await post(alice, { text: 'hi' });
@@ -241,8 +241,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@@ -255,8 +255,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
@@ -269,8 +269,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
@@ -283,8 +283,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 
 		test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
@@ -296,8 +296,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 
 			await api('following/delete', { userId: alice.id }, bob);
 			await api('following/delete', { userId: alice.id }, carol);
@@ -313,8 +313,8 @@ describe('Mute', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true);
-			assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
+			assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
 		});
 	});
 });
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 7ce9f47bc3..5937eb9b49 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -3,16 +3,18 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import type { Repository } from "typeorm";
+
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import { MiNote } from '@/models/Note.js';
 import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
-import { api, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';
+import { api, castAsError, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';
 import type * as misskey from 'misskey-js';
 
 describe('Note', () => {
-	let Notes: any;
+	let Notes: Repository<MiNote>;
 
 	let root: misskey.entities.SignupResponse;
 	let alice: misskey.entities.SignupResponse;
@@ -61,8 +63,8 @@ describe('Note', () => {
 		}, alice);
 
 		assert.strictEqual(res.status, 400);
-		assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
-		assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
+		assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
+		assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
 	}, 1000 * 10);
 
 	test('存在しないファイルで怒られる', async () => {
@@ -72,8 +74,8 @@ describe('Note', () => {
 		}, alice);
 
 		assert.strictEqual(res.status, 400);
-		assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
-		assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
+		assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
+		assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
 	});
 
 	test('不正なファイルIDで怒られる', async () => {
@@ -81,8 +83,8 @@ describe('Note', () => {
 			fileIds: ['kyoppie'],
 		}, alice);
 		assert.strictEqual(res.status, 400);
-		assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE');
-		assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
+		assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
+		assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
 	});
 
 	test('返信できる', async () => {
@@ -101,6 +103,7 @@ describe('Note', () => {
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
 		assert.strictEqual(res.body.createdNote.text, alicePost.text);
 		assert.strictEqual(res.body.createdNote.replyId, alicePost.replyId);
+		assert.ok(res.body.createdNote.reply);
 		assert.strictEqual(res.body.createdNote.reply.text, bobPost.text);
 	});
 
@@ -118,6 +121,7 @@ describe('Note', () => {
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
 		assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
+		assert.ok(res.body.createdNote.renote);
 		assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
 	});
 
@@ -137,6 +141,7 @@ describe('Note', () => {
 		assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
 		assert.strictEqual(res.body.createdNote.text, alicePost.text);
 		assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
+		assert.ok(res.body.createdNote.renote);
 		assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
 	});
 
@@ -218,7 +223,7 @@ describe('Note', () => {
 		}, bob);
 
 		assert.strictEqual(bobReply.status, 400);
-		assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE');
+		assert.strictEqual(castAsError(bobReply.body).error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE');
 	});
 
 	test('visibility: specifiedなノートに対してvisibility: specifiedで返信できる', async () => {
@@ -256,7 +261,7 @@ describe('Note', () => {
 		}, bob);
 
 		assert.strictEqual(bobReply.status, 400);
-		assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY');
+		assert.strictEqual(castAsError(bobReply.body).error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY');
 	});
 
 	test('文字数ぎりぎりで怒られない', async () => {
@@ -333,6 +338,7 @@ describe('Note', () => {
 		assert.strictEqual(res.body.createdNote.text, post.text);
 
 		const noteDoc = await Notes.findOneBy({ id: res.body.createdNote.id });
+		assert.ok(noteDoc);
 		assert.deepStrictEqual(noteDoc.mentions, [bob.id]);
 	});
 
@@ -345,6 +351,7 @@ describe('Note', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
+			assert.ok(res.body.createdNote.files);
 			assert.strictEqual(res.body.createdNote.files.length, 1);
 			assert.strictEqual(res.body.createdNote.files[0].id, file.body!.id);
 		});
@@ -363,8 +370,9 @@ describe('Note', () => {
 
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
-			const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id);
-			assert.notEqual(myNote, null);
+			const myNote = res.body.find(note => note.id === createdNote.body.createdNote.id);
+			assert.ok(myNote);
+			assert.ok(myNote.files);
 			assert.strictEqual(myNote.files.length, 1);
 			assert.strictEqual(myNote.files[0].id, file.body!.id);
 		});
@@ -389,7 +397,9 @@ describe('Note', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 			const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
-			assert.notEqual(myNote, null);
+			assert.ok(myNote);
+			assert.ok(myNote.renote);
+			assert.ok(myNote.renote.files);
 			assert.strictEqual(myNote.renote.files.length, 1);
 			assert.strictEqual(myNote.renote.files[0].id, file.body!.id);
 		});
@@ -415,7 +425,9 @@ describe('Note', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 			const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id);
-			assert.notEqual(myNote, null);
+			assert.ok(myNote);
+			assert.ok(myNote.reply);
+			assert.ok(myNote.reply.files);
 			assert.strictEqual(myNote.reply.files.length, 1);
 			assert.strictEqual(myNote.reply.files[0].id, file.body!.id);
 		});
@@ -446,7 +458,10 @@ describe('Note', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(Array.isArray(res.body), true);
 			const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
-			assert.notEqual(myNote, null);
+			assert.ok(myNote);
+			assert.ok(myNote.renote);
+			assert.ok(myNote.renote.reply);
+			assert.ok(myNote.renote.reply.files);
 			assert.strictEqual(myNote.renote.reply.files.length, 1);
 			assert.strictEqual(myNote.renote.reply.files[0].id, file.body!.id);
 		});
@@ -474,7 +489,7 @@ describe('Note', () => {
 						priority: 0,
 						value: true,
 					},
-				} as any,
+				},
 			}, root);
 
 			assert.strictEqual(res.status, 200);
@@ -498,7 +513,7 @@ describe('Note', () => {
 			}, alice);
 
 			assert.strictEqual(liftnsfw.status, 400);
-			assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE');
+			assert.strictEqual(castAsError(liftnsfw.body).error.code, 'RESTRICTED_BY_ROLE');
 
 			const oldaddnsfw = await api('drive/files/update', {
 				fileId: file.body!.id,
@@ -710,7 +725,7 @@ describe('Note', () => {
 			}, alice);
 
 			assert.strictEqual(note1.status, 400);
-			assert.strictEqual(note1.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+			assert.strictEqual(castAsError(note1.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
 		});
 
 		test('禁止ワードを含む投稿はエラーになる (正規表現)', async () => {
@@ -727,7 +742,7 @@ describe('Note', () => {
 			}, alice);
 
 			assert.strictEqual(note2.status, 400);
-			assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+			assert.strictEqual(castAsError(note2.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
 		});
 
 		test('禁止ワードを含む投稿はエラーになる (スペースアンド)', async () => {
@@ -744,7 +759,7 @@ describe('Note', () => {
 			}, alice);
 
 			assert.strictEqual(note2.status, 400);
-			assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+			assert.strictEqual(castAsError(note2.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
 		});
 
 		test('禁止ワードを含んでるリモートノートもエラーになる', async () => {
@@ -786,7 +801,7 @@ describe('Note', () => {
 						priority: 1,
 						value: 0,
 					},
-				} as any,
+				},
 			}, root);
 
 			assert.strictEqual(res.status, 200);
@@ -807,7 +822,7 @@ describe('Note', () => {
 			}, alice);
 
 			assert.strictEqual(note.status, 400);
-			assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
+			assert.strictEqual(castAsError(note.body).error.code, 'CONTAINS_TOO_MANY_MENTIONS');
 
 			await api('admin/roles/unassign', {
 				userId: alice.id,
@@ -840,7 +855,7 @@ describe('Note', () => {
 						priority: 1,
 						value: 0,
 					},
-				} as any,
+				},
 			}, root);
 
 			assert.strictEqual(res.status, 200);
@@ -863,7 +878,7 @@ describe('Note', () => {
 			}, alice);
 
 			assert.strictEqual(note.status, 400);
-			assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
+			assert.strictEqual(castAsError(note.body).error.code, 'CONTAINS_TOO_MANY_MENTIONS');
 
 			await api('admin/roles/unassign', {
 				userId: alice.id,
@@ -896,7 +911,7 @@ describe('Note', () => {
 						priority: 1,
 						value: 1,
 					},
-				} as any,
+				},
 			}, root);
 
 			assert.strictEqual(res.status, 200);
@@ -951,6 +966,7 @@ describe('Note', () => {
 
 			assert.strictEqual(deleteOneRes.status, 204);
 			let mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
+			assert.ok(mainNote);
 			assert.strictEqual(mainNote.repliesCount, 1);
 
 			const deleteTwoRes = await api('notes/delete', {
@@ -959,6 +975,7 @@ describe('Note', () => {
 
 			assert.strictEqual(deleteTwoRes.status, 204);
 			mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
+			assert.ok(mainNote);
 			assert.strictEqual(mainNote.repliesCount, 0);
 		});
 	});
@@ -980,7 +997,7 @@ describe('Note', () => {
 				}, alice);
 
 				assert.strictEqual(res.status, 400);
-				assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
+				assert.strictEqual(castAsError(res.body).error.code, 'UNAVAILABLE');
 			});
 
 			afterAll(async () => {
@@ -992,7 +1009,7 @@ describe('Note', () => {
 			const res = await api('notes/translate', { noteId: 'foo', targetLang: 'ja' }, alice);
 
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'NO_SUCH_NOTE');
+			assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_NOTE');
 		});
 
 		test('不可視なノートは翻訳できない', async () => {
@@ -1000,7 +1017,7 @@ describe('Note', () => {
 			const bobTranslateAttempt = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, bob);
 
 			assert.strictEqual(bobTranslateAttempt.status, 400);
-			assert.strictEqual(bobTranslateAttempt.body.error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE');
+			assert.strictEqual(castAsError(bobTranslateAttempt.body).error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE');
 		});
 
 		test('text: null なノートを翻訳すると空のレスポンスが返ってくる', async () => {
@@ -1016,7 +1033,7 @@ describe('Note', () => {
 
 			// NOTE: デフォルトでは登録されていないので落ちる
 			assert.strictEqual(res.status, 400);
-			assert.strictEqual(res.body.error.code, 'UNAVAILABLE');
+			assert.strictEqual(castAsError(res.body).error.code, 'UNAVAILABLE');
 		});
 	});
 });
diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts
index f6895c43d8..0f636b9ae2 100644
--- a/packages/backend/test/e2e/renote-mute.ts
+++ b/packages/backend/test/e2e/renote-mute.ts
@@ -42,9 +42,9 @@ describe('Renote Mute', () => {
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolRenote.id), false);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === carolRenote.id), false);
+		assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
 	});
 
 	test('タイムラインにリノートミュートしているユーザーの引用が含まれる', async () => {
@@ -59,9 +59,9 @@ describe('Renote Mute', () => {
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolRenote.id), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === carolRenote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
 	});
 
 	// #12956
@@ -76,8 +76,8 @@ describe('Renote Mute', () => {
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === bobRenote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+		assert.strictEqual(res.body.some(note => note.id === bobRenote.id), true);
 	});
 
 	test('ストリームにリノートミュートしているユーザーのリノートが流れない', async () => {
diff --git a/packages/backend/test/e2e/thread-mute.ts b/packages/backend/test/e2e/thread-mute.ts
index 53bb6eb765..1ac99df884 100644
--- a/packages/backend/test/e2e/thread-mute.ts
+++ b/packages/backend/test/e2e/thread-mute.ts
@@ -33,9 +33,9 @@ describe('Note thread mute', () => {
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
-		assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolReply.id), false);
-		assert.strictEqual(res.body.some((note: any) => note.id === carolReplyWithoutMention.id), false);
+		assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+		assert.strictEqual(res.body.some(note => note.id === carolReply.id), false);
+		assert.strictEqual(res.body.some(note => note.id === carolReplyWithoutMention.id), false);
 	});
 
 	test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
@@ -93,8 +93,8 @@ describe('Note thread mute', () => {
 
 		assert.strictEqual(res.status, 200);
 		assert.strictEqual(Array.isArray(res.body), true);
-		assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReply.id), false);
-		assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReplyWithoutMention.id), false);
+		assert.strictEqual(res.body.some(notification => 'note' in notification && notification.note.id === carolReply.id), false);
+		assert.strictEqual(res.body.some(notification => 'note' in notification && notification.note.id === carolReplyWithoutMention.id), false);
 
 		// NOTE: bobの投稿はスレッドミュート前に行われたため通知に含まれていてもよい
 	});
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index fccc052d99..540b866b28 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -37,8 +37,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('フォローしているユーザーのノートが含まれる', async () => {
@@ -53,8 +53,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
@@ -69,9 +69,9 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: false でフォローしているユーザーの他人への返信が含まれない', async () => {
@@ -86,8 +86,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => {
@@ -103,8 +103,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: true でフォローしているユーザーの他人へのDM返信が含まれない', async () => {
@@ -120,8 +120,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => {
@@ -137,8 +137,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
@@ -156,9 +156,9 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === carolNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => {
@@ -175,8 +175,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
 		});
 
 		test.concurrent('withReplies: false でフォローしているユーザーのそのユーザー自身への返信が含まれる', async () => {
@@ -191,8 +191,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		});
 
 		test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
@@ -207,8 +207,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('自分の他人への返信が含まれる', async () => {
@@ -221,8 +221,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
 		});
 
 		test.concurrent('フォローしているユーザーの他人の投稿のリノートが含まれる', async () => {
@@ -237,8 +237,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => {
@@ -255,8 +255,8 @@ describe('Timelines', () => {
 				withRenotes: false,
 			}, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿の引用が含まれる', async () => {
@@ -273,8 +273,8 @@ describe('Timelines', () => {
 				withRenotes: false,
 			}, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('フォローしているユーザーの他人への visibility: specified なノートが含まれない', async () => {
@@ -288,7 +288,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
@@ -304,8 +304,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
@@ -322,8 +322,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
@@ -338,7 +338,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
@@ -353,7 +353,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => {
@@ -374,10 +374,10 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote1.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote2.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
 		}, 1000 * 10);
 
 		test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
@@ -392,7 +392,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('自分の visibility: specified なノートが含まれる', async () => {
@@ -404,8 +404,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => {
@@ -419,8 +419,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => {
@@ -432,7 +432,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => {
@@ -446,7 +446,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => {
@@ -459,8 +459,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok');
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'ok');
 		});
 
 		/* TODO
@@ -474,8 +474,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok');
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok');
 		});
 		*/
 
@@ -490,7 +490,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 	});
 
@@ -505,8 +505,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('他人の他人への返信が含まれない', async () => {
@@ -519,8 +519,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
 		});
 
 		test.concurrent('他人のその人自身への返信が含まれる', async () => {
@@ -533,8 +533,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		});
 
 		test.concurrent('チャンネル投稿が含まれない', async () => {
@@ -547,7 +547,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('リモートユーザーのノートが含まれない', async () => {
@@ -559,7 +559,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		// 含まれても良いと思うけど実装が面倒なので含まれない
@@ -575,8 +575,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('ミュートしているユーザーのノートが含まれない', async () => {
@@ -591,8 +591,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
@@ -608,8 +608,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
@@ -626,8 +626,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
 		});
 
 		test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
@@ -642,8 +642,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
@@ -656,7 +656,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@@ -670,8 +670,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		}, 1000 * 10);
 	});
 
@@ -685,7 +685,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('ローカルユーザーの visibility: home なノートが含まれない', async () => {
@@ -697,7 +697,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => {
@@ -711,7 +711,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
@@ -726,8 +726,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('他人の他人への返信が含まれない', async () => {
@@ -740,8 +740,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
 		});
 
 		test.concurrent('リモートユーザーのノートが含まれない', async () => {
@@ -753,7 +753,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/local-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
@@ -768,7 +768,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
@@ -783,7 +783,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
@@ -796,7 +796,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@@ -810,8 +810,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		}, 1000 * 10);
 	});
 
@@ -828,7 +828,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => {
@@ -843,7 +843,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => {
@@ -858,7 +858,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
@@ -874,7 +874,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => {
@@ -890,8 +890,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		});
 
 		test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => {
@@ -908,7 +908,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
@@ -925,7 +925,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => {
@@ -942,7 +942,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => {
@@ -958,7 +958,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => {
@@ -974,8 +974,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('リスインしている自分の visibility: followers なノートが含まれる', async () => {
@@ -990,8 +990,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => {
@@ -1007,7 +1007,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => {
@@ -1023,8 +1023,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		}, 1000 * 10);
 
 		test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => {
@@ -1039,8 +1039,8 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => {
@@ -1056,7 +1056,7 @@ describe('Timelines', () => {
 
 			const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 	});
 
@@ -1070,7 +1070,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('フォローしていないユーザーの visibility: followers なノートが含まれない', async () => {
@@ -1082,7 +1082,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
@@ -1096,8 +1096,8 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('自身の visibility: followers なノートが含まれる', async () => {
@@ -1109,8 +1109,8 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: alice.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
-			assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
 		});
 
 		test.concurrent('チャンネル投稿が含まれない', async () => {
@@ -1123,7 +1123,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('[withReplies: false] 他人への返信が含まれない', async () => {
@@ -1137,8 +1137,8 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
 		});
 
 		test.concurrent('[withReplies: true] 他人への返信が含まれる', async () => {
@@ -1152,8 +1152,8 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		});
 
 		test.concurrent('[withReplies: true] 他人への visibility: specified な返信が含まれない', async () => {
@@ -1167,8 +1167,8 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
 		});
 
 		test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@@ -1182,8 +1182,8 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 		}, 1000 * 10);
 
 		test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => {
@@ -1196,7 +1196,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => {
@@ -1209,7 +1209,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => {
@@ -1222,7 +1222,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
 		test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => {
@@ -1237,7 +1237,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => {
@@ -1253,9 +1253,9 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true);
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote3.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote3.id), true);
 		});
 
 		test.concurrent('自身の visibility: specified なノートが含まれる', async () => {
@@ -1267,7 +1267,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
 		});
 
 		test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => {
@@ -1279,7 +1279,7 @@ describe('Timelines', () => {
 
 			const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
 
-			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
 
 		/** @see https://github.com/misskey-dev/misskey/issues/14000 */
diff --git a/packages/backend/test/unit/ApMfmService.ts b/packages/backend/test/unit/ApMfmService.ts
index 79cb81f5c9..e81a321c9b 100644
--- a/packages/backend/test/unit/ApMfmService.ts
+++ b/packages/backend/test/unit/ApMfmService.ts
@@ -23,10 +23,10 @@ describe('ApMfmService', () => {
 
 	describe('getNoteHtml', () => {
 		test('Do not provide _misskey_content for simple text', () => {
-			const note: MiNote = {
+			const note = {
 				text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com',
 				mentionedRemoteUsers: '[]',
-			} as any;
+			};
 
 			const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
 
@@ -35,10 +35,10 @@ describe('ApMfmService', () => {
 		});
 
 		test('Provide _misskey_content for MFM', () => {
-			const note: MiNote = {
+			const note = {
 				text: '$[tada foo]',
 				mentionedRemoteUsers: '[]',
-			} as any;
+			};
 
 			const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
 
diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts
index aa9b34b706..29bd03a201 100644
--- a/packages/backend/test/unit/FileInfoService.ts
+++ b/packages/backend/test/unit/FileInfoService.ts
@@ -12,7 +12,7 @@ import { ModuleMocker } from 'jest-mock';
 import { Test } from '@nestjs/testing';
 import { afterAll, beforeAll, describe, test } from '@jest/globals';
 import { GlobalModule } from '@/GlobalModule.js';
-import { FileInfoService } from '@/core/FileInfoService.js';
+import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
 //import { DI } from '@/di-symbols.js';
 import { AiService } from '@/core/AiService.js';
 import { LoggerService } from '@/core/LoggerService.js';
@@ -28,6 +28,15 @@ const moduleMocker = new ModuleMocker(global);
 describe('FileInfoService', () => {
 	let app: TestingModule;
 	let fileInfoService: FileInfoService;
+	const strip = (fileInfo: FileInfo): Omit<Partial<FileInfo>, 'warnings' | 'blurhash' | 'sensitive' | 'porn'> => {
+		const fi: Partial<FileInfo> = fileInfo;
+		delete fi.warnings;
+		delete fi.sensitive;
+		delete fi.blurhash;
+		delete fi.porn;
+		
+		return fi;
+	}
 
 	beforeAll(async () => {
 		app = await Test.createTestingModule({
@@ -63,11 +72,7 @@ describe('FileInfoService', () => {
 
 	test('Empty file', async () => {
 		const path = `${resources}/emptyfile`;
-		const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-		delete info.warnings;
-		delete info.blurhash;
-		delete info.sensitive;
-		delete info.porn;
+		const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 		assert.deepStrictEqual(info, {
 			size: 0,
 			md5: 'd41d8cd98f00b204e9800998ecf8427e',
@@ -84,11 +89,7 @@ describe('FileInfoService', () => {
 	describe('IMAGE', () => {
 		test('Generic JPEG', async () => {
 			const path = `${resources}/192.jpg`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 5131,
 				md5: '8c9ed0677dd2b8f9f7472c3af247e5e3',
@@ -104,11 +105,7 @@ describe('FileInfoService', () => {
 
 		test('Generic APNG', async () => {
 			const path = `${resources}/anime.png`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 1868,
 				md5: '08189c607bea3b952704676bb3c979e0',
@@ -124,11 +121,7 @@ describe('FileInfoService', () => {
 
 		test('Generic AGIF', async () => {
 			const path = `${resources}/anime.gif`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 2248,
 				md5: '32c47a11555675d9267aee1a86571e7e',
@@ -144,11 +137,7 @@ describe('FileInfoService', () => {
 
 		test('PNG with alpha', async () => {
 			const path = `${resources}/with-alpha.png`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 3772,
 				md5: 'f73535c3e1e27508885b69b10cf6e991',
@@ -164,11 +153,7 @@ describe('FileInfoService', () => {
 
 		test('Generic SVG', async () => {
 			const path = `${resources}/image.svg`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 505,
 				md5: 'b6f52b4b021e7b92cdd04509c7267965',
@@ -185,11 +170,7 @@ describe('FileInfoService', () => {
 		test('SVG with XML definition', async () => {
 			// https://github.com/misskey-dev/misskey/issues/4413
 			const path = `${resources}/with-xml-def.svg`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 544,
 				md5: '4b7a346cde9ccbeb267e812567e33397',
@@ -205,11 +186,7 @@ describe('FileInfoService', () => {
 
 		test('Dimension limit', async () => {
 			const path = `${resources}/25000x25000.png`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 75933,
 				md5: '268c5dde99e17cf8fe09f1ab3f97df56',
@@ -225,11 +202,7 @@ describe('FileInfoService', () => {
 
 		test('Rotate JPEG', async () => {
 			const path = `${resources}/rotate.jpg`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			assert.deepStrictEqual(info, {
 				size: 12624,
 				md5: '68d5b2d8d1d1acbbce99203e3ec3857e',
@@ -247,11 +220,7 @@ describe('FileInfoService', () => {
 	describe('AUDIO', () => {
 		test('MP3', async () => {
 			const path = `${resources}/kick_gaba7.mp3`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			delete info.width;
 			delete info.height;
 			delete info.orientation;
@@ -267,11 +236,7 @@ describe('FileInfoService', () => {
 
 		test('WAV', async () => {
 			const path = `${resources}/kick_gaba7.wav`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			delete info.width;
 			delete info.height;
 			delete info.orientation;
@@ -287,11 +252,7 @@ describe('FileInfoService', () => {
 
 		test('AAC', async () => {
 			const path = `${resources}/kick_gaba7.aac`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			delete info.width;
 			delete info.height;
 			delete info.orientation;
@@ -307,11 +268,7 @@ describe('FileInfoService', () => {
 
 		test('FLAC', async () => {
 			const path = `${resources}/kick_gaba7.flac`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			delete info.width;
 			delete info.height;
 			delete info.orientation;
@@ -327,11 +284,7 @@ describe('FileInfoService', () => {
 
 		test('MPEG-4 AUDIO (M4A)', async () => {
 			const path = `${resources}/kick_gaba7.m4a`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			delete info.width;
 			delete info.height;
 			delete info.orientation;
@@ -347,11 +300,7 @@ describe('FileInfoService', () => {
 
 		test('WEBM AUDIO', async () => {
 			const path = `${resources}/kick_gaba7.webm`;
-			const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any;
-			delete info.warnings;
-			delete info.blurhash;
-			delete info.sensitive;
-			delete info.porn;
+			const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
 			delete info.width;
 			delete info.height;
 			delete info.orientation;
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index 06c3f82601..e70befeebe 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -18,6 +18,7 @@ import { entities } from '../src/postgres.js';
 import { loadConfig } from '../src/config.js';
 import type * as misskey from 'misskey-js';
 import { type Response } from 'node-fetch';
+import { ApiError } from "@/server/api/error.js";
 
 export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
 
@@ -48,27 +49,28 @@ export const successfulApiCall = async <E extends keyof misskey.Endpoints, P ext
 	const res = await api(endpoint, parameters, user);
 	const status = assertion.status ?? (res.body == null ? 204 : 200);
 	assert.strictEqual(res.status, status, inspect(res.body, { depth: 5, colors: true }));
-	return res.body;
+
+	return res.body as misskey.api.SwitchCaseResponseType<E, P>;
 };
 
-export const failedApiCall = async <T, E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: {
+export const failedApiCall = async <E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: {
 	status: number,
 	code: string,
 	id: string
-}): Promise<T> => {
+}): Promise<void> => {
 	const { endpoint, parameters, user } = request;
 	const { status, code, id } = assertion;
 	const res = await api(endpoint, parameters, user);
 	assert.strictEqual(res.status, status, inspect(res.body));
-	assert.strictEqual(res.body.error.code, code, inspect(res.body));
-	assert.strictEqual(res.body.error.id, id, inspect(res.body));
-	return res.body;
+	assert.ok(res.body);
+	assert.strictEqual(castAsError(res.body as any).error.code, code, inspect(res.body));
+	assert.strictEqual(castAsError(res.body as any).error.id, id, inspect(res.body));
 };
 
-export const api = async <E extends keyof misskey.Endpoints>(path: E, params: misskey.Endpoints[E]['req'], me?: UserToken): Promise<{
+export const api = async <E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(path: E, params: P, me?: UserToken): Promise<{
 	status: number,
 	headers: Headers,
-	body: any
+	body: misskey.api.SwitchCaseResponseType<E, P>
 }> => {
 	const bodyAuth: Record<string, string> = {};
 	const headers: Record<string, string> = {
@@ -89,13 +91,14 @@ export const api = async <E extends keyof misskey.Endpoints>(path: E, params: mi
 	});
 
 	const body = res.headers.get('content-type') === 'application/json; charset=utf-8'
-		? await res.json()
+		? await res.json() as misskey.api.SwitchCaseResponseType<E, P>
 		: null;
 
 	return {
 		status: res.status,
 		headers: res.headers,
-		body,
+		// FIXME: removing this non-null assertion: requires better typing around empty response.
+		body: body!,
 	};
 };
 
@@ -141,7 +144,8 @@ export const post = async (user: UserToken, params: misskey.Endpoints['notes/cre
 
 	const res = await api('notes/create', q, user);
 
-	return res.body ? res.body.createdNote : null;
+	// FIXME: the return type should reflect this fact.
+	return (res.body ? res.body.createdNote : null)!;
 };
 
 export const createAppToken = async (user: UserToken, permissions: (typeof misskey.permissions)[number][]) => {
@@ -635,3 +639,9 @@ export async function sendEnvResetRequest() {
 		throw new Error('server env update failed.');
 	}
 }
+
+// 与えられた値を強制的にエラーとみなす。この関数は型安全性を破壊するため、異常系のアサーション以外で用いられるべきではない。
+// FIXME(misskey-js): misskey-jsがエラー情報を公開するようになったらこの関数を廃止する
+export function castAsError(obj: Record<string, unknown>): { error: ApiError } {
+	return obj as { error: ApiError };
+}

From 6dd6fcf88f621d787c6524d81844b1d514434859 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Jul 2024 14:49:50 +0900
Subject: [PATCH 100/206] =?UTF-8?q?enhance(frontend):=20=E3=82=B5=E3=83=BC?=
 =?UTF-8?q?=E3=83=90=E3=83=BC=E6=83=85=E5=A0=B1=E3=83=BB=E3=81=8A=E5=95=8F?=
 =?UTF-8?q?=E3=81=84=E5=90=88=E3=82=8F=E3=81=9B=E3=83=9A=E3=83=BC=E3=82=B8?=
 =?UTF-8?q?=E3=82=92=E6=94=B9=E4=BF=AE=20(#14198)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* improve(frontend): サーバー情報・お問い合わせページを改修 (#238)

* Revert "Revert "enhance(frontend): add contact page" (#208)" (This reverts commit 5a329a09c987b3249f97f9d53af67d1bffb09eea.)

* improve(frontend): サーバー情報・お問い合わせページを改修

(cherry picked from commit e72758d8cda3db009c5d1bf1f4141682931b91f8)

* fix

* Update Changelog

* tweak

* lint

* 既存の翻訳を使用するように

---------

Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
---
 CHANGELOG.md                                  |   2 +
 packages/frontend/src/components/MkMenu.vue   |   1 +
 .../src/components/MkVisitorDashboard.vue     |  11 +-
 .../frontend/src/pages/about.overview.vue     | 205 ++++++++++++++++++
 packages/frontend/src/pages/about.vue         | 201 +----------------
 packages/frontend/src/pages/contact.vue       |  26 ++-
 packages/frontend/src/ui/_common_/common.ts   |  24 +-
 7 files changed, 250 insertions(+), 220 deletions(-)
 create mode 100644 packages/frontend/src/pages/about.overview.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcc2aa29c6..6e411e5329 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,8 @@
 - Enhance: 非ログイン時のハイライトTLのデザインを改善
 - Enhance: フロントエンドのアクセシビリティ改善  
   (Based on https://github.com/taiyme/misskey/pull/226)
+- Enhance: サーバー情報ページ・お問い合わせページを改善  
+  (Cherry-picked from https://github.com/taiyme/misskey/pull/238)
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 68479989b2..2276da1d21 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -54,6 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				:class="['_button', $style.item]"
 				:href="item.href"
 				:target="item.target"
+				:rel="item.target === '_blank' ? 'noopener noreferrer' : undefined"
 				:download="item.download"
 				@click.passive="close(true)"
 				@mouseenter.passive="onItemMouseEnter"
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 4d81bd0283..445780eca7 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 			<div class="_gaps_s" :class="$style.mainActions">
 				<MkButton :class="$style.mainAction" full rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton>
-				<MkButton :class="$style.mainAction" full rounded @click="exploreOtherServers()">{{ i18n.ts.exploreOtherServers }}</MkButton>
+				<MkButton :class="$style.mainAction" full rounded link to="https://misskey-hub.net/servers/">{{ i18n.ts.exploreOtherServers }}</MkButton>
 				<MkButton :class="$style.mainAction" full rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton>
 			</div>
 		</div>
@@ -65,7 +65,8 @@ import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import MkNumber from '@/components/MkNumber.vue';
 import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
-import { openInstanceMenu } from '@/ui/_common_/common';
+import { openInstanceMenu } from '@/ui/_common_/common.js';
+import type { MenuItem } from '@/types/menu.js';
 
 const stats = ref<Misskey.entities.StatsResponse | null>(null);
 
@@ -89,13 +90,9 @@ function signup() {
 	});
 }
 
-function showMenu(ev) {
+function showMenu(ev: MouseEvent) {
 	openInstanceMenu(ev);
 }
-
-function exploreOtherServers() {
-	window.open('https://misskey-hub.net/servers/', '_blank', 'noopener');
-}
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/about.overview.vue b/packages/frontend/src/pages/about.overview.vue
new file mode 100644
index 0000000000..84419b3bef
--- /dev/null
+++ b/packages/frontend/src/pages/about.overview.vue
@@ -0,0 +1,205 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div class="_gaps_m">
+	<div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }">
+		<div style="overflow: clip;">
+			<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/>
+			<div :class="$style.bannerName">
+				<b>{{ instance.name ?? host }}</b>
+			</div>
+		</div>
+	</div>
+
+	<MkKeyValue>
+		<template #key>{{ i18n.ts.description }}</template>
+		<template #value><div v-html="instance.description"></div></template>
+	</MkKeyValue>
+
+	<FormSection>
+		<div class="_gaps_m">
+			<MkKeyValue :copy="version">
+				<template #key>Misskey</template>
+				<template #value>{{ version }}</template>
+			</MkKeyValue>
+			<div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })">
+			</div>
+			<FormLink to="/about-misskey">
+				<template #icon><i class="ti ti-info-circle"></i></template>
+				{{ i18n.ts.aboutMisskey }}
+			</FormLink>
+			<FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external>
+				<template #icon><i class="ti ti-code"></i></template>
+				{{ i18n.ts.sourceCode }}
+			</FormLink>
+			<MkInfo v-else warn>
+				{{ i18n.ts.sourceCodeIsNotYetProvided }}
+			</MkInfo>
+		</div>
+	</FormSection>
+
+	<FormSection>
+		<div class="_gaps_m">
+			<FormSplit>
+				<MkKeyValue :copy="instance.maintainerName">
+					<template #key>{{ i18n.ts.administrator }}</template>
+					<template #value>
+						<template v-if="instance.maintainerName">{{ instance.maintainerName }}</template>
+						<span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span>
+					</template>
+				</MkKeyValue>
+				<MkKeyValue :copy="instance.maintainerEmail">
+					<template #key>{{ i18n.ts.contact }}</template>
+					<template #value>
+						<template v-if="instance.maintainerEmail">{{ instance.maintainerEmail }}</template>
+						<span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span>
+					</template>
+				</MkKeyValue>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.inquiry }}</template>
+					<template #value>
+						<MkLink v-if="instance.inquiryUrl" :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink>
+						<span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span>
+					</template>
+				</MkKeyValue>
+			</FormSplit>
+			<div class="_gaps_s">
+				<FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>
+					<template #icon><i class="ti ti-user-shield"></i></template>
+					<template #default>{{ i18n.ts.impressum }}</template>
+				</FormLink>
+				<MkFolder v-if="instance.serverRules.length > 0">
+					<template #icon><i class="ti ti-checkup-list"></i></template>
+					<template #label>{{ i18n.ts.serverRules }}</template>
+					<ol class="_gaps_s" :class="$style.rules">
+						<li v-for="item in instance.serverRules" :key="item" :class="$style.rule">
+							<div :class="$style.ruleText" v-html="item"></div>
+						</li>
+					</ol>
+				</MkFolder>
+				<FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>
+					<template #icon><i class="ti ti-license"></i></template>
+					<template #default>{{ i18n.ts.termsOfService }}</template>
+				</FormLink>
+				<FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external>
+					<template #icon><i class="ti ti-shield-lock"></i></template>
+					<template #default>{{ i18n.ts.privacyPolicy }}</template>
+				</FormLink>
+				<FormLink v-if="instance.feedbackUrl" :to="instance.feedbackUrl" external>
+					<template #icon><i class="ti ti-message"></i></template>
+					<template #default>{{ i18n.ts.feedback }}</template>
+				</FormLink>
+			</div>
+		</div>
+	</FormSection>
+
+	<FormSuspense v-slot="{ result: stats }" :p="initStats">
+		<FormSection>
+			<template #label>{{ i18n.ts.statistics }}</template>
+			<FormSplit>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.users }}</template>
+					<template #value>{{ number(stats.originalUsersCount) }}</template>
+				</MkKeyValue>
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.notes }}</template>
+					<template #value>{{ number(stats.originalNotesCount) }}</template>
+				</MkKeyValue>
+			</FormSplit>
+		</FormSection>
+	</FormSuspense>
+
+	<FormSection>
+		<template #label>Well-known resources</template>
+		<div class="_gaps_s">
+			<FormLink to="/.well-known/host-meta" external>host-meta</FormLink>
+			<FormLink to="/.well-known/host-meta.json" external>host-meta.json</FormLink>
+			<FormLink to="/.well-known/nodeinfo" external>nodeinfo</FormLink>
+			<FormLink to="/robots.txt" external>robots.txt</FormLink>
+			<FormLink to="/manifest.json" external>manifest.json</FormLink>
+		</div>
+	</FormSection>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { host, version } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import number from '@/filters/number.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import FormLink from '@/components/form/link.vue';
+import FormSection from '@/components/form/section.vue';
+import FormSplit from '@/components/form/split.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import MkLink from '@/components/MkLink.vue';
+
+const initStats = () => misskeyApi('stats', {});
+</script>
+
+<style lang="scss" module>
+.banner {
+	text-align: center;
+	border-radius: 10px;
+	overflow: clip;
+	background-color: var(--panel);
+	background-size: cover;
+	background-position: center center;
+}
+
+.bannerIcon {
+	display: block;
+	margin: 16px auto 0 auto;
+	height: 64px;
+	border-radius: 8px;
+}
+
+.bannerName {
+	display: block;
+	padding: 16px;
+	color: #fff;
+	text-shadow: 0 0 8px #000;
+	background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
+}
+
+.rules {
+	counter-reset: item;
+	list-style: none;
+	padding: 0;
+	margin: 0;
+}
+
+.rule {
+	display: flex;
+	gap: 8px;
+	word-break: break-word;
+
+	&::before {
+		flex-shrink: 0;
+		display: flex;
+		position: sticky;
+		top: calc(var(--stickyTop, 0px) + 8px);
+		counter-increment: item;
+		content: counter(item);
+		width: 32px;
+		height: 32px;
+		line-height: 32px;
+		background-color: var(--accentedBg);
+		color: var(--accent);
+		font-size: 13px;
+		font-weight: bold;
+		align-items: center;
+		justify-content: center;
+		border-radius: 999px;
+	}
+}
+
+.ruleText {
+	padding-top: 6px;
+}
+</style>
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index 324d1c11de..8dfeb6d2a7 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -8,113 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
 	<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
 		<MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20">
-			<div class="_gaps_m">
-				<div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }">
-					<div style="overflow: clip;">
-						<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/>
-						<div :class="$style.bannerName">
-							<b>{{ instance.name ?? host }}</b>
-						</div>
-					</div>
-				</div>
-
-				<MkKeyValue>
-					<template #key>{{ i18n.ts.description }}</template>
-					<template #value><div v-html="instance.description"></div></template>
-				</MkKeyValue>
-
-				<FormSection>
-					<div class="_gaps_m">
-						<MkKeyValue :copy="version">
-							<template #key>Misskey</template>
-							<template #value>{{ version }}</template>
-						</MkKeyValue>
-						<div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })">
-						</div>
-						<FormLink to="/about-misskey">
-							<template #icon><i class="ti ti-info-circle"></i></template>
-							{{ i18n.ts.aboutMisskey }}
-						</FormLink>
-						<FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external>
-							<template #icon><i class="ti ti-code"></i></template>
-							{{ i18n.ts.sourceCode }}
-						</FormLink>
-						<MkInfo v-else warn>
-							{{ i18n.ts.sourceCodeIsNotYetProvided }}
-						</MkInfo>
-					</div>
-				</FormSection>
-
-				<FormSection>
-					<div class="_gaps_m">
-						<FormSplit>
-							<MkKeyValue>
-								<template #key>{{ i18n.ts.administrator }}</template>
-								<template #value>{{ instance.maintainerName }}</template>
-							</MkKeyValue>
-							<MkKeyValue>
-								<template #key>{{ i18n.ts.contact }}</template>
-								<template #value>{{ instance.maintainerEmail }}</template>
-							</MkKeyValue>
-						</FormSplit>
-						<FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>
-							<template #icon><i class="ti ti-user-shield"></i></template>
-							{{ i18n.ts.impressum }}
-						</FormLink>
-						<div class="_gaps_s">
-							<MkFolder v-if="instance.serverRules.length > 0">
-								<template #label>
-									<i class="ti ti-checkup-list"></i>
-									{{ i18n.ts.serverRules }}
-								</template>
-
-								<ol class="_gaps_s" :class="$style.rules">
-									<li v-for="(item, index) in instance.serverRules" :key="index" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li>
-								</ol>
-							</MkFolder>
-							<FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>
-								<template #icon><i class="ti ti-license"></i></template>
-								{{ i18n.ts.termsOfService }}
-							</FormLink>
-							<FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external>
-								<template #icon><i class="ti ti-shield-lock"></i></template>
-								{{ i18n.ts.privacyPolicy }}
-							</FormLink>
-							<FormLink v-if="instance.feedbackUrl" :to="instance.feedbackUrl" external>
-								<template #icon><i class="ti ti-message"></i></template>
-								{{ i18n.ts.feedback }}
-							</FormLink>
-						</div>
-					</div>
-				</FormSection>
-
-				<FormSuspense :p="initStats">
-					<FormSection>
-						<template #label>{{ i18n.ts.statistics }}</template>
-						<FormSplit>
-							<MkKeyValue>
-								<template #key>{{ i18n.ts.users }}</template>
-								<template #value>{{ number(stats.originalUsersCount) }}</template>
-							</MkKeyValue>
-							<MkKeyValue>
-								<template #key>{{ i18n.ts.notes }}</template>
-								<template #value>{{ number(stats.originalNotesCount) }}</template>
-							</MkKeyValue>
-						</FormSplit>
-					</FormSection>
-				</FormSuspense>
-
-				<FormSection>
-					<template #label>Well-known resources</template>
-					<div class="_gaps_s">
-						<FormLink :to="`/.well-known/host-meta`" external>host-meta</FormLink>
-						<FormLink :to="`/.well-known/host-meta.json`" external>host-meta.json</FormLink>
-						<FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink>
-						<FormLink :to="`/robots.txt`" external>robots.txt</FormLink>
-						<FormLink :to="`/manifest.json`" external>manifest.json</FormLink>
-					</div>
-				</FormSection>
-			</div>
+			<XOverview/>
 		</MkSpacer>
 		<MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20">
 			<XEmojis/>
@@ -130,26 +24,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch, ref } from 'vue';
-import * as Misskey from 'misskey-js';
-import XEmojis from './about.emojis.vue';
-import XFederation from './about.federation.vue';
-import { version, host } from '@/config.js';
-import FormLink from '@/components/form/link.vue';
-import FormSection from '@/components/form/section.vue';
-import FormSuspense from '@/components/form/suspense.vue';
-import FormSplit from '@/components/form/split.vue';
-import MkFolder from '@/components/MkFolder.vue';
-import MkKeyValue from '@/components/MkKeyValue.vue';
-import MkInfo from '@/components/MkInfo.vue';
-import MkInstanceStats from '@/components/MkInstanceStats.vue';
-import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
-import { misskeyApi } from '@/scripts/misskey-api.js';
-import number from '@/filters/number.js';
+import { computed, defineAsyncComponent, ref, watch } from 'vue';
 import { i18n } from '@/i18n.js';
-import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { claimAchievement } from '@/scripts/achievements.js';
-import { instance } from '@/instance.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
+
+const XOverview = defineAsyncComponent(() => import('@/pages/about.overview.vue'));
+const XEmojis = defineAsyncComponent(() => import('@/pages/about.emojis.vue'));
+const XFederation = defineAsyncComponent(() => import('@/pages/about.federation.vue'));
+const MkInstanceStats = defineAsyncComponent(() => import('@/components/MkInstanceStats.vue'));
 
 const props = withDefaults(defineProps<{
 	initialTab?: string;
@@ -157,7 +41,6 @@ const props = withDefaults(defineProps<{
 	initialTab: 'overview',
 });
 
-const stats = ref<Misskey.entities.StatsResponse | null>(null);
 const tab = ref(props.initialTab);
 
 watch(tab, () => {
@@ -166,11 +49,6 @@ watch(tab, () => {
 	}
 });
 
-const initStats = () => misskeyApi('stats', {
-}).then((res) => {
-	stats.value = res;
-});
-
 const headerActions = computed(() => []);
 
 const headerTabs = computed(() => [{
@@ -195,64 +73,3 @@ definePageMetadata(() => ({
 	icon: 'ti ti-info-circle',
 }));
 </script>
-
-<style lang="scss" module>
-.banner {
-	text-align: center;
-	border-radius: 10px;
-	overflow: clip;
-	background-size: cover;
-	background-position: center center;
-}
-
-.bannerIcon {
-	display: block;
-	margin: 16px auto 0 auto;
-	height: 64px;
-	border-radius: 8px;
-}
-
-.bannerName {
-	display: block;
-	padding: 16px;
-	color: #fff;
-	text-shadow: 0 0 8px #000;
-	background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
-}
-
-.rules {
-	counter-reset: item;
-	list-style: none;
-	padding: 0;
-	margin: 0;
-}
-
-.rule {
-	display: flex;
-	gap: 8px;
-	word-break: break-word;
-
-	&::before {
-		flex-shrink: 0;
-		display: flex;
-		position: sticky;
-		top: calc(var(--stickyTop, 0px) + 8px);
-		counter-increment: item;
-		content: counter(item);
-		width: 32px;
-		height: 32px;
-		line-height: 32px;
-		background-color: var(--accentedBg);
-		color: var(--accent);
-		font-size: 13px;
-		font-weight: bold;
-		align-items: center;
-		justify-content: center;
-		border-radius: 999px;
-	}
-}
-
-.ruleText {
-	padding-top: 6px;
-}
-</style>
diff --git a/packages/frontend/src/pages/contact.vue b/packages/frontend/src/pages/contact.vue
index bcdcf43275..1f2bee5a77 100644
--- a/packages/frontend/src/pages/contact.vue
+++ b/packages/frontend/src/pages/contact.vue
@@ -7,18 +7,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkStickyContainer>
 	<template #header><MkPageHeader/></template>
 	<MkSpacer :contentMax="600" :marginMin="20">
-		<div class="_gaps">
-			<MkKeyValue>
-				<template #key>{{ i18n.ts.inquiry }}</template>
+		<div class="_gaps_m">
+			<MkKeyValue :copy="instance.maintainerName">
+				<template #key>{{ i18n.ts.administrator }}</template>
 				<template #value>
-					<MkLink :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink>
+					<template v-if="instance.maintainerName">{{ instance.maintainerName }}</template>
+					<span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span>
 				</template>
 			</MkKeyValue>
-
-			<MkKeyValue>
-				<template #key>{{ i18n.ts.email }}</template>
+			<MkKeyValue :copy="instance.maintainerEmail">
+				<template #key>{{ i18n.ts.contact }}</template>
 				<template #value>
-					<div>{{ instance.maintainerEmail }}</div>
+					<template v-if="instance.maintainerEmail">{{ instance.maintainerEmail }}</template>
+					<span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span>
+				</template>
+			</MkKeyValue>
+			<MkKeyValue :copy="instance.inquiryUrl">
+				<template #key>{{ i18n.ts.inquiry }}</template>
+				<template #value>
+					<MkLink v-if="instance.inquiryUrl" :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink>
+					<span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span>
 				</template>
 			</MkKeyValue>
 		</div>
@@ -28,8 +36,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { i18n } from '@/i18n.js';
-import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { instance } from '@/instance.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkLink from '@/components/MkLink.vue';
 
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 20a280f681..74c3028745 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -85,29 +85,29 @@ export function openInstanceMenu(ev: MouseEvent) {
 		icon: 'ti ti-help-circle',
 		to: '/contact',
 	}, (instance.impressumUrl) ? {
+		type: 'a',
 		text: i18n.ts.impressum,
 		icon: 'ti ti-file-invoice',
-		action: () => {
-			window.open(instance.impressumUrl, '_blank', 'noopener');
-		},
+		href: instance.impressumUrl,
+		target: '_blank',
 	} : undefined, (instance.tosUrl) ? {
+		type: 'a',
 		text: i18n.ts.termsOfService,
 		icon: 'ti ti-notebook',
-		action: () => {
-			window.open(instance.tosUrl, '_blank', 'noopener');
-		},
+		href: instance.tosUrl,
+		target: '_blank',
 	} : undefined, (instance.privacyPolicyUrl) ? {
+		type: 'a',
 		text: i18n.ts.privacyPolicy,
 		icon: 'ti ti-shield-lock',
-		action: () => {
-			window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
-		},
+		href: instance.privacyPolicyUrl,
+		target: '_blank',
 	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, {
+		type: 'a',
 		text: i18n.ts.document,
 		icon: 'ti ti-bulb',
-		action: () => {
-			window.open('https://misskey-hub.net/docs/for-users/', '_blank', 'noopener');
-		},
+		href: 'https://misskey-hub.net/docs/for-users/',
+		target: '_blank',
 	}, ($i) ? {
 		text: i18n.ts._initialTutorial.launchTutorial,
 		icon: 'ti ti-presentation',

From 3c032dd5b917c97bf8fb1b87a69f36d56537f493 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Jul 2024 15:27:52 +0900
Subject: [PATCH 101/206] =?UTF-8?q?enhance:=20=E9=9D=9E=E3=83=AD=E3=82=B0?=
 =?UTF-8?q?=E3=82=A4=E3=83=B3=E6=99=82=E3=81=AB=E3=81=AF=E5=88=A5=E3=82=B5?=
 =?UTF-8?q?=E3=83=BC=E3=83=90=E3=83=BC=E3=81=AB=E9=81=B7=E7=A7=BB=E3=81=A7?=
 =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13089)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: 非ログイン時にはMisskey Hub経由で別サーバーに遷移できるように

* fix

* サーバーサイド照会を削除

* クライアント側の照会動作

* hubを経由せずにリモートで続行できるように

* fix と pleaseLogin誘導箇所の追加

* fix

* fix

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |  26 +++-
 locales/ja-JP.yml                             |   8 +-
 .../src/components/MkFollowButton.vue         |   6 +-
 packages/frontend/src/components/MkNote.vue   |  14 +-
 .../src/components/MkNoteDetailed.vue         |  14 +-
 packages/frontend/src/components/MkPoll.vue   |   8 +-
 packages/frontend/src/components/MkSignin.vue | 133 +++++++++++++++---
 .../src/components/MkSigninDialog.vue         |   9 +-
 .../frontend/src/components/MkUserInfo.vue    |   2 +-
 packages/frontend/src/os.ts                   |  10 ++
 packages/frontend/src/pages/flash/flash.vue   |   3 +
 packages/frontend/src/pages/follow.vue        |  71 ----------
 packages/frontend/src/pages/lookup.vue        |  97 +++++++++++++
 packages/frontend/src/pages/user/home.vue     |   4 +-
 packages/frontend/src/router/definition.ts    |  12 +-
 .../frontend/src/scripts/get-user-menu.ts     |   4 +-
 packages/frontend/src/scripts/please-login.ts |  16 ++-
 packages/frontend/src/scripts/url.ts          |   5 +
 19 files changed, 330 insertions(+), 113 deletions(-)
 delete mode 100644 packages/frontend/src/pages/follow.vue
 create mode 100644 packages/frontend/src/pages/lookup.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e411e5329..2156c81474 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
 
 ### Client
 - Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
+- Enhance: 非ログイン時に他サーバーに遷移するアクションを追加
 - Enhance: 非ログイン時のハイライトTLのデザインを改善
 - Enhance: フロントエンドのアクセシビリティ改善  
   (Based on https://github.com/taiyme/misskey/pull/226)
diff --git a/locales/index.d.ts b/locales/index.d.ts
index c2f8e944dd..84a402b0de 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -736,6 +736,22 @@ export interface Locale extends ILocale {
      * リモートで表示
      */
     "showOnRemote": string;
+    /**
+     * リモートで続行
+     */
+    "continueOnRemote": string;
+    /**
+     * Misskey Hubからサーバーを選択
+     */
+    "chooseServerOnMisskeyHub": string;
+    /**
+     * サーバーのドメインを直接指定
+     */
+    "specifyServerHost": string;
+    /**
+     * ドメインを入力してください
+     */
+    "inputHostName": string;
     /**
      * 全般
      */
@@ -1921,9 +1937,13 @@ export interface Locale extends ILocale {
      */
     "onlyOneFileCanBeAttached": string;
     /**
-     * 続行する前に、サインアップまたはサインインが必要です
+     * 続行する前に、登録またはログインが必要です
      */
     "signinRequired": string;
+    /**
+     * 続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があります
+     */
+    "signinOrContinueOnRemote": string;
     /**
      * 招待
      */
@@ -4984,6 +5004,10 @@ export interface Locale extends ILocale {
      * お問い合わせ
      */
     "inquiry": string;
+     /**
+     * もう一度お試しください。
+     */
+    "tryAgain": string;
     "_delivery": {
         /**
          * 配信状態
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 8d117e6dc8..bb3999f0e3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -180,6 +180,10 @@ addAccount: "アカウントを追加"
 reloadAccountsList: "アカウントリストの情報を更新"
 loginFailed: "ログインに失敗しました"
 showOnRemote: "リモートで表示"
+continueOnRemote: "リモートで続行"
+chooseServerOnMisskeyHub: "Misskey Hubからサーバーを選択"
+specifyServerHost: "サーバーのドメインを直接指定"
+inputHostName: "ドメインを入力してください"
 general: "全般"
 wallpaper: "壁紙"
 setWallpaper: "壁紙を設定"
@@ -476,7 +480,8 @@ attachAsFileQuestion: "クリップボードのテキストが長いです。テ
 noMessagesYet: "まだチャットはありません"
 newMessageExists: "新しいメッセージがあります"
 onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
-signinRequired: "続行する前に、サインアップまたはサインインが必要です"
+signinRequired: "続行する前に、登録またはログインが必要です"
+signinOrContinueOnRemote: "続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があります"
 invitations: "招待"
 invitationCode: "招待コード"
 checking: "確認しています"
@@ -1242,6 +1247,7 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ
 noDescription: "説明文はありません"
 alwaysConfirmFollow: "フォローの際常に確認する"
 inquiry: "お問い合わせ"
+tryAgain: "もう一度お試しください。"
 
 _delivery:
   status: "配信状態"
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index ea76950c0d..d8ac8024b4 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -42,6 +42,8 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 import { claimAchievement } from '@/scripts/achievements.js';
+import { pleaseLogin } from '@/scripts/please-login.js';
+import { host } from '@/config.js';
 import { $i } from '@/account.js';
 import { defaultStore } from '@/store.js';
 
@@ -63,7 +65,7 @@ const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFro
 const wait = ref(false);
 const connection = useStream().useChannel('main');
 
-if (props.user.isFollowing == null) {
+if (props.user.isFollowing == null && $i) {
 	misskeyApi('users/show', {
 		userId: props.user.id,
 	})
@@ -78,6 +80,8 @@ function onFollowChange(user: Misskey.entities.UserDetailed) {
 }
 
 async function onClick() {
+	pleaseLogin(undefined, { type: 'web', path: `/@${props.user.username}@${props.user.host ?? host}` });
+
 	wait.value = true;
 
 	try {
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 420ff2c651..c518c7dd41 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -196,6 +196,7 @@ import { MenuItem } from '@/types/menu.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import { shouldCollapsed } from '@/scripts/collapsed.js';
+import { host } from '@/config.js';
 import { isEnabledUrlPreview } from '@/instance.js';
 import { type Keymap } from '@/scripts/hotkey.js';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
@@ -278,6 +279,11 @@ const renoteCollapsed = ref(
 	),
 );
 
+const pleaseLoginContext = {
+	type: 'lookup',
+	path: `https://${host}/notes/${appearNote.value.id}`,
+} as const;
+
 /* Overload FunctionにLintが対応していないのでコメントアウト
 function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
 function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute';
@@ -411,7 +417,7 @@ if (!props.mock) {
 }
 
 function renote(viaKeyboard = false) {
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 	showMovedDialog();
 
 	const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock });
@@ -421,7 +427,7 @@ function renote(viaKeyboard = false) {
 }
 
 function reply(): void {
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 	if (props.mock) {
 		return;
 	}
@@ -434,7 +440,7 @@ function reply(): void {
 }
 
 function react(): void {
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 	showMovedDialog();
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
 		sound.playMisskeySfx('reaction');
@@ -565,7 +571,7 @@ function showRenoteMenu(): void {
 	}
 
 	if (isMyRenote) {
-		pleaseLogin();
+		pleaseLogin(undefined, pleaseLoginContext);
 		os.popupMenu([
 			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
 			{ type: 'divider' },
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index a8fed56c39..737e9a853a 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -222,6 +222,7 @@ import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
 import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
+import { host } from '@/config.js';
 import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/scripts/get-note-menu.js';
 import { useNoteCapture } from '@/scripts/use-note-capture.js';
 import { deepClone } from '@/scripts/clone.js';
@@ -296,6 +297,11 @@ const conversation = ref<Misskey.entities.Note[]>([]);
 const replies = ref<Misskey.entities.Note[]>([]);
 const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i?.id);
 
+const pleaseLoginContext = {
+	type: 'lookup',
+	path: `https://${host}/notes/${appearNote.value.id}`,
+} as const;
+
 const keymap = {
 	'r': () => reply(),
 	'e|a|plus': () => react(),
@@ -396,7 +402,7 @@ if (appearNote.value.reactionAcceptance === 'likeOnly') {
 }
 
 function renote() {
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 	showMovedDialog();
 
 	const { menu } = getRenoteMenu({ note: note.value, renoteButton });
@@ -404,7 +410,7 @@ function renote() {
 }
 
 function reply(): void {
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 	showMovedDialog();
 	os.post({
 		reply: appearNote.value,
@@ -415,7 +421,7 @@ function reply(): void {
 }
 
 function react(): void {
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 	showMovedDialog();
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
 		sound.playMisskeySfx('reaction');
@@ -499,7 +505,7 @@ async function clip(): Promise<void> {
 
 function showRenoteMenu(): void {
 	if (!isMyRenote) return;
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 	os.popupMenu([{
 		text: i18n.ts.unrenote,
 		icon: 'ti ti-trash',
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index a98690f1c3..82e2a605f1 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -34,6 +34,7 @@ import { pleaseLogin } from '@/scripts/please-login.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
+import { host } from '@/config.js';
 import { useInterval } from '@/scripts/use-interval.js';
 
 const props = defineProps<{
@@ -60,6 +61,11 @@ const timer = computed(() => i18n.tsx._poll[
 
 const showResult = ref(props.readOnly || isVoted.value);
 
+const pleaseLoginContext = {
+	type: 'lookup',
+	path: `https://${host}/notes/${props.note.id}`,
+} as const;
+
 // 期限付きアンケート
 if (props.poll.expiresAt) {
 	const tick = () => {
@@ -76,7 +82,7 @@ if (props.poll.expiresAt) {
 }
 
 const vote = async (id) => {
-	pleaseLogin();
+	pleaseLogin(undefined, pleaseLoginContext);
 
 	if (props.readOnly || closed.value || isVoted.value) return;
 
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index db32cdd6a1..746ddd7154 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -6,10 +6,23 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <form :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
 	<div class="_gaps_m">
-		<div v-show="withAvatar" :class="$style.avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : undefined, marginBottom: message ? '1.5em' : undefined }"></div>
+		<div v-show="withAvatar" :class="$style.avatar" :style="{ backgroundImage: user ? `url('${user.avatarUrl}')` : undefined, marginBottom: message ? '1.5em' : undefined }"></div>
 		<MkInfo v-if="message">
 			{{ message }}
 		</MkInfo>
+		<div v-if="openOnRemote" class="_gaps_m">
+			<div class="_gaps_s">
+				<MkButton type="button" rounded primary style="margin: 0 auto;" @click="openRemote(openOnRemote)">
+					{{ i18n.ts.continueOnRemote }} <i class="ti ti-external-link"></i>
+				</MkButton>
+				<button type="button" class="_button" :class="$style.instanceManualSelectButton" @click="specifyHostAndOpenRemote(openOnRemote)">
+					{{ i18n.ts.specifyServerHost }}
+				</button>
+			</div>
+			<div :class="$style.orHr">
+				<p :class="$style.orMsg">{{ i18n.ts.or }}</p>
+			</div>
+		</div>
 		<div v-if="!totpLogin" class="normal-signin _gaps_m">
 			<MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username webauthn" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
 				<template #prefix>@</template>
@@ -28,8 +41,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 					{{ i18n.ts.retry }}
 				</MkButton>
 			</div>
-			<div v-if="user && user.securityKeys" class="or-hr">
-				<p class="or-msg">{{ i18n.ts.or }}</p>
+			<div v-if="user && user.securityKeys" :class="$style.orHr">
+				<p :class="$style.orMsg">{{ i18n.ts.or }}</p>
 			</div>
 			<div class="twofa-group totp-group _gaps">
 				<MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" autocomplete="current-password" :withPasswordToggle="true" required>
@@ -53,6 +66,7 @@ import { defineAsyncComponent, ref } from 'vue';
 import { toUnicode } from 'punycode/';
 import * as Misskey from 'misskey-js';
 import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
+import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
 import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -60,6 +74,7 @@ import MkInfo from '@/components/MkInfo.vue';
 import { host as configHost } from '@/config.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
+import { query, extractDomain } from '@/scripts/url.js';
 import { login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 
@@ -78,22 +93,16 @@ const emit = defineEmits<{
 	(ev: 'login', v: any): void;
 }>();
 
-const props = defineProps({
-	withAvatar: {
-		type: Boolean,
-		required: false,
-		default: true,
-	},
-	autoSet: {
-		type: Boolean,
-		required: false,
-		default: false,
-	},
-	message: {
-		type: String,
-		required: false,
-		default: '',
-	},
+const props = withDefaults(defineProps<{
+	withAvatar?: boolean;
+	autoSet?: boolean;
+	message?: string,
+	openOnRemote?: OpenOnRemoteOptions,
+}>(), {
+	withAvatar: true,
+	autoSet: false,
+	message: '',
+	openOnRemote: undefined,
 });
 
 function onUsernameChange(): void {
@@ -222,6 +231,60 @@ function resetPassword(): void {
 		closed: () => dispose(),
 	});
 }
+
+function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
+	switch (options.type) {
+		case 'web':
+		case 'lookup': {
+			let _path = options.path;
+
+			if (options.type === 'lookup') {
+				// TODO: v2024.2.0以降が浸透してきたら正式なURLに変更する▼
+				// _path = `/lookup?uri=${encodeURIComponent(_path)}`;
+				_path = `/authorize-follow?acct=${encodeURIComponent(_path)}`;
+			}
+
+			if (targetHost) {
+				window.open(`https://${targetHost}${_path}`, '_blank', 'noopener');
+			} else {
+				window.open(`https://misskey-hub.net/mi-web/?path=${encodeURIComponent(_path)}`, '_blank', 'noopener');
+			}
+			break;
+		}
+		case 'share': {
+			const params = query(options.params);			
+			if (targetHost) {
+				window.open(`https://${targetHost}/share?${params}`, '_blank', 'noopener');
+			} else {
+				window.open(`https://misskey-hub.net/share/?${params}`, '_blank', 'noopener');
+			}
+			break;
+		}
+	}
+}
+
+async function specifyHostAndOpenRemote(options: OpenOnRemoteOptions): Promise<void> {
+	const { canceled, result: hostTemp } = await os.inputText({
+		title: i18n.ts.inputHostName,
+		placeholder: 'misskey.example.com',
+	});
+
+	if (canceled) return;
+
+	let targetHost: string | null = hostTemp;
+
+	// ドメイン部分だけを取り出す
+	targetHost = extractDomain(targetHost);
+	if (targetHost == null) {
+		os.alert({
+			type: 'error',
+			title: i18n.ts.invalidValue,
+			text: i18n.ts.tryAgain,
+		});
+		return;
+	}
+	openRemote(options, targetHost);
+}
 </script>
 
 <style lang="scss" module>
@@ -234,4 +297,36 @@ function resetPassword(): void {
 	background-size: cover;
 	border-radius: 100%;
 }
+
+.instanceManualSelectButton {
+	display: block;
+	text-align: center;
+	opacity: .7;
+	font-size: .8em;
+
+	&:hover {
+		text-decoration: underline;
+	}
+}
+
+.orHr {
+	position: relative;
+	margin: .4em auto;
+	width: 100%;
+	height: 1px;
+	background: var(--divider);
+}
+
+.orMsg {
+	position: absolute;
+	top: -.6em;
+	display: inline-block;
+	padding: 0 1em;
+	background: var(--panel);
+	font-size: 0.8em;
+	color: var(--fgOnPanel);
+	margin: 0;
+	left: 50%;
+	transform: translateX(-50%);
+}
 </style>
diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue
index 33355bb99e..524c62b4d3 100644
--- a/packages/frontend/src/components/MkSigninDialog.vue
+++ b/packages/frontend/src/components/MkSigninDialog.vue
@@ -6,21 +6,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <MkModalWindow
 	ref="dialog"
-	:width="370"
-	:height="400"
+	:width="400"
+	:height="430"
 	@close="onClose"
 	@closed="emit('closed')"
 >
 	<template #header>{{ i18n.ts.login }}</template>
 
 	<MkSpacer :marginMin="20" :marginMax="28">
-		<MkSignin :autoSet="autoSet" :message="message" @login="onLogin"/>
+		<MkSignin :autoSet="autoSet" :message="message" :openOnRemote="openOnRemote" @login="onLogin"/>
 	</MkSpacer>
 </MkModalWindow>
 </template>
 
 <script lang="ts" setup>
 import { shallowRef } from 'vue';
+import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
 import MkSignin from '@/components/MkSignin.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { i18n } from '@/i18n.js';
@@ -28,9 +29,11 @@ import { i18n } from '@/i18n.js';
 withDefaults(defineProps<{
 	autoSet?: boolean;
 	message?: string,
+	openOnRemote?: OpenOnRemoteOptions,
 }>(), {
 	autoSet: false,
 	message: '',
+	openOnRemote: undefined,
 });
 
 const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index f247ba8fdd..d6f1ae453c 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<p :class="$style.statusItemLabel">{{ i18n.ts.followers }}</p><span :class="$style.statusItemValue">{{ number(user.followersCount) }}</span>
 		</div>
 	</div>
-	<MkFollowButton v-if="$i && user.id != $i.id" :class="$style.follow" :user="user" mini/>
+	<MkFollowButton v-if="user.id != $i?.id" :class="$style.follow" :user="user" mini/>
 </div>
 </template>
 
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 5e332533ef..a0b0e6c833 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -23,6 +23,7 @@ import MkPopupMenu from '@/components/MkPopupMenu.vue';
 import MkContextMenu from '@/components/MkContextMenu.vue';
 import { MenuItem } from '@/types/menu.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { pleaseLogin } from '@/scripts/please-login.js';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
 import { focusParent } from '@/scripts/focus.js';
@@ -670,6 +671,15 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
 }
 
 export function post(props: Record<string, any> = {}): Promise<void> {
+	pleaseLogin(undefined, (props.initialText || props.initialNote ? {
+		type: 'share',
+		params: {
+			text: props.initialText ?? props.initialNote.text,
+			visibility: props.initialVisibility ?? props.initialNote?.visibility,
+			localOnly: (props.initialLocalOnly || props.initialNote?.localOnly) ? '1' : '0',
+		},
+	} : undefined));
+
 	showMovedDialog();
 	return new Promise(resolve => {
 		// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 40499fde0e..8a63176d00 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -79,6 +79,7 @@ import { defaultStore } from '@/store.js';
 import { $i } from '@/account.js';
 import { isSupportShare } from '@/scripts/navigator.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { pleaseLogin } from '@/scripts/please-login.js';
 
 const props = defineProps<{
 	id: string;
@@ -143,6 +144,7 @@ function shareWithNote() {
 
 function like() {
 	if (!flash.value) return;
+	pleaseLogin();
 
 	os.apiWithDialog('flash/like', {
 		flashId: flash.value.id,
@@ -154,6 +156,7 @@ function like() {
 
 async function unlike() {
 	if (!flash.value) return;
+	pleaseLogin();
 
 	const confirm = await os.confirm({
 		type: 'warning',
diff --git a/packages/frontend/src/pages/follow.vue b/packages/frontend/src/pages/follow.vue
deleted file mode 100644
index 247b0ac639..0000000000
--- a/packages/frontend/src/pages/follow.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and misskey-project
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { } from 'vue';
-import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
-import { i18n } from '@/i18n.js';
-import { defaultStore } from '@/store.js';
-import { mainRouter } from '@/router/main.js';
-
-async function follow(user): Promise<void> {
-	const { canceled } = await os.confirm({
-		type: 'question',
-		text: i18n.tsx.followConfirm({ name: user.name || user.username }),
-	});
-
-	if (canceled) {
-		window.close();
-		return;
-	}
-
-	os.apiWithDialog('following/create', {
-		userId: user.id,
-		withReplies: defaultStore.state.defaultWithReplies,
-	});
-	user.withReplies = defaultStore.state.defaultWithReplies;
-}
-
-const acct = new URL(location.href).searchParams.get('acct');
-if (acct == null) {
-	throw new Error('acct required');
-}
-
-let promise;
-
-if (acct.startsWith('https://')) {
-	promise = misskeyApi('ap/show', {
-		uri: acct,
-	});
-	promise.then(res => {
-		if (res.type === 'User') {
-			follow(res.object);
-		} else if (res.type === 'Note') {
-			mainRouter.push(`/notes/${res.object.id}`);
-		} else {
-			os.alert({
-				type: 'error',
-				text: 'Not a user',
-			}).then(() => {
-				window.close();
-			});
-		}
-	});
-} else {
-	promise = misskeyApi('users/show', Misskey.acct.parse(acct));
-	promise.then(user => {
-		follow(user);
-	});
-}
-
-os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
-</script>
diff --git a/packages/frontend/src/pages/lookup.vue b/packages/frontend/src/pages/lookup.vue
new file mode 100644
index 0000000000..3233953942
--- /dev/null
+++ b/packages/frontend/src/pages/lookup.vue
@@ -0,0 +1,97 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
+	<MkSpacer :contentMax="800">
+		<div v-if="state === 'done'" class="_buttonsCenter">
+			<MkButton @click="close">{{ i18n.ts.close }}</MkButton>
+			<MkButton @click="goToMisskey">{{ i18n.ts.goToMisskey }}</MkButton>
+		</div>
+		<div v-else class="_fullInfo">
+			<MkLoading/>
+		</div>
+	</MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
+import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { mainRouter } from '@/router/main.js';
+import MkButton from '@/components/MkButton.vue';
+
+const state = ref<'fetching' | 'done'>('fetching');
+
+function fetch() {
+	const params = new URL(location.href).searchParams;
+
+	// acctのほうはdeprecated
+	let uri = params.get('uri') ?? params.get('acct');
+	if (uri == null) {
+		state.value = 'done';
+		return;
+	}
+
+	let promise: Promise<any>;
+
+	if (uri.startsWith('https://')) {
+		promise = misskeyApi('ap/show', {
+			uri,
+		});
+		promise.then(res => {
+			if (res.type === 'User') {
+				mainRouter.replace(res.object.host ? `/@${res.object.username}@${res.object.host}` : `/@${res.object.username}`);
+			} else if (res.type === 'Note') {
+				mainRouter.replace(`/notes/${res.object.id}`);
+			} else {
+				os.alert({
+					type: 'error',
+					text: 'Not a user',
+				});
+			}
+		});
+	} else {
+		if (uri.startsWith('acct:')) {
+			uri = uri.slice(5);
+		}
+		promise = misskeyApi('users/show', Misskey.acct.parse(uri));
+		promise.then(user => {
+			mainRouter.replace(user.host ? `/@${user.username}@${user.host}` : `/@${user.username}`);
+		});
+	}
+
+	os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
+}
+
+function close(): void {
+	window.close();
+
+	// 閉じなければ100ms後タイムラインに
+	window.setTimeout(() => {
+		location.href = '/';
+	}, 100);
+}
+
+function goToMisskey(): void {
+	location.href = '/';
+}
+
+fetch();
+
+const headerActions = computed(() => []);
+
+const headerTabs = computed(() => []);
+
+definePageMetadata({
+	title: i18n.ts.lookup,
+	icon: 'ti ti-world-search',
+});
+</script>
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 834d799072..d67990e9a2 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -32,9 +32,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</div>
 						</div>
 						<span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span>
-						<div v-if="$i" class="actions">
+						<div class="actions">
 							<button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button>
-							<MkFollowButton v-if="$i.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
+							<MkFollowButton v-if="$i?.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
 						</div>
 					</div>
 					<MkAvatar class="avatar" :user="user" indicator/>
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index 12ab633af1..f7a219c57e 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -237,8 +237,18 @@ const routes: RouteDef[] = [{
 		origin: 'origin',
 	},
 }, {
+	// Legacy Compatibility	
 	path: '/authorize-follow',
-	component: page(() => import('@/pages/follow.vue')),
+	redirect: '/lookup',
+	loginRequired: true,
+}, {
+	// Mastodon Compatibility
+	path: '/authorize_interaction',
+	redirect: '/lookup',
+	loginRequired: true,
+}, {
+	path: '/lookup',
+	component: page(() => import('@/pages/lookup.vue')),
 	loginRequired: true,
 }, {
 	path: '/share',
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index ac8774fad0..2d1fea8ea4 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -186,7 +186,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 			const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`;
 			copyToClipboard(`${url}/${canonical}`);
 		},
-	}, {
+	}, ...($i ? [{
 		icon: 'ti ti-mail',
 		text: i18n.ts.sendMessage,
 		action: () => {
@@ -259,7 +259,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 				},
 			}));
 		},
-	}] as any;
+	}] : [])] as any;
 
 	if ($i && meId !== user.id) {
 		if (iAmModerator) {
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index 363da5f633..b04062a58a 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -8,12 +8,24 @@ import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { popup } from '@/os.js';
 
-export function pleaseLogin(path?: string) {
+export type OpenOnRemoteOptions = {
+	type: 'web';
+	path: string;
+} | {
+	type: 'lookup';
+	path: string;	
+} | {
+	type: 'share';
+	params: Record<string, string>;
+};
+
+export function pleaseLogin(path?: string, openOnRemote?: OpenOnRemoteOptions) {
 	if ($i) return;
 
 	const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {
 		autoSet: true,
-		message: i18n.ts.signinRequired,
+		message: openOnRemote ? i18n.ts.signinOrContinueOnRemote : i18n.ts.signinRequired,
+		openOnRemote,
 	}, {
 		cancelled: () => {
 			if (path) {
diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts
index e3072b3b7d..c477fb5506 100644
--- a/packages/frontend/src/scripts/url.ts
+++ b/packages/frontend/src/scripts/url.ts
@@ -21,3 +21,8 @@ export function query(obj: Record<string, any>): string {
 export function appendQuery(url: string, query: string): string {
 	return `${url}${/\?/.test(url) ? url.endsWith('?') ? '' : '&' : '?'}${query}`;
 }
+
+export function extractDomain(url: string) {
+	const match = url.match(/^(https)?:?\/{0,2}([^\/]+)/);
+	return match ? match[2] : null;
+}

From 76181385d2a8397c7df68c93f6ce9e5c293fc6b3 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sun, 14 Jul 2024 15:52:43 +0900
Subject: [PATCH 102/206] refactor(misskey-js): enable
 exactOptionalPropertyTypes (#14203)

* refactor(misskey-js): enable exactOptionalPropertyTypes

* refactor(misskey-js): fix error where is appeared by enabling
---
 packages/misskey-js/src/streaming.ts | 4 +++-
 packages/misskey-js/tsconfig.json    | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/packages/misskey-js/src/streaming.ts b/packages/misskey-js/src/streaming.ts
index 0f26857782..83930e621c 100644
--- a/packages/misskey-js/src/streaming.ts
+++ b/packages/misskey-js/src/streaming.ts
@@ -291,7 +291,9 @@ export abstract class Connection<Channel extends AnyOf<Channels> = any> extends
 
 		this.stream = stream;
 		this.channel = channel;
-		this.name = name;
+		if (name !== undefined) {
+			this.name = name;
+		}
 	}
 
 	public send<T extends keyof Channel['receives']>(type: T, body: Channel['receives'][T]): void {
diff --git a/packages/misskey-js/tsconfig.json b/packages/misskey-js/tsconfig.json
index 6e34e332e0..f7bbc47304 100644
--- a/packages/misskey-js/tsconfig.json
+++ b/packages/misskey-js/tsconfig.json
@@ -15,6 +15,7 @@
 		"experimentalDecorators": true,
 		"noImplicitReturns": true,
 		"esModuleInterop": true,
+		"exactOptionalPropertyTypes": true,
 		"typeRoots": [
 			"./node_modules/@types"
 		],

From b9f3fccfac6818c1c25ce06c0d63f7d61ce6cbbb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Jul 2024 16:21:59 +0900
Subject: [PATCH 103/206] =?UTF-8?q?fix(frontend):=20Nested=20Route?=
 =?UTF-8?q?=E3=81=AE=E3=81=A8=E3=81=8D=E3=81=ABRouterView=E3=81=AB?=
 =?UTF-8?q?=E5=BD=93=E3=81=9F=E3=82=8B=E3=82=AD=E3=83=BC=E3=81=8C=E3=83=AB?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AEpath=E3=81=A8=E3=81=B6=E3=81=A1?=
 =?UTF-8?q?=E5=BD=93=E3=81=9F=E3=82=8B=E5=8F=AF=E8=83=BD=E6=80=A7=E3=81=8C?=
 =?UTF-8?q?=E3=81=82=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1420?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 packages/frontend/src/components/global/RouterView.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 06cb30eff1..02a2edee3f 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -60,7 +60,7 @@ function onChange({ resolved, key: newKey }) {
 	if (current == null || 'redirect' in current.route) return;
 	currentPageComponent.value = current.route.component;
 	currentPageProps.value = current.props;
-	key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props));
+	key.value = newKey + JSON.stringify(Object.fromEntries(current.props));
 
 	nextTick(() => {
 		// ページ遷移完了後に再びキャッシュを有効化

From 09d30fef5b274c988e98e52f474afe11ff843111 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sun, 14 Jul 2024 17:27:27 +0900
Subject: [PATCH 104/206] =?UTF-8?q?ci:=20=E3=83=AF=E3=83=BC=E3=82=AF?=
 =?UTF-8?q?=E3=83=95=E3=83=AD=E3=83=BC=E3=81=8C=E6=9B=B4=E6=96=B0=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=AB=E3=82=82=E3=83=AF?=
 =?UTF-8?q?=E3=83=BC=E3=82=AF=E3=83=95=E3=83=AD=E3=83=BC=E3=81=8C=E8=B5=B7?=
 =?UTF-8?q?=E5=8B=95=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?=
 =?UTF-8?q?=E3=82=8B=20(#14207)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ci: include themselves in `on.push.paths`
command: find .github/workflows -type f \( -name '*.yaml' -or -name '*.yml' \) | xargs -I {} yq_4.44.2-linux_x86-64 'select(.on.push.paths != null) | .[0] | map("{}")[0]' {} | xargs -I {} ~/.local/bin/yq_4.44.2-linux_x86-64 -i '.on.push.paths += ["{}"]' {}

* ci: include themselves in `on.pull_request.paths`
command: find .github/workflows -type f \( -name '*.yaml' -or -name '*.yml' \) | xargs -I {} yq_4.44.2-linux_x86-64 'select(.on.pull_request.paths != null) | .[0] | map("{}")[0]' {} | xargs -I {} ~/.local/bin/yq_4.44.2-linux_x86-64 -i '.on.pull_request.paths += ["{}"]' {}
---
 .github/workflows/api-misskey-js.yml           | 3 ++-
 .github/workflows/check-misskey-js-version.yml | 3 ++-
 .github/workflows/get-api-diff.yml             | 2 +-
 .github/workflows/lint.yml                     | 3 ++-
 .github/workflows/locale.yml                   | 3 ++-
 .github/workflows/release-edit-with-push.yml   | 2 +-
 .github/workflows/test-backend.yml             | 3 ++-
 .github/workflows/test-frontend.yml            | 4 ++--
 .github/workflows/test-misskey-js.yml          | 3 ++-
 .github/workflows/validate-api-json.yml        | 3 ++-
 10 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml
index 1b7b68b14f..f9385239b0 100644
--- a/.github/workflows/api-misskey-js.yml
+++ b/.github/workflows/api-misskey-js.yml
@@ -4,10 +4,11 @@ on:
   push:
     paths:
       - packages/misskey-js/**
+      - .github/workflows/api-misskey-js.yml
   pull_request:
     paths:
       - packages/misskey-js/**
-
+      - .github/workflows/api-misskey-js.yml
 jobs:
   report:
 
diff --git a/.github/workflows/check-misskey-js-version.yml b/.github/workflows/check-misskey-js-version.yml
index 325a893605..99c29ac974 100644
--- a/.github/workflows/check-misskey-js-version.yml
+++ b/.github/workflows/check-misskey-js-version.yml
@@ -6,12 +6,13 @@ on:
     paths:
       - packages/misskey-js/package.json
       - package.json
+      - .github/workflows/check-misskey-js-version.yml
   pull_request:
     branches: [ develop ]
     paths:
       - packages/misskey-js/package.json
       - package.json
-
+      - .github/workflows/check-misskey-js-version.yml
 jobs:
   check-version:
     # ルートの package.json と packages/misskey-js/package.json のバージョンが一致しているかを確認する
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 9b9c8f11c4..33fa2ccc39 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -9,7 +9,7 @@ on:
     paths:
       - packages/backend/**
       - .github/workflows/get-api-diff.yml
-
+      - .github/workflows/get-api-diff.yml
 jobs:
   get-from-misskey:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 1a1b30168a..cd8ae0b45b 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -11,6 +11,7 @@ on:
       - packages/sw/**
       - packages/misskey-js/**
       - packages/shared/eslint.config.js
+      - .github/workflows/lint.yml
   pull_request:
     paths:
       - packages/backend/**
@@ -18,7 +19,7 @@ on:
       - packages/sw/**
       - packages/misskey-js/**
       - packages/shared/eslint.config.js
-
+      - .github/workflows/lint.yml
 jobs:
   pnpm_install:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/locale.yml b/.github/workflows/locale.yml
index de2247e772..be3764f3e4 100644
--- a/.github/workflows/locale.yml
+++ b/.github/workflows/locale.yml
@@ -4,10 +4,11 @@ on:
   push:
     paths:
       - locales/**
+      - .github/workflows/locale.yml
   pull_request:
     paths:
       - locales/**
-
+      - .github/workflows/locale.yml
 jobs:
   locale_verify:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/release-edit-with-push.yml b/.github/workflows/release-edit-with-push.yml
index 57657a4ba7..f86c1948f8 100644
--- a/.github/workflows/release-edit-with-push.yml
+++ b/.github/workflows/release-edit-with-push.yml
@@ -6,7 +6,7 @@ on:
       - develop
     paths:
       - 'CHANGELOG.md'
-
+      # - .github/workflows/release-edit-with-push.yml
 env:
   GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index b1c54bb3e7..99ca09a8ec 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -9,12 +9,13 @@ on:
       - packages/backend/**
       # for permissions
       - packages/misskey-js/**
+      - .github/workflows/test-backend.yml
   pull_request:
     paths:
       - packages/backend/**
       # for permissions
       - packages/misskey-js/**
-
+      - .github/workflows/test-backend.yml
 jobs:
   unit:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 9d5053b82a..5acf85cb0e 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -11,7 +11,7 @@ on:
       - packages/misskey-js/**
       # for e2e
       - packages/backend/**
-
+      - .github/workflows/test-frontend.yml
   pull_request:
     paths:
       - packages/frontend/**
@@ -19,7 +19,7 @@ on:
       - packages/misskey-js/**
       # for e2e
       - packages/backend/**
-
+      - .github/workflows/test-frontend.yml
 jobs:
   vitest:
     runs-on: ubuntu-latest
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index 2589d908b8..43f5b5b860 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -8,11 +8,12 @@ on:
     branches: [ develop ]
     paths:
       - packages/misskey-js/**
+      - .github/workflows/test-misskey-js.yml
   pull_request:
     branches: [ develop ]
     paths:
       - packages/misskey-js/**
-
+      - .github/workflows/test-misskey-js.yml
 jobs:
   test:
 
diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml
index 24340e7d81..b931e64790 100644
--- a/.github/workflows/validate-api-json.yml
+++ b/.github/workflows/validate-api-json.yml
@@ -7,10 +7,11 @@ on:
       - develop
     paths:
       - packages/backend/**
+      - .github/workflows/validate-api-json.yml
   pull_request:
     paths:
       - packages/backend/**
-
+      - .github/workflows/validate-api-json.yml
 jobs:
   validate-api-json:
     runs-on: ubuntu-latest

From 722acf5986bda0ddea3a4724d171e4d553037bbf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 14 Jul 2024 17:28:34 +0900
Subject: [PATCH 105/206] fix(frontend): follow-up of #13089 (#14206)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): #13089 を修正

* fix

* 正規表現を強化

* fix
---
 locales/index.d.ts                            |  2 +-
 packages/frontend/src/components/MkNote.vue   | 16 +++++------
 .../src/components/MkNoteDetailed.vue         | 16 +++++------
 packages/frontend/src/components/MkPoll.vue   |  9 ++++---
 packages/frontend/src/components/MkSignin.vue | 10 ++++---
 packages/frontend/src/scripts/please-login.ts | 27 ++++++++++++++++++-
 packages/frontend/src/scripts/url.ts          |  4 +--
 7 files changed, 56 insertions(+), 28 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 84a402b0de..694ee53a1f 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -5004,7 +5004,7 @@ export interface Locale extends ILocale {
      * お問い合わせ
      */
     "inquiry": string;
-     /**
+    /**
      * もう一度お試しください。
      */
     "tryAgain": string;
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index c518c7dd41..13273a53b4 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -174,7 +174,7 @@ import MkPoll from '@/components/MkPoll.vue';
 import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
-import { pleaseLogin } from '@/scripts/please-login.js';
+import { pleaseLogin, type OpenOnRemoteOptions } from '@/scripts/please-login.js';
 import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import number from '@/filters/number.js';
@@ -279,10 +279,10 @@ const renoteCollapsed = ref(
 	),
 );
 
-const pleaseLoginContext = {
+const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
 	type: 'lookup',
-	path: `https://${host}/notes/${appearNote.value.id}`,
-} as const;
+	url: `https://${host}/notes/${appearNote.value.id}`,
+}));
 
 /* Overload FunctionにLintが対応していないのでコメントアウト
 function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;
@@ -417,7 +417,7 @@ if (!props.mock) {
 }
 
 function renote(viaKeyboard = false) {
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 	showMovedDialog();
 
 	const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock });
@@ -427,7 +427,7 @@ function renote(viaKeyboard = false) {
 }
 
 function reply(): void {
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 	if (props.mock) {
 		return;
 	}
@@ -440,7 +440,7 @@ function reply(): void {
 }
 
 function react(): void {
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 	showMovedDialog();
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
 		sound.playMisskeySfx('reaction');
@@ -571,7 +571,7 @@ function showRenoteMenu(): void {
 	}
 
 	if (isMyRenote) {
-		pleaseLogin(undefined, pleaseLoginContext);
+		pleaseLogin(undefined, pleaseLoginContext.value);
 		os.popupMenu([
 			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
 			{ type: 'divider' },
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 737e9a853a..9a3e595789 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -209,7 +209,7 @@ import MkPoll from '@/components/MkPoll.vue';
 import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
-import { pleaseLogin } from '@/scripts/please-login.js';
+import { pleaseLogin, type OpenOnRemoteOptions } from '@/scripts/please-login.js';
 import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import { notePage } from '@/filters/note.js';
@@ -297,10 +297,10 @@ const conversation = ref<Misskey.entities.Note[]>([]);
 const replies = ref<Misskey.entities.Note[]>([]);
 const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i?.id);
 
-const pleaseLoginContext = {
+const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
 	type: 'lookup',
-	path: `https://${host}/notes/${appearNote.value.id}`,
-} as const;
+	url: `https://${host}/notes/${appearNote.value.id}`,
+}));
 
 const keymap = {
 	'r': () => reply(),
@@ -402,7 +402,7 @@ if (appearNote.value.reactionAcceptance === 'likeOnly') {
 }
 
 function renote() {
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 	showMovedDialog();
 
 	const { menu } = getRenoteMenu({ note: note.value, renoteButton });
@@ -410,7 +410,7 @@ function renote() {
 }
 
 function reply(): void {
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 	showMovedDialog();
 	os.post({
 		reply: appearNote.value,
@@ -421,7 +421,7 @@ function reply(): void {
 }
 
 function react(): void {
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 	showMovedDialog();
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
 		sound.playMisskeySfx('reaction');
@@ -505,7 +505,7 @@ async function clip(): Promise<void> {
 
 function showRenoteMenu(): void {
 	if (!isMyRenote) return;
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 	os.popupMenu([{
 		text: i18n.ts.unrenote,
 		icon: 'ti ti-trash',
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index 82e2a605f1..72bd8f4f6c 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -36,6 +36,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { host } from '@/config.js';
 import { useInterval } from '@/scripts/use-interval.js';
+import type { OpenOnRemoteOptions } from '@/scripts/please-login.js';
 
 const props = defineProps<{
 	noteId: string;
@@ -61,10 +62,10 @@ const timer = computed(() => i18n.tsx._poll[
 
 const showResult = ref(props.readOnly || isVoted.value);
 
-const pleaseLoginContext = {
+const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
 	type: 'lookup',
-	path: `https://${host}/notes/${props.note.id}`,
-} as const;
+	url: `https://${host}/notes/${props.noteId}`,
+}));
 
 // 期限付きアンケート
 if (props.poll.expiresAt) {
@@ -82,7 +83,7 @@ if (props.poll.expiresAt) {
 }
 
 const vote = async (id) => {
-	pleaseLogin(undefined, pleaseLoginContext);
+	pleaseLogin(undefined, pleaseLoginContext.value);
 
 	if (props.readOnly || closed.value || isVoted.value) return;
 
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 746ddd7154..a123bbddc6 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -236,12 +236,14 @@ function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
 	switch (options.type) {
 		case 'web':
 		case 'lookup': {
-			let _path = options.path;
+			let _path: string;
 
 			if (options.type === 'lookup') {
-				// TODO: v2024.2.0以降が浸透してきたら正式なURLに変更する▼
+				// TODO: v2024.7.0以降が浸透してきたら正式なURLに変更する▼
 				// _path = `/lookup?uri=${encodeURIComponent(_path)}`;
-				_path = `/authorize-follow?acct=${encodeURIComponent(_path)}`;
+				_path = `/authorize-follow?acct=${encodeURIComponent(options.url)}`;
+			} else {
+				_path = options.path;
 			}
 
 			if (targetHost) {
@@ -252,7 +254,7 @@ function openRemote(options: OpenOnRemoteOptions, targetHost?: string): void {
 			break;
 		}
 		case 'share': {
-			const params = query(options.params);			
+			const params = query(options.params);
 			if (targetHost) {
 				window.open(`https://${targetHost}/share?${params}`, '_blank', 'noopener');
 			} else {
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index b04062a58a..18f05bc7f4 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -9,13 +9,38 @@ import { i18n } from '@/i18n.js';
 import { popup } from '@/os.js';
 
 export type OpenOnRemoteOptions = {
+	/**
+	 * 外部のMisskey Webで特定のパスを開く
+	 */
 	type: 'web';
+
+	/**
+	 * 内部パス(例: `/settings`)
+	 */
 	path: string;
 } | {
+	/**
+	 * 外部のMisskey Webで照会する
+	 */
 	type: 'lookup';
-	path: string;	
+
+	/**
+	 * 照会したいエンティティのURL
+	 *
+	 * (例: `https://misskey.example.com/notes/abcdexxxxyz`)
+	 */
+	url: string;
 } | {
+	/**
+	 * 外部のMisskeyでノートする
+	 */
 	type: 'share';
+
+	/**
+	 * `/share` ページに渡すクエリストリング
+	 *
+	 * @see https://go.misskey-hub.net/spec/share/
+	 */
 	params: Record<string, string>;
 };
 
diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts
index c477fb5506..5a8265af9e 100644
--- a/packages/frontend/src/scripts/url.ts
+++ b/packages/frontend/src/scripts/url.ts
@@ -23,6 +23,6 @@ export function appendQuery(url: string, query: string): string {
 }
 
 export function extractDomain(url: string) {
-	const match = url.match(/^(https)?:?\/{0,2}([^\/]+)/);
-	return match ? match[2] : null;
+	const match = url.match(/^(?:https?:)?(?:\/\/)?(?:[^@\n]+@)?([^:\/\n]+)/im);
+	return match ? match[1] : null;
 }

From c5607d86333bdca38b9ff8c02a5979492f9d602c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 14 Jul 2024 20:14:43 +0900
Subject: [PATCH 106/206] =?UTF-8?q?enhance(backend):=20config=E3=81=ABsign?=
 =?UTF-8?q?ToActivityPubGet=E3=81=AE=E6=8C=87=E5=AE=9A=E3=81=8C=E7=84=A1?=
 =?UTF-8?q?=E3=81=84=E5=A0=B4=E5=90=88true=E3=81=A8=E8=A6=8B=E5=81=9A?=
 =?UTF-8?q?=E3=81=99=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

trueの方が望ましいため
---
 packages/backend/src/config.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 0ac521d409..4cc27898be 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -259,7 +259,7 @@ export function loadConfig(): Config {
 		deliverJobMaxAttempts: config.deliverJobMaxAttempts,
 		inboxJobMaxAttempts: config.inboxJobMaxAttempts,
 		proxyRemoteFiles: config.proxyRemoteFiles,
-		signToActivityPubGet: config.signToActivityPubGet,
+		signToActivityPubGet: config.signToActivityPubGet ?? true,
 		mediaProxy: externalMediaProxy ?? internalMediaProxy,
 		externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
 		videoThumbnailGenerator: config.videoThumbnailGenerator ?

From 4b9c60ad21704e7e75df830205cec2db7afc2baa Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Sun, 14 Jul 2024 20:24:29 +0900
Subject: [PATCH 107/206] =?UTF-8?q?fix(backend):=20=E3=83=A6=E3=83=BC?=
 =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=81=AE=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7?=
 =?UTF-8?q?=E3=83=A7=E3=83=B3=E4=B8=80=E8=A6=A7=E3=81=A7=E3=83=9F=E3=83=A5?=
 =?UTF-8?q?=E3=83=BC=E3=83=88/=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF?=
 =?UTF-8?q?=E3=81=8C=E6=A9=9F=E8=83=BD=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA?=
 =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=9F=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#14100)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: mute/block was not considered on users/reactions

* docs(changelog): update changelog

* chore: Apply suggestion from code review

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>

---------

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 packages/backend/src/misc/is-user-related.ts  |  4 ++++
 .../server/api/endpoints/users/reactions.ts   | 19 +++++++++++++++++--
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2156c81474..e50dbfec9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,7 @@
   4. フォローしていない非アクティブなユーザ
 - Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
+- Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/src/misc/is-user-related.ts b/packages/backend/src/misc/is-user-related.ts
index 93c9b2b814..862d6e6a38 100644
--- a/packages/backend/src/misc/is-user-related.ts
+++ b/packages/backend/src/misc/is-user-related.ts
@@ -4,6 +4,10 @@
  */
 
 export function isUserRelated(note: any, userIds: Set<string>, ignoreAuthor = false): boolean {
+	if (!note) {
+		return false;
+	}
+
 	if (userIds.has(note.userId) && !ignoreAuthor) {
 		return true;
 	}
diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts
index aca883a052..7805ae3288 100644
--- a/packages/backend/src/server/api/endpoints/users/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/users/reactions.ts
@@ -12,6 +12,7 @@ import { DI } from '@/di-symbols.js';
 import { CacheService } from '@/core/CacheService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { RoleService } from '@/core/RoleService.js';
+import { isUserRelated } from '@/misc/is-user-related.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -74,6 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private roleService: RoleService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const userIdsWhoBlockingMe = me ? await this.cacheService.userBlockedCache.fetch(me.id) : new Set<string>();
 			const iAmModerator = me ? await this.roleService.isModerator(me) : false; // Moderators can see reactions of all users
 			if (!iAmModerator) {
 				const user = await this.cacheService.findUserById(ps.userId);
@@ -85,8 +87,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				if ((me == null || me.id !== ps.userId) && !profile.publicReactions) {
 					throw new ApiError(meta.errors.reactionsNotPublic);
 				}
+
+				// early return if me is blocked by requesting user
+				if (userIdsWhoBlockingMe.has(ps.userId)) {
+					return [];
+				}
 			}
 
+			const userIdsWhoMeMuting = me ? await this.cacheService.userMutingsCache.fetch(me.id) : new Set<string>();
+
 			const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'),
 				ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
 				.andWhere('reaction.userId = :userId', { userId: ps.userId })
@@ -94,9 +103,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			this.queryService.generateVisibilityQuery(query, me);
 
-			const reactions = await query
+			const reactions = (await query
 				.limit(ps.limit)
-				.getMany();
+				.getMany()).filter(reaction => {
+				if (reaction.note?.userId === ps.userId) return true; // we can see reactions to note of requesting user
+				if (me && isUserRelated(reaction.note, userIdsWhoBlockingMe)) return false;
+				if (me && isUserRelated(reaction.note, userIdsWhoMeMuting)) return false;
+
+				return true;
+			});
 
 			return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true });
 		});

From d47fd4ffe152d3dbd4b224e63f350e29ace75e81 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sun, 14 Jul 2024 21:29:35 +0900
Subject: [PATCH 108/206] fix: error with trying to handle SIGKILL (#14208)

---
 packages/misskey-bubble-game/build.js | 1 -
 packages/misskey-js/build.js          | 1 -
 packages/misskey-reversi/build.js     | 1 -
 3 files changed, 3 deletions(-)

diff --git a/packages/misskey-bubble-game/build.js b/packages/misskey-bubble-game/build.js
index 0b79f4b915..e626c97a59 100644
--- a/packages/misskey-bubble-game/build.js
+++ b/packages/misskey-bubble-game/build.js
@@ -95,7 +95,6 @@ async function watchSrc() {
 		process.on('SIGHUP', resolve);
 		process.on('SIGINT', resolve);
 		process.on('SIGTERM', resolve);
-		process.on('SIGKILL', resolve);
 		process.on('uncaughtException', reject);
 		process.on('exit', resolve);
 	}).finally(async () => {
diff --git a/packages/misskey-js/build.js b/packages/misskey-js/build.js
index a13d9c1186..a80b71646f 100644
--- a/packages/misskey-js/build.js
+++ b/packages/misskey-js/build.js
@@ -95,7 +95,6 @@ async function watchSrc() {
 		process.on('SIGHUP', resolve);
 		process.on('SIGINT', resolve);
 		process.on('SIGTERM', resolve);
-		process.on('SIGKILL', resolve);
 		process.on('uncaughtException', reject);
 		process.on('exit', resolve);
 	}).finally(async () => {
diff --git a/packages/misskey-reversi/build.js b/packages/misskey-reversi/build.js
index 0b79f4b915..e626c97a59 100644
--- a/packages/misskey-reversi/build.js
+++ b/packages/misskey-reversi/build.js
@@ -95,7 +95,6 @@ async function watchSrc() {
 		process.on('SIGHUP', resolve);
 		process.on('SIGINT', resolve);
 		process.on('SIGTERM', resolve);
-		process.on('SIGKILL', resolve);
 		process.on('uncaughtException', reject);
 		process.on('exit', resolve);
 	}).finally(async () => {

From aa0632727f3de73b0e348f86ffd1c5c3135a46e4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 14 Jul 2024 21:29:57 +0900
Subject: [PATCH 109/206] chore(deps): bump actions/setup-node from 4.0.2 to
 4.0.3 (#14165)

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.2 to 4.0.3.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4.0.2...v4.0.3)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/api-misskey-js.yml           | 2 +-
 .github/workflows/changelog-check.yml          | 2 +-
 .github/workflows/check-misskey-js-autogen.yml | 2 +-
 .github/workflows/get-api-diff.yml             | 2 +-
 .github/workflows/lint.yml                     | 6 +++---
 .github/workflows/locale.yml                   | 2 +-
 .github/workflows/on-release-created.yml       | 2 +-
 .github/workflows/storybook.yml                | 2 +-
 .github/workflows/test-backend.yml             | 4 ++--
 .github/workflows/test-frontend.yml            | 4 ++--
 .github/workflows/test-misskey-js.yml          | 2 +-
 .github/workflows/test-production.yml          | 2 +-
 .github/workflows/validate-api-json.yml        | 2 +-
 13 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml
index f9385239b0..e7db18316c 100644
--- a/.github/workflows/api-misskey-js.yml
+++ b/.github/workflows/api-misskey-js.yml
@@ -21,7 +21,7 @@ jobs:
       - run: corepack enable
 
       - name: Setup Node.js
-        uses: actions/setup-node@v4.0.2
+        uses: actions/setup-node@v4.0.3
         with:
           node-version-file: '.node-version'
           cache: 'pnpm'
diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml
index f254af0d1f..d4e99f966e 100644
--- a/.github/workflows/changelog-check.yml
+++ b/.github/workflows/changelog-check.yml
@@ -14,7 +14,7 @@ jobs:
       - name: Checkout head
         uses: actions/checkout@v4.1.1
       - name: Setup Node.js
-        uses: actions/setup-node@v4.0.2
+        uses: actions/setup-node@v4.0.3
         with:
           node-version-file: '.node-version'
 
diff --git a/.github/workflows/check-misskey-js-autogen.yml b/.github/workflows/check-misskey-js-autogen.yml
index 39acad8bc3..3a2a2d5f8d 100644
--- a/.github/workflows/check-misskey-js-autogen.yml
+++ b/.github/workflows/check-misskey-js-autogen.yml
@@ -28,7 +28,7 @@ jobs:
 
       - name: setup node
         id: setup-node
-        uses: actions/setup-node@v4.0.2
+        uses: actions/setup-node@v4.0.3
         with:
           node-version-file: '.node-version'
           cache: pnpm
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 33fa2ccc39..4afafabf2e 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -34,7 +34,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.2
+      uses: actions/setup-node@v4.0.3
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index cd8ae0b45b..1c5cab208c 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -29,7 +29,7 @@ jobs:
         fetch-depth: 0
         submodules: true
     - uses: pnpm/action-setup@v4
-    - uses: actions/setup-node@v4.0.2
+    - uses: actions/setup-node@v4.0.3
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
@@ -53,7 +53,7 @@ jobs:
         fetch-depth: 0
         submodules: true
     - uses: pnpm/action-setup@v4
-    - uses: actions/setup-node@v4.0.2
+    - uses: actions/setup-node@v4.0.3
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
@@ -76,7 +76,7 @@ jobs:
         fetch-depth: 0
         submodules: true
     - uses: pnpm/action-setup@v4
-    - uses: actions/setup-node@v4.0.2
+    - uses: actions/setup-node@v4.0.3
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
diff --git a/.github/workflows/locale.yml b/.github/workflows/locale.yml
index be3764f3e4..95251bfe31 100644
--- a/.github/workflows/locale.yml
+++ b/.github/workflows/locale.yml
@@ -19,7 +19,7 @@ jobs:
         fetch-depth: 0
         submodules: true
     - uses: pnpm/action-setup@v4
-    - uses: actions/setup-node@v4.0.2
+    - uses: actions/setup-node@v4.0.3
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml
index edfdab99e9..22c04ff297 100644
--- a/.github/workflows/on-release-created.yml
+++ b/.github/workflows/on-release-created.yml
@@ -26,7 +26,7 @@ jobs:
       - name: Install pnpm
         uses: pnpm/action-setup@v4
       - name: Use Node.js ${{ matrix.node-version }}
-        uses: actions/setup-node@v4.0.2
+        uses: actions/setup-node@v4.0.3
         with:
           node-version: ${{ matrix.node-version }}
           cache: 'pnpm'
diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml
index daa76509c8..68452aacaf 100644
--- a/.github/workflows/storybook.yml
+++ b/.github/workflows/storybook.yml
@@ -36,7 +36,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v4
     - name: Use Node.js 20.x
-      uses: actions/setup-node@v4.0.2
+      uses: actions/setup-node@v4.0.3
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 99ca09a8ec..bfb79ef090 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -46,7 +46,7 @@ jobs:
     - name: Install FFmpeg
       uses: FedericoCarboni/setup-ffmpeg@v3
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.2
+      uses: actions/setup-node@v4.0.3
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
@@ -93,7 +93,7 @@ jobs:
       - name: Install pnpm
         uses: pnpm/action-setup@v4
       - name: Use Node.js ${{ matrix.node-version }}
-        uses: actions/setup-node@v4.0.2
+        uses: actions/setup-node@v4.0.3
         with:
           node-version: ${{ matrix.node-version }}
           cache: 'pnpm'
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 5acf85cb0e..c17a9fd387 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -35,7 +35,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.2
+      uses: actions/setup-node@v4.0.3
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
@@ -90,7 +90,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.2
+      uses: actions/setup-node@v4.0.3
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index 43f5b5b860..6ee67e8735 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -31,7 +31,7 @@ jobs:
       - run: corepack enable
 
       - name: Setup Node.js ${{ matrix.node-version }}
-        uses: actions/setup-node@v4.0.2
+        uses: actions/setup-node@v4.0.3
         with:
           node-version: ${{ matrix.node-version }}
           cache: 'pnpm'
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index 7f8db65293..18d02ec030 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -25,7 +25,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.2
+      uses: actions/setup-node@v4.0.3
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml
index b931e64790..90f2929a25 100644
--- a/.github/workflows/validate-api-json.yml
+++ b/.github/workflows/validate-api-json.yml
@@ -27,7 +27,7 @@ jobs:
     - name: Install pnpm
       uses: pnpm/action-setup@v4
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.2
+      uses: actions/setup-node@v4.0.3
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'

From f0b9d70720b27977051563713e02abf38eb2b45a Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Sun, 14 Jul 2024 21:30:57 +0900
Subject: [PATCH 110/206] ci: cache eslint (#14204)

* ci: cache eslint

* dummy commit to trigger

* fix syntax error
---
 .github/workflows/lint.yml    | 11 ++++++++++-
 packages/backend/src/const.ts |  1 +
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 1c5cab208c..c21fc95123 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -40,6 +40,8 @@ jobs:
     needs: [pnpm_install]
     runs-on: ubuntu-latest
     continue-on-error: true
+    env:
+      eslint-cache-version: v1
     strategy:
       matrix:
         workspace:
@@ -59,7 +61,14 @@ jobs:
         cache: 'pnpm'
     - run: corepack enable
     - run: pnpm i --frozen-lockfile
-    - run: pnpm --filter ${{ matrix.workspace }} run eslint
+    - name: Restore eslint cache
+      uses: actions/cache@v4.0.2
+      with:
+        path: node_modules/.cache/eslint
+        key: eslint-${{ env.eslint-cache-version }}-${{ hashFiles('/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
+        restore-keys: |
+          eslint-${{ env.eslint-cache-version }}-${{ hashFiles('/pnpm-lock.yaml') }}-
+    - run: pnpm --filter ${{ matrix.workspace }} run eslint --cache --cache-location node_modules/.cache/eslint --cache-strategy content
 
   typecheck:
     needs: [pnpm_install]
diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts
index a238f4973a..4dc689238b 100644
--- a/packages/backend/src/const.ts
+++ b/packages/backend/src/const.ts
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+// dummy
 export const MAX_NOTE_TEXT_LENGTH = 3000;
 
 export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min

From 16795f18a7d9672f408b81228db67c7df77417f6 Mon Sep 17 00:00:00 2001
From: easrng <easrng@gmail.com>
Date: Sun, 14 Jul 2024 06:31:30 -0600
Subject: [PATCH 111/206] Enhance(frontend): Allow negative delay in MFM
 (#14200)

Co-authored-by: easrng <me@easrng.net>
---
 CHANGELOG.md                                                    | 1 +
 .../frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e50dbfec9b..83f2261c1d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
+- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
 
 ### Client
 - Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index cab8d9c704..0d869892bd 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -65,7 +65,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
 	const validTime = (t: string | boolean | null | undefined) => {
 		if (t == null) return null;
 		if (typeof t === 'boolean') return null;
-		return t.match(/^[0-9.]+s$/) ? t : null;
+		return t.match(/^\-?[0-9.]+s$/) ? t : null;
 	};
 
 	const validColor = (c: unknown): string | null => {

From 1b84760c19a56c8928ac639d514fe28c7d01e42d Mon Sep 17 00:00:00 2001
From: Souma <101255979+5ouma@users.noreply.github.com>
Date: Sun, 14 Jul 2024 21:33:22 +0900
Subject: [PATCH 112/206] enhance(backend): Load settings via environment
 variables (#14179)

* feat(backend): Load settings via environment variables

If they're not loaded from the config file.

* chore(docker): Add hints for environment variables

It supports users to know about them.

* docs(changelog): Add the description about this change

Users can notice what's changed by this PR.

* style(backend): Fix code syntax

To pass the linter.
---
 .config/docker_example.env     |  6 ++++++
 .config/docker_example.yml     |  3 +++
 CHANGELOG.md                   |  1 +
 compose_example.yml            |  2 ++
 packages/backend/src/config.ts | 16 ++++++++++------
 5 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/.config/docker_example.env b/.config/docker_example.env
index 4fe8e76b78..c61248da2e 100644
--- a/.config/docker_example.env
+++ b/.config/docker_example.env
@@ -1,5 +1,11 @@
+# misskey settings
+# MISSKEY_URL=https://example.tld/
+
 # db settings
 POSTGRES_PASSWORD=example-misskey-pass
+# DATABASE_PASSWORD=${POSTGRES_PASSWORD}
 POSTGRES_USER=example-misskey-user
+# DATABASE_USER=${POSTGRES_USER}
 POSTGRES_DB=misskey
+# DATABASE_DB=${POSTGRES_DB}
 DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"
diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index 42ac18de1b..d347882d1a 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -6,6 +6,7 @@
 #───┘ URL └─────────────────────────────────────────────────────
 
 # Final accessible URL seen by a user.
+# You can set url from an environment variable instead.
 url: https://example.tld/
 
 # ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
@@ -38,9 +39,11 @@ db:
   port: 5432
 
   # Database name
+  # You can set db from an environment variable instead.
   db: misskey
 
   # Auth
+  # You can set user and pass from environment variables instead.
   user: example-misskey-user
   pass: example-misskey-pass
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83f2261c1d..14cae5d3de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,7 @@
 - Enhance: エンドポイント`gallery/posts/update`の必須項目を`postId`のみに
 - Enhance: エンドポイント`i/webhook/update`の必須項目を`webhookId`のみに
 - Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに
+- Enhance: `default.yml`内の`url`, `db.db`, `db.user`, `db.pass`を環境変数から読み込めるように
 - Fix: チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
 - Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
diff --git a/compose_example.yml b/compose_example.yml
index 75d0d3a59c..336bd814a7 100644
--- a/compose_example.yml
+++ b/compose_example.yml
@@ -17,6 +17,8 @@ services:
     networks:
       - internal_network
       - external_network
+    # env_file:
+    #   - .config/docker.env
     volumes:
       - ./files:/misskey/files
       - ./.config:/misskey/.config:ro
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 4cc27898be..3e5a1e81cd 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -23,7 +23,7 @@ type RedisOptionsSource = Partial<RedisOptions> & {
  * 設定ファイルの型
  */
 type Source = {
-	url: string;
+	url?: string;
 	port?: number;
 	socket?: string;
 	chmodSocket?: string;
@@ -31,9 +31,9 @@ type Source = {
 	db: {
 		host: string;
 		port: number;
-		db: string;
-		user: string;
-		pass: string;
+		db?: string;
+		user?: string;
+		pass?: string;
 		disableCache?: boolean;
 		extra?: { [x: string]: string };
 	};
@@ -202,13 +202,17 @@ export function loadConfig(): Config {
 		: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
 	const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
 
-	const url = tryCreateUrl(config.url);
+	const url = tryCreateUrl(config.url ?? process.env.MISSKEY_URL ?? '');
 	const version = meta.version;
 	const host = url.host;
 	const hostname = url.hostname;
 	const scheme = url.protocol.replace(/:$/, '');
 	const wsScheme = scheme.replace('http', 'ws');
 
+	const dbDb = config.db.db ?? process.env.DATABASE_DB ?? '';
+	const dbUser = config.db.user ?? process.env.DATABASE_USER ?? '';
+	const dbPass = config.db.pass ?? process.env.DATABASE_PASSWORD ?? '';
+
 	const externalMediaProxy = config.mediaProxy ?
 		config.mediaProxy.endsWith('/') ? config.mediaProxy.substring(0, config.mediaProxy.length - 1) : config.mediaProxy
 		: null;
@@ -231,7 +235,7 @@ export function loadConfig(): Config {
 		apiUrl: `${scheme}://${host}/api`,
 		authUrl: `${scheme}://${host}/auth`,
 		driveUrl: `${scheme}://${host}/files`,
-		db: config.db,
+		db: { ...config.db, db: dbDb, user: dbUser, pass: dbPass },
 		dbReplications: config.dbReplications,
 		dbSlaves: config.dbSlaves,
 		meilisearch: config.meilisearch,

From ce39c3a2fbf9ab9efaff82fdbad84ac9f2311c6e Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Mon, 15 Jul 2024 14:58:48 +0900
Subject: [PATCH 113/206] chore(backend): registed -> registered (#14213)

* chore(backend): registed -> registered

* Update CHANGELOG.md
---
 CHANGELOG.md                                                  | 1 +
 .../backend/src/core/activitypub/models/ApQuestionService.ts  | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 14cae5d3de..d7b5979a21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -56,6 +56,7 @@
 - Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
 - Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
+- Fix: エラーメッセージの誤字を修正 (#14213)
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
index 4fae1e897b..73004d10b0 100644
--- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts
+++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
@@ -74,10 +74,10 @@ export class ApQuestionService {
 
 		//#region このサーバーに既に登録されているか
 		const note = await this.notesRepository.findOneBy({ uri });
-		if (note == null) throw new Error('Question is not registed');
+		if (note == null) throw new Error('Question is not registered');
 
 		const poll = await this.pollsRepository.findOneBy({ noteId: note.id });
-		if (poll == null) throw new Error('Question is not registed');
+		if (poll == null) throw new Error('Question is not registered');
 		//#endregion
 
 		// resolve new Question object

From 1001277d430e84d126c93483d38ce7d6be0e8b48 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Mon, 15 Jul 2024 22:08:02 +0900
Subject: [PATCH 114/206] =?UTF-8?q?fix:=20CHANGELOG.md=E3=81=AE=E8=A8=98?=
 =?UTF-8?q?=E8=BC=89=E3=81=AB=E6=BC=8F=E3=82=8C=E3=81=8C=E3=81=82=E3=81=A3?=
 =?UTF-8?q?=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#14220)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7b5979a21..058aa33719 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,8 @@
   2. フォロー中かつ非アクティブなユーザ
   3. フォローしていないアクティブなユーザ
   4. フォローしていない非アクティブなユーザ
+
+  また、自分自身のアカウントもサジェストされるようになりました。
 - Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
 - Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正

From 3b075c9c441e8fe2d7446a7201cd1950437ba0b6 Mon Sep 17 00:00:00 2001
From: Eiichi Yoshikawa <edo@bari-ikutsu.com>
Date: Tue, 16 Jul 2024 08:38:42 +0900
Subject: [PATCH 115/206] =?UTF-8?q?fix(frontend):=20MkSignin.vue=E3=81=AEc?=
 =?UTF-8?q?redentialRequest=E3=81=8B=E3=82=89Reactivity=E3=82=92=E5=89=8A?=
 =?UTF-8?q?=E9=99=A4=20(#14223)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Remove reactivity from credentialRequest in MkSignin.vue

* Update Changelog
---
 CHANGELOG.md                                  |  1 +
 packages/frontend/src/components/MkSignin.vue | 10 +++++-----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 058aa33719..53bf9233fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@
 - Fix: テーマプレビューが見れない問題を修正
 - Fix: ショートカットキーが連打できる問題を修正  
   (Cherry-picked from https://github.com/taiyme/misskey/pull/234)
+- Fix: MkSignin.vueのcredentialRequestからReactivityを削除(ProxyがPasskey認証処理に渡ることを避けるため)
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index a123bbddc6..781145e1bc 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -87,7 +87,7 @@ const host = ref(toUnicode(configHost));
 const totpLogin = ref(false);
 const isBackupCode = ref(false);
 const queryingKey = ref(false);
-const credentialRequest = ref<CredentialRequestOptions | null>(null);
+let credentialRequest: CredentialRequestOptions | null = null;
 
 const emit = defineEmits<{
 	(ev: 'login', v: any): void;
@@ -122,14 +122,14 @@ function onLogin(res: any): Promise<void> | void {
 }
 
 async function queryKey(): Promise<void> {
-	if (credentialRequest.value == null) return;
+	if (credentialRequest == null) return;
 	queryingKey.value = true;
-	await webAuthnRequest(credentialRequest.value)
+	await webAuthnRequest(credentialRequest)
 		.catch(() => {
 			queryingKey.value = false;
 			return Promise.reject(null);
 		}).then(credential => {
-			credentialRequest.value = null;
+			credentialRequest = null;
 			queryingKey.value = false;
 			signing.value = true;
 			return misskeyApi('signin', {
@@ -160,7 +160,7 @@ function onSubmit(): void {
 			}).then(res => {
 				totpLogin.value = true;
 				signing.value = false;
-				credentialRequest.value = parseRequestOptionsFromJSON({
+				credentialRequest = parseRequestOptionsFromJSON({
 					publicKey: res,
 				});
 			})

From 8ebc3b51f7c7ca8956ac84783837f706a36fafc6 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Tue, 16 Jul 2024 23:27:05 +0900
Subject: [PATCH 116/206] Fix typo (#14231)

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53bf9233fc..a51ff49417 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,7 +39,7 @@
 - Enhance: エンドポイント`i/webhook/update`の必須項目を`webhookId`のみに
 - Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに
 - Enhance: `default.yml`内の`url`, `db.db`, `db.user`, `db.pass`を環境変数から読み込めるように
-- Fix: チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
+- Fix: チャート生成時にinstance.suspensionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
 - Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
 - Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059)

From 070f0e723d522b5ee2f01beb6a4c127798ed3197 Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Wed, 17 Jul 2024 16:55:17 +0900
Subject: [PATCH 117/206] =?UTF-8?q?AiScript=E3=82=920.19.0=E3=81=AB?=
 =?UTF-8?q?=E3=82=A2=E3=83=83=E3=83=97=E3=83=87=E3=83=BC=E3=83=88=20(#1422?=
 =?UTF-8?q?6)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update autogen files

* Update CHANGELOG.md

* Update flash-edit.vue
---
 CHANGELOG.md                                  |   1 +
 packages/frontend/package.json                |   2 +-
 .../frontend/src/pages/flash/flash-edit.vue   |  11 +-
 pnpm-lock.yaml                                | 902 ++++++++++++------
 4 files changed, 610 insertions(+), 306 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a51ff49417..9c634276d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
   (Based on https://github.com/taiyme/misskey/pull/226)
 - Enhance: サーバー情報ページ・お問い合わせページを改善  
   (Cherry-picked from https://github.com/taiyme/misskey/pull/238)
+- Enhance: AiScriptを0.19.0にアップデート
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index a0c1c69883..fbeae08aa0 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -24,7 +24,7 @@
 		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.7",
 		"@rollup/pluginutils": "5.1.0",
-		"@syuilo/aiscript": "0.18.0",
+		"@syuilo/aiscript": "0.19.0",
 		"@tabler/icons-webfont": "3.3.0",
 		"@twemoji/parser": "15.1.1",
 		"@vitejs/plugin-vue": "5.0.5",
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 3445da26a2..0b9f4dfe58 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -37,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
 import * as Misskey from 'misskey-js';
+import { AISCRIPT_VERSION } from '@syuilo/aiscript';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
@@ -48,7 +49,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import { useRouter } from '@/router/supplier.js';
 
-const PRESET_DEFAULT = `/// @ 0.18.0
+const PRESET_DEFAULT = `/// @ ${AISCRIPT_VERSION}
 
 var name = ""
 
@@ -66,7 +67,7 @@ Ui:render([
 ])
 `;
 
-const PRESET_OMIKUJI = `/// @ 0.18.0
+const PRESET_OMIKUJI = `/// @ ${AISCRIPT_VERSION}
 // ユーザーごとに日替わりのおみくじのプリセット
 
 // 選択肢
@@ -109,7 +110,7 @@ Ui:render([
 ])
 `;
 
-const PRESET_SHUFFLE = `/// @ 0.18.0
+const PRESET_SHUFFLE = `/// @ ${AISCRIPT_VERSION}
 // 巻き戻し可能な文字シャッフルのプリセット
 
 let string = "ペペロンチーノ"
@@ -188,7 +189,7 @@ var cursor = 0
 do()
 `;
 
-const PRESET_QUIZ = `/// @ 0.18.0
+const PRESET_QUIZ = `/// @ ${AISCRIPT_VERSION}
 let title = '地理クイズ'
 
 let qas = [{
@@ -301,7 +302,7 @@ qaEls.push(Ui:C:container({
 Ui:render(qaEls)
 `;
 
-const PRESET_TIMELINE = `/// @ 0.18.0
+const PRESET_TIMELINE = `/// @ ${AISCRIPT_VERSION}
 // APIリクエストを行いローカルタイムラインを表示するプリセット
 
 @fetch() {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6e3ddfc5d4..7d3fec8596 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -48,11 +48,11 @@ importers:
     optionalDependencies:
       '@tensorflow/tfjs-core':
         specifier: 4.4.0
-        version: 4.4.0(encoding@0.1.13)
+        version: 4.20.0(encoding@0.1.13)
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 2.0.2
-        version: 2.0.2(@eslint/compat@1.1.0)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)
+        version: 2.0.2(@eslint/compat@1.1.1)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)
       '@types/node':
         specifier: 20.14.9
         version: 20.14.9
@@ -115,7 +115,7 @@ importers:
         version: 3.0.0
       '@fastify/http-proxy':
         specifier: 9.5.0
-        version: 9.5.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        version: 9.5.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       '@fastify/multipart':
         specifier: 8.3.0
         version: 8.3.0
@@ -274,13 +274,13 @@ importers:
         version: 4.1.0
       jsdom:
         specifier: 24.1.0
-        version: 24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        version: 24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       json5:
         specifier: 2.2.3
         version: 2.2.3
       jsonld:
         specifier: 8.3.2
-        version: 8.3.2(web-streams-polyfill@3.2.1)
+        version: 8.3.2(web-streams-polyfill@4.0.0)
       jsrsasign:
         specifier: 11.1.0
         version: 11.1.0
@@ -319,7 +319,7 @@ importers:
         version: 6.9.14
       nsfwjs:
         specifier: 2.4.2
-        version: 2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5))
+        version: 2.4.2(@tensorflow/tfjs@4.20.0(encoding@0.1.13)(seedrandom@3.0.5))
       oauth:
         specifier: 0.10.0
         version: 0.10.0
@@ -433,7 +433,7 @@ importers:
         version: 3.6.7
       ws:
         specifier: 8.17.1
-        version: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        version: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       xev:
         specifier: 3.0.2
         version: 3.0.2
@@ -443,46 +443,46 @@ importers:
         version: 1.3.11
       '@swc/core-darwin-arm64':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-darwin-x64':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-freebsd-x64':
         specifier: 1.3.11
         version: 1.3.11
       '@swc/core-linux-arm-gnueabihf':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-linux-arm64-gnu':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-linux-arm64-musl':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-linux-x64-gnu':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-linux-x64-musl':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-win32-arm64-msvc':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-win32-ia32-msvc':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@swc/core-win32-x64-msvc':
         specifier: 1.3.56
-        version: 1.3.56
+        version: 1.7.0-nightly-20240715.2
       '@tensorflow/tfjs':
         specifier: 4.4.0
-        version: 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
+        version: 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
       '@tensorflow/tfjs-node':
         specifier: 4.4.0
-        version: 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
+        version: 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
       bufferutil:
         specifier: 4.0.7
-        version: 4.0.7
+        version: 4.0.8
       slacc-android-arm-eabi:
         specifier: 0.0.10
         version: 0.0.10
@@ -524,7 +524,7 @@ importers:
         version: 0.0.10
       utf-8-validate:
         specifier: 6.0.3
-        version: 6.0.3
+        version: 6.0.4
     devDependencies:
       '@jest/globals':
         specifier: 29.7.0
@@ -651,10 +651,10 @@ importers:
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
         specifier: 7.15.0
-        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
+        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)
       '@typescript-eslint/parser':
         specifier: 7.15.0
-        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
       aws-sdk-client-mock:
         specifier: 4.0.1
         version: 4.0.1
@@ -663,7 +663,7 @@ importers:
         version: 7.0.3
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
+        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)
       execa:
         specifier: 9.2.0
         version: 9.2.0
@@ -710,8 +710,8 @@ importers:
         specifier: 5.1.0
         version: 5.1.0(rollup@4.18.0)
       '@syuilo/aiscript':
-        specifier: 0.18.0
-        version: 0.18.0
+        specifier: 0.19.0
+        version: 0.19.0
       '@tabler/icons-webfont':
         specifier: 3.3.0
         version: 3.3.0
@@ -720,7 +720,7 @@ importers:
         version: 15.1.1
       '@vitejs/plugin-vue':
         specifier: 5.0.5
-        version: 5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
+        version: 5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))
       '@vue/compiler-sfc':
         specifier: 3.4.31
         version: 3.4.31
@@ -852,7 +852,7 @@ importers:
         version: 1.12.0(vue@3.4.31(typescript@5.5.3))
       vite:
         specifier: 5.3.2
-        version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+        version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
       vue:
         specifier: 3.4.31
         version: 3.4.31(typescript@5.5.3)
@@ -871,7 +871,7 @@ importers:
         version: 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/addon-interactions':
         specifier: 8.1.11
-        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
       '@storybook/addon-links':
         specifier: 8.1.11
         version: 8.1.11(react@18.3.1)
@@ -901,10 +901,10 @@ importers:
         version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)
       '@storybook/react-vite':
         specifier: 8.1.11
-        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
       '@storybook/test':
         specifier: 8.1.11
-        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
       '@storybook/theming':
         specifier: 8.1.11
         version: 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -916,7 +916,7 @@ importers:
         version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))
       '@storybook/vue3-vite':
         specifier: 8.1.11
-        version: 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
+        version: 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))
       '@testing-library/vue':
         specifier: 8.1.0
         version: 8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
@@ -958,13 +958,13 @@ importers:
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
         specifier: 7.15.0
-        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
+        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)
       '@typescript-eslint/parser':
         specifier: 7.15.0
-        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
       '@vitest/coverage-v8':
         specifier: 1.6.0
-        version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+        version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
       '@vue/runtime-core':
         specifier: 3.4.31
         version: 3.4.31
@@ -979,10 +979,10 @@ importers:
         version: 13.13.0
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
+        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)
       eslint-plugin-vue:
         specifier: 9.26.0
-        version: 9.26.0(eslint@9.6.0)
+        version: 9.26.0(eslint@9.7.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
@@ -1021,7 +1021,7 @@ importers:
         version: 2.0.4
       storybook:
         specifier: 8.1.11
-        version: 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+        version: 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
         version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.1.11)(@storybook/manager-api@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.1.11)(@storybook/theming@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.1.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1030,16 +1030,16 @@ importers:
         version: 1.0.3
       vitest:
         specifier: 1.6.0
-        version: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
+        version: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
       vitest-fetch-mock:
         specifier: 0.2.2
-        version: 0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+        version: 0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
       vue-component-type-helpers:
         specifier: 2.0.24
         version: 2.0.24
       vue-eslint-parser:
         specifier: 9.4.3
-        version: 9.4.3(eslint@9.6.0)
+        version: 9.4.3(eslint@9.7.0)
       vue-tsc:
         specifier: 2.0.24
         version: 2.0.24(typescript@5.5.3)
@@ -1067,10 +1067,10 @@ importers:
         version: 3.0.8
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
-        version: 7.1.0(eslint@9.6.0)(typescript@5.3.3)
+        version: 7.1.0(eslint@9.7.0)(typescript@5.3.3)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
@@ -1101,7 +1101,7 @@ importers:
         version: 7.47.0(@types/node@20.14.9)
       '@swc/jest':
         specifier: 0.2.36
-        version: 0.2.36(@swc/core@1.6.6)
+        version: 0.2.36(@swc/core@1.6.13)
       '@types/jest':
         specifier: 29.5.12
         version: 29.5.12
@@ -1110,10 +1110,10 @@ importers:
         version: 20.14.9
       '@typescript-eslint/eslint-plugin':
         specifier: 7.15.0
-        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
+        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)
       '@typescript-eslint/parser':
         specifier: 7.15.0
-        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
       esbuild:
         specifier: 0.22.0
         version: 0.22.0
@@ -1158,10 +1158,10 @@ importers:
         version: 20.9.1
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@9.6.0)(typescript@5.3.3)
+        version: 6.11.0(eslint@9.7.0)(typescript@5.3.3)
       openapi-types:
         specifier: 12.1.3
         version: 12.1.3
@@ -1189,10 +1189,10 @@ importers:
         version: 20.11.5
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
-        version: 7.1.0(eslint@9.6.0)(typescript@5.3.3)
+        version: 7.1.0(eslint@9.7.0)(typescript@5.3.3)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
@@ -1223,13 +1223,13 @@ importers:
     devDependencies:
       '@typescript-eslint/parser':
         specifier: 7.15.0
-        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: '@types/serviceworker@0.0.67'
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
+        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)
       nodemon:
         specifier: 3.1.4
         version: 3.1.4
@@ -2821,12 +2821,16 @@ packages:
     resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
     engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
 
+  '@eslint-community/regexpp@4.11.0':
+    resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
   '@eslint-community/regexpp@4.6.2':
     resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==}
     engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
 
-  '@eslint/compat@1.1.0':
-    resolution: {integrity: sha512-s9Wi/p25+KbzxKlDm3VshQdImhWk+cbdblhwGNnyCU5lpSwtWa4v7VQCxSki0FAUrGA3s8nCWgYzAH41mwQVKQ==}
+  '@eslint/compat@1.1.1':
+    resolution: {integrity: sha512-lpHyRyplhGPL5mGEh6M9O5nnKk0Gz4bFI+Zu6tKlPpDUN7XshWvH9C/px4UVm87IAANE0W81CEsNGbS1KlzXpA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@eslint/config-array@0.17.0':
@@ -2841,6 +2845,10 @@ packages:
     resolution: {integrity: sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
+  '@eslint/js@9.7.0':
+    resolution: {integrity: sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
   '@eslint/object-schema@2.1.4':
     resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3199,6 +3207,9 @@ packages:
   '@jridgewell/source-map@0.3.5':
     resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
 
+  '@jridgewell/source-map@0.3.6':
+    resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+
   '@jridgewell/sourcemap-codec@1.4.14':
     resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
 
@@ -4533,8 +4544,8 @@ packages:
     cpu: [arm64]
     os: [android]
 
-  '@swc/core-darwin-arm64@1.3.56':
-    resolution: {integrity: sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==}
+  '@swc/core-darwin-arm64@1.6.13':
+    resolution: {integrity: sha512-SOF4buAis72K22BGJ3N8y88mLNfxLNprTuJUpzikyMGrvkuBFNcxYtMhmomO0XHsgLDzOJ+hWzcgjRNzjMsUcQ==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [darwin]
@@ -4545,8 +4556,14 @@ packages:
     cpu: [arm64]
     os: [darwin]
 
-  '@swc/core-darwin-x64@1.3.56':
-    resolution: {integrity: sha512-VH5saqYFasdRXJy6RAT+MXm0+IjkMZvOkohJwUei+oA65cKJofQwrJ1jZro8yOJFYvUSI3jgNRGsdBkmo/4hMw==}
+  '@swc/core-darwin-arm64@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-tOMR9yVA1MD320KySitaRT5yUvdzdmuUjX4+sOw2UKHqgk2s68j6JlBHZjdmqpviXXH5qwXWH3ui8ElcHcpN0Q==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@swc/core-darwin-x64@1.6.13':
+    resolution: {integrity: sha512-AW8akFSC+tmPE6YQQvK9S2A1B8pjnXEINg+gGgw0KRUUXunvu1/OEOeC5L2Co1wAwhD7bhnaefi06Qi9AiwOag==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [darwin]
@@ -4557,14 +4574,20 @@ packages:
     cpu: [x64]
     os: [darwin]
 
+  '@swc/core-darwin-x64@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-GVQqC/vSOS/LPXYEF00/+JMwXIFBquIhBy4HEdfybkoRaoeLUHxdqkUhI6dW0lRe85QUrSkg69ylAXwD4foU8Q==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [darwin]
+
   '@swc/core-freebsd-x64@1.3.11':
     resolution: {integrity: sha512-02uqYktPp6WmZfZ2Crc/yIVOcgANtjo8ciHcT7yLHvz7v+S7gx1I2tyNGUFtTX5hcR2IFNGrL8Yj4DvpTABFHg==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [freebsd]
 
-  '@swc/core-linux-arm-gnueabihf@1.3.56':
-    resolution: {integrity: sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==}
+  '@swc/core-linux-arm-gnueabihf@1.6.13':
+    resolution: {integrity: sha512-f4gxxvDXVUm2HLYXRd311mSrmbpQF2MZ4Ja6XCQz1hWAxXdhRl1gpnZ+LH/xIfGSwQChrtLLVrkxdYUCVuIjFg==}
     engines: {node: '>=10'}
     cpu: [arm]
     os: [linux]
@@ -4575,8 +4598,14 @@ packages:
     cpu: [arm]
     os: [linux]
 
-  '@swc/core-linux-arm64-gnu@1.3.56':
-    resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==}
+  '@swc/core-linux-arm-gnueabihf@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-r2VnlO7ABL+fUAEJa/qJeqBKG2lxQV9XcgHIyeWHzcyf9G9frcevcXSHbJSGBIIxiADRuoyrYGfRwzsLXDt7jA==}
+    engines: {node: '>=10'}
+    cpu: [arm]
+    os: [linux]
+
+  '@swc/core-linux-arm64-gnu@1.6.13':
+    resolution: {integrity: sha512-Nf/eoW2CbG8s+9JoLtjl9FByBXyQ5cjdBsA4efO7Zw4p+YSuXDgc8HRPC+E2+ns0praDpKNZtLvDtmF2lL+2Gg==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -4587,8 +4616,14 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@swc/core-linux-arm64-musl@1.3.56':
-    resolution: {integrity: sha512-9gxL09BIiAv8zY0DjfnFf19bo8+P4T9tdhzPwcm+1yPJcY5yr1+YFWLNFzz01agtOj6VlZ2/wUJTaOfdjjtc+A==}
+  '@swc/core-linux-arm64-gnu@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-Pk+RwrvsY1fB2PE3b/RcIbVfvzc3pO8WcTvZjmXQENQ0Af0R+pIR+tnnXweZB48ZdfoBE0sPE3Du1SntoZOcnA==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@swc/core-linux-arm64-musl@1.6.13':
+    resolution: {integrity: sha512-2OysYSYtdw79prJYuKIiux/Gj0iaGEbpS2QZWCIY4X9sGoETJ5iMg+lY+YCrIxdkkNYd7OhIbXdYFyGs/w5LDg==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -4599,8 +4634,14 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@swc/core-linux-x64-gnu@1.3.56':
-    resolution: {integrity: sha512-n0ORNknl50vMRkll3BDO1E4WOqY6iISlPV1ZQCRLWQ6YQ2q8/WAryBxc2OAybcGHBUFkxyACpJukeU1QZ/9tNw==}
+  '@swc/core-linux-arm64-musl@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-spizj6AJ3xxVz/+7sbeXeqkiGrks4u451gGf4pzlhYqWZzxsWjSS9lZXVGJ6vHslofYsQEK0pIsN+F/wOIspUg==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@swc/core-linux-x64-gnu@1.6.13':
+    resolution: {integrity: sha512-PkR4CZYJNk5hcd2+tMWBpnisnmYsUzazI1O5X7VkIGFcGePTqJ/bWlfUIVVExWxvAI33PQFzLbzmN5scyIUyGQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -4611,8 +4652,14 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@swc/core-linux-x64-musl@1.3.56':
-    resolution: {integrity: sha512-r+D34WLAOAlJtfw1gaVWpHRwCncU9nzW9i7w9kSw4HpWYnHJOz54jLGSEmNsrhdTCz1VK2ar+V2ktFUsrlGlDA==}
+  '@swc/core-linux-x64-gnu@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-08zfcePPoBBBcoSHvNWK2OZk4EQtwB/tgFstDf5HROOKr9YqCxVYemWCwBFuqVHr6ALAoEeizPOqCilvk0jQnw==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@swc/core-linux-x64-musl@1.6.13':
+    resolution: {integrity: sha512-OdsY7wryTxCKwGQcwW9jwWg3cxaHBkTTHi91+5nm7hFPpmZMz1HivJrWAMwVE7iXFw+M4l6ugB/wCvpYrUAAjA==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -4623,8 +4670,14 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@swc/core-win32-arm64-msvc@1.3.56':
-    resolution: {integrity: sha512-29Yt75Is6X24z3x8h/xZC1HnDPkPpyLH9mDQiM6Cuc0I9mVr1XSriPEUB2N/awf5IE4SA8c+3IVq1DtKWbkJIw==}
+  '@swc/core-linux-x64-musl@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-hrkT5htUfC4DWHI8tI6DxBHQpw6LU6AuWjMuEJcHtGnHA3BeXxXrZsCPpuxKfUHEEQbdijK67pWWh5SCfrX+Lw==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@swc/core-win32-arm64-msvc@1.6.13':
+    resolution: {integrity: sha512-ap6uNmYjwk9M/+bFEuWRNl3hq4VqgQ/Lk+ID/F5WGqczNr0L7vEf+pOsRAn0F6EV+o/nyb3ePt8rLhE/wjHpPg==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [win32]
@@ -4635,8 +4688,14 @@ packages:
     cpu: [arm64]
     os: [win32]
 
-  '@swc/core-win32-ia32-msvc@1.3.56':
-    resolution: {integrity: sha512-mplp0zbYDrcHtfvkniXlXdB04e2qIjz2Gq/XHKr4Rnc6xVORJjjXF91IemXKpavx2oZYJws+LNJL7UFQ8jyCdQ==}
+  '@swc/core-win32-arm64-msvc@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-3SQj5cKo91+cNorNvOne2cqHW3Wcg+/irYts0lCGy3pruZXcgUKeTQ3kmV8pRPtXCs17YLysZOSyCF9DQIbacw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@swc/core-win32-ia32-msvc@1.6.13':
+    resolution: {integrity: sha512-IJ8KH4yIUHTnS/U1jwQmtbfQals7zWPG0a9hbEfIr4zI0yKzjd83lmtS09lm2Q24QBWOCFGEEbuZxR4tIlvfzA==}
     engines: {node: '>=10'}
     cpu: [ia32]
     os: [win32]
@@ -4647,8 +4706,14 @@ packages:
     cpu: [ia32]
     os: [win32]
 
-  '@swc/core-win32-x64-msvc@1.3.56':
-    resolution: {integrity: sha512-zp8MBnrw/bjdLenO/ifYzHrImSjKunqL0C2IF4LXYNRfcbYFh2NwobsVQMZ20IT0474lKRdlP8Oxdt+bHuXrzA==}
+  '@swc/core-win32-ia32-msvc@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-BiHjqoPhodpgrNXoPaflvpC98NXLa3jI6vV5ZSTLkpJ1JR+T9RcB+unkllr/mjqUZaqafW9AE784A9+wHzja3Q==}
+    engines: {node: '>=10'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@swc/core-win32-x64-msvc@1.6.13':
+    resolution: {integrity: sha512-f6/sx6LMuEnbuxtiSL/EkR0Y6qUHFw1XVrh6rwzKXptTipUdOY+nXpKoh+1UsBm/r7H0/5DtOdrn3q5ZHbFZjQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [win32]
@@ -4659,6 +4724,21 @@ packages:
     cpu: [x64]
     os: [win32]
 
+  '@swc/core-win32-x64-msvc@1.7.0-nightly-20240715.2':
+    resolution: {integrity: sha512-bfSnQxYLOKRakwQo/sJA8I/krU/TfxdlER41OlvEiBXVYEjJFMb7z9NyPsk/DBbqeaA1ywHun8ohBhuEwJWpDQ==}
+    engines: {node: '>=10'}
+    cpu: [x64]
+    os: [win32]
+
+  '@swc/core@1.6.13':
+    resolution: {integrity: sha512-eailUYex6fkfaQTev4Oa3mwn0/e3mQU4H8y1WPuImYQESOQDtVrowwUGDSc19evpBbHpKtwM+hw8nLlhIsF+Tw==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      '@swc/helpers': '*'
+    peerDependenciesMeta:
+      '@swc/helpers':
+        optional: true
+
   '@swc/core@1.6.6':
     resolution: {integrity: sha512-sHfmIUPUXNrQTwFMVCY5V5Ena2GTOeaWjS2GFUpjLhAgVfP90OP67DWow7+cYrfFtqBdILHuWnjkTcd0+uPKlg==}
     engines: {node: '>=10'}
@@ -4683,8 +4763,8 @@ packages:
   '@swc/wasm@1.2.130':
     resolution: {integrity: sha512-rNcJsBxS70+pv8YUWwf5fRlWX6JoY/HJc25HD/F8m6Kv7XhJdqPPMhyX6TKkUBPAG7TWlZYoxa+rHAjPy4Cj3Q==}
 
-  '@syuilo/aiscript@0.18.0':
-    resolution: {integrity: sha512-/iY9Vv4LLjtW/KUzId1QwXC4BlpIEPCMcoT7dyRhYdyxtwhS3Hx4b/4j1HYP+n3Pq9XKyW5zvkY72/+DNu4g6Q==}
+  '@syuilo/aiscript@0.19.0':
+    resolution: {integrity: sha512-ZWG4s1m6RrFjE7NeIMaxFz769YO1jW5ReTrOROrEO4IHheOrjxxJ/Ffe2TUNqX9/XxDloMwfWplKhfSzx8LGMA==}
 
   '@szmarczak/http-timer@4.0.6':
     resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
@@ -4700,44 +4780,44 @@ packages:
   '@tabler/icons@3.3.0':
     resolution: {integrity: sha512-PLVe9d7b59sKytbx00KgeGhQG3N176Ezv8YMmsnSz4s0ifDzMWlp/h2wEfQZ0ZNe8e377GY2OW6kovUe3Rnd0g==}
 
-  '@tensorflow/tfjs-backend-cpu@4.4.0':
-    resolution: {integrity: sha512-d4eln500/qNym78z9IrUUzF0ITBoJGLrxV8xd92kLVoXhg35Mm+zqUXShjFcrH8joOHOFuST0qZ0TbDDqcPzPA==}
+  '@tensorflow/tfjs-backend-cpu@4.20.0':
+    resolution: {integrity: sha512-1QRQ6AqAa/VB8JOArf5nY3Dc/QQHXbfuxgdIdQhKrABEHgvlaWt2Vv696UhIlVl75YoNY+vWlCwBdGQIKYfFGw==}
     engines: {yarn: '>= 1.3.2'}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-core': 4.20.0
 
-  '@tensorflow/tfjs-backend-webgl@4.4.0':
-    resolution: {integrity: sha512-TzQKvfAPgGt9cMG+5bVoTckoG1xr/PVJM/uODkPvzcMqi3j97kuWDXwkYJIgXldStmfiKkU7f5CmyD3Cq3E6BA==}
+  '@tensorflow/tfjs-backend-webgl@4.20.0':
+    resolution: {integrity: sha512-M03fJonJGxm2u3SCzRNA2JLh0gxaAye64SEmGAXOehizowxy42l+lMsPWU8xU7r7mN6PEilBNkuKAf5YJ7Xumg==}
     engines: {yarn: '>= 1.3.2'}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-core': 4.20.0
 
-  '@tensorflow/tfjs-converter@4.4.0':
-    resolution: {integrity: sha512-JUjpRStrAuw37tgPd5UENu0UjQVuJT09yF7KpOur4BriJ0uQqrbEZHMPHmvUtr5nYzkqlXJTuXIyxvEY/olNpg==}
+  '@tensorflow/tfjs-converter@4.20.0':
+    resolution: {integrity: sha512-UJ2ntQ1TNtVHB5qGMwB0j306bs3KH1E1HKJ9Dxvrc6PUaivOV+CPKqmbidOFG5LylXeRC36JBdhe+gVT2nFHNw==}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-core': 4.20.0
 
-  '@tensorflow/tfjs-core@4.4.0':
-    resolution: {integrity: sha512-Anxpc7cAOA0Q7EUXdTbQKMg3reFvrdkgDlaYzH9ZfkMq2CgLV4Au6E/s6HmbYn/VrAtWy9mLY5c/lLJqh4764g==}
+  '@tensorflow/tfjs-core@4.20.0':
+    resolution: {integrity: sha512-m/cc9qDc63al9UhdbXRUYTLGfJJlhuN5tylAX/2pJMLj32c8a6ThGDJYoKzpf32n5g3MQGYLchjClDxeGdXMPQ==}
     engines: {yarn: '>= 1.3.2'}
 
-  '@tensorflow/tfjs-data@4.4.0':
-    resolution: {integrity: sha512-aY4eq4cgrsrXeBU6ABZAAN3tV0fG4YcHd0z+cYuNXnCo+VEQLJnPmhn+xymZ4VQZQH4GXbVS4dV9pXMclFNRFw==}
+  '@tensorflow/tfjs-data@4.20.0':
+    resolution: {integrity: sha512-k6S8joXhoXkatcoT6mYCxBzRCsnrLfnl6xjLe46SnXO0oEEy4Vuzbmp5Ydl1uU2hHr73zL91EdAC1k8Hng/+oA==}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-core': 4.20.0
       seedrandom: ^3.0.5
 
-  '@tensorflow/tfjs-layers@4.4.0':
-    resolution: {integrity: sha512-OGC7shfiD9Gc698hINHK4y9slOJvu5m54tVNm4xf+WSNrw/avvgpar6yyoL5bakYIZNQvFNK75Yr8VRPR7oPeQ==}
+  '@tensorflow/tfjs-layers@4.20.0':
+    resolution: {integrity: sha512-SCHZH29Vyw+Y9eoaJHiaNo6yqM9vD3XCKncoczonRRywejm3FFqddg1AuWAfSE9XoNPE21o9PsknvKLl/Uh+Cg==}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.4.0
+      '@tensorflow/tfjs-core': 4.20.0
 
-  '@tensorflow/tfjs-node@4.4.0':
-    resolution: {integrity: sha512-+JSAddsupjSQUDZeb7QGOFkL3Tty3kjPHx8ethiYFzwTZJHCMvM7wZJd0Fqnjxym6A0KpsmB7SPZgwRRXVIlPA==}
+  '@tensorflow/tfjs-node@4.20.0':
+    resolution: {integrity: sha512-pVSOlzsVqh5ck3aiNPJCltB3ASKjsLqNPvJ28lXn9Xg648U4eHDk8G47m9w4uf0FdVcWDfjPM3hDCbBZ/E2KXg==}
     engines: {node: '>=8.11.0'}
 
-  '@tensorflow/tfjs@4.4.0':
-    resolution: {integrity: sha512-EmCsnzdvawyk4b+4JKaLLuicHcJQRZtL1zSy9AWJLiiHTbDDseYgLxfaCEfLk8v2bUe7SBXwl3n3B7OjgvH11Q==}
+  '@tensorflow/tfjs@4.20.0':
+    resolution: {integrity: sha512-+ZLfJq2jyIOE2/+yKPoyD/gfy3RZypbfMrlzvBDgodTK5jnexprihhX38hxilh9HPWvWQXJqiUjKJP5ECCikrw==}
     hasBin: true
 
   '@testing-library/dom@10.1.0':
@@ -5024,8 +5104,8 @@ packages:
   '@types/mysql@2.15.22':
     resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==}
 
-  '@types/node-fetch@2.6.4':
-    resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
+  '@types/node-fetch@2.6.11':
+    resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
 
   '@types/node@18.17.15':
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
@@ -5120,6 +5200,9 @@ packages:
   '@types/seedrandom@2.4.30':
     resolution: {integrity: sha512-AnxLHewubLVzoF/A4qdxBGHCKifw8cY32iro3DQX9TPcetE95zBeVt3jnsvtvAUf1vwzMfwzp4t/L2yqPlnjkQ==}
 
+  '@types/seedrandom@2.4.34':
+    resolution: {integrity: sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==}
+
   '@types/seedrandom@3.0.8':
     resolution: {integrity: sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==}
 
@@ -5183,9 +5266,6 @@ packages:
   '@types/web-push@3.6.3':
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
 
-  '@types/webgl-ext@0.0.30':
-    resolution: {integrity: sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==}
-
   '@types/wrap-ansi@3.0.0':
     resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==}
 
@@ -5489,8 +5569,8 @@ packages:
       '@vue/server-renderer':
         optional: true
 
-  '@webgpu/types@0.1.30':
-    resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==}
+  '@webgpu/types@0.1.38':
+    resolution: {integrity: sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==}
 
   '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15':
     resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==}
@@ -6007,8 +6087,8 @@ packages:
   buffer@6.0.3:
     resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
 
-  bufferutil@4.0.7:
-    resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
+  bufferutil@4.0.8:
+    resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==}
     engines: {node: '>=6.14.2'}
 
   bullmq@5.8.3:
@@ -7004,6 +7084,10 @@ packages:
     resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
+  eslint-scope@8.0.2:
+    resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
   eslint-visitor-keys@3.4.3:
     resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -7017,6 +7101,11 @@ packages:
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     hasBin: true
 
+  eslint@9.7.0:
+    resolution: {integrity: sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    hasBin: true
+
   espree@10.1.0:
     resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -7038,6 +7127,10 @@ packages:
     resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
     engines: {node: '>=0.10'}
 
+  esquery@1.6.0:
+    resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+    engines: {node: '>=0.10'}
+
   esrecurse@4.3.0:
     resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
     engines: {node: '>=4.0'}
@@ -7342,10 +7435,6 @@ packages:
     resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
     engines: {node: '>= 0.12'}
 
-  form-data@3.0.1:
-    resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
-    engines: {node: '>= 6'}
-
   form-data@4.0.0:
     resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
     engines: {node: '>= 6'}
@@ -7384,15 +7473,12 @@ packages:
     resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
     engines: {node: '>=10'}
 
-  fs-minipass@1.2.7:
-    resolution: {integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==}
-
   fs-minipass@2.1.0:
     resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
     engines: {node: '>= 8'}
 
-  fs-minipass@3.0.2:
-    resolution: {integrity: sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==}
+  fs-minipass@3.0.3:
+    resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
   fs.realpath@1.0.0:
@@ -8964,9 +9050,6 @@ packages:
     resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==}
     engines: {node: '>=8'}
 
-  minipass@2.9.0:
-    resolution: {integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==}
-
   minipass@3.3.6:
     resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
     engines: {node: '>=8'}
@@ -8983,9 +9066,6 @@ packages:
     resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
     engines: {node: '>=16 || 14 >=14.17'}
 
-  minizlib@1.3.3:
-    resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==}
-
   minizlib@2.1.2:
     resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
     engines: {node: '>= 8'}
@@ -9172,6 +9252,15 @@ packages:
       encoding:
         optional: true
 
+  node-fetch@2.6.13:
+    resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+
   node-fetch@2.7.0:
     resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
     engines: {node: 4.x || >=6.0.0}
@@ -9391,6 +9480,10 @@ packages:
     resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
     engines: {node: '>= 0.8.0'}
 
+  optionator@0.9.4:
+    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+    engines: {node: '>= 0.8.0'}
+
   ora@5.4.1:
     resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
     engines: {node: '>=10'}
@@ -11036,10 +11129,6 @@ packages:
   tar-stream@3.1.6:
     resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==}
 
-  tar@4.4.19:
-    resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==}
-    engines: {node: '>=4.5'}
-
   tar@6.2.1:
     resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     engines: {node: '>=10'}
@@ -11068,6 +11157,11 @@ packages:
     engines: {node: '>=10'}
     hasBin: true
 
+  terser@5.31.2:
+    resolution: {integrity: sha512-LGyRZVFm/QElZHy/CPr/O4eNZOZIzsrQ92y4v9UJe/pFJjypje2yI3C2FmPtvUEnhadlSbmG2nXtdcjHOjCfxw==}
+    engines: {node: '>=10'}
+    hasBin: true
+
   test-exclude@6.0.0:
     resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
     engines: {node: '>=8'}
@@ -11535,8 +11629,8 @@ packages:
       '@types/react':
         optional: true
 
-  utf-8-validate@6.0.3:
-    resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==}
+  utf-8-validate@6.0.4:
+    resolution: {integrity: sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==}
     engines: {node: '>=6.14.2'}
 
   util-deprecate@1.0.2:
@@ -11794,6 +11888,10 @@ packages:
     resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
     engines: {node: '>= 8'}
 
+  web-streams-polyfill@4.0.0:
+    resolution: {integrity: sha512-0zJXHRAYEjM2tUfZ2DiSOHAa2aw1tisnnhU3ufD57R8iefL+DcdJyRBRyJpG+NUimDgbTI/lH+gAE1PAvV3Cgw==}
+    engines: {node: '>= 8'}
+
   webidl-conversions@3.0.1:
     resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
 
@@ -11870,6 +11968,10 @@ packages:
     resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
     engines: {node: '>= 10.0.0'}
 
+  word-wrap@1.2.5:
+    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+    engines: {node: '>=0.10.0'}
+
   wordwrap@1.0.0:
     resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
 
@@ -13699,10 +13801,10 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@digitalbazaar/http-client@3.4.1(web-streams-polyfill@3.2.1)':
+  '@digitalbazaar/http-client@3.4.1(web-streams-polyfill@4.0.0)':
     dependencies:
       ky: 0.33.3
-      ky-universal: 0.11.0(ky@0.33.3)(web-streams-polyfill@3.2.1)
+      ky-universal: 0.11.0(ky@0.33.3)(web-streams-polyfill@4.0.0)
       undici: 5.28.2
     transitivePeerDependencies:
       - web-streams-polyfill
@@ -14006,11 +14108,18 @@ snapshots:
       eslint: 9.6.0
       eslint-visitor-keys: 3.4.3
 
+  '@eslint-community/eslint-utils@4.4.0(eslint@9.7.0)':
+    dependencies:
+      eslint: 9.7.0
+      eslint-visitor-keys: 3.4.3
+
   '@eslint-community/regexpp@4.10.0': {}
 
+  '@eslint-community/regexpp@4.11.0': {}
+
   '@eslint-community/regexpp@4.6.2': {}
 
-  '@eslint/compat@1.1.0': {}
+  '@eslint/compat@1.1.1': {}
 
   '@eslint/config-array@0.17.0':
     dependencies:
@@ -14036,6 +14145,8 @@ snapshots:
 
   '@eslint/js@9.6.0': {}
 
+  '@eslint/js@9.7.0': {}
+
   '@eslint/object-schema@2.1.4': {}
 
   '@fal-works/esbuild-plugin-global-externals@2.1.2': {}
@@ -14080,12 +14191,12 @@ snapshots:
     dependencies:
       fast-json-stringify: 5.8.0
 
-  '@fastify/http-proxy@9.5.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)':
+  '@fastify/http-proxy@9.5.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
     dependencies:
       '@fastify/reply-from': 9.0.1
       fast-querystring: 1.1.2
       fastify-plugin: 4.5.0
-      ws: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
     transitivePeerDependencies:
       - bufferutil
       - utf-8-validate
@@ -14474,13 +14585,13 @@ snapshots:
       '@types/yargs': 17.0.19
       chalk: 4.1.2
 
-  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))':
+  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))':
     dependencies:
       glob: 7.2.3
       glob-promise: 4.2.2(glob@7.2.3)
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.5.3)
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
     optionalDependencies:
       typescript: 5.5.3
 
@@ -14507,6 +14618,12 @@ snapshots:
       '@jridgewell/gen-mapping': 0.3.5
       '@jridgewell/trace-mapping': 0.3.25
 
+  '@jridgewell/source-map@0.3.6':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.5
+      '@jridgewell/trace-mapping': 0.3.25
+    optional: true
+
   '@jridgewell/sourcemap-codec@1.4.14': {}
 
   '@jridgewell/sourcemap-codec@1.4.15': {}
@@ -14596,9 +14713,9 @@ snapshots:
 
   '@misskey-dev/browser-image-resizer@2024.1.0': {}
 
-  '@misskey-dev/eslint-plugin@2.0.2(@eslint/compat@1.1.0)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)':
+  '@misskey-dev/eslint-plugin@2.0.2(@eslint/compat@1.1.1)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)':
     dependencies:
-      '@eslint/compat': 1.1.0
+      '@eslint/compat': 1.1.1
       '@typescript-eslint/eslint-plugin': 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
       '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
       eslint: 9.6.0
@@ -15924,11 +16041,11 @@ snapshots:
     dependencies:
       '@storybook/global': 5.0.0
 
-  '@storybook/addon-interactions@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
+  '@storybook/addon-interactions@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
     dependencies:
       '@storybook/global': 5.0.0
       '@storybook/instrumenter': 8.1.11
-      '@storybook/test': 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+      '@storybook/test': 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
       '@storybook/types': 8.1.11
       polished: 4.2.2
       ts-dedent: 2.2.0
@@ -16034,7 +16151,7 @@ snapshots:
       - prettier
       - supports-color
 
-  '@storybook/builder-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))':
+  '@storybook/builder-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))':
     dependencies:
       '@storybook/channels': 8.1.11
       '@storybook/client-logger': 8.1.11
@@ -16053,7 +16170,7 @@ snapshots:
       fs-extra: 11.1.1
       magic-string: 0.30.10
       ts-dedent: 2.2.0
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
     optionalDependencies:
       typescript: 5.5.3
     transitivePeerDependencies:
@@ -16069,7 +16186,7 @@ snapshots:
       telejson: 7.2.0
       tiny-invariant: 1.3.3
 
-  '@storybook/cli@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
+  '@storybook/cli@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)':
     dependencies:
       '@babel/core': 7.24.7
       '@babel/types': 7.24.7
@@ -16077,7 +16194,7 @@ snapshots:
       '@storybook/codemod': 8.1.11
       '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
       '@storybook/core-events': 8.1.11
-      '@storybook/core-server': 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+      '@storybook/core-server': 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
       '@storybook/csf-tools': 8.1.11
       '@storybook/node-logger': 8.1.11
       '@storybook/telemetry': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
@@ -16200,7 +16317,7 @@ snapshots:
       '@storybook/csf': 0.1.9
       ts-dedent: 2.2.0
 
-  '@storybook/core-server@8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)':
+  '@storybook/core-server@8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)':
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@babel/core': 7.24.7
@@ -16246,7 +16363,7 @@ snapshots:
       util: 0.12.5
       util-deprecate: 1.0.2
       watchpack: 2.4.0
-      ws: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -16364,11 +16481,11 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  '@storybook/react-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))':
+  '@storybook/react-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))':
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
       '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
-      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
+      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
       '@storybook/node-logger': 8.1.11
       '@storybook/react': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)
       '@storybook/types': 8.1.11
@@ -16379,7 +16496,7 @@ snapshots:
       react-dom: 18.3.1(react@18.3.1)
       resolve: 1.22.8
       tsconfig-paths: 4.2.0
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -16450,14 +16567,14 @@ snapshots:
       - prettier
       - supports-color
 
-  '@storybook/test@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
+  '@storybook/test@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
     dependencies:
       '@storybook/client-logger': 8.1.11
       '@storybook/core-events': 8.1.11
       '@storybook/instrumenter': 8.1.11
       '@storybook/preview-api': 8.1.11
       '@testing-library/dom': 10.1.0
-      '@testing-library/jest-dom': 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))
+      '@testing-library/jest-dom': 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
       '@testing-library/user-event': 14.5.2(@testing-library/dom@10.1.0)
       '@vitest/expect': 1.6.0
       '@vitest/spy': 1.6.0
@@ -16485,16 +16602,16 @@ snapshots:
       '@types/express': 4.17.17
       file-system-cache: 2.3.0
 
-  '@storybook/vue3-vite@8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))':
+  '@storybook/vue3-vite@8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
-      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
-      '@storybook/core-server': 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
+      '@storybook/core-server': 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
       '@storybook/types': 8.1.11
       '@storybook/vue3': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))
       find-package-json: 1.2.0
       magic-string: 0.30.10
       typescript: 5.5.3
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
       vue-component-meta: 2.0.16(typescript@5.5.3)
       vue-docgen-api: 4.75.1(vue@3.4.31(typescript@5.5.3))
     transitivePeerDependencies:
@@ -16546,71 +16663,117 @@ snapshots:
       '@swc/wasm': 1.2.130
     optional: true
 
-  '@swc/core-darwin-arm64@1.3.56':
+  '@swc/core-darwin-arm64@1.6.13':
     optional: true
 
   '@swc/core-darwin-arm64@1.6.6':
     optional: true
 
-  '@swc/core-darwin-x64@1.3.56':
+  '@swc/core-darwin-arm64@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-darwin-x64@1.6.13':
     optional: true
 
   '@swc/core-darwin-x64@1.6.6':
     optional: true
 
+  '@swc/core-darwin-x64@1.7.0-nightly-20240715.2':
+    optional: true
+
   '@swc/core-freebsd-x64@1.3.11':
     dependencies:
       '@swc/wasm': 1.2.130
     optional: true
 
-  '@swc/core-linux-arm-gnueabihf@1.3.56':
+  '@swc/core-linux-arm-gnueabihf@1.6.13':
     optional: true
 
   '@swc/core-linux-arm-gnueabihf@1.6.6':
     optional: true
 
-  '@swc/core-linux-arm64-gnu@1.3.56':
+  '@swc/core-linux-arm-gnueabihf@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-linux-arm64-gnu@1.6.13':
     optional: true
 
   '@swc/core-linux-arm64-gnu@1.6.6':
     optional: true
 
-  '@swc/core-linux-arm64-musl@1.3.56':
+  '@swc/core-linux-arm64-gnu@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-linux-arm64-musl@1.6.13':
     optional: true
 
   '@swc/core-linux-arm64-musl@1.6.6':
     optional: true
 
-  '@swc/core-linux-x64-gnu@1.3.56':
+  '@swc/core-linux-arm64-musl@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-linux-x64-gnu@1.6.13':
     optional: true
 
   '@swc/core-linux-x64-gnu@1.6.6':
     optional: true
 
-  '@swc/core-linux-x64-musl@1.3.56':
+  '@swc/core-linux-x64-gnu@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-linux-x64-musl@1.6.13':
     optional: true
 
   '@swc/core-linux-x64-musl@1.6.6':
     optional: true
 
-  '@swc/core-win32-arm64-msvc@1.3.56':
+  '@swc/core-linux-x64-musl@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-win32-arm64-msvc@1.6.13':
     optional: true
 
   '@swc/core-win32-arm64-msvc@1.6.6':
     optional: true
 
-  '@swc/core-win32-ia32-msvc@1.3.56':
+  '@swc/core-win32-arm64-msvc@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-win32-ia32-msvc@1.6.13':
     optional: true
 
   '@swc/core-win32-ia32-msvc@1.6.6':
     optional: true
 
-  '@swc/core-win32-x64-msvc@1.3.56':
+  '@swc/core-win32-ia32-msvc@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core-win32-x64-msvc@1.6.13':
     optional: true
 
   '@swc/core-win32-x64-msvc@1.6.6':
     optional: true
 
+  '@swc/core-win32-x64-msvc@1.7.0-nightly-20240715.2':
+    optional: true
+
+  '@swc/core@1.6.13':
+    dependencies:
+      '@swc/counter': 0.1.3
+      '@swc/types': 0.1.9
+    optionalDependencies:
+      '@swc/core-darwin-arm64': 1.6.13
+      '@swc/core-darwin-x64': 1.6.13
+      '@swc/core-linux-arm-gnueabihf': 1.6.13
+      '@swc/core-linux-arm64-gnu': 1.6.13
+      '@swc/core-linux-arm64-musl': 1.6.13
+      '@swc/core-linux-x64-gnu': 1.6.13
+      '@swc/core-linux-x64-musl': 1.6.13
+      '@swc/core-win32-arm64-msvc': 1.6.13
+      '@swc/core-win32-ia32-msvc': 1.6.13
+      '@swc/core-win32-x64-msvc': 1.6.13
+
   '@swc/core@1.6.6':
     dependencies:
       '@swc/counter': 0.1.3
@@ -16629,6 +16792,13 @@ snapshots:
 
   '@swc/counter@0.1.3': {}
 
+  '@swc/jest@0.2.36(@swc/core@1.6.13)':
+    dependencies:
+      '@jest/create-cache-key-function': 29.7.0
+      '@swc/core': 1.6.13
+      '@swc/counter': 0.1.3
+      jsonc-parser: 3.2.0
+
   '@swc/jest@0.2.36(@swc/core@1.6.6)':
     dependencies:
       '@jest/create-cache-key-function': 29.7.0
@@ -16643,7 +16813,7 @@ snapshots:
   '@swc/wasm@1.2.130':
     optional: true
 
-  '@syuilo/aiscript@0.18.0':
+  '@syuilo/aiscript@0.19.0':
     dependencies:
       seedrandom: 3.0.5
       stringz: 2.1.0
@@ -16663,76 +16833,74 @@ snapshots:
 
   '@tabler/icons@3.3.0': {}
 
-  '@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-backend-cpu@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
-      '@types/seedrandom': 2.4.30
+      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@types/seedrandom': 2.4.34
       seedrandom: 3.0.5
 
-  '@tensorflow/tfjs-backend-webgl@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-backend-webgl@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
-      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
+      '@tensorflow/tfjs-backend-cpu': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
+      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
       '@types/offscreencanvas': 2019.3.0
-      '@types/seedrandom': 2.4.30
-      '@types/webgl-ext': 0.0.30
+      '@types/seedrandom': 2.4.34
       seedrandom: 3.0.5
 
-  '@tensorflow/tfjs-converter@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-converter@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
+      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
 
-  '@tensorflow/tfjs-core@4.4.0(encoding@0.1.13)':
+  '@tensorflow/tfjs-core@4.20.0(encoding@0.1.13)':
     dependencies:
       '@types/long': 4.0.2
       '@types/offscreencanvas': 2019.7.0
       '@types/seedrandom': 2.4.30
-      '@types/webgl-ext': 0.0.30
-      '@webgpu/types': 0.1.30
+      '@webgpu/types': 0.1.38
       long: 4.0.0
       node-fetch: 2.6.11(encoding@0.1.13)
       seedrandom: 3.0.5
     transitivePeerDependencies:
       - encoding
 
-  '@tensorflow/tfjs-data@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)':
+  '@tensorflow/tfjs-data@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
-      '@types/node-fetch': 2.6.4
-      node-fetch: 2.6.11(encoding@0.1.13)
+      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@types/node-fetch': 2.6.11
+      node-fetch: 2.6.13(encoding@0.1.13)
       seedrandom: 3.0.5
       string_decoder: 1.3.0
     transitivePeerDependencies:
       - encoding
 
-  '@tensorflow/tfjs-layers@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-layers@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
+      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
 
-  '@tensorflow/tfjs-node@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)':
+  '@tensorflow/tfjs-node@4.20.0(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
       '@mapbox/node-pre-gyp': 1.0.9(encoding@0.1.13)
-      '@tensorflow/tfjs': 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
+      '@tensorflow/tfjs': 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
       adm-zip: 0.5.10
       google-protobuf: 3.21.2
       https-proxy-agent: 2.2.4
       progress: 2.0.3
       rimraf: 2.7.1
-      tar: 4.4.19
+      tar: 6.2.1
     transitivePeerDependencies:
       - encoding
       - seedrandom
       - supports-color
     optional: true
 
-  '@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)':
+  '@tensorflow/tfjs@4.20.0(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
-      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
-      '@tensorflow/tfjs-backend-webgl': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
-      '@tensorflow/tfjs-converter': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
-      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
-      '@tensorflow/tfjs-data': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)
-      '@tensorflow/tfjs-layers': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-backend-cpu': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
+      '@tensorflow/tfjs-backend-webgl': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
+      '@tensorflow/tfjs-converter': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
+      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@tensorflow/tfjs-data': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)
+      '@tensorflow/tfjs-layers': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
       argparse: 1.0.10
       chalk: 4.1.2
       core-js: 3.29.1
@@ -16764,7 +16932,7 @@ snapshots:
       lz-string: 1.5.0
       pretty-format: 27.5.1
 
-  '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
+  '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
     dependencies:
       '@adobe/css-tools': 4.3.3
       '@babel/runtime': 7.23.4
@@ -16778,7 +16946,7 @@ snapshots:
       '@jest/globals': 29.7.0
       '@types/jest': 29.5.12
       jest: 29.7.0(@types/node@20.14.9)
-      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
+      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
 
   '@testing-library/user-event@14.5.2(@testing-library/dom@10.1.0)':
     dependencies:
@@ -17050,10 +17218,10 @@ snapshots:
     dependencies:
       '@types/node': 20.14.9
 
-  '@types/node-fetch@2.6.4':
+  '@types/node-fetch@2.6.11':
     dependencies:
       '@types/node': 20.14.9
-      form-data: 3.0.1
+      form-data: 4.0.0
 
   '@types/node@18.17.15': {}
 
@@ -17154,6 +17322,8 @@ snapshots:
 
   '@types/seedrandom@2.4.30': {}
 
+  '@types/seedrandom@2.4.34': {}
+
   '@types/seedrandom@3.0.8': {}
 
   '@types/semver@7.5.8': {}
@@ -17205,8 +17375,6 @@ snapshots:
     dependencies:
       '@types/node': 20.14.9
 
-  '@types/webgl-ext@0.0.30': {}
-
   '@types/wrap-ansi@3.0.0': {}
 
   '@types/ws@8.5.10':
@@ -17224,16 +17392,16 @@ snapshots:
       '@types/node': 20.14.9
     optional: true
 
-  '@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/type-utils': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.6.0
+      eslint: 9.7.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -17244,16 +17412,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3))(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/type-utils': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.6.0
+      eslint: 9.7.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -17282,27 +17450,45 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@6.11.0(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)':
+    dependencies:
+      '@eslint-community/regexpp': 4.10.0
+      '@typescript-eslint/parser': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+      '@typescript-eslint/scope-manager': 7.15.0
+      '@typescript-eslint/type-utils': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+      '@typescript-eslint/utils': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+      '@typescript-eslint/visitor-keys': 7.15.0
+      eslint: 9.7.0
+      graphemer: 1.4.0
+      ignore: 5.3.1
+      natural-compare: 1.4.0
+      ts-api-utils: 1.3.0(typescript@5.5.3)
+    optionalDependencies:
+      typescript: 5.5.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/parser@6.11.0(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.6.0
+      eslint: 9.7.0
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@7.1.0(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 7.1.0
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.6.0
+      eslint: 9.7.0
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
@@ -17321,6 +17507,19 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3)':
+    dependencies:
+      '@typescript-eslint/scope-manager': 7.15.0
+      '@typescript-eslint/types': 7.15.0
+      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+      '@typescript-eslint/visitor-keys': 7.15.0
+      debug: 4.3.5(supports-color@8.1.1)
+      eslint: 9.7.0
+    optionalDependencies:
+      typescript: 5.5.3
+    transitivePeerDependencies:
+      - supports-color
+
   '@typescript-eslint/scope-manager@6.11.0':
     dependencies:
       '@typescript-eslint/types': 6.11.0
@@ -17336,24 +17535,24 @@ snapshots:
       '@typescript-eslint/types': 7.15.0
       '@typescript-eslint/visitor-keys': 7.15.0
 
-  '@typescript-eslint/type-utils@6.11.0(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/type-utils@6.11.0(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.11.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
       debug: 4.3.5(supports-color@8.1.1)
-      eslint: 9.6.0
+      eslint: 9.7.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/type-utils@7.1.0(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/type-utils@7.1.0(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@9.6.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.6.0
+      eslint: 9.7.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
       typescript: 5.3.3
@@ -17372,6 +17571,18 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@typescript-eslint/type-utils@7.15.0(eslint@9.7.0)(typescript@5.5.3)':
+    dependencies:
+      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+      '@typescript-eslint/utils': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+      debug: 4.3.5(supports-color@8.1.1)
+      eslint: 9.7.0
+      ts-api-utils: 1.3.0(typescript@5.5.3)
+    optionalDependencies:
+      typescript: 5.5.3
+    transitivePeerDependencies:
+      - supports-color
+
   '@typescript-eslint/types@6.11.0': {}
 
   '@typescript-eslint/types@7.1.0': {}
@@ -17422,29 +17633,29 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/utils@6.11.0(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/utils@6.11.0(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
-      eslint: 9.6.0
+      eslint: 9.7.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@7.1.0(eslint@9.6.0)(typescript@5.3.3)':
+  '@typescript-eslint/utils@7.1.0(eslint@9.7.0)(typescript@5.3.3)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 7.1.0
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      eslint: 9.6.0
+      eslint: 9.7.0
       semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
@@ -17461,6 +17672,17 @@ snapshots:
       - supports-color
       - typescript
 
+  '@typescript-eslint/utils@7.15.0(eslint@9.7.0)(typescript@5.5.3)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
+      '@typescript-eslint/scope-manager': 7.15.0
+      '@typescript-eslint/types': 7.15.0
+      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
+      eslint: 9.7.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+
   '@typescript-eslint/visitor-keys@6.11.0':
     dependencies:
       '@typescript-eslint/types': 6.11.0
@@ -17478,12 +17700,12 @@ snapshots:
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-vue@5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))':
+  '@vitejs/plugin-vue@5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))':
     dependencies:
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
       vue: 3.4.31(typescript@5.5.3)
 
-  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1))':
+  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@bcoe/v8-coverage': 0.2.3
@@ -17498,7 +17720,7 @@ snapshots:
       std-env: 3.7.0
       strip-literal: 2.1.0
       test-exclude: 6.0.0
-      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
+      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
     transitivePeerDependencies:
       - supports-color
 
@@ -17660,7 +17882,7 @@ snapshots:
     optionalDependencies:
       '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3))
 
-  '@webgpu/types@0.1.30': {}
+  '@webgpu/types@0.1.38': {}
 
   '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.19.11)':
     dependencies:
@@ -18258,7 +18480,7 @@ snapshots:
       base64-js: 1.5.1
       ieee754: 1.2.1
 
-  bufferutil@4.0.7:
+  bufferutil@4.0.8:
     dependencies:
       node-gyp-build: 4.6.0
     optional: true
@@ -18290,10 +18512,10 @@ snapshots:
   cacache@18.0.0:
     dependencies:
       '@npmcli/fs': 3.1.0
-      fs-minipass: 3.0.2
+      fs-minipass: 3.0.3
       glob: 10.4.2
       lru-cache: 10.2.2
-      minipass: 7.0.4
+      minipass: 7.1.2
       minipass-collect: 1.0.2
       minipass-flush: 1.0.5
       minipass-pipeline: 1.2.4
@@ -19426,6 +19648,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.7.0):
+    dependencies:
+      debug: 3.2.7(supports-color@8.1.1)
+    optionalDependencies:
+      '@typescript-eslint/parser': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+      eslint: 9.7.0
+      eslint-import-resolver-node: 0.3.9
+    transitivePeerDependencies:
+      - supports-color
+
   eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0):
     dependencies:
       array-includes: 3.1.7
@@ -19453,16 +19685,43 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-vue@9.26.0(eslint@9.6.0):
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0):
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
-      eslint: 9.6.0
+      array-includes: 3.1.7
+      array.prototype.findlastindex: 1.2.3
+      array.prototype.flat: 1.3.2
+      array.prototype.flatmap: 1.3.2
+      debug: 3.2.7(supports-color@8.1.1)
+      doctrine: 2.1.0
+      eslint: 9.7.0
+      eslint-import-resolver-node: 0.3.9
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.7.0)
+      hasown: 2.0.0
+      is-core-module: 2.13.1
+      is-glob: 4.0.3
+      minimatch: 3.1.2
+      object.fromentries: 2.0.7
+      object.groupby: 1.0.1
+      object.values: 1.1.7
+      semver: 6.3.1
+      tsconfig-paths: 3.15.0
+    optionalDependencies:
+      '@typescript-eslint/parser': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+    transitivePeerDependencies:
+      - eslint-import-resolver-typescript
+      - eslint-import-resolver-webpack
+      - supports-color
+
+  eslint-plugin-vue@9.26.0(eslint@9.7.0):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
+      eslint: 9.7.0
       globals: 13.24.0
       natural-compare: 1.4.0
       nth-check: 2.1.1
       postcss-selector-parser: 6.0.16
       semver: 7.6.0
-      vue-eslint-parser: 9.4.3(eslint@9.6.0)
+      vue-eslint-parser: 9.4.3(eslint@9.7.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -19479,6 +19738,11 @@ snapshots:
       esrecurse: 4.3.0
       estraverse: 5.3.0
 
+  eslint-scope@8.0.2:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
   eslint-visitor-keys@3.4.3: {}
 
   eslint-visitor-keys@4.0.0: {}
@@ -19522,6 +19786,45 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  eslint@9.7.0:
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
+      '@eslint-community/regexpp': 4.11.0
+      '@eslint/config-array': 0.17.0
+      '@eslint/eslintrc': 3.1.0
+      '@eslint/js': 9.7.0
+      '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.3.0
+      '@nodelib/fs.walk': 1.2.8
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.5(supports-color@8.1.1)
+      escape-string-regexp: 4.0.0
+      eslint-scope: 8.0.2
+      eslint-visitor-keys: 4.0.0
+      espree: 10.1.0
+      esquery: 1.6.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 8.0.0
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      ignore: 5.3.1
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      is-path-inside: 3.0.3
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.4
+      strip-ansi: 6.0.1
+      text-table: 0.2.0
+    transitivePeerDependencies:
+      - supports-color
+
   espree@10.1.0:
     dependencies:
       acorn: 8.12.0
@@ -19544,6 +19847,10 @@ snapshots:
     dependencies:
       estraverse: 5.3.0
 
+  esquery@1.6.0:
+    dependencies:
+      estraverse: 5.3.0
+
   esrecurse@4.3.0:
     dependencies:
       estraverse: 5.3.0
@@ -19970,12 +20277,6 @@ snapshots:
       combined-stream: 1.0.8
       mime-types: 2.1.35
 
-  form-data@3.0.1:
-    dependencies:
-      asynckit: 0.4.0
-      combined-stream: 1.0.8
-      mime-types: 2.1.35
-
   form-data@4.0.0:
     dependencies:
       asynckit: 0.4.0
@@ -20019,18 +20320,13 @@ snapshots:
       jsonfile: 6.1.0
       universalify: 2.0.0
 
-  fs-minipass@1.2.7:
-    dependencies:
-      minipass: 2.9.0
-    optional: true
-
   fs-minipass@2.1.0:
     dependencies:
       minipass: 3.3.6
 
-  fs-minipass@3.0.2:
+  fs-minipass@3.0.3:
     dependencies:
-      minipass: 5.0.0
+      minipass: 7.1.2
 
   fs.realpath@1.0.0: {}
 
@@ -21271,7 +21567,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+  jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4):
     dependencies:
       cssstyle: 4.0.1
       data-urls: 5.0.0
@@ -21292,7 +21588,7 @@ snapshots:
       whatwg-encoding: 3.1.1
       whatwg-mimetype: 4.0.0
       whatwg-url: 14.0.0
-      ws: 8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       xml-name-validator: 5.0.0
     transitivePeerDependencies:
       - bufferutil
@@ -21346,9 +21642,9 @@ snapshots:
     optionalDependencies:
       graceful-fs: 4.2.11
 
-  jsonld@8.3.2(web-streams-polyfill@3.2.1):
+  jsonld@8.3.2(web-streams-polyfill@4.0.0):
     dependencies:
-      '@digitalbazaar/http-client': 3.4.1(web-streams-polyfill@3.2.1)
+      '@digitalbazaar/http-client': 3.4.1(web-streams-polyfill@4.0.0)
       canonicalize: 1.0.8
       lru-cache: 6.0.0
       rdf-canonize: 3.4.0
@@ -21399,13 +21695,13 @@ snapshots:
 
   kleur@3.0.3: {}
 
-  ky-universal@0.11.0(ky@0.33.3)(web-streams-polyfill@3.2.1):
+  ky-universal@0.11.0(ky@0.33.3)(web-streams-polyfill@4.0.0):
     dependencies:
       abort-controller: 3.0.0
       ky: 0.33.3
       node-fetch: 3.3.2
     optionalDependencies:
-      web-streams-polyfill: 3.2.1
+      web-streams-polyfill: 4.0.0
 
   ky@0.33.3: {}
 
@@ -21575,7 +21871,7 @@ snapshots:
       cacache: 18.0.0
       http-cache-semantics: 4.1.1
       is-lambda: 1.0.1
-      minipass: 7.0.4
+      minipass: 7.1.2
       minipass-fetch: 3.0.3
       minipass-flush: 1.0.5
       minipass-pipeline: 1.2.4
@@ -22029,12 +22325,6 @@ snapshots:
     dependencies:
       minipass: 3.3.6
 
-  minipass@2.9.0:
-    dependencies:
-      safe-buffer: 5.2.1
-      yallist: 3.1.1
-    optional: true
-
   minipass@3.3.6:
     dependencies:
       yallist: 4.0.0
@@ -22045,11 +22335,6 @@ snapshots:
 
   minipass@7.1.2: {}
 
-  minizlib@1.3.3:
-    dependencies:
-      minipass: 2.9.0
-    optional: true
-
   minizlib@2.1.2:
     dependencies:
       minipass: 3.3.6
@@ -22241,6 +22526,12 @@ snapshots:
     optionalDependencies:
       encoding: 0.1.13
 
+  node-fetch@2.6.13(encoding@0.1.13):
+    dependencies:
+      whatwg-url: 5.0.0
+    optionalDependencies:
+      encoding: 0.1.13
+
   node-fetch@2.7.0(encoding@0.1.13):
     dependencies:
       whatwg-url: 5.0.0
@@ -22371,10 +22662,10 @@ snapshots:
       set-blocking: 2.0.0
     optional: true
 
-  nsfwjs@2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)):
+  nsfwjs@2.4.2(@tensorflow/tfjs@4.20.0(encoding@0.1.13)(seedrandom@3.0.5)):
     dependencies:
       '@nsfw-filter/gif-frames': 1.0.2
-      '@tensorflow/tfjs': 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
+      '@tensorflow/tfjs': 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
 
   nth-check@2.1.1:
     dependencies:
@@ -22496,6 +22787,15 @@ snapshots:
       prelude-ls: 1.2.1
       type-check: 0.4.0
 
+  optionator@0.9.4:
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.5
+
   ora@5.4.1:
     dependencies:
       bl: 4.1.0
@@ -24038,9 +24338,9 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  storybook@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3):
+  storybook@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4):
     dependencies:
-      '@storybook/cli': 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)
+      '@storybook/cli': 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
     transitivePeerDependencies:
       - '@babel/preset-env'
       - bufferutil
@@ -24236,17 +24536,6 @@ snapshots:
       fast-fifo: 1.3.0
       streamx: 2.15.0
 
-  tar@4.4.19:
-    dependencies:
-      chownr: 1.1.4
-      fs-minipass: 1.2.7
-      minipass: 2.9.0
-      minizlib: 1.3.3
-      mkdirp: 0.5.6
-      safe-buffer: 5.2.1
-      yallist: 3.1.1
-    optional: true
-
   tar@6.2.1:
     dependencies:
       chownr: 2.0.0
@@ -24284,6 +24573,14 @@ snapshots:
       commander: 2.20.3
       source-map-support: 0.5.21
 
+  terser@5.31.2:
+    dependencies:
+      '@jridgewell/source-map': 0.3.6
+      acorn: 8.12.0
+      commander: 2.20.3
+      source-map-support: 0.5.21
+    optional: true
+
   test-exclude@6.0.0:
     dependencies:
       '@istanbuljs/schema': 0.1.3
@@ -24681,7 +24978,7 @@ snapshots:
     optionalDependencies:
       '@types/react': 18.0.28
 
-  utf-8-validate@6.0.3:
+  utf-8-validate@6.0.4:
     dependencies:
       node-gyp-build: 4.6.0
     optional: true
@@ -24745,13 +25042,13 @@ snapshots:
       unist-util-stringify-position: 4.0.0
       vfile-message: 4.0.2
 
-  vite-node@1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
+  vite-node@1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2):
     dependencies:
       cac: 6.7.14
       debug: 4.3.5(supports-color@8.1.1)
       pathe: 1.1.2
       picocolors: 1.0.0
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -24764,7 +25061,7 @@ snapshots:
 
   vite-plugin-turbosnap@1.0.3: {}
 
-  vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
+  vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.38
@@ -24773,16 +25070,16 @@ snapshots:
       '@types/node': 20.14.9
       fsevents: 2.3.3
       sass: 1.77.6
-      terser: 5.31.1
+      terser: 5.31.2
 
-  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)):
+  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)):
     dependencies:
       cross-fetch: 3.1.6(encoding@0.1.13)
-      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1)
+      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
     transitivePeerDependencies:
       - encoding
 
-  vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.77.6)(terser@5.31.1):
+  vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2):
     dependencies:
       '@vitest/expect': 1.6.0
       '@vitest/runner': 1.6.0
@@ -24801,13 +25098,13 @@ snapshots:
       strip-literal: 2.1.0
       tinybench: 2.6.0
       tinypool: 0.8.4
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
-      vite-node: 1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
+      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
+      vite-node: 1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
       why-is-node-running: 2.2.2
     optionalDependencies:
       '@types/node': 20.14.9
       happy-dom: 10.0.3
-      jsdom: 24.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      jsdom: 24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -24878,10 +25175,10 @@ snapshots:
       vue: 3.4.31(typescript@5.5.3)
       vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.31(typescript@5.5.3))
 
-  vue-eslint-parser@9.4.3(eslint@9.6.0):
+  vue-eslint-parser@9.4.3(eslint@9.7.0):
     dependencies:
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.6.0
+      eslint: 9.7.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1
@@ -24968,6 +25265,9 @@ snapshots:
 
   web-streams-polyfill@3.2.1: {}
 
+  web-streams-polyfill@4.0.0:
+    optional: true
+
   webidl-conversions@3.0.1: {}
 
   webidl-conversions@7.0.0: {}
@@ -25052,6 +25352,8 @@ snapshots:
       assert-never: 1.2.1
       babel-walk: 3.0.0-canary-5
 
+  word-wrap@1.2.5: {}
+
   wordwrap@1.0.0: {}
 
   wrap-ansi@6.2.0:
@@ -25085,10 +25387,10 @@ snapshots:
       imurmurhash: 0.1.4
       signal-exit: 3.0.7
 
-  ws@8.17.1(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+  ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4):
     optionalDependencies:
-      bufferutil: 4.0.7
-      utf-8-validate: 6.0.3
+      bufferutil: 4.0.8
+      utf-8-validate: 6.0.4
 
   xev@3.0.2: {}
 

From f0d738d8bf96bee3cf87de377fd39839914ac7d7 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Wed, 17 Jul 2024 08:15:21 +0000
Subject: [PATCH 118/206] Bump version to 2024.7.0-beta.0

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c634276d2..75f739f546 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## Unreleased
+## 2024.7.0
 
 ### Note
 - デッキUIの新着ノートをサウンドで通知する機能の追加(v2024.5.0)に伴い、以前から動作しなくなっていたクライアント設定内の「アンテナ受信」「チャンネル通知」サウンドを削除しました。
diff --git a/package.json b/package.json
index bf8415d212..872a6b4b5e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.5.0",
+	"version": "2024.7.0-beta.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 00342d3dbc..1567ed7e61 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.5.0",
+	"version": "2024.7.0-beta.0",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From bda1de8a676c223cbde012aa041c0a1901af2a3d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Wed, 17 Jul 2024 18:19:06 +0900
Subject: [PATCH 119/206] use pnpm@9.5.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 872a6b4b5e..0dd7afb9e9 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
 		"type": "git",
 		"url": "https://github.com/misskey-dev/misskey.git"
 	},
-	"packageManager": "pnpm@9.0.6",
+	"packageManager": "pnpm@9.5.0",
 	"workspaces": [
 		"packages/frontend",
 		"packages/backend",

From 8b4933cc48a8c296931fa795720297fffa7a9144 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Wed, 17 Jul 2024 19:08:39 +0900
Subject: [PATCH 120/206] fix changelog (wrong category)

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75f739f546..02497883f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,6 @@
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
-- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
 
 ### Client
 - Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
@@ -21,6 +20,7 @@
 - Enhance: サーバー情報ページ・お問い合わせページを改善  
   (Cherry-picked from https://github.com/taiyme/misskey/pull/238)
 - Enhance: AiScriptを0.19.0にアップデート
+- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正

From 68bcd91d57c9a11ae4191dfefa156c4454c8a073 Mon Sep 17 00:00:00 2001
From: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com>
Date: Wed, 17 Jul 2024 21:52:05 +0900
Subject: [PATCH 121/206] chore: Use clipboard API directly (#14227)

* chore: Use clipboard API directly

* fix: Fix lint
---
 packages/frontend/src/components/MkCode.vue   |  2 +-
 .../src/components/MkDrive.folder.vue         |  2 +-
 .../frontend/src/components/MkInviteCode.vue  |  2 +-
 .../frontend/src/components/MkKeyValue.vue    |  2 +-
 .../frontend/src/components/MkPageWindow.vue  |  2 +-
 .../frontend/src/components/global/MkA.vue    |  2 +-
 .../src/components/global/MkCustomEmoji.vue   |  2 +-
 .../src/components/global/MkEmoji.vue         |  2 +-
 packages/frontend/src/os.ts                   |  2 +-
 packages/frontend/src/pages/channel.vue       |  2 +-
 packages/frontend/src/pages/clip.vue          |  2 +-
 .../src/pages/drop-and-fusion.game.vue        |  2 +-
 packages/frontend/src/pages/emojis.emoji.vue  |  2 +-
 packages/frontend/src/pages/flash/flash.vue   |  2 +-
 packages/frontend/src/pages/gallery/post.vue  |  2 +-
 packages/frontend/src/pages/page.vue          |  2 +-
 .../frontend/src/pages/settings/plugin.vue    |  2 +-
 .../src/pages/settings/theme.manage.vue       |  2 +-
 .../frontend/src/scripts/copy-to-clipboard.ts | 31 ++-----------------
 .../src/scripts/get-drive-file-menu.ts        |  2 +-
 .../frontend/src/scripts/get-note-menu.ts     |  2 +-
 .../frontend/src/scripts/get-user-menu.ts     |  2 +-
 22 files changed, 23 insertions(+), 50 deletions(-)

diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index a3c80e743b..1d4c0b6366 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -30,7 +30,7 @@ import * as os from '@/os.js';
 import MkLoading from '@/components/global/MkLoading.vue';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 
 const props = defineProps<{
 	code: string;
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 1790e57c24..c940596cde 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -39,7 +39,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 import { claimAchievement } from '@/scripts/achievements.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { MenuItem } from '@/types/menu.js';
 
 const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/components/MkInviteCode.vue b/packages/frontend/src/components/MkInviteCode.vue
index 1c6f412dc1..de51a98789 100644
--- a/packages/frontend/src/components/MkInviteCode.vue
+++ b/packages/frontend/src/components/MkInviteCode.vue
@@ -62,7 +62,7 @@ import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkButton from '@/components/MkButton.vue';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 
diff --git a/packages/frontend/src/components/MkKeyValue.vue b/packages/frontend/src/components/MkKeyValue.vue
index 20b1ef2be2..50c9e16e5e 100644
--- a/packages/frontend/src/components/MkKeyValue.vue
+++ b/packages/frontend/src/components/MkKeyValue.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index aa4509b14b..bd86b01591 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -33,7 +33,7 @@ import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue'
 import RouterView from '@/components/global/RouterView.vue';
 import MkWindow from '@/components/MkWindow.vue';
 import { popout as _popout } from '@/scripts/popout.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
 import { useScrollPositionManager } from '@/nirax.js';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index d1e9113c48..3a45ca429f 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -16,7 +16,7 @@ export type MkABehavior = 'window' | 'browser' | null;
 <script lang="ts" setup>
 import { computed, inject, shallowRef } from 'vue';
 import * as os from '@/os.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
 import { i18n } from '@/i18n.js';
 import { useRouter } from '@/router/supplier.js';
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index 4581908a8a..dff56cd7f0 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -31,7 +31,7 @@ import { defaultStore } from '@/store.js';
 import { customEmojisMap } from '@/custom-emojis.js';
 import * as os from '@/os.js';
 import { misskeyApiGet } from '@/scripts/misskey-api.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import * as sound from '@/scripts/sound.js';
 import { i18n } from '@/i18n.js';
 import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue
index 65f75642b2..fa780d4ad3 100644
--- a/packages/frontend/src/components/global/MkEmoji.vue
+++ b/packages/frontend/src/components/global/MkEmoji.vue
@@ -14,7 +14,7 @@ import { char2fluentEmojiFilePath, char2twemojiFilePath } from '@/scripts/emoji-
 import { defaultStore } from '@/store.js';
 import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js';
 import * as os from '@/os.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import * as sound from '@/scripts/sound.js';
 import { i18n } from '@/i18n.js';
 
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index a0b0e6c833..3085f33e21 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -22,7 +22,7 @@ import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
 import MkPopupMenu from '@/components/MkPopupMenu.vue';
 import MkContextMenu from '@/components/MkContextMenu.vue';
 import { MenuItem } from '@/types/menu.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { pleaseLogin } from '@/scripts/please-login.js';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index a895df76e8..3c3ff08aee 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -93,7 +93,7 @@ import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
 import { PageHeaderItem } from '@/types/page-header.js';
 import { isSupportShare } from '@/scripts/navigator.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { useRouter } from '@/router/supplier.js';
 
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index fd64a55c65..fb984de368 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -43,7 +43,7 @@ import { url } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
 import { clipsCache } from '@/cache.js';
 import { isSupportShare } from '@/scripts/navigator.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 
 const props = defineProps<{
 	clipId: string,
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index 10bcfa6d4e..0f0b7e1ea8 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -210,7 +210,7 @@ import { apiUrl } from '@/config.js';
 import { $i } from '@/account.js';
 import * as sound from '@/scripts/sound.js';
 import MkRange from '@/components/MkRange.vue';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 
 type FrontendMonoDefinition = {
 	id: string;
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index ae3a2c31e3..97429c29a4 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import { misskeyApiGet } from '@/scripts/misskey-api.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { i18n } from '@/i18n.js';
 import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
 
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 8a63176d00..020463a133 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -78,7 +78,7 @@ import MkCode from '@/components/MkCode.vue';
 import { defaultStore } from '@/store.js';
 import { $i } from '@/account.js';
 import { isSupportShare } from '@/scripts/navigator.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { pleaseLogin } from '@/scripts/please-login.js';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 615675225d..fc2b5f810c 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -77,7 +77,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { defaultStore } from '@/store.js';
 import { $i } from '@/account.js';
 import { isSupportShare } from '@/scripts/navigator.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { useRouter } from '@/router/supplier.js';
 
 const router = useRouter();
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index e2f04eb764..20b776aaa2 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -125,7 +125,7 @@ import { $i } from '@/account.js';
 import { isSupportShare } from '@/scripts/navigator.js';
 import { instance } from '@/instance.js';
 import { getStaticImageUrl } from '@/scripts/media-proxy.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 
 const props = defineProps<{
 	pageName: string;
diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue
index 9804454e66..3c3dcfe41e 100644
--- a/packages/frontend/src/pages/settings/plugin.vue
+++ b/packages/frontend/src/pages/settings/plugin.vue
@@ -82,7 +82,7 @@ import MkCode from '@/components/MkCode.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import * as os from '@/os.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { ColdDeviceStorage } from '@/store.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue
index 8a94d7388b..579ca6b20b 100644
--- a/packages/frontend/src/pages/settings/theme.manage.vue
+++ b/packages/frontend/src/pages/settings/theme.manage.vue
@@ -38,7 +38,7 @@ import MkSelect from '@/components/MkSelect.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
 import { Theme, getBuiltinThemesRef } from '@/scripts/theme.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import * as os from '@/os.js';
 import { getThemes, removeTheme } from '@/theme-store.js';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/scripts/copy-to-clipboard.ts b/packages/frontend/src/scripts/copy-to-clipboard.ts
index 216c0464b3..7e0bb25606 100644
--- a/packages/frontend/src/scripts/copy-to-clipboard.ts
+++ b/packages/frontend/src/scripts/copy-to-clipboard.ts
@@ -6,33 +6,6 @@
 /**
  * Clipboardに値をコピー(TODO: 文字列以外も対応)
  */
-export default val => {
-	// 空div 生成
-	const tmp = document.createElement('div');
-	// 選択用のタグ生成
-	const pre = document.createElement('pre');
-
-	// 親要素のCSSで user-select: none だとコピーできないので書き換える
-	pre.style.webkitUserSelect = 'auto';
-	pre.style.userSelect = 'auto';
-
-	tmp.appendChild(pre).textContent = val;
-
-	// 要素を画面外へ
-	const s = tmp.style;
-	s.position = 'fixed';
-	s.right = '200%';
-
-	// body に追加
-	document.body.appendChild(tmp);
-	// 要素を選択
-	document.getSelection().selectAllChildren(tmp);
-
-	// クリップボードにコピー
-	const result = document.execCommand('copy');
-
-	// 要素削除
-	document.body.removeChild(tmp);
-
-	return result;
+export function copyToClipboard(input: string | null) {
+	if (input) navigator.clipboard.writeText(input);
 };
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index 14c83ed637..7c6c4b4db4 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -6,7 +6,7 @@
 import * as Misskey from 'misskey-js';
 import { defineAsyncComponent } from 'vue';
 import { i18n } from '@/i18n.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { MenuItem } from '@/types/menu.js';
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 418b6abc88..ebb96d1746 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -11,7 +11,7 @@ import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
 import { defaultStore, noteActions } from '@/store.js';
 import { miLocalStorage } from '@/local-storage.js';
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 2d1fea8ea4..dacafb859f 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -7,7 +7,7 @@ import { toUnicode } from 'punycode';
 import { defineAsyncComponent, ref, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
-import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
 import { host, url } from '@/config.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';

From 6942a920c8c5bc51b358341bb9b451addcc4ec2b Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Thu, 18 Jul 2024 00:31:52 +0900
Subject: [PATCH 122/206] refactor(frontend): Improve typing (#14240)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Improve typing

* Remove redundant promise

* Refactor

* Update packages/frontend/src/scripts/mfm-function-picker.ts

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

* Update packages/frontend/src/scripts/mfm-function-picker.ts

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
---
 .../src/scripts/mfm-function-picker.ts        | 29 ++++++++-----------
 1 file changed, 12 insertions(+), 17 deletions(-)

diff --git a/packages/frontend/src/scripts/mfm-function-picker.ts b/packages/frontend/src/scripts/mfm-function-picker.ts
index 8867a8c50f..9938e534c1 100644
--- a/packages/frontend/src/scripts/mfm-function-picker.ts
+++ b/packages/frontend/src/scripts/mfm-function-picker.ts
@@ -7,29 +7,24 @@ import { Ref, nextTick } from 'vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { MFM_TAGS } from '@/const.js';
+import type { MenuItem } from '@/types/menu.js';
 
 /**
  * MFMの装飾のリストを表示する
  */
-export function mfmFunctionPicker(src: any, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
-	return new Promise((res, rej) => {
-		os.popupMenu([{
-			text: i18n.ts.addMfmFunction,
-			type: 'label',
-		}, ...getFunctionList(textArea, textRef)], src);
-	});
+export function mfmFunctionPicker(src: HTMLElement | EventTarget | null, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
+	os.popupMenu([{
+		text: i18n.ts.addMfmFunction,
+		type: 'label',
+	}, ...getFunctionList(textArea, textRef)], src);
 }
 
-function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) : object[] {
-	const ret: object[] = [];
-	MFM_TAGS.forEach(tag => {
-		ret.push({
-			text: tag,
-			icon: 'ti ti-icons',
-			action: () => add(textArea, textRef, tag),
-		});
-	});
-	return ret;
+function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>): MenuItem[] {
+	return MFM_TAGS.map(tag => ({
+		text: tag,
+		icon: 'ti ti-icons',
+		action: () => add(textArea, textRef, tag),
+	}));
 }
 
 function add(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, type: string) {

From 3331f3972a02f8b662a3bf3e5a94f7b0a45ebbb6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 18 Jul 2024 01:22:24 +0900
Subject: [PATCH 123/206] =?UTF-8?q?fix(frontend):=20=E3=80=8C=E3=82=A2?=
 =?UTF-8?q?=E3=83=8B=E3=83=A1=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E7=94=BB?=
 =?UTF-8?q?=E5=83=8F=E3=82=92=E5=86=8D=E7=94=9F=E3=81=97=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=80=8D=E3=81=8C=E3=82=AA=E3=83=B3=E3=81=AE=E3=81=A8=E3=81=8D?=
 =?UTF-8?q?=E3=81=AB=E3=83=90=E3=83=8A=E3=83=BC=E7=94=BB=E5=83=8F=E3=83=BB?=
 =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E8=83=8C=E6=99=AF=E7=94=BB?=
 =?UTF-8?q?=E5=83=8F=E3=81=8C=E3=82=A2=E3=83=8B=E3=83=A1=E3=83=BC=E3=82=B7?=
 =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#14243)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: stop animating banner and backgrounds when stop showing animated images is enabled

(cherry picked from commit 8fe2596316e9688509745706ea424f0b4bfd4136)

* chore: nest ternary

(cherry picked from commit 2783fe5f5bd7c0647db9f9b6fb5e000e4f411092)

* chore: flip ternary

(cherry picked from commit b9d66f824cff373cc53bfa846a56c16f456a6d5b)

* update changelog

---------

Co-authored-by: Marie <marie@kaifa.ch>
---
 CHANGELOG.md                                     |  2 ++
 packages/frontend/src/components/MkUserInfo.vue  |  4 +++-
 packages/frontend/src/components/MkUserPopup.vue |  3 ++-
 packages/frontend/src/pages/user/home.vue        | 11 +++++++++--
 4 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 02497883f0..9eea70d3b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,8 @@
 - Fix: ショートカットキーが連打できる問題を修正  
   (Cherry-picked from https://github.com/taiyme/misskey/pull/234)
 - Fix: MkSignin.vueのcredentialRequestからReactivityを削除(ProxyがPasskey認証処理に渡ることを避けるため)
+- Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正  
+  (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574)
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index d6f1ae453c..f0b9606590 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div class="_panel" :class="$style.root">
-	<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
+	<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''"></div>
 	<MkAvatar :class="$style.avatar" :user="user" indicator/>
 	<div :class="$style.title">
 		<MkA :class="$style.name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
@@ -41,6 +41,8 @@ import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
+import { defaultStore } from '@/store.js';
 
 defineProps<{
 	user: Misskey.entities.UserDetailed;
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index 41b27a1afb..ea1241002e 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 >
 	<div v-if="showing" :class="$style.root" class="_popup _shadow" :style="{ zIndex, top: top + 'px', left: left + 'px' }" @mouseover="() => { emit('mouseover'); }" @mouseleave="() => { emit('mouseleave'); }">
 		<div v-if="user != null">
-			<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''">
+			<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''">
 				<span v-if="$i && $i.id != user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span>
 			</div>
 			<svg viewBox="0 0 128 128" :class="$style.avatarBack">
@@ -67,6 +67,7 @@ import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 import { $i } from '@/account.js';
 import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
 
 const props = defineProps<{
 	showing: boolean;
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index d67990e9a2..1b4ec47469 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -173,6 +173,7 @@ import { confetti } from '@/scripts/confetti.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 import { useRouter } from '@/router/supplier.js';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
 
 function calcAge(birthdate: string): number {
 	const date = new Date(birthdate);
@@ -220,8 +221,14 @@ watch(moderationNote, async () => {
 
 const style = computed(() => {
 	if (props.user.bannerUrl == null) return {};
-	return {
-		backgroundImage: `url(${ props.user.bannerUrl })`,
+	if (defaultStore.state.disableShowingAnimatedImages) {
+		return {
+			backgroundImage: `url(${ getStaticImageUrl(props.user.bannerUrl) })`,
+		};
+	} else {
+		return {
+			backgroundImage: `url(${ props.user.bannerUrl })`,
+		};
 	};
 });
 

From 5f88d56d9699863da58deb243db114da53f12f6b Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 18 Jul 2024 01:28:17 +0900
Subject: [PATCH 124/206] =?UTF-8?q?perf(federation):=20Ed25519=E7=BD=B2?=
 =?UTF-8?q?=E5=90=8D=E3=81=AB=E5=AF=BE=E5=BF=9C=E3=81=99=E3=82=8B=20(#1346?=
 =?UTF-8?q?4)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 1. ed25519キーペアを発行・Personとして公開鍵を送受信

* validate additionalPublicKeys

* getAuthUserFromApIdはmainを選ぶ

* :v:

* fix

* signatureAlgorithm

* set publicKeyCache lifetime

* refresh

* httpMessageSignatureAcceptable

* ED25519_SIGNED_ALGORITHM

* ED25519_PUBLIC_KEY_SIGNATURE_ALGORITHM

* remove sign additionalPublicKeys signature requirements

* httpMessageSignaturesSupported

* httpMessageSignaturesImplementationLevel

* httpMessageSignaturesImplementationLevel: '01'

* perf(federation): Use hint for getAuthUserFromApId (#13470)

* Hint for getAuthUserFromApId

* とどのつまりこれでいいのか?

* use @misskey-dev/node-http-message-signatures

* fix

* signedPost, signedGet

* ap-request.tsを復活させる

* remove digest prerender

* fix test?

* fix test

* add httpMessageSignaturesImplementationLevel to FederationInstance

* ManyToOne

* fetchPersonWithRenewal

* exactKey

* :v:

* use const

* use gen-key-pair fn. from  '@misskey-dev/node-http-message-signatures'

* update node-http-message-signatures

* fix

* @misskey-dev/node-http-message-signatures@0.0.0-alpha.11

* getAuthUserFromApIdでupdatePersonの頻度を増やす

* cacheRaw.date

* use requiredInputs
https://github.com/misskey-dev/misskey/pull/13464#discussion_r1509964359

* update @misskey-dev/node-http-message-signatures

* clean up

* err msg

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

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

* fix httpMessageSignaturesImplementationLevel validation

* fix test

* fix

* comment

* comment

* improve test

* fix

* use Promise.all in genRSAAndEd25519KeyPair

* refreshAndprepareEd25519KeyPair

* refreshAndfindKey

* commetn

* refactor public keys add

* digestプリレンダを復活させる

RFC実装時にどうするか考える

* fix, async

* fix

* !== true

* use save

* Deliver update person when new key generated (not tested)
https://github.com/misskey-dev/misskey/pull/13464#issuecomment-1977049061

* 循環参照で落ちるのを解消?

* fix?

* Revert "fix?"

This reverts commit 0082f6f8e8c5d5febd14933ba9a1ac643f70ca92.

* a

* logger

* log

* change logger

* 秘密鍵の変更は、フラグではなく鍵を引き回すようにする

* addAllKnowingSharedInboxRecipe

* nanka meccha kaeta

* delivre

* キャッシュ有効チェックはロック取得前に行う

* @misskey-dev/node-http-message-signatures@0.0.3

* PrivateKeyPem

* getLocalUserPrivateKey

* fix test

* if

* fix ap-request

* update node-http-message-signatures

* fix type error

* update package

* fix type

* update package

* retry no key

* @misskey-dev/node-http-message-signatures@0.0.8

* fix type error

* log keyid

* logger

* db-resolver

* JSON.stringify

* HTTP Signatureがなかったり使えなかったりしそうな場合にLD Signatureを活用するように

* inbox-delayed use actor if no signature

* ユーザーとキーの同一性チェックはhostの一致にする

* log signature parse err

* save array

* とりあえずtryで囲っておく

* fetchPersonWithRenewalでエラーが起きたら古いデータを返す

* use transactionalEntityManager

* fix spdx

* @misskey-dev/node-http-message-signatures@0.0.10

* add comment

* fix

* publicKeyに配列が入ってもいいようにする
https://github.com/misskey-dev/misskey/pull/13950

* define additionalPublicKeys

* fix

* merge fix

* refreshAndprepareEd25519KeyPair → refreshAndPrepareEd25519KeyPair

* remove gen-key-pair.ts

* defaultMaxListeners = 512

* Revert "defaultMaxListeners = 512"

This reverts commit f2c412c18057a9300540794ccbe4dfbf6d259ed6.

* genRSAAndEd25519KeyPairではキーを直列に生成する?

* maxConcurrency: 8

* maxConcurrency: 16

* maxConcurrency: 8

* Revert "genRSAAndEd25519KeyPairではキーを直列に生成する?"

This reverts commit d0aada55c1ed5aa98f18731ec82f3ac5eb5a6c16.

* maxWorkers: '90%'

* Revert "maxWorkers: '90%'"

This reverts commit 9e0a93f110456320d6485a871f014f7cdab29b33.

* e2e/timelines.tsで個々のテストに対するtimeoutを削除, maxConcurrency: 32

* better error handling of this.userPublickeysRepository.delete

* better comment

* set result to keypairEntityCache

* deliverJobConcurrency: 16, deliverJobPerSec: 1024, inboxJobConcurrency: 4

* inboxJobPerSec: 64

* delete request.headers['host'];

* fix

* // node-fetch will generate this for us. if we keep 'Host', it won't change with redirects!

* move delete host

* modify comment

* modify comment

* fix correct → collect

* refreshAndfindKey → refreshAndFindKey

* modify comment

* modify attachLdSignature

* getApId, InboxProcessorService

* TODO

* [skip ci] add CHANGELOG

---------

Co-authored-by: MeiMei <30769358+mei23@users.noreply.github.com>
Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 .config/docker_example.yml                    |   6 +-
 .config/example.yml                           |   8 +-
 .devcontainer/devcontainer.yml                |   8 +-
 CHANGELOG.md                                  |   2 +
 CONTRIBUTING.md                               |   2 +-
 chart/files/default.yml                       |   8 +-
 .../migration/1708980134301-APMultipleKeys.js |  39 ++++
 .../migration/1709242519122-HttpSignImplLv.js |  16 ++
 .../1709269211718-APMultipleKeysFix1.js       |  16 ++
 packages/backend/package.json                 |   2 +-
 .../backend/src/@types/http-signature.d.ts    |  82 -------
 packages/backend/src/const.ts                 |   5 +
 .../backend/src/core/AccountUpdateService.ts  |  27 ++-
 .../src/core/CreateSystemUserService.ts       |   7 +-
 .../src/core/FetchInstanceMetadataService.ts  |  61 +++--
 .../backend/src/core/GlobalEventService.ts    |   1 +
 .../backend/src/core/HttpRequestService.ts    |   2 +-
 packages/backend/src/core/QueueService.ts     |  17 +-
 packages/backend/src/core/RelayService.ts     |  13 +-
 packages/backend/src/core/SignupService.ts    |  22 +-
 .../backend/src/core/UserKeypairService.ts    | 155 ++++++++++++-
 .../backend/src/core/UserSuspendService.ts    |  66 ++----
 packages/backend/src/core/WebfingerService.ts |   2 +-
 .../core/activitypub/ApDbResolverService.ts   | 178 +++++++++++----
 .../activitypub/ApDeliverManagerService.ts    |  95 +++++++-
 .../src/core/activitypub/ApInboxService.ts    |  11 +-
 .../src/core/activitypub/ApRendererService.ts |  21 +-
 .../src/core/activitypub/ApRequestService.ts  | 210 +++++++-----------
 .../src/core/activitypub/ApResolverService.ts |   8 +-
 .../src/core/activitypub/misc/contexts.ts     |   1 +
 .../activitypub/models/ApPersonService.ts     | 114 ++++++++--
 packages/backend/src/core/activitypub/type.ts |  11 +-
 .../core/entities/InstanceEntityService.ts    |   1 +
 packages/backend/src/misc/cache.ts            |   3 +
 packages/backend/src/misc/gen-key-pair.ts     |  43 +---
 packages/backend/src/models/Instance.ts       |   5 +
 packages/backend/src/models/UserKeypair.ts    |  24 +-
 packages/backend/src/models/UserPublickey.ts  |  14 +-
 .../models/json-schema/federation-instance.ts |   4 +
 .../src/queue/QueueProcessorService.ts        |   8 +-
 .../processors/DeliverProcessorService.ts     |  38 ++--
 .../queue/processors/InboxProcessorService.ts | 130 ++++++-----
 packages/backend/src/queue/types.ts           |  23 +-
 .../src/server/ActivityPubServerService.ts    | 101 ++++-----
 .../src/server/NodeinfoServerService.ts       |   7 +
 .../endpoints/admin/queue/inbox-delayed.ts    |   3 +-
 packages/backend/test/e2e/timelines.ts        |  10 +-
 packages/backend/test/misc/mock-resolver.ts   |   2 +
 .../test/unit/FetchInstanceMetadataService.ts |  23 +-
 packages/backend/test/unit/ap-request.ts      |  90 +++++---
 packages/misskey-js/src/autogen/types.ts      |   1 +
 pnpm-lock.yaml                                |  44 ++--
 52 files changed, 1096 insertions(+), 694 deletions(-)
 create mode 100644 packages/backend/migration/1708980134301-APMultipleKeys.js
 create mode 100644 packages/backend/migration/1709242519122-HttpSignImplLv.js
 create mode 100644 packages/backend/migration/1709269211718-APMultipleKeysFix1.js
 delete mode 100644 packages/backend/src/@types/http-signature.d.ts

diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index d347882d1a..bd0ad2872a 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -164,12 +164,12 @@ id: 'aidx'
 #clusterLimit: 1
 
 # Job concurrency per worker
-# deliverJobConcurrency: 128
-# inboxJobConcurrency: 16
+# deliverJobConcurrency: 16
+# inboxJobConcurrency: 4
 
 # Job rate limiter
 # deliverJobPerSec: 128
-# inboxJobPerSec: 32
+# inboxJobPerSec: 64
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/.config/example.yml b/.config/example.yml
index b11cbd1373..0d525f61c4 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -230,15 +230,15 @@ id: 'aidx'
 #clusterLimit: 1
 
 # Job concurrency per worker
-#deliverJobConcurrency: 128
-#inboxJobConcurrency: 16
+#deliverJobConcurrency: 16
+#inboxJobConcurrency: 4
 #relationshipJobConcurrency: 16
 # What's relationshipJob?:
 #  Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
 
 # Job rate limiter
-#deliverJobPerSec: 128
-#inboxJobPerSec: 32
+#deliverJobPerSec: 1024
+#inboxJobPerSec: 64
 #relationshipJobPerSec: 64
 
 # Job attempts
diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml
index beefcfd0a2..d74d741e02 100644
--- a/.devcontainer/devcontainer.yml
+++ b/.devcontainer/devcontainer.yml
@@ -157,12 +157,12 @@ id: 'aidx'
 #clusterLimit: 1
 
 # Job concurrency per worker
-# deliverJobConcurrency: 128
-# inboxJobConcurrency: 16
+# deliverJobConcurrency: 16
+# inboxJobConcurrency: 4
 
 # Job rate limiter
-# deliverJobPerSec: 128
-# inboxJobPerSec: 32
+# deliverJobPerSec: 1024
+# inboxJobPerSec: 64
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9eea70d3b0..395716bbe2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
 - Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
   - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
+- Feat: 連合に使うHTTP SignaturesがEd25519鍵に対応するように #13464
+  - Ed25519署名に対応するサーバーが増えると、deliverで要求されるサーバーリソースが削減されます
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b718f3703f..532a2dc66f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -185,7 +185,7 @@ TODO
 ## Environment Variable
 
 - `MISSKEY_CONFIG_YML`: Specify the file path of config.yml instead of default.yml (e.g. `2nd.yml`).
-- `MISSKEY_WEBFINGER_USE_HTTP`: If it's set true, WebFinger requests will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION.
+- `MISSKEY_USE_HTTP`: If it's set true, federation requests (like nodeinfo and webfinger) will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION. (was `MISSKEY_WEBFINGER_USE_HTTP`)
 
 ## Continuous integration
 Misskey uses GitHub Actions for executing automated tests.
diff --git a/chart/files/default.yml b/chart/files/default.yml
index f98b8ebfee..4017588fa0 100644
--- a/chart/files/default.yml
+++ b/chart/files/default.yml
@@ -178,12 +178,12 @@ id: "aidx"
 #clusterLimit: 1
 
 # Job concurrency per worker
-# deliverJobConcurrency: 128
-# inboxJobConcurrency: 16
+# deliverJobConcurrency: 16
+# inboxJobConcurrency: 4
 
 # Job rate limiter
-# deliverJobPerSec: 128
-# inboxJobPerSec: 32
+# deliverJobPerSec: 1024
+# inboxJobPerSec: 64
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/packages/backend/migration/1708980134301-APMultipleKeys.js b/packages/backend/migration/1708980134301-APMultipleKeys.js
new file mode 100644
index 0000000000..ca55526c6e
--- /dev/null
+++ b/packages/backend/migration/1708980134301-APMultipleKeys.js
@@ -0,0 +1,39 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class APMultipleKeys1708980134301 {
+    name = 'APMultipleKeys1708980134301'
+
+    async up(queryRunner) {
+        await queryRunner.query(`DROP INDEX "public"."IDX_171e64971c780ebd23fae140bb"`);
+        await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PublicKey" character varying(128)`);
+        await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PrivateKey" character varying(128)`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_10c146e4b39b443ede016f6736d"`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_0db6a5fdb992323449edc8ee421"`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_171e64971c780ebd23fae140bba" PRIMARY KEY ("keyId")`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`);
+        await queryRunner.query(`CREATE INDEX "IDX_10c146e4b39b443ede016f6736" ON "user_publickey" ("userId") `);
+        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
+        await queryRunner.query(`DROP INDEX "public"."IDX_10c146e4b39b443ede016f6736"`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_171e64971c780ebd23fae140bba"`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_0db6a5fdb992323449edc8ee421"`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_10c146e4b39b443ede016f6736d" PRIMARY KEY ("userId")`);
+        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" DROP DEFAULT`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" TYPE "public"."user_profile_followersVisibility_enum_old" USING "followersVisibility"::"text"::"public"."user_profile_followersVisibility_enum_old"`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" SET DEFAULT 'public'`);
+        await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PrivateKey"`);
+        await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PublicKey"`);
+        await queryRunner.query(`CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `);
+    }
+}
diff --git a/packages/backend/migration/1709242519122-HttpSignImplLv.js b/packages/backend/migration/1709242519122-HttpSignImplLv.js
new file mode 100644
index 0000000000..7748bae006
--- /dev/null
+++ b/packages/backend/migration/1709242519122-HttpSignImplLv.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class HttpSignImplLv1709242519122 {
+    name = 'HttpSignImplLv1709242519122'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" ADD "httpMessageSignaturesImplementationLevel" character varying(16) NOT NULL DEFAULT '00'`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "httpMessageSignaturesImplementationLevel"`);
+    }
+}
diff --git a/packages/backend/migration/1709269211718-APMultipleKeysFix1.js b/packages/backend/migration/1709269211718-APMultipleKeysFix1.js
new file mode 100644
index 0000000000..d2011802f2
--- /dev/null
+++ b/packages/backend/migration/1709269211718-APMultipleKeysFix1.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class APMultipleKeys1709269211718 {
+    name = 'APMultipleKeys1709269211718'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`);
+    }
+
+    async down(queryRunner) {
+			await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`);
+    }
+}
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 22fdc5cf16..893171ebd6 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -79,13 +79,13 @@
 		"@fastify/multipart": "8.3.0",
 		"@fastify/static": "7.0.4",
 		"@fastify/view": "9.1.0",
+		"@misskey-dev/node-http-message-signatures": "0.0.10",
 		"@misskey-dev/sharp-read-bmp": "1.2.0",
 		"@misskey-dev/summaly": "5.1.0",
 		"@napi-rs/canvas": "^0.1.53",
 		"@nestjs/common": "10.3.10",
 		"@nestjs/core": "10.3.10",
 		"@nestjs/testing": "10.3.10",
-		"@peertube/http-signature": "1.7.0",
 		"@sentry/node": "8.13.0",
 		"@sentry/profiling-node": "8.13.0",
 		"@simplewebauthn/server": "10.0.0",
diff --git a/packages/backend/src/@types/http-signature.d.ts b/packages/backend/src/@types/http-signature.d.ts
deleted file mode 100644
index 75b62e55f0..0000000000
--- a/packages/backend/src/@types/http-signature.d.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-declare module '@peertube/http-signature' {
-	import type { IncomingMessage, ClientRequest } from 'node:http';
-
-	interface ISignature {
-		keyId: string;
-		algorithm: string;
-		headers: string[];
-		signature: string;
-	}
-
-	interface IOptions {
-		headers?: string[];
-		algorithm?: string;
-		strict?: boolean;
-		authorizationHeaderName?: string;
-	}
-
-	interface IParseRequestOptions extends IOptions {
-		clockSkew?: number;
-	}
-
-	interface IParsedSignature {
-		scheme: string;
-		params: ISignature;
-		signingString: string;
-		algorithm: string;
-		keyId: string;
-	}
-
-	type RequestSignerConstructorOptions =
-		IRequestSignerConstructorOptionsFromProperties |
-		IRequestSignerConstructorOptionsFromFunction;
-
-	interface IRequestSignerConstructorOptionsFromProperties {
-		keyId: string;
-		key: string | Buffer;
-		algorithm?: string;
-	}
-
-	interface IRequestSignerConstructorOptionsFromFunction {
-		sign?: (data: string, cb: (err: any, sig: ISignature) => void) => void;
-	}
-
-	class RequestSigner {
-		constructor(options: RequestSignerConstructorOptions);
-
-		public writeHeader(header: string, value: string): string;
-
-		public writeDateHeader(): string;
-
-		public writeTarget(method: string, path: string): void;
-
-		public sign(cb: (err: any, authz: string) => void): void;
-	}
-
-	interface ISignRequestOptions extends IOptions {
-		keyId: string;
-		key: string;
-		httpVersion?: string;
-	}
-
-	export function parse(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
-	export function parseRequest(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
-
-	export function sign(request: ClientRequest, options: ISignRequestOptions): boolean;
-	export function signRequest(request: ClientRequest, options: ISignRequestOptions): boolean;
-	export function createSigner(): RequestSigner;
-	export function isSigner(obj: any): obj is RequestSigner;
-
-	export function sshKeyToPEM(key: string): string;
-	export function sshKeyFingerprint(key: string): string;
-	export function pemToRsaSSHKey(pem: string, comment: string): string;
-
-	export function verify(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
-	export function verifySignature(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
-	export function verifyHMAC(parsedSignature: IParsedSignature, secret: string): boolean;
-}
diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts
index 4dc689238b..c132cc7e7b 100644
--- a/packages/backend/src/const.ts
+++ b/packages/backend/src/const.ts
@@ -9,6 +9,11 @@ export const MAX_NOTE_TEXT_LENGTH = 3000;
 export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
 export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
 
+export const REMOTE_USER_CACHE_TTL = 1000 * 60 * 60 * 3; // 3hours
+export const REMOTE_USER_MOVE_COOLDOWN = 1000 * 60 * 60 * 24 * 14; // 14days
+
+export const REMOTE_SERVER_CACHE_TTL = 1000 * 60 * 60 * 3; // 3hours
+
 //#region hard limits
 // If you change DB_* values, you must also change the DB schema.
 
diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts
index 69a57b4854..ca0864f679 100644
--- a/packages/backend/src/core/AccountUpdateService.ts
+++ b/packages/backend/src/core/AccountUpdateService.ts
@@ -3,7 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Inject, Injectable } from '@nestjs/common';
+import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
+import { ModuleRef } from '@nestjs/core';
 import { DI } from '@/di-symbols.js';
 import type { UsersRepository } from '@/models/_.js';
 import type { MiUser } from '@/models/User.js';
@@ -12,30 +13,44 @@ import { RelayService } from '@/core/RelayService.js';
 import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
+import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 @Injectable()
-export class AccountUpdateService {
+export class AccountUpdateService implements OnModuleInit {
+	private apDeliverManagerService: ApDeliverManagerService;
 	constructor(
+		private moduleRef: ModuleRef,
+
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
 
 		private userEntityService: UserEntityService,
 		private apRendererService: ApRendererService,
-		private apDeliverManagerService: ApDeliverManagerService,
 		private relayService: RelayService,
 	) {
 	}
 
+	async onModuleInit() {
+		this.apDeliverManagerService = this.moduleRef.get(ApDeliverManagerService.name);
+	}
+
 	@bindThis
-	public async publishToFollowers(userId: MiUser['id']) {
+	/**
+	 * Deliver account update to followers
+	 * @param userId user id
+	 * @param deliverKey optional. Private key to sign the deliver.
+	 */
+	public async publishToFollowers(userId: MiUser['id'], deliverKey?: PrivateKeyWithPem) {
 		const user = await this.usersRepository.findOneBy({ id: userId });
 		if (user == null) throw new Error('user not found');
 
 		// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
 		if (this.userEntityService.isLocalUser(user)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
-			this.apDeliverManagerService.deliverToFollowers(user, content);
-			this.relayService.deliverToRelays(user, content);
+			await Promise.allSettled([
+				this.apDeliverManagerService.deliverToFollowers(user, content, deliverKey),
+				this.relayService.deliverToRelays(user, content, deliverKey),
+			]);
 		}
 	}
 }
diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts
index 6c5b0f6a36..60ddc9cde2 100644
--- a/packages/backend/src/core/CreateSystemUserService.ts
+++ b/packages/backend/src/core/CreateSystemUserService.ts
@@ -7,7 +7,7 @@ import { randomUUID } from 'node:crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import bcrypt from 'bcryptjs';
 import { IsNull, DataSource } from 'typeorm';
-import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
+import { genRSAAndEd25519KeyPair } from '@/misc/gen-key-pair.js';
 import { MiUser } from '@/models/User.js';
 import { MiUserProfile } from '@/models/UserProfile.js';
 import { IdService } from '@/core/IdService.js';
@@ -38,7 +38,7 @@ export class CreateSystemUserService {
 		// Generate secret
 		const secret = generateNativeUserToken();
 
-		const keyPair = await genRsaKeyPair();
+		const keyPair = await genRSAAndEd25519KeyPair();
 
 		let account!: MiUser;
 
@@ -64,9 +64,8 @@ export class CreateSystemUserService {
 			}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));
 
 			await transactionalEntityManager.insert(MiUserKeypair, {
-				publicKey: keyPair.publicKey,
-				privateKey: keyPair.privateKey,
 				userId: account.id,
+				...keyPair,
 			});
 
 			await transactionalEntityManager.insert(MiUserProfile, {
diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts
index aa16468ecb..dc53c8711d 100644
--- a/packages/backend/src/core/FetchInstanceMetadataService.ts
+++ b/packages/backend/src/core/FetchInstanceMetadataService.ts
@@ -15,6 +15,7 @@ import { LoggerService } from '@/core/LoggerService.js';
 import { HttpRequestService } from '@/core/HttpRequestService.js';
 import { bindThis } from '@/decorators.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
+import { REMOTE_SERVER_CACHE_TTL } from '@/const.js';
 import type { DOMWindow } from 'jsdom';
 
 type NodeInfo = {
@@ -24,6 +25,7 @@ type NodeInfo = {
 		version?: unknown;
 	};
 	metadata?: {
+		httpMessageSignaturesImplementationLevel?: unknown,
 		name?: unknown;
 		nodeName?: unknown;
 		nodeDescription?: unknown;
@@ -39,6 +41,7 @@ type NodeInfo = {
 @Injectable()
 export class FetchInstanceMetadataService {
 	private logger: Logger;
+	private httpColon = 'https://';
 
 	constructor(
 		private httpRequestService: HttpRequestService,
@@ -48,6 +51,7 @@ export class FetchInstanceMetadataService {
 		private redisClient: Redis.Redis,
 	) {
 		this.logger = this.loggerService.getLogger('metadata', 'cyan');
+		this.httpColon = process.env.MISSKEY_USE_HTTP?.toLowerCase() === 'true' ? 'http://' : 'https://';
 	}
 
 	@bindThis
@@ -59,7 +63,7 @@ export class FetchInstanceMetadataService {
 		return await this.redisClient.set(
 			`fetchInstanceMetadata:mutex:v2:${host}`, '1',
 			'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395
-			'GET' // 古い値を返す(なかったらnull)
+			'GET', // 古い値を返す(なかったらnull)
 		);
 	}
 
@@ -73,23 +77,24 @@ export class FetchInstanceMetadataService {
 	public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> {
 		const host = instance.host;
 
-		// finallyでunlockされてしまうのでtry内でロックチェックをしない
-		// (returnであってもfinallyは実行される)
-		if (!force && await this.tryLock(host) === '1') {
-			// 1が返ってきていたらロックされているという意味なので、何もしない
-			return;
+		if (!force) {
+			// キャッシュ有効チェックはロック取得前に行う
+			const _instance = await this.federatedInstanceService.fetch(host);
+			const now = Date.now();
+			if (_instance && _instance.infoUpdatedAt != null && (now - _instance.infoUpdatedAt.getTime() < REMOTE_SERVER_CACHE_TTL)) {
+				this.logger.debug(`Skip because updated recently ${_instance.infoUpdatedAt.toJSON()}`);
+				return;
+			}
+
+			// finallyでunlockされてしまうのでtry内でロックチェックをしない
+			// (returnであってもfinallyは実行される)
+			if (await this.tryLock(host) === '1') {
+				// 1が返ってきていたら他にロックされているという意味なので、何もしない
+				return;
+			}
 		}
 
 		try {
-			if (!force) {
-				const _instance = await this.federatedInstanceService.fetch(host);
-				const now = Date.now();
-				if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) {
-					// unlock at the finally caluse
-					return;
-				}
-			}
-
 			this.logger.info(`Fetching metadata of ${instance.host} ...`);
 
 			const [info, dom, manifest] = await Promise.all([
@@ -118,6 +123,14 @@ export class FetchInstanceMetadataService {
 				updates.openRegistrations = info.openRegistrations;
 				updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name ?? null) : null : null;
 				updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email ?? null) : null : null;
+				if (info.metadata && info.metadata.httpMessageSignaturesImplementationLevel && (
+					info.metadata.httpMessageSignaturesImplementationLevel === '01' ||
+					info.metadata.httpMessageSignaturesImplementationLevel === '11'
+				)) {
+					updates.httpMessageSignaturesImplementationLevel = info.metadata.httpMessageSignaturesImplementationLevel;
+				} else {
+					updates.httpMessageSignaturesImplementationLevel = '00';
+				}
 			}
 
 			if (name) updates.name = name;
@@ -129,6 +142,12 @@ export class FetchInstanceMetadataService {
 			await this.federatedInstanceService.update(instance.id, updates);
 
 			this.logger.succ(`Successfuly updated metadata of ${instance.host}`);
+			this.logger.debug('Updated metadata:', {
+				info: !!info,
+				dom: !!dom,
+				manifest: !!manifest,
+				updates,
+			});
 		} catch (e) {
 			this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`);
 		} finally {
@@ -141,7 +160,7 @@ export class FetchInstanceMetadataService {
 		this.logger.info(`Fetching nodeinfo of ${instance.host} ...`);
 
 		try {
-			const wellknown = await this.httpRequestService.getJson('https://' + instance.host + '/.well-known/nodeinfo')
+			const wellknown = await this.httpRequestService.getJson(this.httpColon + instance.host + '/.well-known/nodeinfo')
 				.catch(err => {
 					if (err.statusCode === 404) {
 						throw new Error('No nodeinfo provided');
@@ -184,7 +203,7 @@ export class FetchInstanceMetadataService {
 	private async fetchDom(instance: MiInstance): Promise<DOMWindow['document']> {
 		this.logger.info(`Fetching HTML of ${instance.host} ...`);
 
-		const url = 'https://' + instance.host;
+		const url = this.httpColon + instance.host;
 
 		const html = await this.httpRequestService.getHtml(url);
 
@@ -196,7 +215,7 @@ export class FetchInstanceMetadataService {
 
 	@bindThis
 	private async fetchManifest(instance: MiInstance): Promise<Record<string, unknown> | null> {
-		const url = 'https://' + instance.host;
+		const url = this.httpColon + instance.host;
 
 		const manifestUrl = url + '/manifest.json';
 
@@ -207,7 +226,7 @@ export class FetchInstanceMetadataService {
 
 	@bindThis
 	private async fetchFaviconUrl(instance: MiInstance, doc: DOMWindow['document'] | null): Promise<string | null> {
-		const url = 'https://' + instance.host;
+		const url = this.httpColon + instance.host;
 
 		if (doc) {
 			// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
@@ -234,12 +253,12 @@ export class FetchInstanceMetadataService {
 	@bindThis
 	private async fetchIconUrl(instance: MiInstance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> {
 		if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) {
-			const url = 'https://' + instance.host;
+			const url = this.httpColon + instance.host;
 			return (new URL(manifest.icons[0].src, url)).href;
 		}
 
 		if (doc) {
-			const url = 'https://' + instance.host;
+			const url = this.httpColon + instance.host;
 
 			// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
 			const links = Array.from(doc.getElementsByTagName('link')).reverse();
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index a70743bed2..2a7d8d4bbe 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -245,6 +245,7 @@ export interface InternalEventTypes {
 	unmute: { muterId: MiUser['id']; muteeId: MiUser['id']; };
 	userListMemberAdded: { userListId: MiUserList['id']; memberId: MiUser['id']; };
 	userListMemberRemoved: { userListId: MiUserList['id']; memberId: MiUser['id']; };
+	userKeypairUpdated: { userId: MiUser['id']; };
 }
 
 // name/messages(spec) pairs dictionary
diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts
index 7f3cac7c58..4249c158d7 100644
--- a/packages/backend/src/core/HttpRequestService.ts
+++ b/packages/backend/src/core/HttpRequestService.ts
@@ -70,7 +70,7 @@ export class HttpRequestService {
 			localAddress: config.outgoingAddress,
 		});
 
-		const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128);
+		const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 16);
 
 		this.httpAgent = config.proxy
 			? new HttpProxyAgent({
diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index 80827a500b..dd3f2182b4 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -13,7 +13,6 @@ import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
-import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
 import type {
 	DbJobData,
 	DeliverJobData,
@@ -33,7 +32,7 @@ import type {
 	UserWebhookDeliverQueue,
 	SystemWebhookDeliverQueue,
 } from './QueueModule.js';
-import type httpSignature from '@peertube/http-signature';
+import { genRFC3230DigestHeader, type PrivateKeyWithPem, type ParsedSignature } from '@misskey-dev/node-http-message-signatures';
 import type * as Bull from 'bullmq';
 
 @Injectable()
@@ -90,21 +89,21 @@ export class QueueService {
 	}
 
 	@bindThis
-	public deliver(user: ThinUser, content: IActivity | null, to: string | null, isSharedInbox: boolean) {
+	public async deliver(user: ThinUser, content: IActivity | null, to: string | null, isSharedInbox: boolean, privateKey?: PrivateKeyWithPem) {
 		if (content == null) return null;
 		if (to == null) return null;
 
 		const contentBody = JSON.stringify(content);
-		const digest = ApRequestCreator.createDigest(contentBody);
 
 		const data: DeliverJobData = {
 			user: {
 				id: user.id,
 			},
 			content: contentBody,
-			digest,
+			digest: await genRFC3230DigestHeader(contentBody, 'SHA-256'),
 			to,
 			isSharedInbox,
+			privateKey: privateKey && { keyId: privateKey.keyId, privateKeyPem: privateKey.privateKeyPem },
 		};
 
 		return this.deliverQueue.add(to, data, {
@@ -122,13 +121,13 @@ export class QueueService {
 	 * @param user `{ id: string; }` この関数ではThinUserに変換しないので前もって変換してください
 	 * @param content IActivity | null
 	 * @param inboxes `Map<string, boolean>` / key: to (inbox url), value: isSharedInbox (whether it is sharedInbox)
+	 * @param forceMainKey boolean | undefined, force to use main (rsa) key
 	 * @returns void
 	 */
 	@bindThis
-	public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map<string, boolean>) {
+	public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map<string, boolean>, privateKey?: PrivateKeyWithPem) {
 		if (content == null) return null;
 		const contentBody = JSON.stringify(content);
-		const digest = ApRequestCreator.createDigest(contentBody);
 
 		const opts = {
 			attempts: this.config.deliverJobMaxAttempts ?? 12,
@@ -144,9 +143,9 @@ export class QueueService {
 			data: {
 				user,
 				content: contentBody,
-				digest,
 				to: d[0],
 				isSharedInbox: d[1],
+				privateKey: privateKey && { keyId: privateKey.keyId, privateKeyPem: privateKey.privateKeyPem },
 			} as DeliverJobData,
 			opts,
 		})));
@@ -155,7 +154,7 @@ export class QueueService {
 	}
 
 	@bindThis
-	public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) {
+	public inbox(activity: IActivity, signature: ParsedSignature | null) {
 		const data = {
 			activity: activity,
 			signature,
diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts
index 8dd3d64f5b..ad01f98902 100644
--- a/packages/backend/src/core/RelayService.ts
+++ b/packages/backend/src/core/RelayService.ts
@@ -16,6 +16,8 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { DI } from '@/di-symbols.js';
 import { deepClone } from '@/misc/clone.js';
 import { bindThis } from '@/decorators.js';
+import { UserKeypairService } from './UserKeypairService.js';
+import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 const ACTOR_USERNAME = 'relay.actor' as const;
 
@@ -34,6 +36,7 @@ export class RelayService {
 		private queueService: QueueService,
 		private createSystemUserService: CreateSystemUserService,
 		private apRendererService: ApRendererService,
+		private userKeypairService: UserKeypairService,
 	) {
 		this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10);
 	}
@@ -111,7 +114,7 @@ export class RelayService {
 	}
 
 	@bindThis
-	public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
+	public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any, privateKey?: PrivateKeyWithPem): Promise<void> {
 		if (activity == null) return;
 
 		const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
@@ -121,11 +124,9 @@ export class RelayService {
 
 		const copy = deepClone(activity);
 		if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public'];
+		privateKey = privateKey ?? await this.userKeypairService.getLocalUserPrivateKeyPem(user.id);
+		const signed = await this.apRendererService.attachLdSignature(copy, privateKey);
 
-		const signed = await this.apRendererService.attachLdSignature(copy, user);
-
-		for (const relay of relays) {
-			this.queueService.deliver(user, signed, relay.inbox, false);
-		}
+		this.queueService.deliverMany(user, signed, new Map(relays.map(({ inbox }) => [inbox, false])), privateKey);
 	}
 }
diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts
index 5522ecd6cc..54c6170062 100644
--- a/packages/backend/src/core/SignupService.ts
+++ b/packages/backend/src/core/SignupService.ts
@@ -3,7 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { generateKeyPair } from 'node:crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import bcrypt from 'bcryptjs';
 import { DataSource, IsNull } from 'typeorm';
@@ -21,6 +20,7 @@ import { bindThis } from '@/decorators.js';
 import UsersChart from '@/core/chart/charts/users.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { MetaService } from '@/core/MetaService.js';
+import { genRSAAndEd25519KeyPair } from '@/misc/gen-key-pair.js';
 
 @Injectable()
 export class SignupService {
@@ -93,22 +93,7 @@ export class SignupService {
 			}
 		}
 
-		const keyPair = await new Promise<string[]>((res, rej) =>
-			generateKeyPair('rsa', {
-				modulusLength: 2048,
-				publicKeyEncoding: {
-					type: 'spki',
-					format: 'pem',
-				},
-				privateKeyEncoding: {
-					type: 'pkcs8',
-					format: 'pem',
-					cipher: undefined,
-					passphrase: undefined,
-				},
-			}, (err, publicKey, privateKey) =>
-				err ? rej(err) : res([publicKey, privateKey]),
-			));
+		const keyPair = await genRSAAndEd25519KeyPair();
 
 		let account!: MiUser;
 
@@ -131,9 +116,8 @@ export class SignupService {
 			}));
 
 			await transactionalEntityManager.save(new MiUserKeypair({
-				publicKey: keyPair[0],
-				privateKey: keyPair[1],
 				userId: account.id,
+				...keyPair,
 			}));
 
 			await transactionalEntityManager.save(new MiUserProfile({
diff --git a/packages/backend/src/core/UserKeypairService.ts b/packages/backend/src/core/UserKeypairService.ts
index 51ac99179a..aa90f1e209 100644
--- a/packages/backend/src/core/UserKeypairService.ts
+++ b/packages/backend/src/core/UserKeypairService.ts
@@ -5,41 +5,184 @@
 
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
 import * as Redis from 'ioredis';
+import { genEd25519KeyPair, importPrivateKey, PrivateKey, PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 import type { MiUser } from '@/models/User.js';
 import type { UserKeypairsRepository } from '@/models/_.js';
-import { RedisKVCache } from '@/misc/cache.js';
+import { RedisKVCache, MemoryKVCache } from '@/misc/cache.js';
 import type { MiUserKeypair } from '@/models/UserKeypair.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
+import { GlobalEventService, GlobalEvents } from '@/core/GlobalEventService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import type { webcrypto } from 'node:crypto';
 
 @Injectable()
 export class UserKeypairService implements OnApplicationShutdown {
-	private cache: RedisKVCache<MiUserKeypair>;
+	private keypairEntityCache: RedisKVCache<MiUserKeypair>;
+	private privateKeyObjectCache: MemoryKVCache<webcrypto.CryptoKey>;
 
 	constructor(
 		@Inject(DI.redis)
 		private redisClient: Redis.Redis,
-
+		@Inject(DI.redisForSub)
+		private redisForSub: Redis.Redis,
 		@Inject(DI.userKeypairsRepository)
 		private userKeypairsRepository: UserKeypairsRepository,
+
+		private globalEventService: GlobalEventService,
+		private userEntityService: UserEntityService,
 	) {
-		this.cache = new RedisKVCache<MiUserKeypair>(this.redisClient, 'userKeypair', {
+		this.keypairEntityCache = new RedisKVCache<MiUserKeypair>(this.redisClient, 'userKeypair', {
 			lifetime: 1000 * 60 * 60 * 24, // 24h
 			memoryCacheLifetime: Infinity,
 			fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }),
 			toRedisConverter: (value) => JSON.stringify(value),
 			fromRedisConverter: (value) => JSON.parse(value),
 		});
+		this.privateKeyObjectCache = new MemoryKVCache<webcrypto.CryptoKey>(1000 * 60 * 60 * 1);
+
+		this.redisForSub.on('message', this.onMessage);
 	}
 
 	@bindThis
 	public async getUserKeypair(userId: MiUser['id']): Promise<MiUserKeypair> {
-		return await this.cache.fetch(userId);
+		return await this.keypairEntityCache.fetch(userId);
+	}
+
+	/**
+	 * Get private key [Only PrivateKeyWithPem for queue data etc.]
+	 * @param userIdOrHint user id or MiUserKeypair
+	 * @param preferType
+	 *		If ed25519-like(`ed25519`, `01`, `11`) is specified, ed25519 keypair will be returned if exists.
+	 *		Otherwise, main keypair will be returned.
+	 * @returns
+	 */
+	@bindThis
+	public async getLocalUserPrivateKeyPem(
+		userIdOrHint: MiUser['id'] | MiUserKeypair,
+		preferType?: string,
+	): Promise<PrivateKeyWithPem> {
+		const keypair = typeof userIdOrHint === 'string' ? await this.getUserKeypair(userIdOrHint) : userIdOrHint;
+		if (
+			preferType && ['01', '11', 'ed25519'].includes(preferType.toLowerCase()) &&
+			keypair.ed25519PublicKey != null && keypair.ed25519PrivateKey != null
+		) {
+			return {
+				keyId: `${this.userEntityService.genLocalUserUri(keypair.userId)}#ed25519-key`,
+				privateKeyPem: keypair.ed25519PrivateKey,
+			};
+		}
+		return {
+			keyId: `${this.userEntityService.genLocalUserUri(keypair.userId)}#main-key`,
+			privateKeyPem: keypair.privateKey,
+		};
+	}
+
+	/**
+	 * Get private key [Only PrivateKey for ap request]
+	 * Using cache due to performance reasons of `crypto.subtle.importKey`
+	 * @param userIdOrHint user id, MiUserKeypair, or PrivateKeyWithPem
+	 * @param preferType
+	 * 		If ed25519-like(`ed25519`, `01`, `11`) is specified, ed25519 keypair will be returned if exists.
+	 *		Otherwise, main keypair will be returned. (ignored if userIdOrHint is PrivateKeyWithPem)
+	 * @returns
+	 */
+	@bindThis
+	public async getLocalUserPrivateKey(
+		userIdOrHint: MiUser['id'] | MiUserKeypair | PrivateKeyWithPem,
+		preferType?: string,
+	): Promise<PrivateKey> {
+		if (typeof userIdOrHint === 'object' && 'privateKeyPem' in userIdOrHint) {
+			// userIdOrHint is PrivateKeyWithPem
+			return {
+				keyId: userIdOrHint.keyId,
+				privateKey: await this.privateKeyObjectCache.fetch(userIdOrHint.keyId, async () => {
+					return await importPrivateKey(userIdOrHint.privateKeyPem);
+				}),
+			};
+		}
+
+		const userId = typeof userIdOrHint === 'string' ? userIdOrHint : userIdOrHint.userId;
+		const getKeypair = () => typeof userIdOrHint === 'string' ? this.getUserKeypair(userId) : userIdOrHint;
+
+		if (preferType && ['01', '11', 'ed25519'].includes(preferType.toLowerCase())) {
+			const keyId = `${this.userEntityService.genLocalUserUri(userId)}#ed25519-key`;
+			const fetched = await this.privateKeyObjectCache.fetchMaybe(keyId, async () => {
+				const keypair = await getKeypair();
+				if (keypair.ed25519PublicKey != null && keypair.ed25519PrivateKey != null) {
+					return await importPrivateKey(keypair.ed25519PrivateKey);
+				}
+				return;
+			});
+			if (fetched) {
+				return {
+					keyId,
+					privateKey: fetched,
+				};
+			}
+		}
+
+		const keyId = `${this.userEntityService.genLocalUserUri(userId)}#main-key`;
+		return {
+			keyId,
+			privateKey: await this.privateKeyObjectCache.fetch(keyId, async () => {
+				const keypair = await getKeypair();
+				return await importPrivateKey(keypair.privateKey);
+			}),
+		};
 	}
 
+	@bindThis
+	public async refresh(userId: MiUser['id']): Promise<void> {
+		return await this.keypairEntityCache.refresh(userId);
+	}
+
+	/**
+	 * If DB has ed25519 keypair, refresh cache and return it.
+	 * If not, create, save and return ed25519 keypair.
+	 * @param userId user id
+	 * @returns MiUserKeypair if keypair is created, void if keypair is already exists
+	 */
+	@bindThis
+	public async refreshAndPrepareEd25519KeyPair(userId: MiUser['id']): Promise<MiUserKeypair | void> {
+		await this.refresh(userId);
+		const keypair = await this.keypairEntityCache.fetch(userId);
+		if (keypair.ed25519PublicKey != null) {
+			return;
+		}
+
+		const ed25519 = await genEd25519KeyPair();
+		await this.userKeypairsRepository.update({ userId }, {
+			ed25519PublicKey: ed25519.publicKey,
+			ed25519PrivateKey: ed25519.privateKey,
+		});
+		this.globalEventService.publishInternalEvent('userKeypairUpdated', { userId });
+		const result = {
+			...keypair,
+			ed25519PublicKey: ed25519.publicKey,
+			ed25519PrivateKey: ed25519.privateKey,
+		};
+		this.keypairEntityCache.set(userId, result);
+		return result;
+	}
+
+	@bindThis
+	private async onMessage(_: string, data: string): Promise<void> {
+		const obj = JSON.parse(data);
+
+		if (obj.channel === 'internal') {
+			const { type, body } = obj.message as GlobalEvents['internal']['payload'];
+			switch (type) {
+				case 'userKeypairUpdated': {
+					this.refresh(body.userId);
+					break;
+				}
+			}
+		}
+	}
 	@bindThis
 	public dispose(): void {
-		this.cache.dispose();
+		this.keypairEntityCache.dispose();
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts
index d594a223f4..fc5a68c72e 100644
--- a/packages/backend/src/core/UserSuspendService.ts
+++ b/packages/backend/src/core/UserSuspendService.ts
@@ -3,27 +3,23 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Inject, Injectable } from '@nestjs/common';
-import { Not, IsNull } from 'typeorm';
-import type { FollowingsRepository } from '@/models/_.js';
+import { Injectable } from '@nestjs/common';
 import type { MiUser } from '@/models/User.js';
-import { QueueService } from '@/core/QueueService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
-import { DI } from '@/di-symbols.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
+import { UserKeypairService } from './UserKeypairService.js';
+import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js';
 
 @Injectable()
 export class UserSuspendService {
 	constructor(
-		@Inject(DI.followingsRepository)
-		private followingsRepository: FollowingsRepository,
-
 		private userEntityService: UserEntityService,
-		private queueService: QueueService,
 		private globalEventService: GlobalEventService,
 		private apRendererService: ApRendererService,
+		private userKeypairService: UserKeypairService,
+		private apDeliverManagerService: ApDeliverManagerService,
 	) {
 	}
 
@@ -32,28 +28,12 @@ export class UserSuspendService {
 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
 
 		if (this.userEntityService.isLocalUser(user)) {
-			// 知り得る全SharedInboxにDelete配信
 			const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user));
-
-			const queue: string[] = [];
-
-			const followings = await this.followingsRepository.find({
-				where: [
-					{ followerSharedInbox: Not(IsNull()) },
-					{ followeeSharedInbox: Not(IsNull()) },
-				],
-				select: ['followerSharedInbox', 'followeeSharedInbox'],
-			});
-
-			const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox);
-
-			for (const inbox of inboxes) {
-				if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
-			}
-
-			for (const inbox of queue) {
-				this.queueService.deliver(user, content, inbox, true);
-			}
+			const manager = this.apDeliverManagerService.createDeliverManager(user, content);
+			manager.addAllKnowingSharedInboxRecipe();
+			// process deliver時にはキーペアが消去されているはずなので、ここで挿入する
+			const privateKey = await this.userKeypairService.getLocalUserPrivateKeyPem(user.id, 'main');
+			manager.execute({ privateKey });
 		}
 	}
 
@@ -62,28 +42,12 @@ export class UserSuspendService {
 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
 
 		if (this.userEntityService.isLocalUser(user)) {
-			// 知り得る全SharedInboxにUndo Delete配信
 			const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user), user));
-
-			const queue: string[] = [];
-
-			const followings = await this.followingsRepository.find({
-				where: [
-					{ followerSharedInbox: Not(IsNull()) },
-					{ followeeSharedInbox: Not(IsNull()) },
-				],
-				select: ['followerSharedInbox', 'followeeSharedInbox'],
-			});
-
-			const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox);
-
-			for (const inbox of inboxes) {
-				if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
-			}
-
-			for (const inbox of queue) {
-				this.queueService.deliver(user as any, content, inbox, true);
-			}
+			const manager = this.apDeliverManagerService.createDeliverManager(user, content);
+			manager.addAllKnowingSharedInboxRecipe();
+			// process deliver時にはキーペアが消去されているはずなので、ここで挿入する
+			const privateKey = await this.userKeypairService.getLocalUserPrivateKeyPem(user.id, 'main');
+			manager.execute({ privateKey });
 		}
 	}
 }
diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts
index 374536a741..aa1144778c 100644
--- a/packages/backend/src/core/WebfingerService.ts
+++ b/packages/backend/src/core/WebfingerService.ts
@@ -46,7 +46,7 @@ export class WebfingerService {
 		const m = query.match(mRegex);
 		if (m) {
 			const hostname = m[2];
-			const useHttp = process.env.MISSKEY_WEBFINGER_USE_HTTP && process.env.MISSKEY_WEBFINGER_USE_HTTP.toLowerCase() === 'true';
+			const useHttp = process.env.MISSKEY_USE_HTTP && process.env.MISSKEY_USE_HTTP.toLowerCase() === 'true';
 			return `http${useHttp ? '' : 's'}://${hostname}/.well-known/webfinger?${urlQuery({ resource: `acct:${query}` })}`;
 		}
 
diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts
index f6b70ead44..973394683f 100644
--- a/packages/backend/src/core/activitypub/ApDbResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
+import type { MiUser, NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
 import type { Config } from '@/config.js';
 import { MemoryKVCache } from '@/misc/cache.js';
 import type { MiUserPublickey } from '@/models/UserPublickey.js';
@@ -13,9 +13,12 @@ import { CacheService } from '@/core/CacheService.js';
 import type { MiNote } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
 import { MiLocalUser, MiRemoteUser } from '@/models/User.js';
+import Logger from '@/logger.js';
 import { getApId } from './type.js';
 import { ApPersonService } from './models/ApPersonService.js';
+import { ApLoggerService } from './ApLoggerService.js';
 import type { IObject } from './type.js';
+import { UtilityService } from '../UtilityService.js';
 
 export type UriParseResult = {
 	/** wether the URI was generated by us */
@@ -35,8 +38,8 @@ export type UriParseResult = {
 
 @Injectable()
 export class ApDbResolverService implements OnApplicationShutdown {
-	private publicKeyCache: MemoryKVCache<MiUserPublickey | null>;
-	private publicKeyByUserIdCache: MemoryKVCache<MiUserPublickey | null>;
+	private publicKeyByUserIdCache: MemoryKVCache<MiUserPublickey[] | null>;
+	private logger: Logger;
 
 	constructor(
 		@Inject(DI.config)
@@ -53,9 +56,17 @@ export class ApDbResolverService implements OnApplicationShutdown {
 
 		private cacheService: CacheService,
 		private apPersonService: ApPersonService,
+		private apLoggerService: ApLoggerService,
+		private utilityService: UtilityService,
 	) {
-		this.publicKeyCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
-		this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
+		this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey[] | null>(Infinity);
+		this.logger = this.apLoggerService.logger.createSubLogger('db-resolver');
+	}
+
+	private punyHost(url: string): string {
+		const urlObj = new URL(url);
+		const host = `${this.utilityService.toPuny(urlObj.hostname)}${urlObj.port.length > 0 ? ':' + urlObj.port : ''}`;
+		return host;
 	}
 
 	@bindThis
@@ -116,62 +127,141 @@ export class ApDbResolverService implements OnApplicationShutdown {
 		}
 	}
 
-	/**
-	 * AP KeyId => Misskey User and Key
-	 */
 	@bindThis
-	public async getAuthUserFromKeyId(keyId: string): Promise<{
-		user: MiRemoteUser;
-		key: MiUserPublickey;
-	} | null> {
-		const key = await this.publicKeyCache.fetch(keyId, async () => {
-			const key = await this.userPublickeysRepository.findOneBy({
-				keyId,
-			});
-
-			if (key == null) return null;
-
-			return key;
-		}, key => key != null);
-
-		if (key == null) return null;
-
-		const user = await this.cacheService.findUserById(key.userId).catch(() => null) as MiRemoteUser | null;
-		if (user == null) return null;
-		if (user.isDeleted) return null;
-
-		return {
-			user,
-			key,
-		};
+	private async refreshAndFindKey(userId: MiUser['id'], keyId: string): Promise<MiUserPublickey | null> {
+		this.refreshCacheByUserId(userId);
+		const keys = await this.getPublicKeyByUserId(userId);
+		if (keys == null || !Array.isArray(keys) || keys.length === 0) {
+			this.logger.warn(`No key found (refreshAndFindKey) userId=${userId} keyId=${keyId} keys=${JSON.stringify(keys)}`);
+			return null;
+		}
+		const exactKey = keys.find(x => x.keyId === keyId);
+		if (exactKey) return exactKey;
+		this.logger.warn(`No exact key found (refreshAndFindKey) userId=${userId} keyId=${keyId} keys=${JSON.stringify(keys)}`);
+		return null;
 	}
 
 	/**
 	 * AP Actor id => Misskey User and Key
+	 * @param uri AP Actor id
+	 * @param keyId Key id to find. If not specified, main key will be selected.
+	 * @returns
+	 *	1. `null` if the user and key host do not match
+	 *	2. `{ user: null, key: null }` if the user is not found
+	 *	3. `{ user: MiRemoteUser, key: null }` if key is not found
+	 *  4. `{ user: MiRemoteUser, key: MiUserPublickey }` if both are found
 	 */
 	@bindThis
-	public async getAuthUserFromApId(uri: string): Promise<{
+	public async getAuthUserFromApId(uri: string, keyId?: string): Promise<{
 		user: MiRemoteUser;
 		key: MiUserPublickey | null;
-	} | null> {
-		const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser;
-		if (user.isDeleted) return null;
+	} | {
+		user: null;
+		key: null;
+	} |
+	null> {
+		if (keyId) {
+			if (this.punyHost(uri) !== this.punyHost(keyId)) {
+				/**
+				 * keyIdはURL形式かつkeyIdのホストはuriのホストと一致するはず
+				 * (ApPersonService.validateActorに由来)
+				 *
+				 * ただ、Mastodonはリプライ関連で他人のトゥートをHTTP Signature署名して送ってくることがある
+				 * そのような署名は有効性に疑問があるので無視することにする
+				 * ここではuriとkeyIdのホストが一致しない場合は無視する
+				 * ハッシュをなくしたkeyIdとuriの同一性を比べてみてもいいが、`uri#*-key`というkeyIdを設定するのが
+				 * 決まりごとというわけでもないため幅を持たせることにする
+				 *
+				 *
+				 * The keyId should be in URL format and its host should match the host of the uri
+				 * (derived from ApPersonService.validateActor)
+				 *
+				 * However, Mastodon sometimes sends toots from other users with HTTP Signature signing for reply-related purposes
+				 * Such signatures are of questionable validity, so we choose to ignore them
+				 * Here, we ignore cases where the hosts of uri and keyId do not match
+				 * We could also compare the equality of keyId without the hash and uri, but since setting a keyId like `uri#*-key`
+				 * is not a strict rule, we decide to allow for some flexibility
+				 */
+				this.logger.warn(`actor uri and keyId are not matched uri=${uri} keyId=${keyId}`);
+				return null;
+			}
+		}
 
-		const key = await this.publicKeyByUserIdCache.fetch(
-			user.id,
-			() => this.userPublickeysRepository.findOneBy({ userId: user.id }),
+		const user = await this.apPersonService.resolvePerson(uri, undefined, true) as MiRemoteUser;
+		if (user.isDeleted) return { user: null, key: null };
+
+		const keys = await this.getPublicKeyByUserId(user.id);
+
+		if (keys == null || !Array.isArray(keys) || keys.length === 0) {
+			this.logger.warn(`No key found uri=${uri} userId=${user.id} keys=${JSON.stringify(keys)}`);
+			return { user, key: null };
+		}
+
+		if (!keyId) {
+			// Choose the main-like
+			const mainKey = keys.find(x => {
+				try {
+					const url = new URL(x.keyId);
+					const path = url.pathname.split('/').pop()?.toLowerCase();
+					if (url.hash) {
+						if (url.hash.toLowerCase().includes('main')) {
+							return true;
+						}
+					} else if (path?.includes('main') || path === 'publickey') {
+						return true;
+					}
+				} catch { /* noop */ }
+
+				return false;
+			});
+			return { user, key: mainKey ?? keys[0] };
+		}
+
+		const exactKey = keys.find(x => x.keyId === keyId);
+		if (exactKey) return { user, key: exactKey };
+
+		/**
+		 * keyIdで見つからない場合、まずはキャッシュを更新して再取得
+		 * If not found with keyId, update cache and reacquire
+		 */
+		const cacheRaw = this.publicKeyByUserIdCache.cache.get(user.id);
+		if (cacheRaw && cacheRaw.date > Date.now() - 1000 * 60 * 12) {
+			const exactKey = await this.refreshAndFindKey(user.id, keyId);
+			if (exactKey) return { user, key: exactKey };
+		}
+
+		/**
+		 * lastFetchedAtでの更新制限を弱めて再取得
+		 * Reacquisition with weakened update limit at lastFetchedAt
+		 */
+		if (user.lastFetchedAt == null || user.lastFetchedAt < new Date(Date.now() - 1000 * 60 * 12)) {
+			this.logger.info(`Fetching user to find public key uri=${uri} userId=${user.id} keyId=${keyId}`);
+			const renewed = await this.apPersonService.fetchPersonWithRenewal(uri, 0);
+			if (renewed == null || renewed.isDeleted) return null;
+
+			return { user, key: await this.refreshAndFindKey(user.id, keyId) };
+		}
+
+		this.logger.warn(`No key found uri=${uri} userId=${user.id} keyId=${keyId}`);
+		return { user, key: null };
+	}
+
+	@bindThis
+	public async getPublicKeyByUserId(userId: MiUser['id']): Promise<MiUserPublickey[] | null> {
+		return await this.publicKeyByUserIdCache.fetch(
+			userId,
+			() => this.userPublickeysRepository.find({ where: { userId } }),
 			v => v != null,
 		);
+	}
 
-		return {
-			user,
-			key,
-		};
+	@bindThis
+	public refreshCacheByUserId(userId: MiUser['id']): void {
+		this.publicKeyByUserIdCache.delete(userId);
 	}
 
 	@bindThis
 	public dispose(): void {
-		this.publicKeyCache.dispose();
 		this.publicKeyByUserIdCache.dispose();
 	}
 
diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
index 5d07cd8e8f..db3302e6ff 100644
--- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
+++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
@@ -9,10 +9,14 @@ import { DI } from '@/di-symbols.js';
 import type { FollowingsRepository } from '@/models/_.js';
 import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
 import { QueueService } from '@/core/QueueService.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
 import type { IActivity } from '@/core/activitypub/type.js';
 import { ThinUser } from '@/queue/types.js';
+import { AccountUpdateService } from '@/core/AccountUpdateService.js';
+import type Logger from '@/logger.js';
+import { UserKeypairService } from '../UserKeypairService.js';
+import { ApLoggerService } from './ApLoggerService.js';
+import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 interface IRecipe {
 	type: string;
@@ -27,12 +31,19 @@ interface IDirectRecipe extends IRecipe {
 	to: MiRemoteUser;
 }
 
+interface IAllKnowingSharedInboxRecipe extends IRecipe {
+	type: 'AllKnowingSharedInbox';
+}
+
 const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe =>
 	recipe.type === 'Followers';
 
 const isDirect = (recipe: IRecipe): recipe is IDirectRecipe =>
 	recipe.type === 'Direct';
 
+const isAllKnowingSharedInbox = (recipe: IRecipe): recipe is IAllKnowingSharedInboxRecipe =>
+	recipe.type === 'AllKnowingSharedInbox';
+
 class DeliverManager {
 	private actor: ThinUser;
 	private activity: IActivity | null;
@@ -40,16 +51,18 @@ class DeliverManager {
 
 	/**
 	 * Constructor
-	 * @param userEntityService
+	 * @param userKeypairService
 	 * @param followingsRepository
 	 * @param queueService
 	 * @param actor Actor
 	 * @param activity Activity to deliver
 	 */
 	constructor(
-		private userEntityService: UserEntityService,
+		private userKeypairService: UserKeypairService,
 		private followingsRepository: FollowingsRepository,
 		private queueService: QueueService,
+		private accountUpdateService: AccountUpdateService,
+		private logger: Logger,
 
 		actor: { id: MiUser['id']; host: null; },
 		activity: IActivity | null,
@@ -91,6 +104,18 @@ class DeliverManager {
 		this.addRecipe(recipe);
 	}
 
+	/**
+	 * Add recipe for all-knowing shared inbox deliver
+	 */
+	@bindThis
+	public addAllKnowingSharedInboxRecipe(): void {
+		const deliver: IAllKnowingSharedInboxRecipe = {
+			type: 'AllKnowingSharedInbox',
+		};
+
+		this.addRecipe(deliver);
+	}
+
 	/**
 	 * Add recipe
 	 * @param recipe Recipe
@@ -104,11 +129,44 @@ class DeliverManager {
 	 * Execute delivers
 	 */
 	@bindThis
-	public async execute(): Promise<void> {
+	public async execute(opts?: { privateKey?: PrivateKeyWithPem }): Promise<void> {
+		//#region MIGRATION
+		if (!opts?.privateKey) {
+			/**
+			 * ed25519の署名がなければ追加する
+			 */
+			const created = await this.userKeypairService.refreshAndPrepareEd25519KeyPair(this.actor.id);
+			if (created) {
+				// createdが存在するということは新規作成されたということなので、フォロワーに配信する
+				this.logger.info(`ed25519 key pair created for user ${this.actor.id} and publishing to followers`);
+				// リモートに配信
+				const keyPair = await this.userKeypairService.getLocalUserPrivateKeyPem(created, 'main');
+				await this.accountUpdateService.publishToFollowers(this.actor.id, keyPair);
+			}
+		}
+		//#endregion
+
+		//#region collect inboxes by recipes
 		// The value flags whether it is shared or not.
 		// key: inbox URL, value: whether it is sharedInbox
 		const inboxes = new Map<string, boolean>();
 
+		if (this.recipes.some(r => isAllKnowingSharedInbox(r))) {
+			// all-knowing shared inbox
+			const followings = await this.followingsRepository.find({
+				where: [
+					{ followerSharedInbox: Not(IsNull()) },
+					{ followeeSharedInbox: Not(IsNull()) },
+				],
+				select: ['followerSharedInbox', 'followeeSharedInbox'],
+			});
+
+			for (const following of followings) {
+				if (following.followeeSharedInbox) inboxes.set(following.followeeSharedInbox, true);
+				if (following.followerSharedInbox) inboxes.set(following.followerSharedInbox, true);
+			}
+		}
+
 		// build inbox list
 		// Process follower recipes first to avoid duplication when processing direct recipes later.
 		if (this.recipes.some(r => isFollowers(r))) {
@@ -142,39 +200,49 @@ class DeliverManager {
 
 			inboxes.set(recipe.to.inbox, false);
 		}
+		//#endregion
 
 		// deliver
-		await this.queueService.deliverMany(this.actor, this.activity, inboxes);
+		await this.queueService.deliverMany(this.actor, this.activity, inboxes, opts?.privateKey);
+		this.logger.info(`Deliver queues dispatched: inboxes=${inboxes.size} actorId=${this.actor.id} activityId=${this.activity?.id}`);
 	}
 }
 
 @Injectable()
 export class ApDeliverManagerService {
+	private logger: Logger;
+
 	constructor(
 		@Inject(DI.followingsRepository)
 		private followingsRepository: FollowingsRepository,
 
-		private userEntityService: UserEntityService,
+		private userKeypairService: UserKeypairService,
 		private queueService: QueueService,
+		private accountUpdateService: AccountUpdateService,
+		private apLoggerService: ApLoggerService,
 	) {
+		this.logger = this.apLoggerService.logger.createSubLogger('deliver-manager');
 	}
 
 	/**
 	 * Deliver activity to followers
 	 * @param actor
 	 * @param activity Activity
+	 * @param forceMainKey Force to use main (rsa) key
 	 */
 	@bindThis
-	public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise<void> {
+	public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, privateKey?: PrivateKeyWithPem): Promise<void> {
 		const manager = new DeliverManager(
-			this.userEntityService,
+			this.userKeypairService,
 			this.followingsRepository,
 			this.queueService,
+			this.accountUpdateService,
+			this.logger,
 			actor,
 			activity,
 		);
 		manager.addFollowersRecipe();
-		await manager.execute();
+		await manager.execute({ privateKey });
 	}
 
 	/**
@@ -186,9 +254,11 @@ export class ApDeliverManagerService {
 	@bindThis
 	public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise<void> {
 		const manager = new DeliverManager(
-			this.userEntityService,
+			this.userKeypairService,
 			this.followingsRepository,
 			this.queueService,
+			this.accountUpdateService,
+			this.logger,
 			actor,
 			activity,
 		);
@@ -199,10 +269,11 @@ export class ApDeliverManagerService {
 	@bindThis
 	public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
 		return new DeliverManager(
-			this.userEntityService,
+			this.userKeypairService,
 			this.followingsRepository,
 			this.queueService,
-
+			this.accountUpdateService,
+			this.logger,
 			actor,
 			activity,
 		);
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index e2164fec1d..1bef9fe071 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -114,15 +114,8 @@ export class ApInboxService {
 			result = await this.performOneActivity(actor, activity);
 		}
 
-		// ついでにリモートユーザーの情報が古かったら更新しておく
-		if (actor.uri) {
-			if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
-				setImmediate(() => {
-					this.apPersonService.updatePerson(actor.uri);
-				});
-			}
-		}
-		return result;
+		// ついでにリモートユーザーの情報が古かったら更新しておく?
+		// → No, この関数が呼び出される前に署名検証で更新されているはず
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 98e944f347..5d7419f934 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -22,7 +22,6 @@ import { UserKeypairService } from '@/core/UserKeypairService.js';
 import { MfmService } from '@/core/MfmService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
-import type { MiUserKeypair } from '@/models/UserKeypair.js';
 import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import { CustomEmojiService } from '@/core/CustomEmojiService.js';
@@ -31,6 +30,7 @@ import { JsonLdService } from './JsonLdService.js';
 import { ApMfmService } from './ApMfmService.js';
 import { CONTEXT } from './misc/contexts.js';
 import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
+import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 @Injectable()
 export class ApRendererService {
@@ -251,15 +251,15 @@ export class ApRendererService {
 	}
 
 	@bindThis
-	public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey {
+	public renderKey(user: MiLocalUser, publicKey: string, postfix?: string): IKey {
 		return {
-			id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
+			id: `${this.userEntityService.genLocalUserUri(user.id)}${postfix ?? '/publickey'}`,
 			type: 'Key',
 			owner: this.userEntityService.genLocalUserUri(user.id),
-			publicKeyPem: createPublicKey(key.publicKey).export({
+			publicKeyPem: createPublicKey(publicKey).export({
 				type: 'spki',
 				format: 'pem',
-			}),
+			}) as string,
 		};
 	}
 
@@ -499,7 +499,10 @@ export class ApRendererService {
 			tag,
 			manuallyApprovesFollowers: user.isLocked,
 			discoverable: user.isExplorable,
-			publicKey: this.renderKey(user, keypair, '#main-key'),
+			publicKey: this.renderKey(user, keypair.publicKey, '#main-key'),
+			additionalPublicKeys: [
+				...(keypair.ed25519PublicKey ? [this.renderKey(user, keypair.ed25519PublicKey, '#ed25519-key')] : []),
+			],
 			isCat: user.isCat,
 			attachment: attachment.length ? attachment : undefined,
 		};
@@ -622,12 +625,10 @@ export class ApRendererService {
 	}
 
 	@bindThis
-	public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> {
-		const keypair = await this.userKeypairService.getUserKeypair(user.id);
-
+	public async attachLdSignature(activity: any, key: PrivateKeyWithPem): Promise<IActivity> {
 		const jsonLd = this.jsonLdService.use();
 		jsonLd.debug = false;
-		activity = await jsonLd.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`);
+		activity = await jsonLd.signRsaSignature2017(activity, key.privateKeyPem, key.keyId);
 
 		return activity;
 	}
diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts
index 93ac8ce9a7..0cae91316b 100644
--- a/packages/backend/src/core/activitypub/ApRequestService.ts
+++ b/packages/backend/src/core/activitypub/ApRequestService.ts
@@ -3,9 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as crypto from 'node:crypto';
 import { URL } from 'node:url';
 import { Inject, Injectable } from '@nestjs/common';
+import { genRFC3230DigestHeader, signAsDraftToRequest } from '@misskey-dev/node-http-message-signatures';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import type { MiUser } from '@/models/User.js';
@@ -15,122 +15,61 @@ import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
 import type Logger from '@/logger.js';
 import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
+import type { PrivateKeyWithPem, PrivateKey } from '@misskey-dev/node-http-message-signatures';
 
-type Request = {
-	url: string;
-	method: string;
-	headers: Record<string, string>;
-};
+export async function createSignedPost(args: { level: string; key: PrivateKey; url: string; body: string; digest?: string, additionalHeaders: Record<string, string> }) {
+	const u = new URL(args.url);
+	const request = {
+		url: u.href,
+		method: 'POST',
+		headers: {
+			'Date': new Date().toUTCString(),
+			'Host': u.host,
+			'Content-Type': 'application/activity+json',
+			...args.additionalHeaders,
+		} as Record<string, string>,
+	};
 
-type Signed = {
-	request: Request;
-	signingString: string;
-	signature: string;
-	signatureHeader: string;
-};
+	// TODO: httpMessageSignaturesImplementationLevelによって新規格で通信をするようにする
+	const digestHeader = args.digest ?? await genRFC3230DigestHeader(args.body, 'SHA-256');
+	request.headers['Digest'] = digestHeader;
 
-type PrivateKey = {
-	privateKeyPem: string;
-	keyId: string;
-};
+	const result = await signAsDraftToRequest(
+		request,
+		args.key,
+		['(request-target)', 'date', 'host', 'digest'],
+	);
 
-export class ApRequestCreator {
-	static createSignedPost(args: { key: PrivateKey, url: string, body: string, digest?: string, additionalHeaders: Record<string, string> }): Signed {
-		const u = new URL(args.url);
-		const digestHeader = args.digest ?? this.createDigest(args.body);
+	return {
+		request,
+		...result,
+	};
+}
 
-		const request: Request = {
-			url: u.href,
-			method: 'POST',
-			headers: this.#objectAssignWithLcKey({
-				'Date': new Date().toUTCString(),
-				'Host': u.host,
-				'Content-Type': 'application/activity+json',
-				'Digest': digestHeader,
-			}, args.additionalHeaders),
-		};
+export async function createSignedGet(args: { level: string; key: PrivateKey; url: string; additionalHeaders: Record<string, string> }) {
+	const u = new URL(args.url);
+	const request = {
+		url: u.href,
+		method: 'GET',
+		headers: {
+			'Accept': 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
+			'Date': new Date().toUTCString(),
+			'Host': new URL(args.url).host,
+			...args.additionalHeaders,
+		} as Record<string, string>,
+	};
 
-		const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']);
+	// TODO: httpMessageSignaturesImplementationLevelによって新規格で通信をするようにする
+	const result = await signAsDraftToRequest(
+		request,
+		args.key,
+		['(request-target)', 'date', 'host', 'accept'],
+	);
 
-		return {
-			request,
-			signingString: result.signingString,
-			signature: result.signature,
-			signatureHeader: result.signatureHeader,
-		};
-	}
-
-	static createDigest(body: string) {
-		return `SHA-256=${crypto.createHash('sha256').update(body).digest('base64')}`;
-	}
-
-	static createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }): Signed {
-		const u = new URL(args.url);
-
-		const request: Request = {
-			url: u.href,
-			method: 'GET',
-			headers: this.#objectAssignWithLcKey({
-				'Accept': 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
-				'Date': new Date().toUTCString(),
-				'Host': new URL(args.url).host,
-			}, args.additionalHeaders),
-		};
-
-		const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']);
-
-		return {
-			request,
-			signingString: result.signingString,
-			signature: result.signature,
-			signatureHeader: result.signatureHeader,
-		};
-	}
-
-	static #signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed {
-		const signingString = this.#genSigningString(request, includeHeaders);
-		const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64');
-		const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`;
-
-		request.headers = this.#objectAssignWithLcKey(request.headers, {
-			Signature: signatureHeader,
-		});
-		// node-fetch will generate this for us. if we keep 'Host', it won't change with redirects!
-		delete request.headers['host'];
-
-		return {
-			request,
-			signingString,
-			signature,
-			signatureHeader,
-		};
-	}
-
-	static #genSigningString(request: Request, includeHeaders: string[]): string {
-		request.headers = this.#lcObjectKey(request.headers);
-
-		const results: string[] = [];
-
-		for (const key of includeHeaders.map(x => x.toLowerCase())) {
-			if (key === '(request-target)') {
-				results.push(`(request-target): ${request.method.toLowerCase()} ${new URL(request.url).pathname}`);
-			} else {
-				results.push(`${key}: ${request.headers[key]}`);
-			}
-		}
-
-		return results.join('\n');
-	}
-
-	static #lcObjectKey(src: Record<string, string>): Record<string, string> {
-		const dst: Record<string, string> = {};
-		for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key];
-		return dst;
-	}
-
-	static #objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>): Record<string, string> {
-		return Object.assign(this.#lcObjectKey(a), this.#lcObjectKey(b));
-	}
+	return {
+		request,
+		...result,
+	};
 }
 
 @Injectable()
@@ -150,21 +89,28 @@ export class ApRequestService {
 	}
 
 	@bindThis
-	public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown, digest?: string): Promise<void> {
+	public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown, level: string, digest?: string, key?: PrivateKeyWithPem): Promise<void> {
 		const body = typeof object === 'string' ? object : JSON.stringify(object);
-
-		const keypair = await this.userKeypairService.getUserKeypair(user.id);
-
-		const req = ApRequestCreator.createSignedPost({
-			key: {
-				privateKeyPem: keypair.privateKey,
-				keyId: `${this.config.url}/users/${user.id}#main-key`,
-			},
+		const keyFetched = await this.userKeypairService.getLocalUserPrivateKey(key ?? user.id, level);
+		const req = await createSignedPost({
+			level,
+			key: keyFetched,
 			url,
 			body,
-			digest,
 			additionalHeaders: {
+				'User-Agent': this.config.userAgent,
 			},
+			digest,
+		});
+
+		// node-fetch will generate this for us. if we keep 'Host', it won't change with redirects!
+		delete req.request.headers['Host'];
+
+		this.logger.debug('create signed post', {
+			version: 'draft',
+			level,
+			url,
+			keyId: keyFetched.keyId,
 		});
 
 		await this.httpRequestService.send(url, {
@@ -180,19 +126,27 @@ export class ApRequestService {
 	 * @param url URL to fetch
 	 */
 	@bindThis
-	public async signedGet(url: string, user: { id: MiUser['id'] }): Promise<unknown> {
-		const keypair = await this.userKeypairService.getUserKeypair(user.id);
-
-		const req = ApRequestCreator.createSignedGet({
-			key: {
-				privateKeyPem: keypair.privateKey,
-				keyId: `${this.config.url}/users/${user.id}#main-key`,
-			},
+	public async signedGet(url: string, user: { id: MiUser['id'] }, level: string): Promise<unknown> {
+		const key = await this.userKeypairService.getLocalUserPrivateKey(user.id, level);
+		const req = await createSignedGet({
+			level,
+			key,
 			url,
 			additionalHeaders: {
+				'User-Agent': this.config.userAgent,
 			},
 		});
 
+		// node-fetch will generate this for us. if we keep 'Host', it won't change with redirects!
+		delete req.request.headers['Host'];
+
+		this.logger.debug('create signed get', {
+			version: 'draft',
+			level,
+			url,
+			keyId: key.keyId,
+		});
+
 		const res = await this.httpRequestService.send(url, {
 			method: req.request.method,
 			headers: req.request.headers,
diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts
index bb3c40f093..727ff6f956 100644
--- a/packages/backend/src/core/activitypub/ApResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApResolverService.ts
@@ -16,6 +16,7 @@ import { UtilityService } from '@/core/UtilityService.js';
 import { bindThis } from '@/decorators.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import type Logger from '@/logger.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 import { isCollectionOrOrderedCollection } from './type.js';
 import { ApDbResolverService } from './ApDbResolverService.js';
 import { ApRendererService } from './ApRendererService.js';
@@ -41,6 +42,7 @@ export class Resolver {
 		private httpRequestService: HttpRequestService,
 		private apRendererService: ApRendererService,
 		private apDbResolverService: ApDbResolverService,
+		private federatedInstanceService: FederatedInstanceService,
 		private loggerService: LoggerService,
 		private recursionLimit = 100,
 	) {
@@ -103,8 +105,10 @@ export class Resolver {
 			this.user = await this.instanceActorService.getInstanceActor();
 		}
 
+		const server = await this.federatedInstanceService.fetch(host);
+
 		const object = (this.user
-			? await this.apRequestService.signedGet(value, this.user) as IObject
+			? await this.apRequestService.signedGet(value, this.user, server.httpMessageSignaturesImplementationLevel) as IObject
 			: await this.httpRequestService.getActivityJson(value)) as IObject;
 
 		if (
@@ -200,6 +204,7 @@ export class ApResolverService {
 		private httpRequestService: HttpRequestService,
 		private apRendererService: ApRendererService,
 		private apDbResolverService: ApDbResolverService,
+		private federatedInstanceService: FederatedInstanceService,
 		private loggerService: LoggerService,
 	) {
 	}
@@ -220,6 +225,7 @@ export class ApResolverService {
 			this.httpRequestService,
 			this.apRendererService,
 			this.apDbResolverService,
+			this.federatedInstanceService,
 			this.loggerService,
 		);
 	}
diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts
index feb8c42c56..fc4e3e3bef 100644
--- a/packages/backend/src/core/activitypub/misc/contexts.ts
+++ b/packages/backend/src/core/activitypub/misc/contexts.ts
@@ -134,6 +134,7 @@ const security_v1 = {
 		'privateKey': { '@id': 'sec:privateKey', '@type': '@id' },
 		'privateKeyPem': 'sec:privateKeyPem',
 		'publicKey': { '@id': 'sec:publicKey', '@type': '@id' },
+		'additionalPublicKeys': { '@id': 'sec:publicKey', '@type': '@id' },
 		'publicKeyBase58': 'sec:publicKeyBase58',
 		'publicKeyPem': 'sec:publicKeyPem',
 		'publicKeyWif': 'sec:publicKeyWif',
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index 457205e023..c41fc713d5 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -3,9 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { verify } from 'crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import promiseLimit from 'promise-limit';
-import { DataSource } from 'typeorm';
+import { DataSource, In, Not } from 'typeorm';
 import { ModuleRef } from '@nestjs/core';
 import { DI } from '@/di-symbols.js';
 import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
@@ -39,6 +40,7 @@ import { MetaService } from '@/core/MetaService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
 import type { AccountMoveService } from '@/core/AccountMoveService.js';
 import { checkHttps } from '@/misc/check-https.js';
+import { REMOTE_USER_CACHE_TTL, REMOTE_USER_MOVE_COOLDOWN } from '@/const.js';
 import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js';
 import { extractApHashtags } from './tag.js';
 import type { OnModuleInit } from '@nestjs/common';
@@ -48,7 +50,7 @@ import type { ApResolverService, Resolver } from '../ApResolverService.js';
 import type { ApLoggerService } from '../ApLoggerService.js';
 // eslint-disable-next-line @typescript-eslint/consistent-type-imports
 import type { ApImageService } from './ApImageService.js';
-import type { IActor, IObject } from '../type.js';
+import type { IActor, IKey, IObject } from '../type.js';
 
 const nameLength = 128;
 const summaryLength = 2048;
@@ -185,13 +187,38 @@ export class ApPersonService implements OnModuleInit {
 		}
 
 		if (x.publicKey) {
-			if (typeof x.publicKey.id !== 'string') {
-				throw new Error('invalid Actor: publicKey.id is not a string');
+			const publicKeys = Array.isArray(x.publicKey) ? x.publicKey : [x.publicKey];
+
+			for (const publicKey of publicKeys) {
+				if (typeof publicKey.id !== 'string') {
+					throw new Error('invalid Actor: publicKey.id is not a string');
+				}
+
+				const publicKeyIdHost = this.punyHost(publicKey.id);
+				if (publicKeyIdHost !== expectHost) {
+					throw new Error('invalid Actor: publicKey.id has different host');
+				}
+			}
+		}
+
+		if (x.additionalPublicKeys) {
+			if (!x.publicKey) {
+				throw new Error('invalid Actor: additionalPublicKeys is set but publicKey is not');
 			}
 
-			const publicKeyIdHost = this.punyHost(x.publicKey.id);
-			if (publicKeyIdHost !== expectHost) {
-				throw new Error('invalid Actor: publicKey.id has different host');
+			if (!Array.isArray(x.additionalPublicKeys)) {
+				throw new Error('invalid Actor: additionalPublicKeys is not an array');
+			}
+
+			for (const key of x.additionalPublicKeys) {
+				if (typeof key.id !== 'string') {
+					throw new Error('invalid Actor: additionalPublicKeys.id is not a string');
+				}
+
+				const keyIdHost = this.punyHost(key.id);
+				if (keyIdHost !== expectHost) {
+					throw new Error('invalid Actor: additionalPublicKeys.id has different host');
+				}
 			}
 		}
 
@@ -228,6 +255,33 @@ export class ApPersonService implements OnModuleInit {
 		return null;
 	}
 
+	/**
+	 * uriからUser(Person)をフェッチします。
+	 *
+	 * Misskeyに対象のPersonが登録されていればそれを返し、登録がなければnullを返します。
+	 * また、TTLが0でない場合、TTLを過ぎていた場合はupdatePersonを実行します。
+	 */
+	@bindThis
+	async fetchPersonWithRenewal(uri: string, TTL = REMOTE_USER_CACHE_TTL): Promise<MiLocalUser | MiRemoteUser | null> {
+		const exist = await this.fetchPerson(uri);
+		if (exist == null) return null;
+
+		if (this.userEntityService.isRemoteUser(exist)) {
+			if (TTL === 0 || exist.lastFetchedAt == null || Date.now() - exist.lastFetchedAt.getTime() > TTL) {
+				this.logger.debug('fetchPersonWithRenewal: renew', { uri, TTL, lastFetchedAt: exist.lastFetchedAt });
+				try {
+					await this.updatePerson(exist.uri);
+					return await this.fetchPerson(uri);
+				} catch (err) {
+					this.logger.error('error occurred while renewing user', { err });
+				}
+			}
+			this.logger.debug('fetchPersonWithRenewal: use cache', { uri, TTL, lastFetchedAt: exist.lastFetchedAt });
+		}
+
+		return exist;
+	}
+
 	private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Partial<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>>> {
 		if (user == null) throw new Error('failed to create user: user is null');
 
@@ -363,11 +417,15 @@ export class ApPersonService implements OnModuleInit {
 				}));
 
 				if (person.publicKey) {
-					await transactionalEntityManager.save(new MiUserPublickey({
-						userId: user.id,
-						keyId: person.publicKey.id,
-						keyPem: person.publicKey.publicKeyPem,
-					}));
+					const publicKeys = new Map<string, IKey>();
+					(person.additionalPublicKeys ?? []).forEach(key => publicKeys.set(key.id, key));
+					(Array.isArray(person.publicKey) ? person.publicKey : [person.publicKey]).forEach(key => publicKeys.set(key.id, key));
+
+					await transactionalEntityManager.save(Array.from(publicKeys.values(), key => new MiUserPublickey({
+						keyId: key.id,
+						userId: user!.id,
+						keyPem: key.publicKeyPem,
+					})));
 				}
 			});
 		} catch (e) {
@@ -513,11 +571,29 @@ export class ApPersonService implements OnModuleInit {
 		// Update user
 		await this.usersRepository.update(exist.id, updates);
 
-		if (person.publicKey) {
-			await this.userPublickeysRepository.update({ userId: exist.id }, {
-				keyId: person.publicKey.id,
-				keyPem: person.publicKey.publicKeyPem,
+		try {
+			// Deleteアクティビティ受信時にもここが走ってsaveがuserforeign key制約エラーを吐くことがある
+			// とりあえずtry-catchで囲っておく
+			const publicKeys = new Map<string, IKey>();
+			if (person.publicKey) {
+				(person.additionalPublicKeys ?? []).forEach(key => publicKeys.set(key.id, key));
+				(Array.isArray(person.publicKey) ? person.publicKey : [person.publicKey]).forEach(key => publicKeys.set(key.id, key));
+
+				await this.userPublickeysRepository.save(Array.from(publicKeys.values(), key => ({
+					keyId: key.id,
+					userId: exist.id,
+					keyPem: key.publicKeyPem,
+				})));
+			}
+
+			this.userPublickeysRepository.delete({
+				keyId: Not(In(Array.from(publicKeys.keys()))),
+				userId: exist.id,
+			}).catch(err => {
+				this.logger.error('something happened while deleting remote user public keys:', { userId: exist.id, err });
 			});
+		} catch (err) {
+			this.logger.error('something happened while updating remote user public keys:', { userId: exist.id, err });
 		}
 
 		let _description: string | null = null;
@@ -559,7 +635,7 @@ export class ApPersonService implements OnModuleInit {
 			exist.movedAt == null ||
 			// 以前のmovingから14日以上経過した場合のみ移行処理を許可
 			// (Mastodonのクールダウン期間は30日だが若干緩めに設定しておく)
-			exist.movedAt.getTime() + 1000 * 60 * 60 * 24 * 14 < updated.movedAt.getTime()
+			exist.movedAt.getTime() + REMOTE_USER_MOVE_COOLDOWN < updated.movedAt.getTime()
 		)) {
 			this.logger.info(`Start to process Move of @${updated.username}@${updated.host} (${uri})`);
 			return this.processRemoteMove(updated, movePreventUris)
@@ -582,9 +658,9 @@ export class ApPersonService implements OnModuleInit {
 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 	 */
 	@bindThis
-	public async resolvePerson(uri: string, resolver?: Resolver): Promise<MiLocalUser | MiRemoteUser> {
+	public async resolvePerson(uri: string, resolver?: Resolver, withRenewal = false): Promise<MiLocalUser | MiRemoteUser> {
 		//#region このサーバーに既に登録されていたらそれを返す
-		const exist = await this.fetchPerson(uri);
+		const exist = withRenewal ? await this.fetchPersonWithRenewal(uri) : await this.fetchPerson(uri);
 		if (exist) return exist;
 		//#endregion
 
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index 5b6c6c8ca6..1d55971660 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -55,7 +55,7 @@ export function getOneApId(value: ApObject): string {
 export function getApId(value: string | IObject): string {
 	if (typeof value === 'string') return value;
 	if (typeof value.id === 'string') return value.id;
-	throw new Error('cannot detemine id');
+	throw new Error('cannot determine id');
 }
 
 /**
@@ -169,10 +169,8 @@ export interface IActor extends IObject {
 	discoverable?: boolean;
 	inbox: string;
 	sharedInbox?: string;	// 後方互換性のため
-	publicKey?: {
-		id: string;
-		publicKeyPem: string;
-	};
+	publicKey?: IKey | IKey[];
+	additionalPublicKeys?: IKey[];
 	followers?: string | ICollection | IOrderedCollection;
 	following?: string | ICollection | IOrderedCollection;
 	featured?: string | IOrderedCollection;
@@ -236,8 +234,9 @@ export const isEmoji = (object: IObject): object is IApEmoji =>
 
 export interface IKey extends IObject {
 	type: 'Key';
+	id: string;
 	owner: string;
-	publicKeyPem: string | Buffer;
+	publicKeyPem: string;
 }
 
 export interface IApDocument extends IObject {
diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts
index 9117b13914..fd0f55c6ab 100644
--- a/packages/backend/src/core/entities/InstanceEntityService.ts
+++ b/packages/backend/src/core/entities/InstanceEntityService.ts
@@ -56,6 +56,7 @@ export class InstanceEntityService {
 			infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
 			latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null,
 			moderationNote: iAmModerator ? instance.moderationNote : null,
+			httpMessageSignaturesImplementationLevel: instance.httpMessageSignaturesImplementationLevel,
 		};
 	}
 
diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts
index bba64a06ef..f498c110bf 100644
--- a/packages/backend/src/misc/cache.ts
+++ b/packages/backend/src/misc/cache.ts
@@ -195,6 +195,9 @@ export class MemoryKVCache<T> {
 	private lifetime: number;
 	private gcIntervalHandle: NodeJS.Timeout;
 
+	/**
+	 * @param lifetime キャッシュの生存期間 (ms)
+	 */
 	constructor(lifetime: MemoryKVCache<never>['lifetime']) {
 		this.cache = new Map();
 		this.lifetime = lifetime;
diff --git a/packages/backend/src/misc/gen-key-pair.ts b/packages/backend/src/misc/gen-key-pair.ts
index 02a303dc0a..0b033ec33e 100644
--- a/packages/backend/src/misc/gen-key-pair.ts
+++ b/packages/backend/src/misc/gen-key-pair.ts
@@ -3,39 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as crypto from 'node:crypto';
-import * as util from 'node:util';
+import { genEd25519KeyPair, genRsaKeyPair } from '@misskey-dev/node-http-message-signatures';
 
-const generateKeyPair = util.promisify(crypto.generateKeyPair);
-
-export async function genRsaKeyPair(modulusLength = 2048) {
-	return await generateKeyPair('rsa', {
-		modulusLength,
-		publicKeyEncoding: {
-			type: 'spki',
-			format: 'pem',
-		},
-		privateKeyEncoding: {
-			type: 'pkcs8',
-			format: 'pem',
-			cipher: undefined,
-			passphrase: undefined,
-		},
-	});
-}
-
-export async function genEcKeyPair(namedCurve: 'prime256v1' | 'secp384r1' | 'secp521r1' | 'curve25519' = 'prime256v1') {
-	return await generateKeyPair('ec', {
-		namedCurve,
-		publicKeyEncoding: {
-			type: 'spki',
-			format: 'pem',
-		},
-		privateKeyEncoding: {
-			type: 'pkcs8',
-			format: 'pem',
-			cipher: undefined,
-			passphrase: undefined,
-		},
-	});
+export async function genRSAAndEd25519KeyPair(rsaModulusLength = 4096) {
+	const [rsa, ed25519] = await Promise.all([genRsaKeyPair(rsaModulusLength), genEd25519KeyPair()]);
+	return {
+		publicKey: rsa.publicKey,
+		privateKey: rsa.privateKey,
+		ed25519PublicKey: ed25519.publicKey,
+		ed25519PrivateKey: ed25519.privateKey,
+	};
 }
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index 17cd5c6665..f2f2831cf1 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -158,4 +158,9 @@ export class MiInstance {
 		length: 16384, default: '',
 	})
 	public moderationNote: string;
+
+	@Column('varchar', {
+		length: 16, default: '00', nullable: false,
+	})
+	public httpMessageSignaturesImplementationLevel: string;
 }
diff --git a/packages/backend/src/models/UserKeypair.ts b/packages/backend/src/models/UserKeypair.ts
index f5252d126c..afa74ef11a 100644
--- a/packages/backend/src/models/UserKeypair.ts
+++ b/packages/backend/src/models/UserKeypair.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm';
+import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne } from 'typeorm';
 import { id } from './util/id.js';
 import { MiUser } from './User.js';
 
@@ -12,22 +12,42 @@ export class MiUserKeypair {
 	@PrimaryColumn(id())
 	public userId: MiUser['id'];
 
-	@OneToOne(type => MiUser, {
+	@ManyToOne(type => MiUser, {
 		onDelete: 'CASCADE',
 	})
 	@JoinColumn()
 	public user: MiUser | null;
 
+	/**
+	 * RSA public key
+	 */
 	@Column('varchar', {
 		length: 4096,
 	})
 	public publicKey: string;
 
+	/**
+	 * RSA private key
+	 */
 	@Column('varchar', {
 		length: 4096,
 	})
 	public privateKey: string;
 
+	@Column('varchar', {
+		length: 128,
+		nullable: true,
+		default: null,
+	})
+	public ed25519PublicKey: string | null;
+
+	@Column('varchar', {
+		length: 128,
+		nullable: true,
+		default: null,
+	})
+	public ed25519PrivateKey: string | null;
+
 	constructor(data: Partial<MiUserKeypair>) {
 		if (data == null) return;
 
diff --git a/packages/backend/src/models/UserPublickey.ts b/packages/backend/src/models/UserPublickey.ts
index 6bcd785304..0ecff2bcbe 100644
--- a/packages/backend/src/models/UserPublickey.ts
+++ b/packages/backend/src/models/UserPublickey.ts
@@ -9,7 +9,13 @@ import { MiUser } from './User.js';
 
 @Entity('user_publickey')
 export class MiUserPublickey {
-	@PrimaryColumn(id())
+	@PrimaryColumn('varchar', {
+		length: 256,
+	})
+	public keyId: string;
+
+	@Index()
+	@Column(id())
 	public userId: MiUser['id'];
 
 	@OneToOne(type => MiUser, {
@@ -18,12 +24,6 @@ export class MiUserPublickey {
 	@JoinColumn()
 	public user: MiUser | null;
 
-	@Index({ unique: true })
-	@Column('varchar', {
-		length: 256,
-	})
-	public keyId: string;
-
 	@Column('varchar', {
 		length: 4096,
 	})
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index ed40d405c6..c02e7f557a 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -116,5 +116,9 @@ export const packedFederationInstanceSchema = {
 			type: 'string',
 			optional: true, nullable: true,
 		},
+		httpMessageSignaturesImplementationLevel: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
 	},
 } as const;
diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index 7bd74f3210..169b22c3f5 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -250,9 +250,9 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			}, {
 				...baseQueueOptions(this.config, QUEUE.DELIVER),
 				autorun: false,
-				concurrency: this.config.deliverJobConcurrency ?? 128,
+				concurrency: this.config.deliverJobConcurrency ?? 16,
 				limiter: {
-					max: this.config.deliverJobPerSec ?? 128,
+					max: this.config.deliverJobPerSec ?? 1024,
 					duration: 1000,
 				},
 				settings: {
@@ -290,9 +290,9 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			}, {
 				...baseQueueOptions(this.config, QUEUE.INBOX),
 				autorun: false,
-				concurrency: this.config.inboxJobConcurrency ?? 16,
+				concurrency: this.config.inboxJobConcurrency ?? 4,
 				limiter: {
-					max: this.config.inboxJobPerSec ?? 32,
+					max: this.config.inboxJobPerSec ?? 64,
 					duration: 1000,
 				},
 				settings: {
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index d665945861..3bd9187e8b 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -73,25 +73,33 @@ export class DeliverProcessorService {
 		}
 
 		try {
-			await this.apRequestService.signedPost(job.data.user, job.data.to, job.data.content, job.data.digest);
+			const _server = await this.federatedInstanceService.fetch(host);
+			await this.fetchInstanceMetadataService.fetchInstanceMetadata(_server).then(() => {});
+			const server = await this.federatedInstanceService.fetch(host);
+
+			await this.apRequestService.signedPost(
+				job.data.user,
+				job.data.to,
+				job.data.content,
+				server.httpMessageSignaturesImplementationLevel,
+				job.data.digest,
+				job.data.privateKey,
+			);
 
 			// Update stats
-			this.federatedInstanceService.fetch(host).then(i => {
-				if (i.isNotResponding) {
-					this.federatedInstanceService.update(i.id, {
-						isNotResponding: false,
-						notRespondingSince: null,
-					});
-				}
+			if (server.isNotResponding) {
+				this.federatedInstanceService.update(server.id, {
+					isNotResponding: false,
+					notRespondingSince: null,
+				});
+			}
 
-				this.fetchInstanceMetadataService.fetchInstanceMetadata(i);
-				this.apRequestChart.deliverSucc();
-				this.federationChart.deliverd(i.host, true);
+			this.apRequestChart.deliverSucc();
+			this.federationChart.deliverd(server.host, true);
 
-				if (meta.enableChartsForFederatedInstances) {
-					this.instanceChart.requestSent(i.host, true);
-				}
-			});
+			if (meta.enableChartsForFederatedInstances) {
+				this.instanceChart.requestSent(server.host, true);
+			}
 
 			return 'Success';
 		} catch (res) {
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index fa7009f8f5..935c623df1 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -5,8 +5,8 @@
 
 import { URL } from 'node:url';
 import { Injectable } from '@nestjs/common';
-import httpSignature from '@peertube/http-signature';
 import * as Bull from 'bullmq';
+import { verifyDraftSignature } from '@misskey-dev/node-http-message-signatures';
 import type Logger from '@/logger.js';
 import { MetaService } from '@/core/MetaService.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
@@ -20,6 +20,7 @@ import type { MiRemoteUser } from '@/models/User.js';
 import type { MiUserPublickey } from '@/models/UserPublickey.js';
 import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
 import { StatusError } from '@/misc/status-error.js';
+import * as Acct from '@/misc/acct.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
 import { JsonLdService } from '@/core/activitypub/JsonLdService.js';
@@ -52,8 +53,15 @@ export class InboxProcessorService {
 
 	@bindThis
 	public async process(job: Bull.Job<InboxJobData>): Promise<string> {
-		const signature = job.data.signature;	// HTTP-signature
+		const signature = job.data.signature ?
+			'version' in job.data.signature ? job.data.signature.value : job.data.signature
+			: null;
+		if (Array.isArray(signature)) {
+			// RFC 9401はsignatureが配列になるが、とりあえずエラーにする
+			throw new Error('signature is array');
+		}
 		let activity = job.data.activity;
+		let actorUri = getApId(activity.actor);
 
 		//#region Log
 		const info = Object.assign({}, activity);
@@ -61,7 +69,7 @@ export class InboxProcessorService {
 		this.logger.debug(JSON.stringify(info, null, 2));
 		//#endregion
 
-		const host = this.utilityService.toPuny(new URL(signature.keyId).hostname);
+		const host = this.utilityService.toPuny(new URL(actorUri).hostname);
 
 		// ブロックしてたら中断
 		const meta = await this.metaService.fetch();
@@ -69,69 +77,76 @@ export class InboxProcessorService {
 			return `Blocked request: ${host}`;
 		}
 
-		const keyIdLower = signature.keyId.toLowerCase();
-		if (keyIdLower.startsWith('acct:')) {
-			return `Old keyId is no longer supported. ${keyIdLower}`;
-		}
-
 		// HTTP-Signature keyIdを元にDBから取得
-		let authUser: {
-			user: MiRemoteUser;
-			key: MiUserPublickey | null;
-		} | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
+		let authUser: Awaited<ReturnType<typeof this.apDbResolverService.getAuthUserFromApId>> = null;
+		let httpSignatureIsValid = null as boolean | null;
 
-		// keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得
-		if (authUser == null) {
-			try {
-				authUser = await this.apDbResolverService.getAuthUserFromApId(getApId(activity.actor));
-			} catch (err) {
-				// 対象が4xxならスキップ
-				if (err instanceof StatusError) {
-					if (!err.isRetryable) {
-						throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
-					}
-					throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
+		try {
+			authUser = await this.apDbResolverService.getAuthUserFromApId(actorUri, signature?.keyId);
+		} catch (err) {
+			// 対象が4xxならスキップ
+			if (err instanceof StatusError) {
+				if (!err.isRetryable) {
+					throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
 				}
+				throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
 			}
 		}
 
-		// それでもわからなければ終了
-		if (authUser == null) {
+		// authUser.userがnullならスキップ
+		if (authUser != null && authUser.user == null) {
 			throw new Bull.UnrecoverableError('skip: failed to resolve user');
 		}
 
-		// publicKey がなくても終了
-		if (authUser.key == null) {
-			throw new Bull.UnrecoverableError('skip: failed to resolve user publicKey');
+		if (signature != null && authUser != null) {
+			if (signature.keyId.toLowerCase().startsWith('acct:')) {
+				this.logger.warn(`Old keyId is no longer supported. lowerKeyId=${signature.keyId.toLowerCase()}`);
+			} else if (authUser.key != null) {
+				// keyがなかったらLD Signatureで検証するべき
+				// HTTP-Signatureの検証
+				const errorLogger = (ms: any) => this.logger.error(ms);
+				httpSignatureIsValid = await verifyDraftSignature(signature, authUser.key.keyPem, errorLogger);
+				this.logger.debug('Inbox message validation: ', {
+					userId: authUser.user.id,
+					userAcct: Acct.toString(authUser.user),
+					parsedKeyId: signature.keyId,
+					foundKeyId: authUser.key.keyId,
+					httpSignatureValid: httpSignatureIsValid,
+				});
+			}
 		}
 
-		// HTTP-Signatureの検証
-		const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
-
-		// また、signatureのsignerは、activity.actorと一致する必要がある
-		if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
+		if (
+			authUser == null ||
+			httpSignatureIsValid !== true ||
+			authUser.user.uri !== actorUri // 一応チェック
+		) {
 			// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
 			const ldSignature = activity.signature;
-			if (ldSignature) {
+
+			if (ldSignature && ldSignature.creator) {
 				if (ldSignature.type !== 'RsaSignature2017') {
 					throw new Bull.UnrecoverableError(`skip: unsupported LD-signature type ${ldSignature.type}`);
 				}
 
-				// ldSignature.creator: https://example.oom/users/user#main-key
-				// みたいになっててUserを引っ張れば公開キーも入ることを期待する
-				if (ldSignature.creator) {
-					const candicate = ldSignature.creator.replace(/#.*/, '');
-					await this.apPersonService.resolvePerson(candicate).catch(() => null);
+				if (ldSignature.creator.toLowerCase().startsWith('acct:')) {
+					throw new Bull.UnrecoverableError(`old key not supported ${ldSignature.creator}`);
 				}
 
-				// keyIdからLD-Signatureのユーザーを取得
-				authUser = await this.apDbResolverService.getAuthUserFromKeyId(ldSignature.creator);
+				authUser = await this.apDbResolverService.getAuthUserFromApId(actorUri, ldSignature.creator);
+
 				if (authUser == null) {
-					throw new Bull.UnrecoverableError('skip: LD-Signatureのユーザーが取得できませんでした');
+					throw new Bull.UnrecoverableError(`skip: LD-Signatureのactorとcreatorが一致しませんでした uri=${actorUri} creator=${ldSignature.creator}`);
+				}
+				if (authUser.user == null) {
+					throw new Bull.UnrecoverableError(`skip: LD-Signatureのユーザーが取得できませんでした uri=${actorUri} creator=${ldSignature.creator}`);
+				}
+				// 一応actorチェック
+				if (authUser.user.uri !== actorUri) {
+					throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${actorUri})`);
 				}
-
 				if (authUser.key == null) {
-					throw new Bull.UnrecoverableError('skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした');
+					throw new Bull.UnrecoverableError(`skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした uri=${actorUri} creator=${ldSignature.creator}`);
 				}
 
 				const jsonLd = this.jsonLdService.use();
@@ -142,13 +157,27 @@ export class InboxProcessorService {
 					throw new Bull.UnrecoverableError('skip: LD-Signatureの検証に失敗しました');
 				}
 
+				// ブロックしてたら中断
+				const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
+				if (this.utilityService.isBlockedHost(meta.blockedHosts, ldHost)) {
+					throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`);
+				}
+
 				// アクティビティを正規化
+				// GHSA-2vxv-pv3m-3wvj
 				delete activity.signature;
 				try {
 					activity = await jsonLd.compact(activity) as IActivity;
 				} catch (e) {
 					throw new Bull.UnrecoverableError(`skip: failed to compact activity: ${e}`);
 				}
+
+				// actorが正規化前後で一致しているか確認
+				actorUri = getApId(activity.actor);
+				if (authUser.user.uri !== actorUri) {
+					throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity(after normalization).actor(${actorUri})`);
+				}
+
 				// TODO: 元のアクティビティと非互換な形に正規化される場合は転送をスキップする
 				// https://github.com/mastodon/mastodon/blob/664b0ca/app/services/activitypub/process_collection_service.rb#L24-L29
 				activity.signature = ldSignature;
@@ -158,19 +187,8 @@ export class InboxProcessorService {
 				delete compactedInfo['@context'];
 				this.logger.debug(`compacted: ${JSON.stringify(compactedInfo, null, 2)}`);
 				//#endregion
-
-				// もう一度actorチェック
-				if (authUser.user.uri !== activity.actor) {
-					throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`);
-				}
-
-				// ブロックしてたら中断
-				const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
-				if (this.utilityService.isBlockedHost(meta.blockedHosts, ldHost)) {
-					throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`);
-				}
 			} else {
-				throw new Bull.UnrecoverableError(`skip: http-signature verification failed and no LD-Signature. keyId=${signature.keyId}`);
+				throw new Bull.UnrecoverableError(`skip: http-signature verification failed and no LD-Signature. http_signature_keyId=${signature?.keyId}`);
 			}
 		}
 
diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts
index a4077a0547..f2466f2e3d 100644
--- a/packages/backend/src/queue/types.ts
+++ b/packages/backend/src/queue/types.ts
@@ -9,7 +9,24 @@ import type { MiNote } from '@/models/Note.js';
 import type { MiUser } from '@/models/User.js';
 import type { MiWebhook } from '@/models/Webhook.js';
 import type { IActivity } from '@/core/activitypub/type.js';
-import type httpSignature from '@peertube/http-signature';
+import type { ParsedSignature, PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
+
+/**
+ * @peertube/http-signature 時代の古いデータにも対応しておく
+ * TODO: 2026年ぐらいには消す
+ */
+export interface OldParsedSignature {
+	scheme: 'Signature';
+	params: {
+		keyId: string;
+		algorithm: string;
+		headers: string[];
+		signature: string;
+	};
+	signingString: string;
+	algorithm: string;
+	keyId: string;
+}
 
 export type DeliverJobData = {
 	/** Actor */
@@ -22,11 +39,13 @@ export type DeliverJobData = {
 	to: string;
 	/** whether it is sharedInbox */
 	isSharedInbox: boolean;
+	/** force to use main (rsa) key */
+	privateKey?: PrivateKeyWithPem;
 };
 
 export type InboxJobData = {
 	activity: IActivity;
-	signature: httpSignature.IParsedSignature;
+	signature: ParsedSignature | OldParsedSignature | null;
 };
 
 export type RelationshipJobData = {
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 3255d64621..753eaad047 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -3,11 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as crypto from 'node:crypto';
 import { IncomingMessage } from 'node:http';
 import { Inject, Injectable } from '@nestjs/common';
 import fastifyAccepts from '@fastify/accepts';
-import httpSignature from '@peertube/http-signature';
+import { verifyDigestHeader, parseRequestSignature } from '@misskey-dev/node-http-message-signatures';
 import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
 import accepts from 'accepts';
 import vary from 'vary';
@@ -31,12 +30,17 @@ import { IActivity } from '@/core/activitypub/type.js';
 import { isQuote, isRenote } from '@/misc/is-renote.js';
 import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
 import type { FindOptionsWhere } from 'typeorm';
+import { LoggerService } from '@/core/LoggerService.js';
+import Logger from '@/logger.js';
 
 const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
 const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8';
 
 @Injectable()
 export class ActivityPubServerService {
+	private logger: Logger;
+	private inboxLogger: Logger;
+
 	constructor(
 		@Inject(DI.config)
 		private config: Config,
@@ -71,8 +75,11 @@ export class ActivityPubServerService {
 		private queueService: QueueService,
 		private userKeypairService: UserKeypairService,
 		private queryService: QueryService,
+		private loggerService: LoggerService,
 	) {
 		//this.createServer = this.createServer.bind(this);
+		this.logger = this.loggerService.getLogger('server-ap', 'gray');
+		this.inboxLogger = this.logger.createSubLogger('inbox', 'gray');
 	}
 
 	@bindThis
@@ -100,70 +107,44 @@ export class ActivityPubServerService {
 	}
 
 	@bindThis
-	private inbox(request: FastifyRequest, reply: FastifyReply) {
-		let signature;
+	private async inbox(request: FastifyRequest, reply: FastifyReply) {
+		if (request.body == null) {
+			this.inboxLogger.warn('request body is empty');
+			reply.code(400);
+			return;
+		}
+
+		let signature: ReturnType<typeof parseRequestSignature>;
+
+		const verifyDigest = await verifyDigestHeader(request.raw, request.rawBody || '', true);
+		if (verifyDigest !== true) {
+			this.inboxLogger.warn('digest verification failed');
+			reply.code(401);
+			return;
+		}
 
 		try {
-			signature = httpSignature.parseRequest(request.raw, { 'headers': [] });
-		} catch (e) {
+			signature = parseRequestSignature(request.raw, {
+				requiredInputs: {
+					draft: ['(request-target)', 'digest', 'host', 'date'],
+				},
+			});
+		} catch (err) {
+			this.inboxLogger.warn('signature header parsing failed', { err });
+
+			if (typeof request.body === 'object' && 'signature' in request.body) {
+				// LD SignatureがあればOK
+				this.queueService.inbox(request.body as IActivity, null);
+				reply.code(202);
+				return;
+			}
+
+			this.inboxLogger.warn('signature header parsing failed and LD signature not found');
 			reply.code(401);
 			return;
 		}
 
-		if (signature.params.headers.indexOf('host') === -1
-			|| request.headers.host !== this.config.host) {
-			// Host not specified or not match.
-			reply.code(401);
-			return;
-		}
-
-		if (signature.params.headers.indexOf('digest') === -1) {
-			// Digest not found.
-			reply.code(401);
-		} else {
-			const digest = request.headers.digest;
-
-			if (typeof digest !== 'string') {
-				// Huh?
-				reply.code(401);
-				return;
-			}
-
-			const re = /^([a-zA-Z0-9\-]+)=(.+)$/;
-			const match = digest.match(re);
-
-			if (match == null) {
-				// Invalid digest
-				reply.code(401);
-				return;
-			}
-
-			const algo = match[1].toUpperCase();
-			const digestValue = match[2];
-
-			if (algo !== 'SHA-256') {
-				// Unsupported digest algorithm
-				reply.code(401);
-				return;
-			}
-
-			if (request.rawBody == null) {
-				// Bad request
-				reply.code(400);
-				return;
-			}
-
-			const hash = crypto.createHash('sha256').update(request.rawBody).digest('base64');
-
-			if (hash !== digestValue) {
-				// Invalid digest
-				reply.code(401);
-				return;
-			}
-		}
-
 		this.queueService.inbox(request.body as IActivity, signature);
-
 		reply.code(202);
 	}
 
@@ -640,7 +621,7 @@ export class ActivityPubServerService {
 			if (this.userEntityService.isLocalUser(user)) {
 				reply.header('Cache-Control', 'public, max-age=180');
 				this.setResponseType(request, reply);
-				return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair)));
+				return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair.publicKey)));
 			} else {
 				reply.code(400);
 				return;
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index cc18997fdc..c0f8084768 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -94,6 +94,13 @@ export class NodeinfoServerService {
 					localComments: 0,
 				},
 				metadata: {
+					/**
+					 * '00': Draft, RSA only
+					 * '01': Draft, Ed25519 suported
+					 * '11': RFC 9421, Ed25519 supported
+					 */
+					httpMessageSignaturesImplementationLevel: '01',
+
 					nodeName: meta.name,
 					nodeDescription: meta.description,
 					nodeAdmins: [{
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
index 305ae1af1d..bfe230da8d 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
@@ -56,7 +56,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const res = [] as [string, number][];
 
 			for (const job of jobs) {
-				const host = new URL(job.data.signature.keyId).host;
+				const signature = job.data.signature ? 'version' in job.data.signature ? job.data.signature.value : job.data.signature : null;
+				const host = signature ? Array.isArray(signature) ? 'TODO' : new URL(signature.keyId).host : new URL(job.data.activity.actor).host;
 				if (res.find(x => x[0] === host)) {
 					res.find(x => x[0] === host)![1]++;
 				} else {
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index 540b866b28..fce1eacf00 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -378,7 +378,7 @@ describe('Timelines', () => {
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 			assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
-		}, 1000 * 10);
+		});
 
 		test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
@@ -672,7 +672,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		}, 1000 * 10);
+		});
 	});
 
 	describe('Social TL', () => {
@@ -812,7 +812,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		}, 1000 * 10);
+		});
 	});
 
 	describe('User List TL', () => {
@@ -1025,7 +1025,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		}, 1000 * 10);
+		});
 
 		test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
@@ -1184,7 +1184,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		}, 1000 * 10);
+		});
 
 		test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts
index 3c7e796700..485506ee64 100644
--- a/packages/backend/test/misc/mock-resolver.ts
+++ b/packages/backend/test/misc/mock-resolver.ts
@@ -14,6 +14,7 @@ import type { InstanceActorService } from '@/core/InstanceActorService.js';
 import type { LoggerService } from '@/core/LoggerService.js';
 import type { MetaService } from '@/core/MetaService.js';
 import type { UtilityService } from '@/core/UtilityService.js';
+import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 import { bindThis } from '@/decorators.js';
 import type {
 	FollowRequestsRepository,
@@ -47,6 +48,7 @@ export class MockResolver extends Resolver {
 			{} as HttpRequestService,
 			{} as ApRendererService,
 			{} as ApDbResolverService,
+			{} as FederatedInstanceService,
 			loggerService,
 		);
 	}
diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts
index bf8f3ab0e3..2e66b81fcd 100644
--- a/packages/backend/test/unit/FetchInstanceMetadataService.ts
+++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts
@@ -75,62 +75,61 @@ describe('FetchInstanceMetadataService', () => {
 	test('Lock and update', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any);
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date(now - 10 * 1000 * 60 * 60 * 24) } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
+		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
-		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
 		expect(httpRequestService.getJson).toHaveBeenCalled();
 	});
 
-	test('Lock and don\'t update', async () => {
+	test('Don\'t lock and update if recently updated', async () => {
 		redisClient.set = mockRedis();
-		const now = Date.now();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any);
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date() } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
-		expect(tryLockSpy).toHaveBeenCalledTimes(1);
-		expect(unlockSpy).toHaveBeenCalledTimes(1);
 		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
+		expect(tryLockSpy).toHaveBeenCalledTimes(0);
+		expect(unlockSpy).toHaveBeenCalledTimes(0);
 		expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
 	});
 
 	test('Do nothing when lock not acquired', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date(now - 10 * 1000 * 60 * 60 * 24) } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		await fetchInstanceMetadataService.tryLock('example.com');
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
+		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(0);
-		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
 		expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
 	});
 
-	test('Do when lock not acquired but forced', async () => {
+	test('Do when forced', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date(now - 10 * 1000 * 60 * 60 * 24) } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		await fetchInstanceMetadataService.tryLock('example.com');
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true);
+		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
 		expect(tryLockSpy).toHaveBeenCalledTimes(0);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
-		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
 		expect(httpRequestService.getJson).toHaveBeenCalled();
 	});
 });
diff --git a/packages/backend/test/unit/ap-request.ts b/packages/backend/test/unit/ap-request.ts
index d3d39240dc..50894c8b81 100644
--- a/packages/backend/test/unit/ap-request.ts
+++ b/packages/backend/test/unit/ap-request.ts
@@ -4,10 +4,8 @@
  */
 
 import * as assert from 'assert';
-import httpSignature from '@peertube/http-signature';
-
-import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
-import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
+import { verifyDraftSignature, parseRequestSignature, genEd25519KeyPair, genRsaKeyPair, importPrivateKey } from '@misskey-dev/node-http-message-signatures';
+import { createSignedGet, createSignedPost } from '@/core/activitypub/ApRequestService.js';
 
 export const buildParsedSignature = (signingString: string, signature: string, algorithm: string) => {
 	return {
@@ -24,38 +22,68 @@ export const buildParsedSignature = (signingString: string, signature: string, a
 	};
 };
 
-describe('ap-request', () => {
-	test('createSignedPost with verify', async () => {
-		const keypair = await genRsaKeyPair();
-		const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
-		const url = 'https://example.com/inbox';
-		const activity = { a: 1 };
-		const body = JSON.stringify(activity);
-		const headers = {
-			'User-Agent': 'UA',
-		};
+async function getKeyPair(level: string) {
+	if (level === '00') {
+		return await genRsaKeyPair();
+	} else if (level === '01') {
+		return await genEd25519KeyPair();
+	}
+	throw new Error('Invalid level');
+}
 
-		const req = ApRequestCreator.createSignedPost({ key, url, body, additionalHeaders: headers });
+describe('ap-request post', () => {
+	const url = 'https://example.com/inbox';
+	const activity = { a: 1 };
+	const body = JSON.stringify(activity);
+	const headers = {
+		'User-Agent': 'UA',
+	};
 
-		const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256');
+	describe.each(['00', '01'])('createSignedPost with verify', (level) => {
+		test('pem', async () => {
+			const keypair = await getKeyPair(level);
+			const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
 
-		const result = httpSignature.verifySignature(parsed, keypair.publicKey);
-		assert.deepStrictEqual(result, true);
-	});
+			const req = await createSignedPost({ level, key, url, body, additionalHeaders: headers });
 
-	test('createSignedGet with verify', async () => {
-		const keypair = await genRsaKeyPair();
-		const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
-		const url = 'https://example.com/outbox';
-		const headers = {
-			'User-Agent': 'UA',
-		};
+			const parsed = parseRequestSignature(req.request);
+			expect(parsed.version).toBe('draft');
+			expect(Array.isArray(parsed.value)).toBe(false);
+			const verify = await verifyDraftSignature(parsed.value as any, keypair.publicKey);
+			assert.deepStrictEqual(verify, true);
+		});
+		test('imported', async () => {
+			const keypair = await getKeyPair(level);
+			const key = { keyId: 'x', 'privateKey': await importPrivateKey(keypair.privateKey) };
 
-		const req = ApRequestCreator.createSignedGet({ key, url, additionalHeaders: headers });
+			const req = await createSignedPost({ level, key, url, body, additionalHeaders: headers });
 
-		const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256');
-
-		const result = httpSignature.verifySignature(parsed, keypair.publicKey);
-		assert.deepStrictEqual(result, true);
+			const parsed = parseRequestSignature(req.request);
+			expect(parsed.version).toBe('draft');
+			expect(Array.isArray(parsed.value)).toBe(false);
+			const verify = await verifyDraftSignature(parsed.value as any, keypair.publicKey);
+			assert.deepStrictEqual(verify, true);
+		});
+	});
+});
+
+describe('ap-request get', () => {
+	describe.each(['00', '01'])('createSignedGet with verify', (level) => {
+		test('pass', async () => {
+			const keypair = await getKeyPair(level);
+			const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
+			const url = 'https://example.com/outbox';
+			const headers = {
+				'User-Agent': 'UA',
+			};
+
+			const req = await createSignedGet({ level, key, url, additionalHeaders: headers });
+
+			const parsed = parseRequestSignature(req.request);
+			expect(parsed.version).toBe('draft');
+			expect(Array.isArray(parsed.value)).toBe(false);
+			const verify = await verifyDraftSignature(parsed.value as any, keypair.publicKey);
+			assert.deepStrictEqual(verify, true);
+		});
 	});
 });
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index d3c857219b..2a7e5a323d 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4608,6 +4608,7 @@ export type components = {
       /** Format: date-time */
       latestRequestReceivedAt: string | null;
       moderationNote?: string | null;
+      httpMessageSignaturesImplementationLevel: string;
     };
     GalleryPost: {
       /**
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7d3fec8596..2d426c2fa8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -125,6 +125,9 @@ importers:
       '@fastify/view':
         specifier: 9.1.0
         version: 9.1.0
+      '@misskey-dev/node-http-message-signatures':
+        specifier: 0.0.10
+        version: 0.0.10
       '@misskey-dev/sharp-read-bmp':
         specifier: 1.2.0
         version: 1.2.0
@@ -143,9 +146,6 @@ importers:
       '@nestjs/testing':
         specifier: 10.3.10
         version: 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10))
-      '@peertube/http-signature':
-        specifier: 1.7.0
-        version: 1.7.0
       '@sentry/node':
         specifier: 8.13.0
         version: 8.13.0
@@ -3228,6 +3228,11 @@ packages:
   '@kurkle/color@0.3.2':
     resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
 
+  '@lapo/asn1js@2.0.4':
+    resolution: {integrity: sha512-KJD3wQAZxozcraJdWp3utDU6DEZgAVBGp9INCdptUpZaXCEYkpwNb7h7wyYh5y6DxtpvIud8k0suhWJ/z2rKvw==}
+    engines: {node: '>=12.20.0'}
+    hasBin: true
+
   '@levischuck/tiny-cbor@0.2.2':
     resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==}
 
@@ -3281,6 +3286,10 @@ packages:
       eslint-plugin-import: '>= 2'
       globals: '>= 15'
 
+  '@misskey-dev/node-http-message-signatures@0.0.10':
+    resolution: {integrity: sha512-HiAuc//tOU077KFUJhHYLAPWku9enTpOFIqQiK6l2i2mIizRvv7HhV7Y+yuav5quDOAz+WZGK/i5C9OR5fkKIg==}
+    engines: {node: '>=18.4.0'}
+
   '@misskey-dev/sharp-read-bmp@1.2.0':
     resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
 
@@ -3671,10 +3680,6 @@ packages:
   '@peculiar/asn1-x509@2.3.8':
     resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==}
 
-  '@peertube/http-signature@1.7.0':
-    resolution: {integrity: sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==}
-    engines: {node: '>=0.10'}
-
   '@pkgjs/parseargs@0.11.0':
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
@@ -10524,6 +10529,9 @@ packages:
     resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
     engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
 
+  rfc4648@1.5.3:
+    resolution: {integrity: sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ==}
+
   rfdc@1.3.0:
     resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
 
@@ -11075,6 +11083,10 @@ packages:
     resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==}
     engines: {node: '>=14.16'}
 
+  structured-headers@1.0.1:
+    resolution: {integrity: sha512-QYBxdBtA4Tl5rFPuqmbmdrS9kbtren74RTJTcs0VSQNVV5iRhJD4QlYTLD0+81SBwUQctjEQzjTRI3WG4DzICA==}
+    engines: {node: '>= 14', npm: '>=6'}
+
   stylehacks@6.1.1:
     resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==}
     engines: {node: ^14 || ^16 || >=18.0}
@@ -14642,6 +14654,8 @@ snapshots:
 
   '@kurkle/color@0.3.2': {}
 
+  '@lapo/asn1js@2.0.4': {}
+
   '@levischuck/tiny-cbor@0.2.2': {}
 
   '@lukeed/csprng@1.0.1': {}
@@ -14722,6 +14736,12 @@ snapshots:
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
       globals: 15.7.0
 
+  '@misskey-dev/node-http-message-signatures@0.0.10':
+    dependencies:
+      '@lapo/asn1js': 2.0.4
+      rfc4648: 1.5.3
+      structured-headers: 1.0.1
+
   '@misskey-dev/sharp-read-bmp@1.2.0':
     dependencies:
       decode-bmp: 0.2.1
@@ -15182,12 +15202,6 @@ snapshots:
       pvtsutils: 1.3.5
       tslib: 2.6.2
 
-  '@peertube/http-signature@1.7.0':
-    dependencies:
-      assert-plus: 1.0.0
-      jsprim: 1.4.2
-      sshpk: 1.17.0
-
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
@@ -23879,6 +23893,8 @@ snapshots:
 
   reusify@1.0.4: {}
 
+  rfc4648@1.5.3: {}
+
   rfdc@1.3.0: {}
 
   rimraf@2.6.3:
@@ -24474,6 +24490,8 @@ snapshots:
       '@tokenizer/token': 0.3.0
       peek-readable: 5.0.0
 
+  structured-headers@1.0.1: {}
+
   stylehacks@6.1.1(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0

From 25b65925f76f2be1f28e20f36405f8b9d104665e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 18 Jul 2024 01:47:11 +0900
Subject: [PATCH 125/206] =?UTF-8?q?refactor:=20misskey-assets=E3=82=B5?=
 =?UTF-8?q?=E3=83=96=E3=83=A2=E3=82=B8=E3=83=A5=E3=83=BC=E3=83=AB=E3=82=92?=
 =?UTF-8?q?=E5=89=8A=E9=99=A4=20(#12818)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (change) misskey-assetsサブモジュールを削除

* なんか残ってた
---
 .dockerignore                           | 1 -
 .gitmodules                             | 3 ---
 misskey-assets                          | 1 -
 packages/frontend/.storybook/changes.ts | 1 -
 4 files changed, 6 deletions(-)
 delete mode 160000 misskey-assets

diff --git a/.dockerignore b/.dockerignore
index 7dbb06e1d0..f204349160 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -12,7 +12,6 @@ node_modules/
 packages/*/node_modules
 redis/
 files/
-misskey-assets/
 fluent-emojis/
 .pnp.*
 
diff --git a/.gitmodules b/.gitmodules
index 225a69a652..3218575273 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "misskey-assets"]
-	path = misskey-assets
-	url = https://github.com/misskey-dev/assets.git
 [submodule "fluent-emojis"]
 	path = fluent-emojis
 	url = https://github.com/misskey-dev/emojis.git
diff --git a/misskey-assets b/misskey-assets
deleted file mode 160000
index 0179793ec8..0000000000
--- a/misskey-assets
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5
diff --git a/packages/frontend/.storybook/changes.ts b/packages/frontend/.storybook/changes.ts
index bc7601441c..1299910499 100644
--- a/packages/frontend/.storybook/changes.ts
+++ b/packages/frontend/.storybook/changes.ts
@@ -53,7 +53,6 @@ await fs.readFile(
 			'../../assets/**',
 			'../../fluent-emojis/**',
 			'../../locales/ja-JP.yml',
-			'../../misskey-assets/**',
 			'assets/**',
 			'public/**',
 			'../../pnpm-lock.yaml',

From ec91e1889907c3c1512ad8d750817596d022188c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 18 Jul 2024 01:56:02 +0900
Subject: [PATCH 126/206] fix(frontend): add missing import (follow-up of
 #12265)

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

diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 1b4ec47469..3039ec7499 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -167,6 +167,7 @@ import number from '@/filters/number.js';
 import { userPage } from '@/filters/user.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
 import { $i, iAmModerator } from '@/account.js';
 import { dateString } from '@/filters/date.js';
 import { confetti } from '@/scripts/confetti.js';

From a54d04392378b640249083f6f5cad31c707e5c5d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 18 Jul 2024 02:05:40 +0900
Subject: [PATCH 127/206] chore: ignore misskey-assets (follow-up of #12818 )

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 3466984cf6..45170902b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,6 +59,7 @@ ormconfig.json
 temp
 /packages/frontend/src/**/*.stories.ts
 tsdoc-metadata.json
+misskey-assets
 
 # blender backups
 *.blend1

From cfdad45092ac2497a66b6b25f90bef7c466292a8 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 18 Jul 2024 02:06:31 +0900
Subject: [PATCH 128/206] =?UTF-8?q?fix:=20=E3=82=BD=E3=83=BC=E3=82=B7?=
 =?UTF-8?q?=E3=83=A3=E3=83=AB=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4?=
 =?UTF-8?q?=E3=83=B3=E3=81=AB=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB=E3=82=BF?=
 =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AB=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=82=8B=E8=87=AA=E5=88=86=E3=81=B8?=
 =?UTF-8?q?=E3=81=AE=E3=83=AA=E3=83=97=E3=83=A9=E3=82=A4=E3=81=8C=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13978)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
---
 CHANGELOG.md                                                     | 1 +
 .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts    | 1 +
 2 files changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 395716bbe2..10c44c4f8b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -65,6 +65,7 @@
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
 - Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
 - Fix: エラーメッセージの誤字を修正 (#14213)
+- Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
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 5acc9706d3..f6084b5763 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -139,6 +139,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				timelineConfig = [
 					`homeTimeline:${me.id}`,
 					'localTimeline',
+					`localTimelineWithReplyTo:${me.id}`,
 				];
 			}
 

From 73a42ea2ee448ae085b6bd39bf56ca6c6189f3a3 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Wed, 17 Jul 2024 17:23:58 +0000
Subject: [PATCH 129/206] Bump version to 2024.7.0-beta.1

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

diff --git a/package.json b/package.json
index 0dd7afb9e9..44466aaae6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-beta.0",
+	"version": "2024.7.0-beta.1",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1567ed7e61..d494b6b58e 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-beta.0",
+	"version": "2024.7.0-beta.1",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From c3a6da19d79c69f7a60157f48398b76345ced31f Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Thu, 18 Jul 2024 08:53:45 +0900
Subject: [PATCH 130/206] =?UTF-8?q?chore:=20CHANGELOG=E3=81=AB=E3=82=B8?=
 =?UTF-8?q?=E3=83=A7=E3=83=96=E3=82=AD=E3=83=A5=E3=83=BC=E8=A8=AD=E5=AE=9A?=
 =?UTF-8?q?=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E8=BF=BD=E8=A8=98=20(follo?=
 =?UTF-8?q?w-up=20of=20#13464)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10c44c4f8b..ad0a88e949 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,12 @@
   - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
 - Feat: 連合に使うHTTP SignaturesがEd25519鍵に対応するように #13464
   - Ed25519署名に対応するサーバーが増えると、deliverで要求されるサーバーリソースが削減されます
+	- ジョブキューのconfig設定のデフォルト値を変更しました。  
+	  default.ymlでジョブキューの並列度を設定している場合は、従前よりもconcurrencyの値をより下げるとパフォーマンスが改善する可能性があります。
+		* deliverJobConcurrency: 16 (←128)
+		* deliverJobPerSec: 1024 (←128)
+		* inboxJobConcurrency: 4 (←16)
+		* inboxJobPerSec: 64 (←32)
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正

From de166a8ed4b0e185e49c71825c92b4f94e2f350f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 18 Jul 2024 08:55:36 +0900
Subject: [PATCH 131/206] =?UTF-8?q?fix(backend):=20=E3=83=AA=E3=83=8E?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E3=81=8C?=
 =?UTF-8?q?=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=81=8C=E5=88=87?=
 =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=81=BE=E3=81=A7=E5=8A=B9=E3=81=8B=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1424?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fix: RenoteMuteがキャッシュが切れるまで効かない問題を修正

(cherry picked from commit e9601029b52e0ad43d9131b555b614e56c84ebc1)

* update changelog

* :art:

* remove unused import

* 消したときもキャッシュを飛ばすように

* lint

---------

Co-authored-by: mattyatea <mattyacocacora0@gmail.com>
---
 CHANGELOG.md                                  |  2 +
 packages/backend/src/core/CoreModule.ts       |  6 +++
 .../src/core/UserRenoteMutingService.ts       | 52 +++++++++++++++++++
 .../api/endpoints/renote-mute/create.ts       | 23 ++++----
 .../api/endpoints/renote-mute/delete.ts       |  8 +--
 5 files changed, 74 insertions(+), 17 deletions(-)
 create mode 100644 packages/backend/src/core/UserRenoteMutingService.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad0a88e949..8728ebf733 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -72,6 +72,8 @@
 - Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
 - Fix: エラーメッセージの誤字を修正 (#14213)
 - Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正
+- Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正  
+  (Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1)
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 0208540afa..c9427bbeb7 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -61,6 +61,7 @@ import { UserFollowingService } from './UserFollowingService.js';
 import { UserKeypairService } from './UserKeypairService.js';
 import { UserListService } from './UserListService.js';
 import { UserMutingService } from './UserMutingService.js';
+import { UserRenoteMutingService } from './UserRenoteMutingService.js';
 import { UserSuspendService } from './UserSuspendService.js';
 import { UserAuthService } from './UserAuthService.js';
 import { VideoProcessingService } from './VideoProcessingService.js';
@@ -203,6 +204,7 @@ const $UserFollowingService: Provider = { provide: 'UserFollowingService', useEx
 const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisting: UserKeypairService };
 const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService };
 const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService };
+const $UserRenoteMutingService: Provider = { provide: 'UserRenoteMutingService', useExisting: UserRenoteMutingService };
 const $UserSearchService: Provider = { provide: 'UserSearchService', useExisting: UserSearchService };
 const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService };
 const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService };
@@ -350,6 +352,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserKeypairService,
 		UserListService,
 		UserMutingService,
+		UserRenoteMutingService,
 		UserSearchService,
 		UserSuspendService,
 		UserAuthService,
@@ -493,6 +496,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserKeypairService,
 		$UserListService,
 		$UserMutingService,
+		$UserRenoteMutingService,
 		$UserSearchService,
 		$UserSuspendService,
 		$UserAuthService,
@@ -637,6 +641,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserKeypairService,
 		UserListService,
 		UserMutingService,
+		UserRenoteMutingService,
 		UserSearchService,
 		UserSuspendService,
 		UserAuthService,
@@ -779,6 +784,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserKeypairService,
 		$UserListService,
 		$UserMutingService,
+		$UserRenoteMutingService,
 		$UserSearchService,
 		$UserSuspendService,
 		$UserAuthService,
diff --git a/packages/backend/src/core/UserRenoteMutingService.ts b/packages/backend/src/core/UserRenoteMutingService.ts
new file mode 100644
index 0000000000..bdc5e23f4b
--- /dev/null
+++ b/packages/backend/src/core/UserRenoteMutingService.ts
@@ -0,0 +1,52 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project , Type4ny-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { In } from 'typeorm';
+import type { RenoteMutingsRepository } from '@/models/_.js';
+import type { MiRenoteMuting } from '@/models/RenoteMuting.js';
+
+import { IdService } from '@/core/IdService.js';
+import type { MiUser } from '@/models/User.js';
+import { DI } from '@/di-symbols.js';
+import { bindThis } from '@/decorators.js';
+import { CacheService } from '@/core/CacheService.js';
+
+@Injectable()
+export class UserRenoteMutingService {
+	constructor(
+		@Inject(DI.renoteMutingsRepository)
+		private renoteMutingsRepository: RenoteMutingsRepository,
+
+		private idService: IdService,
+		private cacheService: CacheService,
+	) {
+	}
+
+	@bindThis
+	public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
+		await this.renoteMutingsRepository.insert({
+			id: this.idService.gen(),
+			muterId: user.id,
+			muteeId: target.id,
+		});
+
+		await this.cacheService.renoteMutingsCache.refresh(user.id);
+	}
+
+	@bindThis
+	public async unmute(mutings: MiRenoteMuting[]): Promise<void> {
+		if (mutings.length === 0) return;
+
+		await this.renoteMutingsRepository.delete({
+			id: In(mutings.map(m => m.id)),
+		});
+
+		const muterIds = [...new Set(mutings.map(m => m.muterId))];
+		for (const muterId of muterIds) {
+			await this.cacheService.renoteMutingsCache.refresh(muterId);
+		}
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts
index 39bf0cc428..84a1f010d4 100644
--- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts
+++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts
@@ -6,12 +6,11 @@
 import { Inject, Injectable } from '@nestjs/common';
 import ms from 'ms';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import { IdService } from '@/core/IdService.js';
-import type { RenoteMutingsRepository } from '@/models/_.js';
-import type { MiRenoteMuting } from '@/models/RenoteMuting.js';
 import { DI } from '@/di-symbols.js';
 import { GetterService } from '@/server/api/GetterService.js';
 import { ApiError } from '../../error.js';
+import { UserRenoteMutingService } from "@/core/UserRenoteMutingService.js";
+import type { RenoteMutingsRepository } from '@/models/_.js';
 
 export const meta = {
 	tags: ['account'],
@@ -62,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private renoteMutingsRepository: RenoteMutingsRepository,
 
 		private getterService: GetterService,
-		private idService: IdService,
+		private userRenoteMutingService: UserRenoteMutingService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const muter = me;
@@ -79,21 +78,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			});
 
 			// Check if already muting
-			const exist = await this.renoteMutingsRepository.findOneBy({
-				muterId: muter.id,
-				muteeId: mutee.id,
+			const exist = await this.renoteMutingsRepository.exists({
+				where: {
+					muterId: muter.id,
+					muteeId: mutee.id,
+				},
 			});
 
-			if (exist != null) {
+			if (exist === true) {
 				throw new ApiError(meta.errors.alreadyMuting);
 			}
 
 			// Create mute
-			await this.renoteMutingsRepository.insert({
-				id: this.idService.gen(),
-				muterId: muter.id,
-				muteeId: mutee.id,
-			} as MiRenoteMuting);
+			await this.userRenoteMutingService.mute(muter, mutee);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts
index 6e037cc07e..1a584b8404 100644
--- a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts
+++ b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts
@@ -5,10 +5,11 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import type { RenoteMutingsRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { GetterService } from '@/server/api/GetterService.js';
 import { ApiError } from '../../error.js';
+import { UserRenoteMutingService } from "@/core/UserRenoteMutingService.js";
+import type { RenoteMutingsRepository } from '@/models/_.js';
 
 export const meta = {
 	tags: ['account'],
@@ -53,6 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private renoteMutingsRepository: RenoteMutingsRepository,
 
 		private getterService: GetterService,
+		private userRenoteMutingService: UserRenoteMutingService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const muter = me;
@@ -79,9 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			// Delete mute
-			await this.renoteMutingsRepository.delete({
-				id: exist.id,
-			});
+			await this.userRenoteMutingService.unmute([exist]);
 		});
 	}
 }

From e716c201c65128bb04299f509c5ccedbc7061a64 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Thu, 18 Jul 2024 08:57:02 +0900
Subject: [PATCH 132/206] =?UTF-8?q?docs:=20=E9=96=8B=E7=99=BA=E7=92=B0?=
 =?UTF-8?q?=E5=A2=83=E3=81=AE=E3=82=BB=E3=83=83=E3=83=88=E3=82=A2=E3=83=83?=
 =?UTF-8?q?=E3=83=97=E6=89=8B=E9=A0=86=E3=82=92=E8=A9=B3=E7=B4=B0=E3=81=AB?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=20(#14235)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* docs: mentioning Devcontainer

fix #13753

* revise

* revise 2

* Apply suggestions from code review
per https://github.com/misskey-dev/misskey/pull/14235#discussion_r1680883942

Co-authored-by: anatawa12 <anatawa12@icloud.com>

* 下の方にあったDevcontainerのセクションをマージ

* revise 3

* Update CONTRIBUTING.md

https://github.com/misskey-dev/misskey/pull/14235#discussion_r1680928026

Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com>

* mention Meilisearch

* Update CONTRIBUTING.md

---------

Co-authored-by: anatawa12 <anatawa12@icloud.com>
Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com>
---
 CONTRIBUTING.md | 52 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 20 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 532a2dc66f..9a56345e6e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -106,6 +106,38 @@ If your language is not listed in Crowdin, please open an issue.
 ![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)
 
 ## Development
+### Setup
+Before developing, you have to set up environment. Misskey requires Redis, PostgreSQL, and FFmpeg.
+
+You would want to install Meilisearch to experiment related features. Technically, meilisearch is not strict requirement, but some features and tests require it.
+
+There are a few ways to proceed.
+
+#### Use system-wide software
+You could install them in system-wide (such as from package manager).
+
+#### Use `docker compose`
+You could obtain middleware container by typing `docker compose -f $PROJECT_ROOT/compose.local-db.yml up -d`.
+
+#### Use Devcontainer
+Devcontainer also has necessary setting. This method can be done by connecting from VSCode.
+
+Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
+To use Dev Container, open the project directory on VSCode with Dev Containers installed.  
+**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled.
+
+It will run the following command automatically inside the container.
+``` bash
+git submodule update --init
+pnpm install --frozen-lockfile
+cp .devcontainer/devcontainer.yml .config/default.yml
+pnpm build
+pnpm migrate
+```
+
+After finishing the migration, you can proceed.
+
+### Start developing
 During development, it is useful to use the
 
 ```
@@ -135,26 +167,6 @@ MK_DEV_PREFER=backend pnpm dev
 - To change the port of Vite, specify with `VITE_PORT` environment variable.
 - HMR may not work in some environments such as Windows.
 
-### Dev Container
-Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
-To use Dev Container, open the project directory on VSCode with Dev Containers installed.  
-**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled.
-
-It will run the following command automatically inside the container.
-``` bash
-git submodule update --init
-pnpm install --frozen-lockfile
-cp .devcontainer/devcontainer.yml .config/default.yml
-pnpm build
-pnpm migrate
-```
-
-After finishing the migration, run the `pnpm dev` command to start the development server.
-
-``` bash
-pnpm dev
-```
-
 ## Testing
 - Test codes are located in [`/packages/backend/test`](/packages/backend/test).
 

From 4f85b6aa9158190bbdd9246bb8565d5c80081706 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 18 Jul 2024 15:41:32 +0900
Subject: [PATCH 133/206] =?UTF-8?q?fix(frontend):=20Twitch=E3=81=AE?=
 =?UTF-8?q?=E5=9F=8B=E3=82=81=E8=BE=BC=E3=81=BF=E3=81=8C=E9=96=8B=E3=81=91?=
 =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#14247)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): twitchの埋め込みが開けない問題を修正

* Update Changelog

* fix test
---
 CHANGELOG.md                                  |  1 +
 .../frontend/src/components/MkUrlPreview.vue  |  3 ++-
 .../src/components/MkYouTubePlayer.vue        |  3 ++-
 .../src/scripts/player-url-transform.ts       | 26 +++++++++++++++++++
 4 files changed, 31 insertions(+), 2 deletions(-)
 create mode 100644 packages/frontend/src/scripts/player-url-transform.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8728ebf733..60bc28e077 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,7 @@
 - Fix: MkSignin.vueのcredentialRequestからReactivityを削除(ProxyがPasskey認証処理に渡ることを避けるため)
 - Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574)
+- Fix: Twitchの埋め込みが開けない問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index 8df5e0fe40..c868a22045 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			scrolling="no"
 			:allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')"
 			:class="$style.playerIframe"
-			:src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')"
+			:src="transformPlayerUrl(player.url)"
 			:style="{ border: 0 }"
 		></iframe>
 		<span v-else>invalid url</span>
@@ -91,6 +91,7 @@ import * as os from '@/os.js';
 import { deviceKind } from '@/scripts/device-kind.js';
 import MkButton from '@/components/MkButton.vue';
 import { versatileLang } from '@/scripts/intl-const.js';
+import { transformPlayerUrl } from '@/scripts/player-url-transform.js';
 import { defaultStore } from '@/store.js';
 
 type SummalyResult = Awaited<ReturnType<typeof summaly>>;
diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue
index 1fad222fc5..e3711b3463 100644
--- a/packages/frontend/src/components/MkYouTubePlayer.vue
+++ b/packages/frontend/src/components/MkYouTubePlayer.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div class="poamfof">
 		<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
 			<div v-if="player.url && (player.url.startsWith('http://') || player.url.startsWith('https://'))" class="player">
-				<iframe v-if="!fetching" :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/>
+				<iframe v-if="!fetching" :src="transformPlayerUrl(player.url)" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
 			</div>
 			<span v-else>invalid url</span>
 		</Transition>
@@ -27,6 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import MkWindow from '@/components/MkWindow.vue';
 import { versatileLang } from '@/scripts/intl-const.js';
+import { transformPlayerUrl } from '@/scripts/player-url-transform.js';
 import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/scripts/player-url-transform.ts b/packages/frontend/src/scripts/player-url-transform.ts
new file mode 100644
index 0000000000..53b2a9e441
--- /dev/null
+++ b/packages/frontend/src/scripts/player-url-transform.ts
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+import { hostname } from '@/config.js';
+
+export function transformPlayerUrl(url: string): string {
+	const urlObj = new URL(url);
+	if (!['https:', 'http:'].includes(urlObj.protocol)) throw new Error('Invalid protocol');
+
+	const urlParams = new URLSearchParams(urlObj.search);
+
+	if (urlObj.hostname === 'player.twitch.tv') {
+		// TwitchはCSPの制約あり
+		// https://dev.twitch.tv/docs/embed/video-and-clips/
+		urlParams.set('parent', hostname);
+		urlParams.set('allowfullscreen', '');
+		urlParams.set('autoplay', 'true');
+	} else {
+		urlParams.set('autoplay', '1');
+		urlParams.set('auto_play', '1');
+	}
+	urlObj.search = urlParams.toString();
+
+	return urlObj.toString();
+}

From ec1c392f1e409e707ba81dfcd45112efb418c7aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 18 Jul 2024 15:44:18 +0900
Subject: [PATCH 134/206] =?UTF-8?q?fix(frontend):=20=E5=AD=90=E3=83=A1?=
 =?UTF-8?q?=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=AE=E6=9C=80=E5=A4=A7=E9=95=B7?=
 =?UTF-8?q?=E8=AA=BF=E6=95=B4=E3=81=8C=E8=A1=8C=E3=82=8F=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#14003)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 子メニューの最大長調整が行われていない問題を修正

* Update Changelog

* fix

* changelog

* Revert "fix"

This reverts commit 39fb326d49eedf484342c78a61c0dba8e223e596.

* Revert "fix(frontend): 子メニューの最大長調整が行われていない問題を修正"

This reverts commit ea58bf7a53fc8a254b7fbdf222a676e23527358c.

* use css

* maxHeightをchildから定義するように

* use css min
---
 CHANGELOG.md                                | 1 +
 packages/frontend/src/components/MkMenu.vue | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60bc28e077..8ed34ab050 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@
 - Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正  
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574)
 - Fix: Twitchの埋め込みが開けない問題を修正
+- Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 2276da1d21..c0728d56fa 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		}"
 		:style="{
 			width: (width && !asDrawer) ? `${width}px` : '',
-			maxHeight: maxHeight ? `${maxHeight}px` : '',
+			maxHeight: maxHeight ? `min(${maxHeight}px, calc(100dvh - 32px))` : 'calc(100dvh - 32px)',
 		}"
 		@keydown.stop="() => {}"
 		@contextmenu.self.prevent="() => {}"

From 10ce7bf3c45c3e09dc86f1b9c3a0d7e79c23f5ee Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 18 Jul 2024 20:04:23 +0900
Subject: [PATCH 135/206] kill any from streaming API Implementation (#14251)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore: add JsonValue type

* refactor: kill any from Connection.ts

* refactor: fix StreamEventEmitter contains undefined instead of null

* refactor: kill any from channels

* docs(changelog): Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題

* fix license header

* fix lints
---
 CHANGELOG.md                                  |  2 +
 .../backend/src/core/GlobalEventService.ts    | 28 +++++----
 packages/backend/src/misc/json-value.ts       |  8 +++
 .../src/server/api/stream/Connection.ts       | 57 ++++++++++++-------
 .../backend/src/server/api/stream/channel.ts  | 13 +++--
 .../src/server/api/stream/channels/admin.ts   |  3 +-
 .../src/server/api/stream/channels/antenna.ts |  6 +-
 .../src/server/api/stream/channels/channel.ts |  6 +-
 .../src/server/api/stream/channels/drive.ts   |  3 +-
 .../api/stream/channels/global-timeline.ts    |  7 ++-
 .../src/server/api/stream/channels/hashtag.ts |  7 ++-
 .../api/stream/channels/home-timeline.ts      |  7 ++-
 .../api/stream/channels/hybrid-timeline.ts    |  9 +--
 .../api/stream/channels/local-timeline.ts     |  9 +--
 .../src/server/api/stream/channels/main.ts    |  3 +-
 .../server/api/stream/channels/queue-stats.ts | 10 +++-
 .../api/stream/channels/reversi-game.ts       | 33 ++++++++---
 .../src/server/api/stream/channels/reversi.ts |  3 +-
 .../api/stream/channels/role-timeline.ts      |  6 +-
 .../api/stream/channels/server-stats.ts       |  8 ++-
 .../server/api/stream/channels/user-list.ts   | 10 ++--
 21 files changed, 155 insertions(+), 83 deletions(-)
 create mode 100644 packages/backend/src/misc/json-value.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ed34ab050..14d638ebe1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ### Note
 - デッキUIの新着ノートをサウンドで通知する機能の追加(v2024.5.0)に伴い、以前から動作しなくなっていたクライアント設定内の「アンテナ受信」「チャンネル通知」サウンドを削除しました。
+- Streaming APIにて入力が不正な場合にはそのメッセージを無視するようになりました。 #14251
 
 ### General
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
@@ -76,6 +77,7 @@
 - Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正
 - Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正  
   (Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1)
+- Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 #14251
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index 2a7d8d4bbe..312bcfb3b5 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -209,6 +209,10 @@ type SerializedAll<T> = {
 	[K in keyof T]: Serialized<T[K]>;
 };
 
+type UndefinedAsNullAll<T> = {
+	[K in keyof T]: T[K] extends undefined ? null : T[K];
+}
+
 export interface InternalEventTypes {
 	userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; };
 	userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; };
@@ -248,43 +252,45 @@ export interface InternalEventTypes {
 	userKeypairUpdated: { userId: MiUser['id']; };
 }
 
+type EventTypesToEventPayload<T> = EventUnionFromDictionary<UndefinedAsNullAll<SerializedAll<T>>>;
+
 // name/messages(spec) pairs dictionary
 export type GlobalEvents = {
 	internal: {
 		name: 'internal';
-		payload: EventUnionFromDictionary<SerializedAll<InternalEventTypes>>;
+		payload: EventTypesToEventPayload<InternalEventTypes>;
 	};
 	broadcast: {
 		name: 'broadcast';
-		payload: EventUnionFromDictionary<SerializedAll<BroadcastTypes>>;
+		payload: EventTypesToEventPayload<BroadcastTypes>;
 	};
 	main: {
 		name: `mainStream:${MiUser['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<MainEventTypes>>;
+		payload: EventTypesToEventPayload<MainEventTypes>;
 	};
 	drive: {
 		name: `driveStream:${MiUser['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<DriveEventTypes>>;
+		payload: EventTypesToEventPayload<DriveEventTypes>;
 	};
 	note: {
 		name: `noteStream:${MiNote['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<NoteStreamEventTypes>>;
+		payload: EventTypesToEventPayload<NoteStreamEventTypes>;
 	};
 	userList: {
 		name: `userListStream:${MiUserList['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<UserListEventTypes>>;
+		payload: EventTypesToEventPayload<UserListEventTypes>;
 	};
 	roleTimeline: {
 		name: `roleTimelineStream:${MiRole['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<RoleTimelineEventTypes>>;
+		payload: EventTypesToEventPayload<RoleTimelineEventTypes>;
 	};
 	antenna: {
 		name: `antennaStream:${MiAntenna['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<AntennaEventTypes>>;
+		payload: EventTypesToEventPayload<AntennaEventTypes>;
 	};
 	admin: {
 		name: `adminStream:${MiUser['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<AdminEventTypes>>;
+		payload: EventTypesToEventPayload<AdminEventTypes>;
 	};
 	notes: {
 		name: 'notesStream';
@@ -292,11 +298,11 @@ export type GlobalEvents = {
 	};
 	reversi: {
 		name: `reversiStream:${MiUser['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<ReversiEventTypes>>;
+		payload: EventTypesToEventPayload<ReversiEventTypes>;
 	};
 	reversiGame: {
 		name: `reversiGameStream:${MiReversiGame['id']}`;
-		payload: EventUnionFromDictionary<SerializedAll<ReversiGameEventTypes>>;
+		payload: EventTypesToEventPayload<ReversiGameEventTypes>;
 	};
 };
 
diff --git a/packages/backend/src/misc/json-value.ts b/packages/backend/src/misc/json-value.ts
new file mode 100644
index 0000000000..7994441791
--- /dev/null
+++ b/packages/backend/src/misc/json-value.ts
@@ -0,0 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export type JsonValue = JsonArray | JsonObject | string | number | boolean | null;
+export type JsonObject = {[K in string]?: JsonValue};
+export type JsonArray = JsonValue[];
diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts
index 41c0feccc7..96082827f8 100644
--- a/packages/backend/src/server/api/stream/Connection.ts
+++ b/packages/backend/src/server/api/stream/Connection.ts
@@ -14,6 +14,7 @@ import { CacheService } from '@/core/CacheService.js';
 import { MiFollowing, MiUserProfile } from '@/models/_.js';
 import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js';
 import { ChannelFollowingService } from '@/core/ChannelFollowingService.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import type { ChannelsService } from './ChannelsService.js';
 import type { EventEmitter } from 'events';
 import type Channel from './channel.js';
@@ -28,7 +29,7 @@ export default class Connection {
 	private wsConnection: WebSocket.WebSocket;
 	public subscriber: StreamEventEmitter;
 	private channels: Channel[] = [];
-	private subscribingNotes: any = {};
+	private subscribingNotes: Partial<Record<string, number>> = {};
 	private cachedNotes: Packed<'Note'>[] = [];
 	public userProfile: MiUserProfile | null = null;
 	public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
@@ -101,7 +102,7 @@ export default class Connection {
 	 */
 	@bindThis
 	private async onWsConnectionMessage(data: WebSocket.RawData) {
-		let obj: Record<string, any>;
+		let obj: JsonObject;
 
 		try {
 			obj = JSON.parse(data.toString());
@@ -111,6 +112,8 @@ export default class Connection {
 
 		const { type, body } = obj;
 
+		if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
+
 		switch (type) {
 			case 'readNotification': this.onReadNotification(body); break;
 			case 'subNote': this.onSubscribeNote(body); break;
@@ -151,7 +154,7 @@ export default class Connection {
 	}
 
 	@bindThis
-	private readNote(body: any) {
+	private readNote(body: JsonObject) {
 		const id = body.id;
 
 		const note = this.cachedNotes.find(n => n.id === id);
@@ -163,7 +166,7 @@ export default class Connection {
 	}
 
 	@bindThis
-	private onReadNotification(payload: any) {
+	private onReadNotification(payload: JsonObject) {
 		this.notificationService.readAllNotification(this.user!.id);
 	}
 
@@ -171,16 +174,14 @@ export default class Connection {
 	 * 投稿購読要求時
 	 */
 	@bindThis
-	private onSubscribeNote(payload: any) {
-		if (!payload.id) return;
+	private onSubscribeNote(payload: JsonObject) {
+		if (!payload.id || typeof payload.id !== 'string') return;
 
-		if (this.subscribingNotes[payload.id] == null) {
-			this.subscribingNotes[payload.id] = 0;
-		}
+		const current = this.subscribingNotes[payload.id] ?? 0;
+		const updated = current + 1;
+		this.subscribingNotes[payload.id] = updated;
 
-		this.subscribingNotes[payload.id]++;
-
-		if (this.subscribingNotes[payload.id] === 1) {
+		if (updated === 1) {
 			this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage);
 		}
 	}
@@ -189,11 +190,14 @@ export default class Connection {
 	 * 投稿購読解除要求時
 	 */
 	@bindThis
-	private onUnsubscribeNote(payload: any) {
-		if (!payload.id) return;
+	private onUnsubscribeNote(payload: JsonObject) {
+		if (!payload.id || typeof payload.id !== 'string') return;
 
-		this.subscribingNotes[payload.id]--;
-		if (this.subscribingNotes[payload.id] <= 0) {
+		const current = this.subscribingNotes[payload.id];
+		if (current == null) return;
+		const updated = current - 1;
+		this.subscribingNotes[payload.id] = updated;
+		if (updated <= 0) {
 			delete this.subscribingNotes[payload.id];
 			this.subscriber.off(`noteStream:${payload.id}`, this.onNoteStreamMessage);
 		}
@@ -212,17 +216,22 @@ export default class Connection {
 	 * チャンネル接続要求時
 	 */
 	@bindThis
-	private onChannelConnectRequested(payload: any) {
+	private onChannelConnectRequested(payload: JsonObject) {
 		const { channel, id, params, pong } = payload;
-		this.connectChannel(id, params, channel, pong);
+		if (typeof id !== 'string') return;
+		if (typeof channel !== 'string') return;
+		if (typeof pong !== 'boolean' && typeof pong !== 'undefined' && pong !== null) return;
+		if (typeof params !== 'undefined' && (typeof params !== 'object' || params === null || Array.isArray(params))) return;
+		this.connectChannel(id, params, channel, pong ?? undefined);
 	}
 
 	/**
 	 * チャンネル切断要求時
 	 */
 	@bindThis
-	private onChannelDisconnectRequested(payload: any) {
+	private onChannelDisconnectRequested(payload: JsonObject) {
 		const { id } = payload;
+		if (typeof id !== 'string') return;
 		this.disconnectChannel(id);
 	}
 
@@ -230,7 +239,7 @@ export default class Connection {
 	 * クライアントにメッセージ送信
 	 */
 	@bindThis
-	public sendMessageToWs(type: string, payload: any) {
+	public sendMessageToWs(type: string, payload: JsonObject) {
 		this.wsConnection.send(JSON.stringify({
 			type: type,
 			body: payload,
@@ -241,7 +250,7 @@ export default class Connection {
 	 * チャンネルに接続
 	 */
 	@bindThis
-	public connectChannel(id: string, params: any, channel: string, pong = false) {
+	public connectChannel(id: string, params: JsonObject | undefined, channel: string, pong = false) {
 		const channelService = this.channelsService.getChannelService(channel);
 
 		if (channelService.requireCredential && this.user == null) {
@@ -288,7 +297,11 @@ export default class Connection {
 	 * @param data メッセージ
 	 */
 	@bindThis
-	private onChannelMessageRequested(data: any) {
+	private onChannelMessageRequested(data: JsonObject) {
+		if (typeof data.id !== 'string') return;
+		if (typeof data.type !== 'string') return;
+		if (typeof data.body === 'undefined') return;
+
 		const channel = this.channels.find(c => c.id === data.id);
 		if (channel != null && channel.onMessage != null) {
 			channel.onMessage(data.type, data.body);
diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts
index a267d27fba..84cb552369 100644
--- a/packages/backend/src/server/api/stream/channel.ts
+++ b/packages/backend/src/server/api/stream/channel.ts
@@ -8,6 +8,7 @@ import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
 import type { Packed } from '@/misc/json-schema.js';
+import type { JsonObject, JsonValue } from '@/misc/json-value.js';
 import type Connection from './Connection.js';
 
 /**
@@ -81,10 +82,12 @@ export default abstract class Channel {
 		this.connection = connection;
 	}
 
+	public send(payload: { type: string, body: JsonValue }): void
+	public send(type: string, payload: JsonValue): void
 	@bindThis
-	public send(typeOrPayload: any, payload?: any) {
-		const type = payload === undefined ? typeOrPayload.type : typeOrPayload;
-		const body = payload === undefined ? typeOrPayload.body : payload;
+	public send(typeOrPayload: { type: string, body: JsonValue } | string, payload?: JsonValue) {
+		const type = payload === undefined ? (typeOrPayload as { type: string, body: JsonValue }).type : (typeOrPayload as string);
+		const body = payload === undefined ? (typeOrPayload as { type: string, body: JsonValue }).body : payload;
 
 		this.connection.sendMessageToWs('channel', {
 			id: this.id,
@@ -93,11 +96,11 @@ export default abstract class Channel {
 		});
 	}
 
-	public abstract init(params: any): void;
+	public abstract init(params: JsonObject): void;
 
 	public dispose?(): void;
 
-	public onMessage?(type: string, body: any): void;
+	public onMessage?(type: string, body: JsonValue): void;
 }
 
 export type MiChannelService<T extends boolean> = {
diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts
index 92b6d2ac04..355d5dba21 100644
--- a/packages/backend/src/server/api/stream/channels/admin.ts
+++ b/packages/backend/src/server/api/stream/channels/admin.ts
@@ -5,6 +5,7 @@
 
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class AdminChannel extends Channel {
@@ -14,7 +15,7 @@ class AdminChannel extends Channel {
 	public static kind = 'read:admin:stream';
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		// Subscribe admin stream
 		this.subscriber.on(`adminStream:${this.user!.id}`, data => {
 			this.send(data);
diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts
index 4a1d2dd109..53dc7f18b6 100644
--- a/packages/backend/src/server/api/stream/channels/antenna.ts
+++ b/packages/backend/src/server/api/stream/channels/antenna.ts
@@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class AntennaChannel extends Channel {
@@ -27,8 +28,9 @@ class AntennaChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
-		this.antennaId = params.antennaId as string;
+	public async init(params: JsonObject) {
+		if (typeof params.antennaId !== 'string') return;
+		this.antennaId = params.antennaId;
 
 		// Subscribe stream
 		this.subscriber.on(`antennaStream:${this.antennaId}`, this.onEvent);
diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts
index 140dd3dd9b..7108e0cd6e 100644
--- a/packages/backend/src/server/api/stream/channels/channel.ts
+++ b/packages/backend/src/server/api/stream/channels/channel.ts
@@ -8,6 +8,7 @@ import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class ChannelChannel extends Channel {
@@ -27,8 +28,9 @@ class ChannelChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
-		this.channelId = params.channelId as string;
+	public async init(params: JsonObject) {
+		if (typeof params.channelId !== 'string') return;
+		this.channelId = params.channelId;
 
 		// Subscribe stream
 		this.subscriber.on('notesStream', this.onNote);
diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts
index 0d9b486305..03768f3d23 100644
--- a/packages/backend/src/server/api/stream/channels/drive.ts
+++ b/packages/backend/src/server/api/stream/channels/drive.ts
@@ -5,6 +5,7 @@
 
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class DriveChannel extends Channel {
@@ -14,7 +15,7 @@ class DriveChannel extends Channel {
 	public static kind = 'read:account';
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		// Subscribe drive stream
 		this.subscriber.on(`driveStream:${this.user!.id}`, data => {
 			this.send(data);
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 17116258d8..ed56fe0d40 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class GlobalTimelineChannel extends Channel {
@@ -32,12 +33,12 @@ class GlobalTimelineChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
 		if (!policies.gtlAvailable) return;
 
-		this.withRenotes = params.withRenotes ?? true;
-		this.withFiles = params.withFiles ?? false;
+		this.withRenotes = !!(params.withRenotes ?? true);
+		this.withFiles = !!(params.withFiles ?? false);
 
 		// Subscribe events
 		this.subscriber.on('notesStream', this.onNote);
diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts
index 57bada5d9c..8105f15cb1 100644
--- a/packages/backend/src/server/api/stream/channels/hashtag.ts
+++ b/packages/backend/src/server/api/stream/channels/hashtag.ts
@@ -9,6 +9,7 @@ import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class HashtagChannel extends Channel {
@@ -28,11 +29,11 @@ class HashtagChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
+		if (!Array.isArray(params.q)) return;
+		if (!params.q.every(x => Array.isArray(x) && x.every(y => typeof y === 'string'))) return;
 		this.q = params.q;
 
-		if (this.q == null) return;
-
 		// Subscribe stream
 		this.subscriber.on('notesStream', this.onNote);
 	}
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 878a3180cb..1f440732a6 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 @@ import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class HomeTimelineChannel extends Channel {
@@ -29,9 +30,9 @@ class HomeTimelineChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
-		this.withRenotes = params.withRenotes ?? true;
-		this.withFiles = params.withFiles ?? false;
+	public async init(params: JsonObject) {
+		this.withRenotes = !!(params.withRenotes ?? true);
+		this.withFiles = !!(params.withFiles ?? false);
 
 		this.subscriber.on('notesStream', this.onNote);
 	}
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 575d23d53c..6938b6e3ea 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class HybridTimelineChannel extends Channel {
@@ -34,13 +35,13 @@ class HybridTimelineChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any): Promise<void> {
+	public async init(params: JsonObject): Promise<void> {
 		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
 		if (!policies.ltlAvailable) return;
 
-		this.withRenotes = params.withRenotes ?? true;
-		this.withReplies = params.withReplies ?? false;
-		this.withFiles = params.withFiles ?? false;
+		this.withRenotes = !!(params.withRenotes ?? true);
+		this.withReplies = !!(params.withReplies ?? false);
+		this.withFiles = !!(params.withFiles ?? false);
 
 		// Subscribe events
 		this.subscriber.on('notesStream', this.onNote);
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 442d08ae51..491029f5de 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class LocalTimelineChannel extends Channel {
@@ -33,13 +34,13 @@ class LocalTimelineChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
 		if (!policies.ltlAvailable) return;
 
-		this.withRenotes = params.withRenotes ?? true;
-		this.withReplies = params.withReplies ?? false;
-		this.withFiles = params.withFiles ?? false;
+		this.withRenotes = !!(params.withRenotes ?? true);
+		this.withReplies = !!(params.withReplies ?? false);
+		this.withFiles = !!(params.withFiles ?? false);
 
 		// Subscribe events
 		this.subscriber.on('notesStream', this.onNote);
diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts
index a12976d69d..863d7f4c4e 100644
--- a/packages/backend/src/server/api/stream/channels/main.ts
+++ b/packages/backend/src/server/api/stream/channels/main.ts
@@ -7,6 +7,7 @@ import { Injectable } from '@nestjs/common';
 import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class MainChannel extends Channel {
@@ -25,7 +26,7 @@ class MainChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		// Subscribe main stream channel
 		this.subscriber.on(`mainStream:${this.user!.id}`, async data => {
 			switch (data.type) {
diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts
index 061aa76904..ff7e740226 100644
--- a/packages/backend/src/server/api/stream/channels/queue-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts
@@ -6,6 +6,7 @@
 import Xev from 'xev';
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
+import type { JsonObject, JsonValue } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 const ev = new Xev();
@@ -22,19 +23,22 @@ class QueueStatsChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		ev.addListener('queueStats', this.onStats);
 	}
 
 	@bindThis
-	private onStats(stats: any) {
+	private onStats(stats: JsonObject) {
 		this.send('stats', stats);
 	}
 
 	@bindThis
-	public onMessage(type: string, body: any) {
+	public onMessage(type: string, body: JsonValue) {
 		switch (type) {
 			case 'requestLog':
+				if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
+				if (typeof body.id !== 'string') return;
+				if (typeof body.length !== 'number') return;
 				ev.once(`queueStatsLog:${body.id}`, statsLog => {
 					this.send('statsLog', statsLog);
 				});
diff --git a/packages/backend/src/server/api/stream/channels/reversi-game.ts b/packages/backend/src/server/api/stream/channels/reversi-game.ts
index f4a3a09367..17823a164a 100644
--- a/packages/backend/src/server/api/stream/channels/reversi-game.ts
+++ b/packages/backend/src/server/api/stream/channels/reversi-game.ts
@@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import { ReversiService } from '@/core/ReversiService.js';
 import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js';
+import type { JsonObject, JsonValue } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class ReversiGameChannel extends Channel {
@@ -28,25 +29,41 @@ class ReversiGameChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
-		this.gameId = params.gameId as string;
+	public async init(params: JsonObject) {
+		if (typeof params.gameId !== 'string') return;
+		this.gameId = params.gameId;
 
 		this.subscriber.on(`reversiGameStream:${this.gameId}`, this.send);
 	}
 
 	@bindThis
-	public onMessage(type: string, body: any) {
+	public onMessage(type: string, body: JsonValue) {
 		switch (type) {
-			case 'ready': this.ready(body); break;
-			case 'updateSettings': this.updateSettings(body.key, body.value); break;
-			case 'cancel': this.cancelGame(); break;
-			case 'putStone': this.putStone(body.pos, body.id); break;
+			case 'ready':
+				if (typeof body !== 'boolean') return;
+				this.ready(body);
+				break;
+			case 'updateSettings':
+				if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
+				if (typeof body.key !== 'string') return;
+				if (typeof body.value !== 'object' || body.value === null || Array.isArray(body.value)) return;
+				this.updateSettings(body.key, body.value);
+				break;
+			case 'cancel':
+				this.cancelGame();
+				break;
+			case 'putStone':
+				if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
+				if (typeof body.pos !== 'number') return;
+				if (typeof body.id !== 'string') return;
+				this.putStone(body.pos, body.id);
+				break;
 			case 'claimTimeIsUp': this.claimTimeIsUp(); break;
 		}
 	}
 
 	@bindThis
-	private async updateSettings(key: string, value: any) {
+	private async updateSettings(key: string, value: JsonObject) {
 		if (this.user == null) return;
 
 		this.reversiService.updateSettings(this.gameId!, this.user, key, value);
diff --git a/packages/backend/src/server/api/stream/channels/reversi.ts b/packages/backend/src/server/api/stream/channels/reversi.ts
index 3998a0fd36..6e88939724 100644
--- a/packages/backend/src/server/api/stream/channels/reversi.ts
+++ b/packages/backend/src/server/api/stream/channels/reversi.ts
@@ -5,6 +5,7 @@
 
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class ReversiChannel extends Channel {
@@ -21,7 +22,7 @@ class ReversiChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		this.subscriber.on(`reversiStream:${this.user!.id}`, this.send);
 	}
 
diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts
index 6a4ad22460..fcfa26c38b 100644
--- a/packages/backend/src/server/api/stream/channels/role-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts
@@ -8,6 +8,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class RoleTimelineChannel extends Channel {
@@ -28,8 +29,9 @@ class RoleTimelineChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
-		this.roleId = params.roleId as string;
+	public async init(params: JsonObject) {
+		if (typeof params.roleId !== 'string') return;
+		this.roleId = params.roleId;
 
 		this.subscriber.on(`roleTimelineStream:${this.roleId}`, this.onEvent);
 	}
diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts
index eb4d8c9992..6258afba35 100644
--- a/packages/backend/src/server/api/stream/channels/server-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/server-stats.ts
@@ -6,6 +6,7 @@
 import Xev from 'xev';
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
+import type { JsonObject, JsonValue } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 const ev = new Xev();
@@ -22,19 +23,20 @@ class ServerStatsChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
+	public async init(params: JsonObject) {
 		ev.addListener('serverStats', this.onStats);
 	}
 
 	@bindThis
-	private onStats(stats: any) {
+	private onStats(stats: JsonObject) {
 		this.send('stats', stats);
 	}
 
 	@bindThis
-	public onMessage(type: string, body: any) {
+	public onMessage(type: string, body: JsonValue) {
 		switch (type) {
 			case 'requestLog':
+				if (typeof body !== 'object' || body === null || Array.isArray(body)) return;
 				ev.once(`serverStatsLog:${body.id}`, statsLog => {
 					this.send('statsLog', statsLog);
 				});
diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts
index 14b30a157c..4f38351e94 100644
--- a/packages/backend/src/server/api/stream/channels/user-list.ts
+++ b/packages/backend/src/server/api/stream/channels/user-list.ts
@@ -10,6 +10,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
+import type { JsonObject } from '@/misc/json-value.js';
 import Channel, { type MiChannelService } from '../channel.js';
 
 class UserListChannel extends Channel {
@@ -36,10 +37,11 @@ class UserListChannel extends Channel {
 	}
 
 	@bindThis
-	public async init(params: any) {
-		this.listId = params.listId as string;
-		this.withFiles = params.withFiles ?? false;
-		this.withRenotes = params.withRenotes ?? true;
+	public async init(params: JsonObject) {
+		if (typeof params.listId !== 'string') return;
+		this.listId = params.listId;
+		this.withFiles = !!(params.withFiles ?? false);
+		this.withRenotes = !!(params.withRenotes ?? true);
 
 		// Check existence and owner
 		const listExist = await this.userListsRepository.exists({

From 615e60f25cde9115f0e044200e8adcab5eb5004d Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Fri, 19 Jul 2024 09:52:39 +0900
Subject: [PATCH 136/206] chore: modernize issue template (#14263)

---
 .github/ISSUE_TEMPLATE/01_bug-report.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/01_bug-report.yml b/.github/ISSUE_TEMPLATE/01_bug-report.yml
index ac2b39cc12..315e712c30 100644
--- a/.github/ISSUE_TEMPLATE/01_bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/01_bug-report.yml
@@ -53,8 +53,8 @@ body:
         Examples:
           * Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4
           * Browser: Chrome 113.0.5672.126
-          * Server URL: misskey.io
-          * Misskey: 13.x.x
+          * Server URL: misskey.example.com
+          * Misskey: 2024.x.x
       value: |
         * Model and OS of the device(s):
         * Browser:
@@ -74,11 +74,11 @@ body:
 
         Examples:
           * Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment
-          * Misskey: 13.x.x
+          * Misskey: 2024.x.x
           * Node: 20.x.x
           * PostgreSQL: 15.x.x
           * Redis: 7.x.x
-          * OS and Architecture: Ubuntu 22.04.2 LTS aarch64
+          * OS and Architecture: Ubuntu 24.04.2 LTS aarch64
       value: |
         * Installation Method or Hosting Service:
         * Misskey:

From 54d0a4637847aa13700b40ecb1d5edabd048cc2c Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Fri, 19 Jul 2024 09:53:49 +0900
Subject: [PATCH 137/206] =?UTF-8?q?fix(frontend):=20=E5=80=8B=E4=BA=BA?=
 =?UTF-8?q?=E5=AE=9B=E3=81=A6=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0?=
 =?UTF-8?q?=E3=81=8A=E7=9F=A5=E3=82=89=E3=81=9B=E3=81=8C=E5=8D=B3=E6=99=82?=
 =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=20(#14260)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 個人向けお知らせが即時ダイアログで出ない問題

* Update CHANGELOG
---
 CHANGELOG.md                            |  1 +
 packages/frontend/src/boot/main-boot.ts | 10 ++++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 14d638ebe1..1e06f16bdf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,7 @@
   (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574)
 - Fix: Twitchの埋め込みが開けない問題を修正
 - Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正
+- Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index d327016317..2a549d1e8b 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -5,6 +5,7 @@
 
 import { createApp, defineAsyncComponent, markRaw } from 'vue';
 import { common } from './common.js';
+import type * as Misskey from 'misskey-js';
 import { ui } from '@/config.js';
 import { i18n } from '@/i18n.js';
 import { alert, confirm, popup, post, toast } from '@/os.js';
@@ -113,7 +114,7 @@ export async function mainBoot() {
 			});
 		}
 
-		stream.on('announcementCreated', (ev) => {
+		function onAnnouncementCreated (ev: { announcement: Misskey.entities.Announcement }) {
 			const announcement = ev.announcement;
 			if (announcement.display === 'dialog') {
 				const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAnnouncementDialog.vue')), {
@@ -122,7 +123,9 @@ export async function mainBoot() {
 					closed: () => dispose(),
 				});
 			}
-		});
+		}
+
+		stream.on('announcementCreated', onAnnouncementCreated);
 
 		if ($i.isDeleted) {
 			alert({
@@ -315,6 +318,9 @@ export async function mainBoot() {
 			updateAccount({ hasUnreadAnnouncement: false });
 		});
 
+		// 個人宛てお知らせが発行されたとき
+		main.on('announcementCreated', onAnnouncementCreated);
+
 		// トークンが再生成されたとき
 		// このままではMisskeyが利用できないので強制的にサインアウトさせる
 		main.on('myTokenRegenerated', () => {

From 1f24a8cb5a307c3ff621577189a2a618b9dcfdc4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 19 Jul 2024 09:57:01 +0900
Subject: [PATCH 138/206] =?UTF-8?q?enhance(frontend):=20=E3=82=BB=E3=83=B3?=
 =?UTF-8?q?=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=81=AA=E3=83=A1=E3=83=87?=
 =?UTF-8?q?=E3=82=A3=E3=82=A2=E3=82=92=E9=96=8B=E3=81=8F=E9=9A=9B=E3=81=AB?=
 =?UTF-8?q?=E7=A2=BA=E8=AA=8D=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0?=
 =?UTF-8?q?=E3=82=92=E5=87=BA=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#14115)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): センシティブなメディアを開く際に確認ダイアログを出せるように

* Update Changelog
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  8 ++++++
 locales/ja-JP.yml                             |  2 ++
 .../frontend/src/components/MkMediaAudio.vue  | 14 +++++++++-
 .../frontend/src/components/MkMediaBanner.vue | 26 ++++++++++++-------
 .../frontend/src/components/MkMediaImage.vue  | 12 ++++++++-
 .../frontend/src/components/MkMediaList.vue   |  8 +++---
 .../frontend/src/components/MkMediaVideo.vue  | 14 +++++++++-
 .../frontend/src/pages/settings/general.vue   |  3 +++
 packages/frontend/src/store.ts                |  4 +++
 10 files changed, 75 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e06f16bdf..7cf77d6083 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@
   (Cherry-picked from https://github.com/taiyme/misskey/pull/238)
 - Enhance: AiScriptを0.19.0にアップデート
 - Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
+- Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 694ee53a1f..55c65f2aed 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -5008,6 +5008,14 @@ export interface Locale extends ILocale {
      * もう一度お試しください。
      */
     "tryAgain": string;
+    /**
+     * センシティブなメディアを表示するとき確認する
+     */
+    "confirmWhenRevealingSensitiveMedia": string;
+    /**
+     * センシティブなメディアです。表示しますか?
+     */
+    "sensitiveMediaRevealConfirm": string;
     "_delivery": {
         /**
          * 配信状態
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index bb3999f0e3..3ca4b46682 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1248,6 +1248,8 @@ noDescription: "説明文はありません"
 alwaysConfirmFollow: "フォローの際常に確認する"
 inquiry: "お問い合わせ"
 tryAgain: "もう一度お試しください。"
+confirmWhenRevealingSensitiveMedia: "センシティブなメディアを表示するとき確認する"
+sensitiveMediaRevealConfirm: "センシティブなメディアです。表示しますか?"
 
 _delivery:
   status: "配信状態"
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index 582cf238c0..a080550ddf 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	@contextmenu.stop
 	@keydown.stop
 >
-	<button v-if="hide" :class="$style.hidden" @click="hide = false">
+	<button v-if="hide" :class="$style.hidden" @click="show">
 		<div :class="$style.hiddenTextWrapper">
 			<b v-if="audio.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.audio}${audio.size ? ' ' + bytes(audio.size) : ''})` : '' }}</b>
 			<b v-else style="display: block;"><i class="ti ti-music"></i> {{ defaultStore.state.dataSaver.media && audio.size ? bytes(audio.size) : i18n.ts.audio }}</b>
@@ -156,6 +156,18 @@ const audioEl = shallowRef<HTMLAudioElement>();
 // eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore'));
 
+async function show() {
+	if (props.audio.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+		const { canceled } = await os.confirm({
+			type: 'question',
+			text: i18n.ts.sensitiveMediaRevealConfirm,
+		});
+		if (canceled) return;
+	}
+
+	hide.value = false;
+}
+
 // Menu
 const menuShowing = ref(false);
 
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index a219848b7f..11995e1f3b 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div :class="$style.root">
 	<MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
-	<div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="hide = false">
+	<div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show">
 		<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
 		<b>{{ i18n.ts.sensitive }}</b>
 		<span>{{ i18n.ts.clickToShow }}</span>
@@ -24,24 +24,30 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, watch, ref } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
 import MkMediaAudio from '@/components/MkMediaAudio.vue';
 
-const props = withDefaults(defineProps<{
+const props = defineProps<{
 	media: Misskey.entities.DriveFile;
-}>(), {
-});
+}>();
 
-const audioEl = shallowRef<HTMLAudioElement>();
 const hide = ref(true);
 
-watch(audioEl, () => {
-	if (audioEl.value) {
-		audioEl.value.volume = 0.3;
+async function show() {
+	if (props.media.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+		const { canceled } = await os.confirm({
+			type: 'question',
+			text: i18n.ts.sensitiveMediaRevealConfirm,
+		});
+		if (canceled) return;
 	}
-});
+
+	hide.value = false;
+}
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 82f36fe5c4..0d1409e2c8 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -83,11 +83,21 @@ const url = computed(() => (props.raw || defaultStore.state.loadRawImages)
 		: props.image.thumbnailUrl,
 );
 
-function onclick() {
+async function onclick(ev: MouseEvent) {
 	if (!props.controls) {
 		return;
 	}
+
 	if (hide.value) {
+		ev.stopPropagation();
+		if (props.image.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+			const { canceled } = await os.confirm({
+				type: 'question',
+				text: i18n.ts.sensitiveMediaRevealConfirm,
+			});
+			if (canceled) return;
+		}
+
 		hide.value = false;
 	}
 }
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 24b177d255..2300802dcf 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -138,15 +138,13 @@ onMounted(() => {
 		pswpModule: PhotoSwipe,
 	});
 
-	lightbox.on('itemData', (ev) => {
-		const { itemData } = ev;
-
+	lightbox.addFilter('itemData', (itemData) => {
 		// element is children
 		const { element } = itemData;
 
 		const id = element?.dataset.id;
 		const file = props.mediaList.find(media => media.id === id);
-		if (!file) return;
+		if (!file) return itemData;
 
 		itemData.src = file.url;
 		itemData.w = Number(file.properties.width);
@@ -158,6 +156,8 @@ onMounted(() => {
 		itemData.alt = file.comment ?? file.name;
 		itemData.comment = file.comment ?? file.name;
 		itemData.thumbCropped = true;
+
+		return itemData;
 	});
 
 	lightbox.on('uiRegister', () => {
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 0ec0039df4..7c5a365148 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	@contextmenu.stop
 	@keydown.stop
 >
-	<button v-if="hide" :class="$style.hidden" @click="hide = false">
+	<button v-if="hide" :class="$style.hidden" @click="show">
 		<div :class="$style.hiddenTextWrapper">
 			<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
 			<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ defaultStore.state.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
@@ -176,6 +176,18 @@ function hasFocus() {
 // eslint-disable-next-line vue/no-setup-props-reactivity-loss
 const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
 
+async function show() {
+	if (props.video.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) {
+		const { canceled } = await os.confirm({
+			type: 'question',
+			text: i18n.ts.sensitiveMediaRevealConfirm,
+		});
+		if (canceled) return;
+	}
+
+	hide.value = false;
+}
+
 // Menu
 const menuShowing = ref(false);
 
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index cfc63f2a08..9e429f8dbd 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -169,6 +169,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="disableStreamingTimeline">{{ i18n.ts.disableStreamingTimeline }}</MkSwitch>
 				<MkSwitch v-model="enableHorizontalSwipe">{{ i18n.ts.enableHorizontalSwipe }}</MkSwitch>
 				<MkSwitch v-model="alwaysConfirmFollow">{{ i18n.ts.alwaysConfirmFollow }}</MkSwitch>
+				<MkSwitch v-model="confirmWhenRevealingSensitiveMedia">{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</MkSwitch>
 			</div>
 			<MkSelect v-model="serverDisconnectedBehavior">
 				<template #label>{{ i18n.ts.whenServerDisconnected }}</template>
@@ -315,6 +316,7 @@ const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enabl
 const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe'));
 const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer'));
 const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
+const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
@@ -357,6 +359,7 @@ watch([
 	disableStreamingTimeline,
 	enableSeasonalScreenEffect,
 	alwaysConfirmFollow,
+	confirmWhenRevealingSensitiveMedia,
 ], async () => {
 	await reloadAsk();
 });
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 9cb2742069..dbf6b8716f 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -454,6 +454,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: true,
 	},
+	confirmWhenRevealingSensitiveMedia: {
+		where: 'device',
+		default: false,
+	},
 
 	sound_masterVolume: {
 		where: 'device',

From 6920f0fa7e5b0919572ee4ebaeb92a617ec344d1 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Fri, 19 Jul 2024 10:05:34 +0900
Subject: [PATCH 139/206] Disable ESLint for migration files (#14262)

---
 packages/backend/eslint.config.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/eslint.config.js b/packages/backend/eslint.config.js
index 318b7fd340..4fd9f0cd51 100644
--- a/packages/backend/eslint.config.js
+++ b/packages/backend/eslint.config.js
@@ -4,7 +4,7 @@ import sharedConfig from '../shared/eslint.config.js';
 export default [
 	...sharedConfig,
 	{
-		ignores: ['**/node_modules', 'built', '@types/**/*'],
+		ignores: ['**/node_modules', 'built', '@types/**/*', 'migration'],
 	},
 	{
 		files: ['**/*.ts', '**/*.tsx'],

From 56a43dc01d15f1d41bbda9a973da18294389d7c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 19 Jul 2024 10:11:44 +0900
Subject: [PATCH 140/206] =?UTF-8?q?fix(frontend):=20blurhash=E3=81=8C?=
 =?UTF-8?q?=E7=84=A1=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AB=E4=BD=95=E3=82=82?=
 =?UTF-8?q?=E5=87=BA=E5=8A=9B=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E3=81=AE?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#14250)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): blurhashが無い場合に何も出力されないのを修正

* Update Changelog

* Update packages/frontend/src/components/MkImgWithBlurhash.vue

Co-authored-by: tamaina <tamaina@hotmail.co.jp>

* attempt to fix test

* Update packages/frontend/src/components/MkImgWithBlurhash.vue

Co-authored-by: tamaina <tamaina@hotmail.co.jp>

* attempt to ignore test

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
---
 CHANGELOG.md                                           |  1 +
 packages/frontend/src/components/MkImgWithBlurhash.vue | 10 +++++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cf77d6083..b020c214bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,6 +45,7 @@
 - Fix: Twitchの埋め込みが開けない問題を修正
 - Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正
 - Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正
+- Fix: 一部の画像がセンシティブ指定されているときに画面に何も表示されないことがあるのを修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index 617404f5c4..8d301f16bd 100644
--- a/packages/frontend/src/components/MkImgWithBlurhash.vue
+++ b/packages/frontend/src/components/MkImgWithBlurhash.vue
@@ -151,22 +151,26 @@ function drawImage(bitmap: CanvasImageSource) {
 }
 
 function drawAvg() {
-	if (!canvas.value || !props.hash) return;
+	if (!canvas.value) return;
+
+	const color = (props.hash != null && extractAvgColorFromBlurhash(props.hash)) || '#888';
 
 	const ctx = canvas.value.getContext('2d');
 	if (!ctx) return;
 
 	// avgColorでお茶をにごす
 	ctx.beginPath();
-	ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
+	ctx.fillStyle = color;
 	ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value);
 }
 
 async function draw() {
-	if (props.hash == null) return;
+	if (import.meta.env.MODE === 'test' && props.hash == null) return;
 
 	drawAvg();
 
+	if (props.hash == null) return;
+
 	if (props.onlyAvgColor) return;
 
 	const work = await canvasPromise;

From efb04293bb01fba6d7d31278d6a8546eae2d9503 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Fri, 19 Jul 2024 16:47:12 +0900
Subject: [PATCH 141/206] docs(misskey-js): fix broken i-want-you image link in
 README.md (#14265)

---
 packages/misskey-js/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/misskey-js/README.md b/packages/misskey-js/README.md
index 63d4b36c56..4753e2434b 100644
--- a/packages/misskey-js/README.md
+++ b/packages/misskey-js/README.md
@@ -154,5 +154,5 @@ stream.on('_disconnected_', () => {
 ---
 
 <div align="center">
-	<a href="https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md"><img src="https://raw.githubusercontent.com/misskey-dev/assets/main/i-want-you.png" width="300"></a>
+	<a href="https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md"><img src="https://assets.misskey-hub.net/public/i-want-you.png" width="300"></a>
 </div>

From 337b42bcb179bdfb993888ed94342a0158e8f3cb Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 20 Jul 2024 21:33:20 +0900
Subject: [PATCH 142/206] revert 5f88d56d96
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

バグがある(かつすぐに修正できそうにない) & まだレビュー途中で意図せずマージされたため
---
 .config/docker_example.yml                    |   6 +-
 .config/example.yml                           |   8 +-
 .devcontainer/devcontainer.yml                |   8 +-
 CHANGELOG.md                                  |   8 -
 CONTRIBUTING.md                               |   2 +-
 chart/files/default.yml                       |   8 +-
 .../migration/1708980134301-APMultipleKeys.js |  39 ----
 .../migration/1709242519122-HttpSignImplLv.js |  16 --
 .../1709269211718-APMultipleKeysFix1.js       |  16 --
 packages/backend/package.json                 |   2 +-
 .../backend/src/@types/http-signature.d.ts    |  82 +++++++
 packages/backend/src/const.ts                 |   5 -
 .../backend/src/core/AccountUpdateService.ts  |  27 +--
 .../src/core/CreateSystemUserService.ts       |   7 +-
 .../src/core/FetchInstanceMetadataService.ts  |  61 ++---
 .../backend/src/core/GlobalEventService.ts    |   1 -
 .../backend/src/core/HttpRequestService.ts    |   2 +-
 packages/backend/src/core/QueueService.ts     |  17 +-
 packages/backend/src/core/RelayService.ts     |  13 +-
 packages/backend/src/core/SignupService.ts    |  22 +-
 .../backend/src/core/UserKeypairService.ts    | 155 +------------
 .../backend/src/core/UserSuspendService.ts    |  66 ++++--
 packages/backend/src/core/WebfingerService.ts |   2 +-
 .../core/activitypub/ApDbResolverService.ts   | 178 ++++-----------
 .../activitypub/ApDeliverManagerService.ts    |  95 +-------
 .../src/core/activitypub/ApInboxService.ts    |  11 +-
 .../src/core/activitypub/ApRendererService.ts |  21 +-
 .../src/core/activitypub/ApRequestService.ts  | 212 +++++++++++-------
 .../src/core/activitypub/ApResolverService.ts |   8 +-
 .../src/core/activitypub/misc/contexts.ts     |   1 -
 .../activitypub/models/ApPersonService.ts     | 114 ++--------
 packages/backend/src/core/activitypub/type.ts |  11 +-
 .../core/entities/InstanceEntityService.ts    |   1 -
 packages/backend/src/misc/cache.ts            |   3 -
 packages/backend/src/misc/gen-key-pair.ts     |  43 +++-
 packages/backend/src/models/Instance.ts       |   5 -
 packages/backend/src/models/UserKeypair.ts    |  24 +-
 packages/backend/src/models/UserPublickey.ts  |  14 +-
 .../models/json-schema/federation-instance.ts |   4 -
 .../src/queue/QueueProcessorService.ts        |   8 +-
 .../processors/DeliverProcessorService.ts     |  38 ++--
 .../queue/processors/InboxProcessorService.ts | 132 +++++------
 packages/backend/src/queue/types.ts           |  23 +-
 .../src/server/ActivityPubServerService.ts    |  91 +++++---
 .../src/server/NodeinfoServerService.ts       |   7 -
 .../endpoints/admin/queue/inbox-delayed.ts    |   3 +-
 packages/backend/test/e2e/timelines.ts        |  10 +-
 packages/backend/test/misc/mock-resolver.ts   |   2 -
 .../test/unit/FetchInstanceMetadataService.ts |  23 +-
 packages/backend/test/unit/ap-request.ts      |  90 +++-----
 packages/misskey-js/src/autogen/types.ts      |   1 -
 pnpm-lock.yaml                                |  44 ++--
 52 files changed, 691 insertions(+), 1099 deletions(-)
 delete mode 100644 packages/backend/migration/1708980134301-APMultipleKeys.js
 delete mode 100644 packages/backend/migration/1709242519122-HttpSignImplLv.js
 delete mode 100644 packages/backend/migration/1709269211718-APMultipleKeysFix1.js
 create mode 100644 packages/backend/src/@types/http-signature.d.ts

diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index bd0ad2872a..d347882d1a 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -164,12 +164,12 @@ id: 'aidx'
 #clusterLimit: 1
 
 # Job concurrency per worker
-# deliverJobConcurrency: 16
-# inboxJobConcurrency: 4
+# deliverJobConcurrency: 128
+# inboxJobConcurrency: 16
 
 # Job rate limiter
 # deliverJobPerSec: 128
-# inboxJobPerSec: 64
+# inboxJobPerSec: 32
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/.config/example.yml b/.config/example.yml
index 0d525f61c4..b11cbd1373 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -230,15 +230,15 @@ id: 'aidx'
 #clusterLimit: 1
 
 # Job concurrency per worker
-#deliverJobConcurrency: 16
-#inboxJobConcurrency: 4
+#deliverJobConcurrency: 128
+#inboxJobConcurrency: 16
 #relationshipJobConcurrency: 16
 # What's relationshipJob?:
 #  Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
 
 # Job rate limiter
-#deliverJobPerSec: 1024
-#inboxJobPerSec: 64
+#deliverJobPerSec: 128
+#inboxJobPerSec: 32
 #relationshipJobPerSec: 64
 
 # Job attempts
diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml
index d74d741e02..beefcfd0a2 100644
--- a/.devcontainer/devcontainer.yml
+++ b/.devcontainer/devcontainer.yml
@@ -157,12 +157,12 @@ id: 'aidx'
 #clusterLimit: 1
 
 # Job concurrency per worker
-# deliverJobConcurrency: 16
-# inboxJobConcurrency: 4
+# deliverJobConcurrency: 128
+# inboxJobConcurrency: 16
 
 # Job rate limiter
-# deliverJobPerSec: 1024
-# inboxJobPerSec: 64
+# deliverJobPerSec: 128
+# inboxJobPerSec: 32
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b020c214bc..f429033aa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,14 +8,6 @@
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
 - Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
   - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
-- Feat: 連合に使うHTTP SignaturesがEd25519鍵に対応するように #13464
-  - Ed25519署名に対応するサーバーが増えると、deliverで要求されるサーバーリソースが削減されます
-	- ジョブキューのconfig設定のデフォルト値を変更しました。  
-	  default.ymlでジョブキューの並列度を設定している場合は、従前よりもconcurrencyの値をより下げるとパフォーマンスが改善する可能性があります。
-		* deliverJobConcurrency: 16 (←128)
-		* deliverJobPerSec: 1024 (←128)
-		* inboxJobConcurrency: 4 (←16)
-		* inboxJobPerSec: 64 (←32)
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9a56345e6e..afc21ea594 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -197,7 +197,7 @@ TODO
 ## Environment Variable
 
 - `MISSKEY_CONFIG_YML`: Specify the file path of config.yml instead of default.yml (e.g. `2nd.yml`).
-- `MISSKEY_USE_HTTP`: If it's set true, federation requests (like nodeinfo and webfinger) will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION. (was `MISSKEY_WEBFINGER_USE_HTTP`)
+- `MISSKEY_WEBFINGER_USE_HTTP`: If it's set true, WebFinger requests will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION.
 
 ## Continuous integration
 Misskey uses GitHub Actions for executing automated tests.
diff --git a/chart/files/default.yml b/chart/files/default.yml
index 4017588fa0..f98b8ebfee 100644
--- a/chart/files/default.yml
+++ b/chart/files/default.yml
@@ -178,12 +178,12 @@ id: "aidx"
 #clusterLimit: 1
 
 # Job concurrency per worker
-# deliverJobConcurrency: 16
-# inboxJobConcurrency: 4
+# deliverJobConcurrency: 128
+# inboxJobConcurrency: 16
 
 # Job rate limiter
-# deliverJobPerSec: 1024
-# inboxJobPerSec: 64
+# deliverJobPerSec: 128
+# inboxJobPerSec: 32
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/packages/backend/migration/1708980134301-APMultipleKeys.js b/packages/backend/migration/1708980134301-APMultipleKeys.js
deleted file mode 100644
index ca55526c6e..0000000000
--- a/packages/backend/migration/1708980134301-APMultipleKeys.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export class APMultipleKeys1708980134301 {
-    name = 'APMultipleKeys1708980134301'
-
-    async up(queryRunner) {
-        await queryRunner.query(`DROP INDEX "public"."IDX_171e64971c780ebd23fae140bb"`);
-        await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PublicKey" character varying(128)`);
-        await queryRunner.query(`ALTER TABLE "user_keypair" ADD "ed25519PrivateKey" character varying(128)`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_10c146e4b39b443ede016f6736d"`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_0db6a5fdb992323449edc8ee421"`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_171e64971c780ebd23fae140bba" PRIMARY KEY ("keyId")`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`);
-        await queryRunner.query(`CREATE INDEX "IDX_10c146e4b39b443ede016f6736" ON "user_publickey" ("userId") `);
-        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
-    }
-
-    async down(queryRunner) {
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`);
-        await queryRunner.query(`DROP INDEX "public"."IDX_10c146e4b39b443ede016f6736"`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_171e64971c780ebd23fae140bba"`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_0db6a5fdb992323449edc8ee421" PRIMARY KEY ("userId", "keyId")`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "PK_0db6a5fdb992323449edc8ee421"`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "PK_10c146e4b39b443ede016f6736d" PRIMARY KEY ("userId")`);
-        await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
-        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" DROP DEFAULT`);
-        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" TYPE "public"."user_profile_followersVisibility_enum_old" USING "followersVisibility"::"text"::"public"."user_profile_followersVisibility_enum_old"`);
-        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "followersVisibility" SET DEFAULT 'public'`);
-        await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PrivateKey"`);
-        await queryRunner.query(`ALTER TABLE "user_keypair" DROP COLUMN "ed25519PublicKey"`);
-        await queryRunner.query(`CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `);
-    }
-}
diff --git a/packages/backend/migration/1709242519122-HttpSignImplLv.js b/packages/backend/migration/1709242519122-HttpSignImplLv.js
deleted file mode 100644
index 7748bae006..0000000000
--- a/packages/backend/migration/1709242519122-HttpSignImplLv.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export class HttpSignImplLv1709242519122 {
-    name = 'HttpSignImplLv1709242519122'
-
-    async up(queryRunner) {
-        await queryRunner.query(`ALTER TABLE "instance" ADD "httpMessageSignaturesImplementationLevel" character varying(16) NOT NULL DEFAULT '00'`);
-    }
-
-    async down(queryRunner) {
-        await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "httpMessageSignaturesImplementationLevel"`);
-    }
-}
diff --git a/packages/backend/migration/1709269211718-APMultipleKeysFix1.js b/packages/backend/migration/1709269211718-APMultipleKeysFix1.js
deleted file mode 100644
index d2011802f2..0000000000
--- a/packages/backend/migration/1709269211718-APMultipleKeysFix1.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export class APMultipleKeys1709269211718 {
-    name = 'APMultipleKeys1709269211718'
-
-    async up(queryRunner) {
-        await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`);
-    }
-
-    async down(queryRunner) {
-			await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`);
-    }
-}
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 893171ebd6..22fdc5cf16 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -79,13 +79,13 @@
 		"@fastify/multipart": "8.3.0",
 		"@fastify/static": "7.0.4",
 		"@fastify/view": "9.1.0",
-		"@misskey-dev/node-http-message-signatures": "0.0.10",
 		"@misskey-dev/sharp-read-bmp": "1.2.0",
 		"@misskey-dev/summaly": "5.1.0",
 		"@napi-rs/canvas": "^0.1.53",
 		"@nestjs/common": "10.3.10",
 		"@nestjs/core": "10.3.10",
 		"@nestjs/testing": "10.3.10",
+		"@peertube/http-signature": "1.7.0",
 		"@sentry/node": "8.13.0",
 		"@sentry/profiling-node": "8.13.0",
 		"@simplewebauthn/server": "10.0.0",
diff --git a/packages/backend/src/@types/http-signature.d.ts b/packages/backend/src/@types/http-signature.d.ts
new file mode 100644
index 0000000000..75b62e55f0
--- /dev/null
+++ b/packages/backend/src/@types/http-signature.d.ts
@@ -0,0 +1,82 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+declare module '@peertube/http-signature' {
+	import type { IncomingMessage, ClientRequest } from 'node:http';
+
+	interface ISignature {
+		keyId: string;
+		algorithm: string;
+		headers: string[];
+		signature: string;
+	}
+
+	interface IOptions {
+		headers?: string[];
+		algorithm?: string;
+		strict?: boolean;
+		authorizationHeaderName?: string;
+	}
+
+	interface IParseRequestOptions extends IOptions {
+		clockSkew?: number;
+	}
+
+	interface IParsedSignature {
+		scheme: string;
+		params: ISignature;
+		signingString: string;
+		algorithm: string;
+		keyId: string;
+	}
+
+	type RequestSignerConstructorOptions =
+		IRequestSignerConstructorOptionsFromProperties |
+		IRequestSignerConstructorOptionsFromFunction;
+
+	interface IRequestSignerConstructorOptionsFromProperties {
+		keyId: string;
+		key: string | Buffer;
+		algorithm?: string;
+	}
+
+	interface IRequestSignerConstructorOptionsFromFunction {
+		sign?: (data: string, cb: (err: any, sig: ISignature) => void) => void;
+	}
+
+	class RequestSigner {
+		constructor(options: RequestSignerConstructorOptions);
+
+		public writeHeader(header: string, value: string): string;
+
+		public writeDateHeader(): string;
+
+		public writeTarget(method: string, path: string): void;
+
+		public sign(cb: (err: any, authz: string) => void): void;
+	}
+
+	interface ISignRequestOptions extends IOptions {
+		keyId: string;
+		key: string;
+		httpVersion?: string;
+	}
+
+	export function parse(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
+	export function parseRequest(request: IncomingMessage, options?: IParseRequestOptions): IParsedSignature;
+
+	export function sign(request: ClientRequest, options: ISignRequestOptions): boolean;
+	export function signRequest(request: ClientRequest, options: ISignRequestOptions): boolean;
+	export function createSigner(): RequestSigner;
+	export function isSigner(obj: any): obj is RequestSigner;
+
+	export function sshKeyToPEM(key: string): string;
+	export function sshKeyFingerprint(key: string): string;
+	export function pemToRsaSSHKey(pem: string, comment: string): string;
+
+	export function verify(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
+	export function verifySignature(parsedSignature: IParsedSignature, pubkey: string | Buffer): boolean;
+	export function verifyHMAC(parsedSignature: IParsedSignature, secret: string): boolean;
+}
diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts
index c132cc7e7b..4dc689238b 100644
--- a/packages/backend/src/const.ts
+++ b/packages/backend/src/const.ts
@@ -9,11 +9,6 @@ export const MAX_NOTE_TEXT_LENGTH = 3000;
 export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
 export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
 
-export const REMOTE_USER_CACHE_TTL = 1000 * 60 * 60 * 3; // 3hours
-export const REMOTE_USER_MOVE_COOLDOWN = 1000 * 60 * 60 * 24 * 14; // 14days
-
-export const REMOTE_SERVER_CACHE_TTL = 1000 * 60 * 60 * 3; // 3hours
-
 //#region hard limits
 // If you change DB_* values, you must also change the DB schema.
 
diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts
index ca0864f679..69a57b4854 100644
--- a/packages/backend/src/core/AccountUpdateService.ts
+++ b/packages/backend/src/core/AccountUpdateService.ts
@@ -3,8 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
-import { ModuleRef } from '@nestjs/core';
+import { Inject, Injectable } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
 import type { UsersRepository } from '@/models/_.js';
 import type { MiUser } from '@/models/User.js';
@@ -13,44 +12,30 @@ import { RelayService } from '@/core/RelayService.js';
 import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
-import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 @Injectable()
-export class AccountUpdateService implements OnModuleInit {
-	private apDeliverManagerService: ApDeliverManagerService;
+export class AccountUpdateService {
 	constructor(
-		private moduleRef: ModuleRef,
-
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
 
 		private userEntityService: UserEntityService,
 		private apRendererService: ApRendererService,
+		private apDeliverManagerService: ApDeliverManagerService,
 		private relayService: RelayService,
 	) {
 	}
 
-	async onModuleInit() {
-		this.apDeliverManagerService = this.moduleRef.get(ApDeliverManagerService.name);
-	}
-
 	@bindThis
-	/**
-	 * Deliver account update to followers
-	 * @param userId user id
-	 * @param deliverKey optional. Private key to sign the deliver.
-	 */
-	public async publishToFollowers(userId: MiUser['id'], deliverKey?: PrivateKeyWithPem) {
+	public async publishToFollowers(userId: MiUser['id']) {
 		const user = await this.usersRepository.findOneBy({ id: userId });
 		if (user == null) throw new Error('user not found');
 
 		// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
 		if (this.userEntityService.isLocalUser(user)) {
 			const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
-			await Promise.allSettled([
-				this.apDeliverManagerService.deliverToFollowers(user, content, deliverKey),
-				this.relayService.deliverToRelays(user, content, deliverKey),
-			]);
+			this.apDeliverManagerService.deliverToFollowers(user, content);
+			this.relayService.deliverToRelays(user, content);
 		}
 	}
 }
diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts
index 60ddc9cde2..6c5b0f6a36 100644
--- a/packages/backend/src/core/CreateSystemUserService.ts
+++ b/packages/backend/src/core/CreateSystemUserService.ts
@@ -7,7 +7,7 @@ import { randomUUID } from 'node:crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import bcrypt from 'bcryptjs';
 import { IsNull, DataSource } from 'typeorm';
-import { genRSAAndEd25519KeyPair } from '@/misc/gen-key-pair.js';
+import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
 import { MiUser } from '@/models/User.js';
 import { MiUserProfile } from '@/models/UserProfile.js';
 import { IdService } from '@/core/IdService.js';
@@ -38,7 +38,7 @@ export class CreateSystemUserService {
 		// Generate secret
 		const secret = generateNativeUserToken();
 
-		const keyPair = await genRSAAndEd25519KeyPair();
+		const keyPair = await genRsaKeyPair();
 
 		let account!: MiUser;
 
@@ -64,8 +64,9 @@ export class CreateSystemUserService {
 			}).then(x => transactionalEntityManager.findOneByOrFail(MiUser, x.identifiers[0]));
 
 			await transactionalEntityManager.insert(MiUserKeypair, {
+				publicKey: keyPair.publicKey,
+				privateKey: keyPair.privateKey,
 				userId: account.id,
-				...keyPair,
 			});
 
 			await transactionalEntityManager.insert(MiUserProfile, {
diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts
index dc53c8711d..aa16468ecb 100644
--- a/packages/backend/src/core/FetchInstanceMetadataService.ts
+++ b/packages/backend/src/core/FetchInstanceMetadataService.ts
@@ -15,7 +15,6 @@ import { LoggerService } from '@/core/LoggerService.js';
 import { HttpRequestService } from '@/core/HttpRequestService.js';
 import { bindThis } from '@/decorators.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
-import { REMOTE_SERVER_CACHE_TTL } from '@/const.js';
 import type { DOMWindow } from 'jsdom';
 
 type NodeInfo = {
@@ -25,7 +24,6 @@ type NodeInfo = {
 		version?: unknown;
 	};
 	metadata?: {
-		httpMessageSignaturesImplementationLevel?: unknown,
 		name?: unknown;
 		nodeName?: unknown;
 		nodeDescription?: unknown;
@@ -41,7 +39,6 @@ type NodeInfo = {
 @Injectable()
 export class FetchInstanceMetadataService {
 	private logger: Logger;
-	private httpColon = 'https://';
 
 	constructor(
 		private httpRequestService: HttpRequestService,
@@ -51,7 +48,6 @@ export class FetchInstanceMetadataService {
 		private redisClient: Redis.Redis,
 	) {
 		this.logger = this.loggerService.getLogger('metadata', 'cyan');
-		this.httpColon = process.env.MISSKEY_USE_HTTP?.toLowerCase() === 'true' ? 'http://' : 'https://';
 	}
 
 	@bindThis
@@ -63,7 +59,7 @@ export class FetchInstanceMetadataService {
 		return await this.redisClient.set(
 			`fetchInstanceMetadata:mutex:v2:${host}`, '1',
 			'EX', 30, // 30秒したら自動でロック解除 https://github.com/misskey-dev/misskey/issues/13506#issuecomment-1975375395
-			'GET', // 古い値を返す(なかったらnull)
+			'GET' // 古い値を返す(なかったらnull)
 		);
 	}
 
@@ -77,24 +73,23 @@ export class FetchInstanceMetadataService {
 	public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> {
 		const host = instance.host;
 
-		if (!force) {
-			// キャッシュ有効チェックはロック取得前に行う
-			const _instance = await this.federatedInstanceService.fetch(host);
-			const now = Date.now();
-			if (_instance && _instance.infoUpdatedAt != null && (now - _instance.infoUpdatedAt.getTime() < REMOTE_SERVER_CACHE_TTL)) {
-				this.logger.debug(`Skip because updated recently ${_instance.infoUpdatedAt.toJSON()}`);
-				return;
-			}
-
-			// finallyでunlockされてしまうのでtry内でロックチェックをしない
-			// (returnであってもfinallyは実行される)
-			if (await this.tryLock(host) === '1') {
-				// 1が返ってきていたら他にロックされているという意味なので、何もしない
-				return;
-			}
+		// finallyでunlockされてしまうのでtry内でロックチェックをしない
+		// (returnであってもfinallyは実行される)
+		if (!force && await this.tryLock(host) === '1') {
+			// 1が返ってきていたらロックされているという意味なので、何もしない
+			return;
 		}
 
 		try {
+			if (!force) {
+				const _instance = await this.federatedInstanceService.fetch(host);
+				const now = Date.now();
+				if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) {
+					// unlock at the finally caluse
+					return;
+				}
+			}
+
 			this.logger.info(`Fetching metadata of ${instance.host} ...`);
 
 			const [info, dom, manifest] = await Promise.all([
@@ -123,14 +118,6 @@ export class FetchInstanceMetadataService {
 				updates.openRegistrations = info.openRegistrations;
 				updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name ?? null) : null : null;
 				updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email ?? null) : null : null;
-				if (info.metadata && info.metadata.httpMessageSignaturesImplementationLevel && (
-					info.metadata.httpMessageSignaturesImplementationLevel === '01' ||
-					info.metadata.httpMessageSignaturesImplementationLevel === '11'
-				)) {
-					updates.httpMessageSignaturesImplementationLevel = info.metadata.httpMessageSignaturesImplementationLevel;
-				} else {
-					updates.httpMessageSignaturesImplementationLevel = '00';
-				}
 			}
 
 			if (name) updates.name = name;
@@ -142,12 +129,6 @@ export class FetchInstanceMetadataService {
 			await this.federatedInstanceService.update(instance.id, updates);
 
 			this.logger.succ(`Successfuly updated metadata of ${instance.host}`);
-			this.logger.debug('Updated metadata:', {
-				info: !!info,
-				dom: !!dom,
-				manifest: !!manifest,
-				updates,
-			});
 		} catch (e) {
 			this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`);
 		} finally {
@@ -160,7 +141,7 @@ export class FetchInstanceMetadataService {
 		this.logger.info(`Fetching nodeinfo of ${instance.host} ...`);
 
 		try {
-			const wellknown = await this.httpRequestService.getJson(this.httpColon + instance.host + '/.well-known/nodeinfo')
+			const wellknown = await this.httpRequestService.getJson('https://' + instance.host + '/.well-known/nodeinfo')
 				.catch(err => {
 					if (err.statusCode === 404) {
 						throw new Error('No nodeinfo provided');
@@ -203,7 +184,7 @@ export class FetchInstanceMetadataService {
 	private async fetchDom(instance: MiInstance): Promise<DOMWindow['document']> {
 		this.logger.info(`Fetching HTML of ${instance.host} ...`);
 
-		const url = this.httpColon + instance.host;
+		const url = 'https://' + instance.host;
 
 		const html = await this.httpRequestService.getHtml(url);
 
@@ -215,7 +196,7 @@ export class FetchInstanceMetadataService {
 
 	@bindThis
 	private async fetchManifest(instance: MiInstance): Promise<Record<string, unknown> | null> {
-		const url = this.httpColon + instance.host;
+		const url = 'https://' + instance.host;
 
 		const manifestUrl = url + '/manifest.json';
 
@@ -226,7 +207,7 @@ export class FetchInstanceMetadataService {
 
 	@bindThis
 	private async fetchFaviconUrl(instance: MiInstance, doc: DOMWindow['document'] | null): Promise<string | null> {
-		const url = this.httpColon + instance.host;
+		const url = 'https://' + instance.host;
 
 		if (doc) {
 			// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
@@ -253,12 +234,12 @@ export class FetchInstanceMetadataService {
 	@bindThis
 	private async fetchIconUrl(instance: MiInstance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> {
 		if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) {
-			const url = this.httpColon + instance.host;
+			const url = 'https://' + instance.host;
 			return (new URL(manifest.icons[0].src, url)).href;
 		}
 
 		if (doc) {
-			const url = this.httpColon + instance.host;
+			const url = 'https://' + instance.host;
 
 			// https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043
 			const links = Array.from(doc.getElementsByTagName('link')).reverse();
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index 312bcfb3b5..87aa70713e 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -249,7 +249,6 @@ export interface InternalEventTypes {
 	unmute: { muterId: MiUser['id']; muteeId: MiUser['id']; };
 	userListMemberAdded: { userListId: MiUserList['id']; memberId: MiUser['id']; };
 	userListMemberRemoved: { userListId: MiUserList['id']; memberId: MiUser['id']; };
-	userKeypairUpdated: { userId: MiUser['id']; };
 }
 
 type EventTypesToEventPayload<T> = EventUnionFromDictionary<UndefinedAsNullAll<SerializedAll<T>>>;
diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts
index 4249c158d7..7f3cac7c58 100644
--- a/packages/backend/src/core/HttpRequestService.ts
+++ b/packages/backend/src/core/HttpRequestService.ts
@@ -70,7 +70,7 @@ export class HttpRequestService {
 			localAddress: config.outgoingAddress,
 		});
 
-		const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 16);
+		const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128);
 
 		this.httpAgent = config.proxy
 			? new HttpProxyAgent({
diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index dd3f2182b4..80827a500b 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -13,6 +13,7 @@ import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
+import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
 import type {
 	DbJobData,
 	DeliverJobData,
@@ -32,7 +33,7 @@ import type {
 	UserWebhookDeliverQueue,
 	SystemWebhookDeliverQueue,
 } from './QueueModule.js';
-import { genRFC3230DigestHeader, type PrivateKeyWithPem, type ParsedSignature } from '@misskey-dev/node-http-message-signatures';
+import type httpSignature from '@peertube/http-signature';
 import type * as Bull from 'bullmq';
 
 @Injectable()
@@ -89,21 +90,21 @@ export class QueueService {
 	}
 
 	@bindThis
-	public async deliver(user: ThinUser, content: IActivity | null, to: string | null, isSharedInbox: boolean, privateKey?: PrivateKeyWithPem) {
+	public deliver(user: ThinUser, content: IActivity | null, to: string | null, isSharedInbox: boolean) {
 		if (content == null) return null;
 		if (to == null) return null;
 
 		const contentBody = JSON.stringify(content);
+		const digest = ApRequestCreator.createDigest(contentBody);
 
 		const data: DeliverJobData = {
 			user: {
 				id: user.id,
 			},
 			content: contentBody,
-			digest: await genRFC3230DigestHeader(contentBody, 'SHA-256'),
+			digest,
 			to,
 			isSharedInbox,
-			privateKey: privateKey && { keyId: privateKey.keyId, privateKeyPem: privateKey.privateKeyPem },
 		};
 
 		return this.deliverQueue.add(to, data, {
@@ -121,13 +122,13 @@ export class QueueService {
 	 * @param user `{ id: string; }` この関数ではThinUserに変換しないので前もって変換してください
 	 * @param content IActivity | null
 	 * @param inboxes `Map<string, boolean>` / key: to (inbox url), value: isSharedInbox (whether it is sharedInbox)
-	 * @param forceMainKey boolean | undefined, force to use main (rsa) key
 	 * @returns void
 	 */
 	@bindThis
-	public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map<string, boolean>, privateKey?: PrivateKeyWithPem) {
+	public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map<string, boolean>) {
 		if (content == null) return null;
 		const contentBody = JSON.stringify(content);
+		const digest = ApRequestCreator.createDigest(contentBody);
 
 		const opts = {
 			attempts: this.config.deliverJobMaxAttempts ?? 12,
@@ -143,9 +144,9 @@ export class QueueService {
 			data: {
 				user,
 				content: contentBody,
+				digest,
 				to: d[0],
 				isSharedInbox: d[1],
-				privateKey: privateKey && { keyId: privateKey.keyId, privateKeyPem: privateKey.privateKeyPem },
 			} as DeliverJobData,
 			opts,
 		})));
@@ -154,7 +155,7 @@ export class QueueService {
 	}
 
 	@bindThis
-	public inbox(activity: IActivity, signature: ParsedSignature | null) {
+	public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) {
 		const data = {
 			activity: activity,
 			signature,
diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts
index ad01f98902..8dd3d64f5b 100644
--- a/packages/backend/src/core/RelayService.ts
+++ b/packages/backend/src/core/RelayService.ts
@@ -16,8 +16,6 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { DI } from '@/di-symbols.js';
 import { deepClone } from '@/misc/clone.js';
 import { bindThis } from '@/decorators.js';
-import { UserKeypairService } from './UserKeypairService.js';
-import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 const ACTOR_USERNAME = 'relay.actor' as const;
 
@@ -36,7 +34,6 @@ export class RelayService {
 		private queueService: QueueService,
 		private createSystemUserService: CreateSystemUserService,
 		private apRendererService: ApRendererService,
-		private userKeypairService: UserKeypairService,
 	) {
 		this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10);
 	}
@@ -114,7 +111,7 @@ export class RelayService {
 	}
 
 	@bindThis
-	public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any, privateKey?: PrivateKeyWithPem): Promise<void> {
+	public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
 		if (activity == null) return;
 
 		const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
@@ -124,9 +121,11 @@ export class RelayService {
 
 		const copy = deepClone(activity);
 		if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public'];
-		privateKey = privateKey ?? await this.userKeypairService.getLocalUserPrivateKeyPem(user.id);
-		const signed = await this.apRendererService.attachLdSignature(copy, privateKey);
 
-		this.queueService.deliverMany(user, signed, new Map(relays.map(({ inbox }) => [inbox, false])), privateKey);
+		const signed = await this.apRendererService.attachLdSignature(copy, user);
+
+		for (const relay of relays) {
+			this.queueService.deliver(user, signed, relay.inbox, false);
+		}
 	}
 }
diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts
index 54c6170062..5522ecd6cc 100644
--- a/packages/backend/src/core/SignupService.ts
+++ b/packages/backend/src/core/SignupService.ts
@@ -3,6 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { generateKeyPair } from 'node:crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import bcrypt from 'bcryptjs';
 import { DataSource, IsNull } from 'typeorm';
@@ -20,7 +21,6 @@ import { bindThis } from '@/decorators.js';
 import UsersChart from '@/core/chart/charts/users.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { MetaService } from '@/core/MetaService.js';
-import { genRSAAndEd25519KeyPair } from '@/misc/gen-key-pair.js';
 
 @Injectable()
 export class SignupService {
@@ -93,7 +93,22 @@ export class SignupService {
 			}
 		}
 
-		const keyPair = await genRSAAndEd25519KeyPair();
+		const keyPair = await new Promise<string[]>((res, rej) =>
+			generateKeyPair('rsa', {
+				modulusLength: 2048,
+				publicKeyEncoding: {
+					type: 'spki',
+					format: 'pem',
+				},
+				privateKeyEncoding: {
+					type: 'pkcs8',
+					format: 'pem',
+					cipher: undefined,
+					passphrase: undefined,
+				},
+			}, (err, publicKey, privateKey) =>
+				err ? rej(err) : res([publicKey, privateKey]),
+			));
 
 		let account!: MiUser;
 
@@ -116,8 +131,9 @@ export class SignupService {
 			}));
 
 			await transactionalEntityManager.save(new MiUserKeypair({
+				publicKey: keyPair[0],
+				privateKey: keyPair[1],
 				userId: account.id,
-				...keyPair,
 			}));
 
 			await transactionalEntityManager.save(new MiUserProfile({
diff --git a/packages/backend/src/core/UserKeypairService.ts b/packages/backend/src/core/UserKeypairService.ts
index aa90f1e209..51ac99179a 100644
--- a/packages/backend/src/core/UserKeypairService.ts
+++ b/packages/backend/src/core/UserKeypairService.ts
@@ -5,184 +5,41 @@
 
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
 import * as Redis from 'ioredis';
-import { genEd25519KeyPair, importPrivateKey, PrivateKey, PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 import type { MiUser } from '@/models/User.js';
 import type { UserKeypairsRepository } from '@/models/_.js';
-import { RedisKVCache, MemoryKVCache } from '@/misc/cache.js';
+import { RedisKVCache } from '@/misc/cache.js';
 import type { MiUserKeypair } from '@/models/UserKeypair.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
-import { GlobalEventService, GlobalEvents } from '@/core/GlobalEventService.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import type { webcrypto } from 'node:crypto';
 
 @Injectable()
 export class UserKeypairService implements OnApplicationShutdown {
-	private keypairEntityCache: RedisKVCache<MiUserKeypair>;
-	private privateKeyObjectCache: MemoryKVCache<webcrypto.CryptoKey>;
+	private cache: RedisKVCache<MiUserKeypair>;
 
 	constructor(
 		@Inject(DI.redis)
 		private redisClient: Redis.Redis,
-		@Inject(DI.redisForSub)
-		private redisForSub: Redis.Redis,
+
 		@Inject(DI.userKeypairsRepository)
 		private userKeypairsRepository: UserKeypairsRepository,
-
-		private globalEventService: GlobalEventService,
-		private userEntityService: UserEntityService,
 	) {
-		this.keypairEntityCache = new RedisKVCache<MiUserKeypair>(this.redisClient, 'userKeypair', {
+		this.cache = new RedisKVCache<MiUserKeypair>(this.redisClient, 'userKeypair', {
 			lifetime: 1000 * 60 * 60 * 24, // 24h
 			memoryCacheLifetime: Infinity,
 			fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }),
 			toRedisConverter: (value) => JSON.stringify(value),
 			fromRedisConverter: (value) => JSON.parse(value),
 		});
-		this.privateKeyObjectCache = new MemoryKVCache<webcrypto.CryptoKey>(1000 * 60 * 60 * 1);
-
-		this.redisForSub.on('message', this.onMessage);
 	}
 
 	@bindThis
 	public async getUserKeypair(userId: MiUser['id']): Promise<MiUserKeypair> {
-		return await this.keypairEntityCache.fetch(userId);
+		return await this.cache.fetch(userId);
 	}
 
-	/**
-	 * Get private key [Only PrivateKeyWithPem for queue data etc.]
-	 * @param userIdOrHint user id or MiUserKeypair
-	 * @param preferType
-	 *		If ed25519-like(`ed25519`, `01`, `11`) is specified, ed25519 keypair will be returned if exists.
-	 *		Otherwise, main keypair will be returned.
-	 * @returns
-	 */
-	@bindThis
-	public async getLocalUserPrivateKeyPem(
-		userIdOrHint: MiUser['id'] | MiUserKeypair,
-		preferType?: string,
-	): Promise<PrivateKeyWithPem> {
-		const keypair = typeof userIdOrHint === 'string' ? await this.getUserKeypair(userIdOrHint) : userIdOrHint;
-		if (
-			preferType && ['01', '11', 'ed25519'].includes(preferType.toLowerCase()) &&
-			keypair.ed25519PublicKey != null && keypair.ed25519PrivateKey != null
-		) {
-			return {
-				keyId: `${this.userEntityService.genLocalUserUri(keypair.userId)}#ed25519-key`,
-				privateKeyPem: keypair.ed25519PrivateKey,
-			};
-		}
-		return {
-			keyId: `${this.userEntityService.genLocalUserUri(keypair.userId)}#main-key`,
-			privateKeyPem: keypair.privateKey,
-		};
-	}
-
-	/**
-	 * Get private key [Only PrivateKey for ap request]
-	 * Using cache due to performance reasons of `crypto.subtle.importKey`
-	 * @param userIdOrHint user id, MiUserKeypair, or PrivateKeyWithPem
-	 * @param preferType
-	 * 		If ed25519-like(`ed25519`, `01`, `11`) is specified, ed25519 keypair will be returned if exists.
-	 *		Otherwise, main keypair will be returned. (ignored if userIdOrHint is PrivateKeyWithPem)
-	 * @returns
-	 */
-	@bindThis
-	public async getLocalUserPrivateKey(
-		userIdOrHint: MiUser['id'] | MiUserKeypair | PrivateKeyWithPem,
-		preferType?: string,
-	): Promise<PrivateKey> {
-		if (typeof userIdOrHint === 'object' && 'privateKeyPem' in userIdOrHint) {
-			// userIdOrHint is PrivateKeyWithPem
-			return {
-				keyId: userIdOrHint.keyId,
-				privateKey: await this.privateKeyObjectCache.fetch(userIdOrHint.keyId, async () => {
-					return await importPrivateKey(userIdOrHint.privateKeyPem);
-				}),
-			};
-		}
-
-		const userId = typeof userIdOrHint === 'string' ? userIdOrHint : userIdOrHint.userId;
-		const getKeypair = () => typeof userIdOrHint === 'string' ? this.getUserKeypair(userId) : userIdOrHint;
-
-		if (preferType && ['01', '11', 'ed25519'].includes(preferType.toLowerCase())) {
-			const keyId = `${this.userEntityService.genLocalUserUri(userId)}#ed25519-key`;
-			const fetched = await this.privateKeyObjectCache.fetchMaybe(keyId, async () => {
-				const keypair = await getKeypair();
-				if (keypair.ed25519PublicKey != null && keypair.ed25519PrivateKey != null) {
-					return await importPrivateKey(keypair.ed25519PrivateKey);
-				}
-				return;
-			});
-			if (fetched) {
-				return {
-					keyId,
-					privateKey: fetched,
-				};
-			}
-		}
-
-		const keyId = `${this.userEntityService.genLocalUserUri(userId)}#main-key`;
-		return {
-			keyId,
-			privateKey: await this.privateKeyObjectCache.fetch(keyId, async () => {
-				const keypair = await getKeypair();
-				return await importPrivateKey(keypair.privateKey);
-			}),
-		};
-	}
-
-	@bindThis
-	public async refresh(userId: MiUser['id']): Promise<void> {
-		return await this.keypairEntityCache.refresh(userId);
-	}
-
-	/**
-	 * If DB has ed25519 keypair, refresh cache and return it.
-	 * If not, create, save and return ed25519 keypair.
-	 * @param userId user id
-	 * @returns MiUserKeypair if keypair is created, void if keypair is already exists
-	 */
-	@bindThis
-	public async refreshAndPrepareEd25519KeyPair(userId: MiUser['id']): Promise<MiUserKeypair | void> {
-		await this.refresh(userId);
-		const keypair = await this.keypairEntityCache.fetch(userId);
-		if (keypair.ed25519PublicKey != null) {
-			return;
-		}
-
-		const ed25519 = await genEd25519KeyPair();
-		await this.userKeypairsRepository.update({ userId }, {
-			ed25519PublicKey: ed25519.publicKey,
-			ed25519PrivateKey: ed25519.privateKey,
-		});
-		this.globalEventService.publishInternalEvent('userKeypairUpdated', { userId });
-		const result = {
-			...keypair,
-			ed25519PublicKey: ed25519.publicKey,
-			ed25519PrivateKey: ed25519.privateKey,
-		};
-		this.keypairEntityCache.set(userId, result);
-		return result;
-	}
-
-	@bindThis
-	private async onMessage(_: string, data: string): Promise<void> {
-		const obj = JSON.parse(data);
-
-		if (obj.channel === 'internal') {
-			const { type, body } = obj.message as GlobalEvents['internal']['payload'];
-			switch (type) {
-				case 'userKeypairUpdated': {
-					this.refresh(body.userId);
-					break;
-				}
-			}
-		}
-	}
 	@bindThis
 	public dispose(): void {
-		this.keypairEntityCache.dispose();
+		this.cache.dispose();
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts
index fc5a68c72e..d594a223f4 100644
--- a/packages/backend/src/core/UserSuspendService.ts
+++ b/packages/backend/src/core/UserSuspendService.ts
@@ -3,23 +3,27 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable } from '@nestjs/common';
+import { Not, IsNull } from 'typeorm';
+import type { FollowingsRepository } from '@/models/_.js';
 import type { MiUser } from '@/models/User.js';
+import { QueueService } from '@/core/QueueService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { DI } from '@/di-symbols.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
-import { UserKeypairService } from './UserKeypairService.js';
-import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js';
 
 @Injectable()
 export class UserSuspendService {
 	constructor(
+		@Inject(DI.followingsRepository)
+		private followingsRepository: FollowingsRepository,
+
 		private userEntityService: UserEntityService,
+		private queueService: QueueService,
 		private globalEventService: GlobalEventService,
 		private apRendererService: ApRendererService,
-		private userKeypairService: UserKeypairService,
-		private apDeliverManagerService: ApDeliverManagerService,
 	) {
 	}
 
@@ -28,12 +32,28 @@ export class UserSuspendService {
 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
 
 		if (this.userEntityService.isLocalUser(user)) {
+			// 知り得る全SharedInboxにDelete配信
 			const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user));
-			const manager = this.apDeliverManagerService.createDeliverManager(user, content);
-			manager.addAllKnowingSharedInboxRecipe();
-			// process deliver時にはキーペアが消去されているはずなので、ここで挿入する
-			const privateKey = await this.userKeypairService.getLocalUserPrivateKeyPem(user.id, 'main');
-			manager.execute({ privateKey });
+
+			const queue: string[] = [];
+
+			const followings = await this.followingsRepository.find({
+				where: [
+					{ followerSharedInbox: Not(IsNull()) },
+					{ followeeSharedInbox: Not(IsNull()) },
+				],
+				select: ['followerSharedInbox', 'followeeSharedInbox'],
+			});
+
+			const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox);
+
+			for (const inbox of inboxes) {
+				if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
+			}
+
+			for (const inbox of queue) {
+				this.queueService.deliver(user, content, inbox, true);
+			}
 		}
 	}
 
@@ -42,12 +62,28 @@ export class UserSuspendService {
 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
 
 		if (this.userEntityService.isLocalUser(user)) {
+			// 知り得る全SharedInboxにUndo Delete配信
 			const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user), user));
-			const manager = this.apDeliverManagerService.createDeliverManager(user, content);
-			manager.addAllKnowingSharedInboxRecipe();
-			// process deliver時にはキーペアが消去されているはずなので、ここで挿入する
-			const privateKey = await this.userKeypairService.getLocalUserPrivateKeyPem(user.id, 'main');
-			manager.execute({ privateKey });
+
+			const queue: string[] = [];
+
+			const followings = await this.followingsRepository.find({
+				where: [
+					{ followerSharedInbox: Not(IsNull()) },
+					{ followeeSharedInbox: Not(IsNull()) },
+				],
+				select: ['followerSharedInbox', 'followeeSharedInbox'],
+			});
+
+			const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox);
+
+			for (const inbox of inboxes) {
+				if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
+			}
+
+			for (const inbox of queue) {
+				this.queueService.deliver(user as any, content, inbox, true);
+			}
 		}
 	}
 }
diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts
index aa1144778c..374536a741 100644
--- a/packages/backend/src/core/WebfingerService.ts
+++ b/packages/backend/src/core/WebfingerService.ts
@@ -46,7 +46,7 @@ export class WebfingerService {
 		const m = query.match(mRegex);
 		if (m) {
 			const hostname = m[2];
-			const useHttp = process.env.MISSKEY_USE_HTTP && process.env.MISSKEY_USE_HTTP.toLowerCase() === 'true';
+			const useHttp = process.env.MISSKEY_WEBFINGER_USE_HTTP && process.env.MISSKEY_WEBFINGER_USE_HTTP.toLowerCase() === 'true';
 			return `http${useHttp ? '' : 's'}://${hostname}/.well-known/webfinger?${urlQuery({ resource: `acct:${query}` })}`;
 		}
 
diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts
index 973394683f..f6b70ead44 100644
--- a/packages/backend/src/core/activitypub/ApDbResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import type { MiUser, NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
+import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
 import type { Config } from '@/config.js';
 import { MemoryKVCache } from '@/misc/cache.js';
 import type { MiUserPublickey } from '@/models/UserPublickey.js';
@@ -13,12 +13,9 @@ import { CacheService } from '@/core/CacheService.js';
 import type { MiNote } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
 import { MiLocalUser, MiRemoteUser } from '@/models/User.js';
-import Logger from '@/logger.js';
 import { getApId } from './type.js';
 import { ApPersonService } from './models/ApPersonService.js';
-import { ApLoggerService } from './ApLoggerService.js';
 import type { IObject } from './type.js';
-import { UtilityService } from '../UtilityService.js';
 
 export type UriParseResult = {
 	/** wether the URI was generated by us */
@@ -38,8 +35,8 @@ export type UriParseResult = {
 
 @Injectable()
 export class ApDbResolverService implements OnApplicationShutdown {
-	private publicKeyByUserIdCache: MemoryKVCache<MiUserPublickey[] | null>;
-	private logger: Logger;
+	private publicKeyCache: MemoryKVCache<MiUserPublickey | null>;
+	private publicKeyByUserIdCache: MemoryKVCache<MiUserPublickey | null>;
 
 	constructor(
 		@Inject(DI.config)
@@ -56,17 +53,9 @@ export class ApDbResolverService implements OnApplicationShutdown {
 
 		private cacheService: CacheService,
 		private apPersonService: ApPersonService,
-		private apLoggerService: ApLoggerService,
-		private utilityService: UtilityService,
 	) {
-		this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey[] | null>(Infinity);
-		this.logger = this.apLoggerService.logger.createSubLogger('db-resolver');
-	}
-
-	private punyHost(url: string): string {
-		const urlObj = new URL(url);
-		const host = `${this.utilityService.toPuny(urlObj.hostname)}${urlObj.port.length > 0 ? ':' + urlObj.port : ''}`;
-		return host;
+		this.publicKeyCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
+		this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
 	}
 
 	@bindThis
@@ -127,141 +116,62 @@ export class ApDbResolverService implements OnApplicationShutdown {
 		}
 	}
 
+	/**
+	 * AP KeyId => Misskey User and Key
+	 */
 	@bindThis
-	private async refreshAndFindKey(userId: MiUser['id'], keyId: string): Promise<MiUserPublickey | null> {
-		this.refreshCacheByUserId(userId);
-		const keys = await this.getPublicKeyByUserId(userId);
-		if (keys == null || !Array.isArray(keys) || keys.length === 0) {
-			this.logger.warn(`No key found (refreshAndFindKey) userId=${userId} keyId=${keyId} keys=${JSON.stringify(keys)}`);
-			return null;
-		}
-		const exactKey = keys.find(x => x.keyId === keyId);
-		if (exactKey) return exactKey;
-		this.logger.warn(`No exact key found (refreshAndFindKey) userId=${userId} keyId=${keyId} keys=${JSON.stringify(keys)}`);
-		return null;
+	public async getAuthUserFromKeyId(keyId: string): Promise<{
+		user: MiRemoteUser;
+		key: MiUserPublickey;
+	} | null> {
+		const key = await this.publicKeyCache.fetch(keyId, async () => {
+			const key = await this.userPublickeysRepository.findOneBy({
+				keyId,
+			});
+
+			if (key == null) return null;
+
+			return key;
+		}, key => key != null);
+
+		if (key == null) return null;
+
+		const user = await this.cacheService.findUserById(key.userId).catch(() => null) as MiRemoteUser | null;
+		if (user == null) return null;
+		if (user.isDeleted) return null;
+
+		return {
+			user,
+			key,
+		};
 	}
 
 	/**
 	 * AP Actor id => Misskey User and Key
-	 * @param uri AP Actor id
-	 * @param keyId Key id to find. If not specified, main key will be selected.
-	 * @returns
-	 *	1. `null` if the user and key host do not match
-	 *	2. `{ user: null, key: null }` if the user is not found
-	 *	3. `{ user: MiRemoteUser, key: null }` if key is not found
-	 *  4. `{ user: MiRemoteUser, key: MiUserPublickey }` if both are found
 	 */
 	@bindThis
-	public async getAuthUserFromApId(uri: string, keyId?: string): Promise<{
+	public async getAuthUserFromApId(uri: string): Promise<{
 		user: MiRemoteUser;
 		key: MiUserPublickey | null;
-	} | {
-		user: null;
-		key: null;
-	} |
-	null> {
-		if (keyId) {
-			if (this.punyHost(uri) !== this.punyHost(keyId)) {
-				/**
-				 * keyIdはURL形式かつkeyIdのホストはuriのホストと一致するはず
-				 * (ApPersonService.validateActorに由来)
-				 *
-				 * ただ、Mastodonはリプライ関連で他人のトゥートをHTTP Signature署名して送ってくることがある
-				 * そのような署名は有効性に疑問があるので無視することにする
-				 * ここではuriとkeyIdのホストが一致しない場合は無視する
-				 * ハッシュをなくしたkeyIdとuriの同一性を比べてみてもいいが、`uri#*-key`というkeyIdを設定するのが
-				 * 決まりごとというわけでもないため幅を持たせることにする
-				 *
-				 *
-				 * The keyId should be in URL format and its host should match the host of the uri
-				 * (derived from ApPersonService.validateActor)
-				 *
-				 * However, Mastodon sometimes sends toots from other users with HTTP Signature signing for reply-related purposes
-				 * Such signatures are of questionable validity, so we choose to ignore them
-				 * Here, we ignore cases where the hosts of uri and keyId do not match
-				 * We could also compare the equality of keyId without the hash and uri, but since setting a keyId like `uri#*-key`
-				 * is not a strict rule, we decide to allow for some flexibility
-				 */
-				this.logger.warn(`actor uri and keyId are not matched uri=${uri} keyId=${keyId}`);
-				return null;
-			}
-		}
+	} | null> {
+		const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser;
+		if (user.isDeleted) return null;
 
-		const user = await this.apPersonService.resolvePerson(uri, undefined, true) as MiRemoteUser;
-		if (user.isDeleted) return { user: null, key: null };
-
-		const keys = await this.getPublicKeyByUserId(user.id);
-
-		if (keys == null || !Array.isArray(keys) || keys.length === 0) {
-			this.logger.warn(`No key found uri=${uri} userId=${user.id} keys=${JSON.stringify(keys)}`);
-			return { user, key: null };
-		}
-
-		if (!keyId) {
-			// Choose the main-like
-			const mainKey = keys.find(x => {
-				try {
-					const url = new URL(x.keyId);
-					const path = url.pathname.split('/').pop()?.toLowerCase();
-					if (url.hash) {
-						if (url.hash.toLowerCase().includes('main')) {
-							return true;
-						}
-					} else if (path?.includes('main') || path === 'publickey') {
-						return true;
-					}
-				} catch { /* noop */ }
-
-				return false;
-			});
-			return { user, key: mainKey ?? keys[0] };
-		}
-
-		const exactKey = keys.find(x => x.keyId === keyId);
-		if (exactKey) return { user, key: exactKey };
-
-		/**
-		 * keyIdで見つからない場合、まずはキャッシュを更新して再取得
-		 * If not found with keyId, update cache and reacquire
-		 */
-		const cacheRaw = this.publicKeyByUserIdCache.cache.get(user.id);
-		if (cacheRaw && cacheRaw.date > Date.now() - 1000 * 60 * 12) {
-			const exactKey = await this.refreshAndFindKey(user.id, keyId);
-			if (exactKey) return { user, key: exactKey };
-		}
-
-		/**
-		 * lastFetchedAtでの更新制限を弱めて再取得
-		 * Reacquisition with weakened update limit at lastFetchedAt
-		 */
-		if (user.lastFetchedAt == null || user.lastFetchedAt < new Date(Date.now() - 1000 * 60 * 12)) {
-			this.logger.info(`Fetching user to find public key uri=${uri} userId=${user.id} keyId=${keyId}`);
-			const renewed = await this.apPersonService.fetchPersonWithRenewal(uri, 0);
-			if (renewed == null || renewed.isDeleted) return null;
-
-			return { user, key: await this.refreshAndFindKey(user.id, keyId) };
-		}
-
-		this.logger.warn(`No key found uri=${uri} userId=${user.id} keyId=${keyId}`);
-		return { user, key: null };
-	}
-
-	@bindThis
-	public async getPublicKeyByUserId(userId: MiUser['id']): Promise<MiUserPublickey[] | null> {
-		return await this.publicKeyByUserIdCache.fetch(
-			userId,
-			() => this.userPublickeysRepository.find({ where: { userId } }),
+		const key = await this.publicKeyByUserIdCache.fetch(
+			user.id,
+			() => this.userPublickeysRepository.findOneBy({ userId: user.id }),
 			v => v != null,
 		);
-	}
 
-	@bindThis
-	public refreshCacheByUserId(userId: MiUser['id']): void {
-		this.publicKeyByUserIdCache.delete(userId);
+		return {
+			user,
+			key,
+		};
 	}
 
 	@bindThis
 	public dispose(): void {
+		this.publicKeyCache.dispose();
 		this.publicKeyByUserIdCache.dispose();
 	}
 
diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
index db3302e6ff..5d07cd8e8f 100644
--- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
+++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts
@@ -9,14 +9,10 @@ import { DI } from '@/di-symbols.js';
 import type { FollowingsRepository } from '@/models/_.js';
 import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
 import { QueueService } from '@/core/QueueService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
 import type { IActivity } from '@/core/activitypub/type.js';
 import { ThinUser } from '@/queue/types.js';
-import { AccountUpdateService } from '@/core/AccountUpdateService.js';
-import type Logger from '@/logger.js';
-import { UserKeypairService } from '../UserKeypairService.js';
-import { ApLoggerService } from './ApLoggerService.js';
-import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 interface IRecipe {
 	type: string;
@@ -31,19 +27,12 @@ interface IDirectRecipe extends IRecipe {
 	to: MiRemoteUser;
 }
 
-interface IAllKnowingSharedInboxRecipe extends IRecipe {
-	type: 'AllKnowingSharedInbox';
-}
-
 const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe =>
 	recipe.type === 'Followers';
 
 const isDirect = (recipe: IRecipe): recipe is IDirectRecipe =>
 	recipe.type === 'Direct';
 
-const isAllKnowingSharedInbox = (recipe: IRecipe): recipe is IAllKnowingSharedInboxRecipe =>
-	recipe.type === 'AllKnowingSharedInbox';
-
 class DeliverManager {
 	private actor: ThinUser;
 	private activity: IActivity | null;
@@ -51,18 +40,16 @@ class DeliverManager {
 
 	/**
 	 * Constructor
-	 * @param userKeypairService
+	 * @param userEntityService
 	 * @param followingsRepository
 	 * @param queueService
 	 * @param actor Actor
 	 * @param activity Activity to deliver
 	 */
 	constructor(
-		private userKeypairService: UserKeypairService,
+		private userEntityService: UserEntityService,
 		private followingsRepository: FollowingsRepository,
 		private queueService: QueueService,
-		private accountUpdateService: AccountUpdateService,
-		private logger: Logger,
 
 		actor: { id: MiUser['id']; host: null; },
 		activity: IActivity | null,
@@ -104,18 +91,6 @@ class DeliverManager {
 		this.addRecipe(recipe);
 	}
 
-	/**
-	 * Add recipe for all-knowing shared inbox deliver
-	 */
-	@bindThis
-	public addAllKnowingSharedInboxRecipe(): void {
-		const deliver: IAllKnowingSharedInboxRecipe = {
-			type: 'AllKnowingSharedInbox',
-		};
-
-		this.addRecipe(deliver);
-	}
-
 	/**
 	 * Add recipe
 	 * @param recipe Recipe
@@ -129,44 +104,11 @@ class DeliverManager {
 	 * Execute delivers
 	 */
 	@bindThis
-	public async execute(opts?: { privateKey?: PrivateKeyWithPem }): Promise<void> {
-		//#region MIGRATION
-		if (!opts?.privateKey) {
-			/**
-			 * ed25519の署名がなければ追加する
-			 */
-			const created = await this.userKeypairService.refreshAndPrepareEd25519KeyPair(this.actor.id);
-			if (created) {
-				// createdが存在するということは新規作成されたということなので、フォロワーに配信する
-				this.logger.info(`ed25519 key pair created for user ${this.actor.id} and publishing to followers`);
-				// リモートに配信
-				const keyPair = await this.userKeypairService.getLocalUserPrivateKeyPem(created, 'main');
-				await this.accountUpdateService.publishToFollowers(this.actor.id, keyPair);
-			}
-		}
-		//#endregion
-
-		//#region collect inboxes by recipes
+	public async execute(): Promise<void> {
 		// The value flags whether it is shared or not.
 		// key: inbox URL, value: whether it is sharedInbox
 		const inboxes = new Map<string, boolean>();
 
-		if (this.recipes.some(r => isAllKnowingSharedInbox(r))) {
-			// all-knowing shared inbox
-			const followings = await this.followingsRepository.find({
-				where: [
-					{ followerSharedInbox: Not(IsNull()) },
-					{ followeeSharedInbox: Not(IsNull()) },
-				],
-				select: ['followerSharedInbox', 'followeeSharedInbox'],
-			});
-
-			for (const following of followings) {
-				if (following.followeeSharedInbox) inboxes.set(following.followeeSharedInbox, true);
-				if (following.followerSharedInbox) inboxes.set(following.followerSharedInbox, true);
-			}
-		}
-
 		// build inbox list
 		// Process follower recipes first to avoid duplication when processing direct recipes later.
 		if (this.recipes.some(r => isFollowers(r))) {
@@ -200,49 +142,39 @@ class DeliverManager {
 
 			inboxes.set(recipe.to.inbox, false);
 		}
-		//#endregion
 
 		// deliver
-		await this.queueService.deliverMany(this.actor, this.activity, inboxes, opts?.privateKey);
-		this.logger.info(`Deliver queues dispatched: inboxes=${inboxes.size} actorId=${this.actor.id} activityId=${this.activity?.id}`);
+		await this.queueService.deliverMany(this.actor, this.activity, inboxes);
 	}
 }
 
 @Injectable()
 export class ApDeliverManagerService {
-	private logger: Logger;
-
 	constructor(
 		@Inject(DI.followingsRepository)
 		private followingsRepository: FollowingsRepository,
 
-		private userKeypairService: UserKeypairService,
+		private userEntityService: UserEntityService,
 		private queueService: QueueService,
-		private accountUpdateService: AccountUpdateService,
-		private apLoggerService: ApLoggerService,
 	) {
-		this.logger = this.apLoggerService.logger.createSubLogger('deliver-manager');
 	}
 
 	/**
 	 * Deliver activity to followers
 	 * @param actor
 	 * @param activity Activity
-	 * @param forceMainKey Force to use main (rsa) key
 	 */
 	@bindThis
-	public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, privateKey?: PrivateKeyWithPem): Promise<void> {
+	public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise<void> {
 		const manager = new DeliverManager(
-			this.userKeypairService,
+			this.userEntityService,
 			this.followingsRepository,
 			this.queueService,
-			this.accountUpdateService,
-			this.logger,
 			actor,
 			activity,
 		);
 		manager.addFollowersRecipe();
-		await manager.execute({ privateKey });
+		await manager.execute();
 	}
 
 	/**
@@ -254,11 +186,9 @@ export class ApDeliverManagerService {
 	@bindThis
 	public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise<void> {
 		const manager = new DeliverManager(
-			this.userKeypairService,
+			this.userEntityService,
 			this.followingsRepository,
 			this.queueService,
-			this.accountUpdateService,
-			this.logger,
 			actor,
 			activity,
 		);
@@ -269,11 +199,10 @@ export class ApDeliverManagerService {
 	@bindThis
 	public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
 		return new DeliverManager(
-			this.userKeypairService,
+			this.userEntityService,
 			this.followingsRepository,
 			this.queueService,
-			this.accountUpdateService,
-			this.logger,
+
 			actor,
 			activity,
 		);
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 1bef9fe071..e2164fec1d 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -114,8 +114,15 @@ export class ApInboxService {
 			result = await this.performOneActivity(actor, activity);
 		}
 
-		// ついでにリモートユーザーの情報が古かったら更新しておく?
-		// → No, この関数が呼び出される前に署名検証で更新されているはず
+		// ついでにリモートユーザーの情報が古かったら更新しておく
+		if (actor.uri) {
+			if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
+				setImmediate(() => {
+					this.apPersonService.updatePerson(actor.uri);
+				});
+			}
+		}
+		return result;
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 5d7419f934..98e944f347 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -22,6 +22,7 @@ import { UserKeypairService } from '@/core/UserKeypairService.js';
 import { MfmService } from '@/core/MfmService.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
+import type { MiUserKeypair } from '@/models/UserKeypair.js';
 import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import { CustomEmojiService } from '@/core/CustomEmojiService.js';
@@ -30,7 +31,6 @@ import { JsonLdService } from './JsonLdService.js';
 import { ApMfmService } from './ApMfmService.js';
 import { CONTEXT } from './misc/contexts.js';
 import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
-import type { PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
 
 @Injectable()
 export class ApRendererService {
@@ -251,15 +251,15 @@ export class ApRendererService {
 	}
 
 	@bindThis
-	public renderKey(user: MiLocalUser, publicKey: string, postfix?: string): IKey {
+	public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey {
 		return {
-			id: `${this.userEntityService.genLocalUserUri(user.id)}${postfix ?? '/publickey'}`,
+			id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
 			type: 'Key',
 			owner: this.userEntityService.genLocalUserUri(user.id),
-			publicKeyPem: createPublicKey(publicKey).export({
+			publicKeyPem: createPublicKey(key.publicKey).export({
 				type: 'spki',
 				format: 'pem',
-			}) as string,
+			}),
 		};
 	}
 
@@ -499,10 +499,7 @@ export class ApRendererService {
 			tag,
 			manuallyApprovesFollowers: user.isLocked,
 			discoverable: user.isExplorable,
-			publicKey: this.renderKey(user, keypair.publicKey, '#main-key'),
-			additionalPublicKeys: [
-				...(keypair.ed25519PublicKey ? [this.renderKey(user, keypair.ed25519PublicKey, '#ed25519-key')] : []),
-			],
+			publicKey: this.renderKey(user, keypair, '#main-key'),
 			isCat: user.isCat,
 			attachment: attachment.length ? attachment : undefined,
 		};
@@ -625,10 +622,12 @@ export class ApRendererService {
 	}
 
 	@bindThis
-	public async attachLdSignature(activity: any, key: PrivateKeyWithPem): Promise<IActivity> {
+	public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> {
+		const keypair = await this.userKeypairService.getUserKeypair(user.id);
+
 		const jsonLd = this.jsonLdService.use();
 		jsonLd.debug = false;
-		activity = await jsonLd.signRsaSignature2017(activity, key.privateKeyPem, key.keyId);
+		activity = await jsonLd.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`);
 
 		return activity;
 	}
diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts
index 0cae91316b..93ac8ce9a7 100644
--- a/packages/backend/src/core/activitypub/ApRequestService.ts
+++ b/packages/backend/src/core/activitypub/ApRequestService.ts
@@ -3,9 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import * as crypto from 'node:crypto';
 import { URL } from 'node:url';
 import { Inject, Injectable } from '@nestjs/common';
-import { genRFC3230DigestHeader, signAsDraftToRequest } from '@misskey-dev/node-http-message-signatures';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import type { MiUser } from '@/models/User.js';
@@ -15,61 +15,122 @@ import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
 import type Logger from '@/logger.js';
 import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
-import type { PrivateKeyWithPem, PrivateKey } from '@misskey-dev/node-http-message-signatures';
 
-export async function createSignedPost(args: { level: string; key: PrivateKey; url: string; body: string; digest?: string, additionalHeaders: Record<string, string> }) {
-	const u = new URL(args.url);
-	const request = {
-		url: u.href,
-		method: 'POST',
-		headers: {
-			'Date': new Date().toUTCString(),
-			'Host': u.host,
-			'Content-Type': 'application/activity+json',
-			...args.additionalHeaders,
-		} as Record<string, string>,
-	};
+type Request = {
+	url: string;
+	method: string;
+	headers: Record<string, string>;
+};
 
-	// TODO: httpMessageSignaturesImplementationLevelによって新規格で通信をするようにする
-	const digestHeader = args.digest ?? await genRFC3230DigestHeader(args.body, 'SHA-256');
-	request.headers['Digest'] = digestHeader;
+type Signed = {
+	request: Request;
+	signingString: string;
+	signature: string;
+	signatureHeader: string;
+};
 
-	const result = await signAsDraftToRequest(
-		request,
-		args.key,
-		['(request-target)', 'date', 'host', 'digest'],
-	);
+type PrivateKey = {
+	privateKeyPem: string;
+	keyId: string;
+};
 
-	return {
-		request,
-		...result,
-	};
-}
+export class ApRequestCreator {
+	static createSignedPost(args: { key: PrivateKey, url: string, body: string, digest?: string, additionalHeaders: Record<string, string> }): Signed {
+		const u = new URL(args.url);
+		const digestHeader = args.digest ?? this.createDigest(args.body);
 
-export async function createSignedGet(args: { level: string; key: PrivateKey; url: string; additionalHeaders: Record<string, string> }) {
-	const u = new URL(args.url);
-	const request = {
-		url: u.href,
-		method: 'GET',
-		headers: {
-			'Accept': 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
-			'Date': new Date().toUTCString(),
-			'Host': new URL(args.url).host,
-			...args.additionalHeaders,
-		} as Record<string, string>,
-	};
+		const request: Request = {
+			url: u.href,
+			method: 'POST',
+			headers: this.#objectAssignWithLcKey({
+				'Date': new Date().toUTCString(),
+				'Host': u.host,
+				'Content-Type': 'application/activity+json',
+				'Digest': digestHeader,
+			}, args.additionalHeaders),
+		};
 
-	// TODO: httpMessageSignaturesImplementationLevelによって新規格で通信をするようにする
-	const result = await signAsDraftToRequest(
-		request,
-		args.key,
-		['(request-target)', 'date', 'host', 'accept'],
-	);
+		const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']);
 
-	return {
-		request,
-		...result,
-	};
+		return {
+			request,
+			signingString: result.signingString,
+			signature: result.signature,
+			signatureHeader: result.signatureHeader,
+		};
+	}
+
+	static createDigest(body: string) {
+		return `SHA-256=${crypto.createHash('sha256').update(body).digest('base64')}`;
+	}
+
+	static createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }): Signed {
+		const u = new URL(args.url);
+
+		const request: Request = {
+			url: u.href,
+			method: 'GET',
+			headers: this.#objectAssignWithLcKey({
+				'Accept': 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
+				'Date': new Date().toUTCString(),
+				'Host': new URL(args.url).host,
+			}, args.additionalHeaders),
+		};
+
+		const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']);
+
+		return {
+			request,
+			signingString: result.signingString,
+			signature: result.signature,
+			signatureHeader: result.signatureHeader,
+		};
+	}
+
+	static #signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed {
+		const signingString = this.#genSigningString(request, includeHeaders);
+		const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64');
+		const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`;
+
+		request.headers = this.#objectAssignWithLcKey(request.headers, {
+			Signature: signatureHeader,
+		});
+		// node-fetch will generate this for us. if we keep 'Host', it won't change with redirects!
+		delete request.headers['host'];
+
+		return {
+			request,
+			signingString,
+			signature,
+			signatureHeader,
+		};
+	}
+
+	static #genSigningString(request: Request, includeHeaders: string[]): string {
+		request.headers = this.#lcObjectKey(request.headers);
+
+		const results: string[] = [];
+
+		for (const key of includeHeaders.map(x => x.toLowerCase())) {
+			if (key === '(request-target)') {
+				results.push(`(request-target): ${request.method.toLowerCase()} ${new URL(request.url).pathname}`);
+			} else {
+				results.push(`${key}: ${request.headers[key]}`);
+			}
+		}
+
+		return results.join('\n');
+	}
+
+	static #lcObjectKey(src: Record<string, string>): Record<string, string> {
+		const dst: Record<string, string> = {};
+		for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key];
+		return dst;
+	}
+
+	static #objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>): Record<string, string> {
+		return Object.assign(this.#lcObjectKey(a), this.#lcObjectKey(b));
+	}
 }
 
 @Injectable()
@@ -89,28 +150,21 @@ export class ApRequestService {
 	}
 
 	@bindThis
-	public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown, level: string, digest?: string, key?: PrivateKeyWithPem): Promise<void> {
+	public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown, digest?: string): Promise<void> {
 		const body = typeof object === 'string' ? object : JSON.stringify(object);
-		const keyFetched = await this.userKeypairService.getLocalUserPrivateKey(key ?? user.id, level);
-		const req = await createSignedPost({
-			level,
-			key: keyFetched,
+
+		const keypair = await this.userKeypairService.getUserKeypair(user.id);
+
+		const req = ApRequestCreator.createSignedPost({
+			key: {
+				privateKeyPem: keypair.privateKey,
+				keyId: `${this.config.url}/users/${user.id}#main-key`,
+			},
 			url,
 			body,
-			additionalHeaders: {
-				'User-Agent': this.config.userAgent,
-			},
 			digest,
-		});
-
-		// node-fetch will generate this for us. if we keep 'Host', it won't change with redirects!
-		delete req.request.headers['Host'];
-
-		this.logger.debug('create signed post', {
-			version: 'draft',
-			level,
-			url,
-			keyId: keyFetched.keyId,
+			additionalHeaders: {
+			},
 		});
 
 		await this.httpRequestService.send(url, {
@@ -126,27 +180,19 @@ export class ApRequestService {
 	 * @param url URL to fetch
 	 */
 	@bindThis
-	public async signedGet(url: string, user: { id: MiUser['id'] }, level: string): Promise<unknown> {
-		const key = await this.userKeypairService.getLocalUserPrivateKey(user.id, level);
-		const req = await createSignedGet({
-			level,
-			key,
+	public async signedGet(url: string, user: { id: MiUser['id'] }): Promise<unknown> {
+		const keypair = await this.userKeypairService.getUserKeypair(user.id);
+
+		const req = ApRequestCreator.createSignedGet({
+			key: {
+				privateKeyPem: keypair.privateKey,
+				keyId: `${this.config.url}/users/${user.id}#main-key`,
+			},
 			url,
 			additionalHeaders: {
-				'User-Agent': this.config.userAgent,
 			},
 		});
 
-		// node-fetch will generate this for us. if we keep 'Host', it won't change with redirects!
-		delete req.request.headers['Host'];
-
-		this.logger.debug('create signed get', {
-			version: 'draft',
-			level,
-			url,
-			keyId: key.keyId,
-		});
-
 		const res = await this.httpRequestService.send(url, {
 			method: req.request.method,
 			headers: req.request.headers,
diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts
index 727ff6f956..bb3c40f093 100644
--- a/packages/backend/src/core/activitypub/ApResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApResolverService.ts
@@ -16,7 +16,6 @@ import { UtilityService } from '@/core/UtilityService.js';
 import { bindThis } from '@/decorators.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import type Logger from '@/logger.js';
-import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 import { isCollectionOrOrderedCollection } from './type.js';
 import { ApDbResolverService } from './ApDbResolverService.js';
 import { ApRendererService } from './ApRendererService.js';
@@ -42,7 +41,6 @@ export class Resolver {
 		private httpRequestService: HttpRequestService,
 		private apRendererService: ApRendererService,
 		private apDbResolverService: ApDbResolverService,
-		private federatedInstanceService: FederatedInstanceService,
 		private loggerService: LoggerService,
 		private recursionLimit = 100,
 	) {
@@ -105,10 +103,8 @@ export class Resolver {
 			this.user = await this.instanceActorService.getInstanceActor();
 		}
 
-		const server = await this.federatedInstanceService.fetch(host);
-
 		const object = (this.user
-			? await this.apRequestService.signedGet(value, this.user, server.httpMessageSignaturesImplementationLevel) as IObject
+			? await this.apRequestService.signedGet(value, this.user) as IObject
 			: await this.httpRequestService.getActivityJson(value)) as IObject;
 
 		if (
@@ -204,7 +200,6 @@ export class ApResolverService {
 		private httpRequestService: HttpRequestService,
 		private apRendererService: ApRendererService,
 		private apDbResolverService: ApDbResolverService,
-		private federatedInstanceService: FederatedInstanceService,
 		private loggerService: LoggerService,
 	) {
 	}
@@ -225,7 +220,6 @@ export class ApResolverService {
 			this.httpRequestService,
 			this.apRendererService,
 			this.apDbResolverService,
-			this.federatedInstanceService,
 			this.loggerService,
 		);
 	}
diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts
index fc4e3e3bef..feb8c42c56 100644
--- a/packages/backend/src/core/activitypub/misc/contexts.ts
+++ b/packages/backend/src/core/activitypub/misc/contexts.ts
@@ -134,7 +134,6 @@ const security_v1 = {
 		'privateKey': { '@id': 'sec:privateKey', '@type': '@id' },
 		'privateKeyPem': 'sec:privateKeyPem',
 		'publicKey': { '@id': 'sec:publicKey', '@type': '@id' },
-		'additionalPublicKeys': { '@id': 'sec:publicKey', '@type': '@id' },
 		'publicKeyBase58': 'sec:publicKeyBase58',
 		'publicKeyPem': 'sec:publicKeyPem',
 		'publicKeyWif': 'sec:publicKeyWif',
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index c41fc713d5..457205e023 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -3,10 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { verify } from 'crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import promiseLimit from 'promise-limit';
-import { DataSource, In, Not } from 'typeorm';
+import { DataSource } from 'typeorm';
 import { ModuleRef } from '@nestjs/core';
 import { DI } from '@/di-symbols.js';
 import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
@@ -40,7 +39,6 @@ import { MetaService } from '@/core/MetaService.js';
 import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
 import type { AccountMoveService } from '@/core/AccountMoveService.js';
 import { checkHttps } from '@/misc/check-https.js';
-import { REMOTE_USER_CACHE_TTL, REMOTE_USER_MOVE_COOLDOWN } from '@/const.js';
 import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js';
 import { extractApHashtags } from './tag.js';
 import type { OnModuleInit } from '@nestjs/common';
@@ -50,7 +48,7 @@ import type { ApResolverService, Resolver } from '../ApResolverService.js';
 import type { ApLoggerService } from '../ApLoggerService.js';
 // eslint-disable-next-line @typescript-eslint/consistent-type-imports
 import type { ApImageService } from './ApImageService.js';
-import type { IActor, IKey, IObject } from '../type.js';
+import type { IActor, IObject } from '../type.js';
 
 const nameLength = 128;
 const summaryLength = 2048;
@@ -187,38 +185,13 @@ export class ApPersonService implements OnModuleInit {
 		}
 
 		if (x.publicKey) {
-			const publicKeys = Array.isArray(x.publicKey) ? x.publicKey : [x.publicKey];
-
-			for (const publicKey of publicKeys) {
-				if (typeof publicKey.id !== 'string') {
-					throw new Error('invalid Actor: publicKey.id is not a string');
-				}
-
-				const publicKeyIdHost = this.punyHost(publicKey.id);
-				if (publicKeyIdHost !== expectHost) {
-					throw new Error('invalid Actor: publicKey.id has different host');
-				}
-			}
-		}
-
-		if (x.additionalPublicKeys) {
-			if (!x.publicKey) {
-				throw new Error('invalid Actor: additionalPublicKeys is set but publicKey is not');
+			if (typeof x.publicKey.id !== 'string') {
+				throw new Error('invalid Actor: publicKey.id is not a string');
 			}
 
-			if (!Array.isArray(x.additionalPublicKeys)) {
-				throw new Error('invalid Actor: additionalPublicKeys is not an array');
-			}
-
-			for (const key of x.additionalPublicKeys) {
-				if (typeof key.id !== 'string') {
-					throw new Error('invalid Actor: additionalPublicKeys.id is not a string');
-				}
-
-				const keyIdHost = this.punyHost(key.id);
-				if (keyIdHost !== expectHost) {
-					throw new Error('invalid Actor: additionalPublicKeys.id has different host');
-				}
+			const publicKeyIdHost = this.punyHost(x.publicKey.id);
+			if (publicKeyIdHost !== expectHost) {
+				throw new Error('invalid Actor: publicKey.id has different host');
 			}
 		}
 
@@ -255,33 +228,6 @@ export class ApPersonService implements OnModuleInit {
 		return null;
 	}
 
-	/**
-	 * uriからUser(Person)をフェッチします。
-	 *
-	 * Misskeyに対象のPersonが登録されていればそれを返し、登録がなければnullを返します。
-	 * また、TTLが0でない場合、TTLを過ぎていた場合はupdatePersonを実行します。
-	 */
-	@bindThis
-	async fetchPersonWithRenewal(uri: string, TTL = REMOTE_USER_CACHE_TTL): Promise<MiLocalUser | MiRemoteUser | null> {
-		const exist = await this.fetchPerson(uri);
-		if (exist == null) return null;
-
-		if (this.userEntityService.isRemoteUser(exist)) {
-			if (TTL === 0 || exist.lastFetchedAt == null || Date.now() - exist.lastFetchedAt.getTime() > TTL) {
-				this.logger.debug('fetchPersonWithRenewal: renew', { uri, TTL, lastFetchedAt: exist.lastFetchedAt });
-				try {
-					await this.updatePerson(exist.uri);
-					return await this.fetchPerson(uri);
-				} catch (err) {
-					this.logger.error('error occurred while renewing user', { err });
-				}
-			}
-			this.logger.debug('fetchPersonWithRenewal: use cache', { uri, TTL, lastFetchedAt: exist.lastFetchedAt });
-		}
-
-		return exist;
-	}
-
 	private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Partial<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>>> {
 		if (user == null) throw new Error('failed to create user: user is null');
 
@@ -417,15 +363,11 @@ export class ApPersonService implements OnModuleInit {
 				}));
 
 				if (person.publicKey) {
-					const publicKeys = new Map<string, IKey>();
-					(person.additionalPublicKeys ?? []).forEach(key => publicKeys.set(key.id, key));
-					(Array.isArray(person.publicKey) ? person.publicKey : [person.publicKey]).forEach(key => publicKeys.set(key.id, key));
-
-					await transactionalEntityManager.save(Array.from(publicKeys.values(), key => new MiUserPublickey({
-						keyId: key.id,
-						userId: user!.id,
-						keyPem: key.publicKeyPem,
-					})));
+					await transactionalEntityManager.save(new MiUserPublickey({
+						userId: user.id,
+						keyId: person.publicKey.id,
+						keyPem: person.publicKey.publicKeyPem,
+					}));
 				}
 			});
 		} catch (e) {
@@ -571,29 +513,11 @@ export class ApPersonService implements OnModuleInit {
 		// Update user
 		await this.usersRepository.update(exist.id, updates);
 
-		try {
-			// Deleteアクティビティ受信時にもここが走ってsaveがuserforeign key制約エラーを吐くことがある
-			// とりあえずtry-catchで囲っておく
-			const publicKeys = new Map<string, IKey>();
-			if (person.publicKey) {
-				(person.additionalPublicKeys ?? []).forEach(key => publicKeys.set(key.id, key));
-				(Array.isArray(person.publicKey) ? person.publicKey : [person.publicKey]).forEach(key => publicKeys.set(key.id, key));
-
-				await this.userPublickeysRepository.save(Array.from(publicKeys.values(), key => ({
-					keyId: key.id,
-					userId: exist.id,
-					keyPem: key.publicKeyPem,
-				})));
-			}
-
-			this.userPublickeysRepository.delete({
-				keyId: Not(In(Array.from(publicKeys.keys()))),
-				userId: exist.id,
-			}).catch(err => {
-				this.logger.error('something happened while deleting remote user public keys:', { userId: exist.id, err });
+		if (person.publicKey) {
+			await this.userPublickeysRepository.update({ userId: exist.id }, {
+				keyId: person.publicKey.id,
+				keyPem: person.publicKey.publicKeyPem,
 			});
-		} catch (err) {
-			this.logger.error('something happened while updating remote user public keys:', { userId: exist.id, err });
 		}
 
 		let _description: string | null = null;
@@ -635,7 +559,7 @@ export class ApPersonService implements OnModuleInit {
 			exist.movedAt == null ||
 			// 以前のmovingから14日以上経過した場合のみ移行処理を許可
 			// (Mastodonのクールダウン期間は30日だが若干緩めに設定しておく)
-			exist.movedAt.getTime() + REMOTE_USER_MOVE_COOLDOWN < updated.movedAt.getTime()
+			exist.movedAt.getTime() + 1000 * 60 * 60 * 24 * 14 < updated.movedAt.getTime()
 		)) {
 			this.logger.info(`Start to process Move of @${updated.username}@${updated.host} (${uri})`);
 			return this.processRemoteMove(updated, movePreventUris)
@@ -658,9 +582,9 @@ export class ApPersonService implements OnModuleInit {
 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 	 */
 	@bindThis
-	public async resolvePerson(uri: string, resolver?: Resolver, withRenewal = false): Promise<MiLocalUser | MiRemoteUser> {
+	public async resolvePerson(uri: string, resolver?: Resolver): Promise<MiLocalUser | MiRemoteUser> {
 		//#region このサーバーに既に登録されていたらそれを返す
-		const exist = withRenewal ? await this.fetchPersonWithRenewal(uri) : await this.fetchPerson(uri);
+		const exist = await this.fetchPerson(uri);
 		if (exist) return exist;
 		//#endregion
 
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index 1d55971660..5b6c6c8ca6 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -55,7 +55,7 @@ export function getOneApId(value: ApObject): string {
 export function getApId(value: string | IObject): string {
 	if (typeof value === 'string') return value;
 	if (typeof value.id === 'string') return value.id;
-	throw new Error('cannot determine id');
+	throw new Error('cannot detemine id');
 }
 
 /**
@@ -169,8 +169,10 @@ export interface IActor extends IObject {
 	discoverable?: boolean;
 	inbox: string;
 	sharedInbox?: string;	// 後方互換性のため
-	publicKey?: IKey | IKey[];
-	additionalPublicKeys?: IKey[];
+	publicKey?: {
+		id: string;
+		publicKeyPem: string;
+	};
 	followers?: string | ICollection | IOrderedCollection;
 	following?: string | ICollection | IOrderedCollection;
 	featured?: string | IOrderedCollection;
@@ -234,9 +236,8 @@ export const isEmoji = (object: IObject): object is IApEmoji =>
 
 export interface IKey extends IObject {
 	type: 'Key';
-	id: string;
 	owner: string;
-	publicKeyPem: string;
+	publicKeyPem: string | Buffer;
 }
 
 export interface IApDocument extends IObject {
diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts
index fd0f55c6ab..9117b13914 100644
--- a/packages/backend/src/core/entities/InstanceEntityService.ts
+++ b/packages/backend/src/core/entities/InstanceEntityService.ts
@@ -56,7 +56,6 @@ export class InstanceEntityService {
 			infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
 			latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null,
 			moderationNote: iAmModerator ? instance.moderationNote : null,
-			httpMessageSignaturesImplementationLevel: instance.httpMessageSignaturesImplementationLevel,
 		};
 	}
 
diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts
index f498c110bf..bba64a06ef 100644
--- a/packages/backend/src/misc/cache.ts
+++ b/packages/backend/src/misc/cache.ts
@@ -195,9 +195,6 @@ export class MemoryKVCache<T> {
 	private lifetime: number;
 	private gcIntervalHandle: NodeJS.Timeout;
 
-	/**
-	 * @param lifetime キャッシュの生存期間 (ms)
-	 */
 	constructor(lifetime: MemoryKVCache<never>['lifetime']) {
 		this.cache = new Map();
 		this.lifetime = lifetime;
diff --git a/packages/backend/src/misc/gen-key-pair.ts b/packages/backend/src/misc/gen-key-pair.ts
index 0b033ec33e..02a303dc0a 100644
--- a/packages/backend/src/misc/gen-key-pair.ts
+++ b/packages/backend/src/misc/gen-key-pair.ts
@@ -3,14 +3,39 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { genEd25519KeyPair, genRsaKeyPair } from '@misskey-dev/node-http-message-signatures';
+import * as crypto from 'node:crypto';
+import * as util from 'node:util';
 
-export async function genRSAAndEd25519KeyPair(rsaModulusLength = 4096) {
-	const [rsa, ed25519] = await Promise.all([genRsaKeyPair(rsaModulusLength), genEd25519KeyPair()]);
-	return {
-		publicKey: rsa.publicKey,
-		privateKey: rsa.privateKey,
-		ed25519PublicKey: ed25519.publicKey,
-		ed25519PrivateKey: ed25519.privateKey,
-	};
+const generateKeyPair = util.promisify(crypto.generateKeyPair);
+
+export async function genRsaKeyPair(modulusLength = 2048) {
+	return await generateKeyPair('rsa', {
+		modulusLength,
+		publicKeyEncoding: {
+			type: 'spki',
+			format: 'pem',
+		},
+		privateKeyEncoding: {
+			type: 'pkcs8',
+			format: 'pem',
+			cipher: undefined,
+			passphrase: undefined,
+		},
+	});
+}
+
+export async function genEcKeyPair(namedCurve: 'prime256v1' | 'secp384r1' | 'secp521r1' | 'curve25519' = 'prime256v1') {
+	return await generateKeyPair('ec', {
+		namedCurve,
+		publicKeyEncoding: {
+			type: 'spki',
+			format: 'pem',
+		},
+		privateKeyEncoding: {
+			type: 'pkcs8',
+			format: 'pem',
+			cipher: undefined,
+			passphrase: undefined,
+		},
+	});
 }
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index f2f2831cf1..17cd5c6665 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -158,9 +158,4 @@ export class MiInstance {
 		length: 16384, default: '',
 	})
 	public moderationNote: string;
-
-	@Column('varchar', {
-		length: 16, default: '00', nullable: false,
-	})
-	public httpMessageSignaturesImplementationLevel: string;
 }
diff --git a/packages/backend/src/models/UserKeypair.ts b/packages/backend/src/models/UserKeypair.ts
index afa74ef11a..f5252d126c 100644
--- a/packages/backend/src/models/UserKeypair.ts
+++ b/packages/backend/src/models/UserKeypair.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne } from 'typeorm';
+import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm';
 import { id } from './util/id.js';
 import { MiUser } from './User.js';
 
@@ -12,42 +12,22 @@ export class MiUserKeypair {
 	@PrimaryColumn(id())
 	public userId: MiUser['id'];
 
-	@ManyToOne(type => MiUser, {
+	@OneToOne(type => MiUser, {
 		onDelete: 'CASCADE',
 	})
 	@JoinColumn()
 	public user: MiUser | null;
 
-	/**
-	 * RSA public key
-	 */
 	@Column('varchar', {
 		length: 4096,
 	})
 	public publicKey: string;
 
-	/**
-	 * RSA private key
-	 */
 	@Column('varchar', {
 		length: 4096,
 	})
 	public privateKey: string;
 
-	@Column('varchar', {
-		length: 128,
-		nullable: true,
-		default: null,
-	})
-	public ed25519PublicKey: string | null;
-
-	@Column('varchar', {
-		length: 128,
-		nullable: true,
-		default: null,
-	})
-	public ed25519PrivateKey: string | null;
-
 	constructor(data: Partial<MiUserKeypair>) {
 		if (data == null) return;
 
diff --git a/packages/backend/src/models/UserPublickey.ts b/packages/backend/src/models/UserPublickey.ts
index 0ecff2bcbe..6bcd785304 100644
--- a/packages/backend/src/models/UserPublickey.ts
+++ b/packages/backend/src/models/UserPublickey.ts
@@ -9,13 +9,7 @@ import { MiUser } from './User.js';
 
 @Entity('user_publickey')
 export class MiUserPublickey {
-	@PrimaryColumn('varchar', {
-		length: 256,
-	})
-	public keyId: string;
-
-	@Index()
-	@Column(id())
+	@PrimaryColumn(id())
 	public userId: MiUser['id'];
 
 	@OneToOne(type => MiUser, {
@@ -24,6 +18,12 @@ export class MiUserPublickey {
 	@JoinColumn()
 	public user: MiUser | null;
 
+	@Index({ unique: true })
+	@Column('varchar', {
+		length: 256,
+	})
+	public keyId: string;
+
 	@Column('varchar', {
 		length: 4096,
 	})
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index c02e7f557a..ed40d405c6 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -116,9 +116,5 @@ export const packedFederationInstanceSchema = {
 			type: 'string',
 			optional: true, nullable: true,
 		},
-		httpMessageSignaturesImplementationLevel: {
-			type: 'string',
-			optional: false, nullable: false,
-		},
 	},
 } as const;
diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index 169b22c3f5..7bd74f3210 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -250,9 +250,9 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			}, {
 				...baseQueueOptions(this.config, QUEUE.DELIVER),
 				autorun: false,
-				concurrency: this.config.deliverJobConcurrency ?? 16,
+				concurrency: this.config.deliverJobConcurrency ?? 128,
 				limiter: {
-					max: this.config.deliverJobPerSec ?? 1024,
+					max: this.config.deliverJobPerSec ?? 128,
 					duration: 1000,
 				},
 				settings: {
@@ -290,9 +290,9 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			}, {
 				...baseQueueOptions(this.config, QUEUE.INBOX),
 				autorun: false,
-				concurrency: this.config.inboxJobConcurrency ?? 4,
+				concurrency: this.config.inboxJobConcurrency ?? 16,
 				limiter: {
-					max: this.config.inboxJobPerSec ?? 64,
+					max: this.config.inboxJobPerSec ?? 32,
 					duration: 1000,
 				},
 				settings: {
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index 3bd9187e8b..d665945861 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -73,33 +73,25 @@ export class DeliverProcessorService {
 		}
 
 		try {
-			const _server = await this.federatedInstanceService.fetch(host);
-			await this.fetchInstanceMetadataService.fetchInstanceMetadata(_server).then(() => {});
-			const server = await this.federatedInstanceService.fetch(host);
-
-			await this.apRequestService.signedPost(
-				job.data.user,
-				job.data.to,
-				job.data.content,
-				server.httpMessageSignaturesImplementationLevel,
-				job.data.digest,
-				job.data.privateKey,
-			);
+			await this.apRequestService.signedPost(job.data.user, job.data.to, job.data.content, job.data.digest);
 
 			// Update stats
-			if (server.isNotResponding) {
-				this.federatedInstanceService.update(server.id, {
-					isNotResponding: false,
-					notRespondingSince: null,
-				});
-			}
+			this.federatedInstanceService.fetch(host).then(i => {
+				if (i.isNotResponding) {
+					this.federatedInstanceService.update(i.id, {
+						isNotResponding: false,
+						notRespondingSince: null,
+					});
+				}
 
-			this.apRequestChart.deliverSucc();
-			this.federationChart.deliverd(server.host, true);
+				this.fetchInstanceMetadataService.fetchInstanceMetadata(i);
+				this.apRequestChart.deliverSucc();
+				this.federationChart.deliverd(i.host, true);
 
-			if (meta.enableChartsForFederatedInstances) {
-				this.instanceChart.requestSent(server.host, true);
-			}
+				if (meta.enableChartsForFederatedInstances) {
+					this.instanceChart.requestSent(i.host, true);
+				}
+			});
 
 			return 'Success';
 		} catch (res) {
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 935c623df1..fa7009f8f5 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -5,8 +5,8 @@
 
 import { URL } from 'node:url';
 import { Injectable } from '@nestjs/common';
+import httpSignature from '@peertube/http-signature';
 import * as Bull from 'bullmq';
-import { verifyDraftSignature } from '@misskey-dev/node-http-message-signatures';
 import type Logger from '@/logger.js';
 import { MetaService } from '@/core/MetaService.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
@@ -20,7 +20,6 @@ import type { MiRemoteUser } from '@/models/User.js';
 import type { MiUserPublickey } from '@/models/UserPublickey.js';
 import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
 import { StatusError } from '@/misc/status-error.js';
-import * as Acct from '@/misc/acct.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
 import { JsonLdService } from '@/core/activitypub/JsonLdService.js';
@@ -53,15 +52,8 @@ export class InboxProcessorService {
 
 	@bindThis
 	public async process(job: Bull.Job<InboxJobData>): Promise<string> {
-		const signature = job.data.signature ?
-			'version' in job.data.signature ? job.data.signature.value : job.data.signature
-			: null;
-		if (Array.isArray(signature)) {
-			// RFC 9401はsignatureが配列になるが、とりあえずエラーにする
-			throw new Error('signature is array');
-		}
+		const signature = job.data.signature;	// HTTP-signature
 		let activity = job.data.activity;
-		let actorUri = getApId(activity.actor);
 
 		//#region Log
 		const info = Object.assign({}, activity);
@@ -69,7 +61,7 @@ export class InboxProcessorService {
 		this.logger.debug(JSON.stringify(info, null, 2));
 		//#endregion
 
-		const host = this.utilityService.toPuny(new URL(actorUri).hostname);
+		const host = this.utilityService.toPuny(new URL(signature.keyId).hostname);
 
 		// ブロックしてたら中断
 		const meta = await this.metaService.fetch();
@@ -77,76 +69,69 @@ export class InboxProcessorService {
 			return `Blocked request: ${host}`;
 		}
 
-		// HTTP-Signature keyIdを元にDBから取得
-		let authUser: Awaited<ReturnType<typeof this.apDbResolverService.getAuthUserFromApId>> = null;
-		let httpSignatureIsValid = null as boolean | null;
+		const keyIdLower = signature.keyId.toLowerCase();
+		if (keyIdLower.startsWith('acct:')) {
+			return `Old keyId is no longer supported. ${keyIdLower}`;
+		}
 
-		try {
-			authUser = await this.apDbResolverService.getAuthUserFromApId(actorUri, signature?.keyId);
-		} catch (err) {
-			// 対象が4xxならスキップ
-			if (err instanceof StatusError) {
-				if (!err.isRetryable) {
-					throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
+		// HTTP-Signature keyIdを元にDBから取得
+		let authUser: {
+			user: MiRemoteUser;
+			key: MiUserPublickey | null;
+		} | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
+
+		// keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得
+		if (authUser == null) {
+			try {
+				authUser = await this.apDbResolverService.getAuthUserFromApId(getApId(activity.actor));
+			} catch (err) {
+				// 対象が4xxならスキップ
+				if (err instanceof StatusError) {
+					if (!err.isRetryable) {
+						throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
+					}
+					throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
 				}
-				throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
 			}
 		}
 
-		// authUser.userがnullならスキップ
-		if (authUser != null && authUser.user == null) {
+		// それでもわからなければ終了
+		if (authUser == null) {
 			throw new Bull.UnrecoverableError('skip: failed to resolve user');
 		}
 
-		if (signature != null && authUser != null) {
-			if (signature.keyId.toLowerCase().startsWith('acct:')) {
-				this.logger.warn(`Old keyId is no longer supported. lowerKeyId=${signature.keyId.toLowerCase()}`);
-			} else if (authUser.key != null) {
-				// keyがなかったらLD Signatureで検証するべき
-				// HTTP-Signatureの検証
-				const errorLogger = (ms: any) => this.logger.error(ms);
-				httpSignatureIsValid = await verifyDraftSignature(signature, authUser.key.keyPem, errorLogger);
-				this.logger.debug('Inbox message validation: ', {
-					userId: authUser.user.id,
-					userAcct: Acct.toString(authUser.user),
-					parsedKeyId: signature.keyId,
-					foundKeyId: authUser.key.keyId,
-					httpSignatureValid: httpSignatureIsValid,
-				});
-			}
+		// publicKey がなくても終了
+		if (authUser.key == null) {
+			throw new Bull.UnrecoverableError('skip: failed to resolve user publicKey');
 		}
 
-		if (
-			authUser == null ||
-			httpSignatureIsValid !== true ||
-			authUser.user.uri !== actorUri // 一応チェック
-		) {
+		// HTTP-Signatureの検証
+		const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
+
+		// また、signatureのsignerは、activity.actorと一致する必要がある
+		if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
 			// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
 			const ldSignature = activity.signature;
-
-			if (ldSignature && ldSignature.creator) {
+			if (ldSignature) {
 				if (ldSignature.type !== 'RsaSignature2017') {
 					throw new Bull.UnrecoverableError(`skip: unsupported LD-signature type ${ldSignature.type}`);
 				}
 
-				if (ldSignature.creator.toLowerCase().startsWith('acct:')) {
-					throw new Bull.UnrecoverableError(`old key not supported ${ldSignature.creator}`);
+				// ldSignature.creator: https://example.oom/users/user#main-key
+				// みたいになっててUserを引っ張れば公開キーも入ることを期待する
+				if (ldSignature.creator) {
+					const candicate = ldSignature.creator.replace(/#.*/, '');
+					await this.apPersonService.resolvePerson(candicate).catch(() => null);
 				}
 
-				authUser = await this.apDbResolverService.getAuthUserFromApId(actorUri, ldSignature.creator);
-
+				// keyIdからLD-Signatureのユーザーを取得
+				authUser = await this.apDbResolverService.getAuthUserFromKeyId(ldSignature.creator);
 				if (authUser == null) {
-					throw new Bull.UnrecoverableError(`skip: LD-Signatureのactorとcreatorが一致しませんでした uri=${actorUri} creator=${ldSignature.creator}`);
-				}
-				if (authUser.user == null) {
-					throw new Bull.UnrecoverableError(`skip: LD-Signatureのユーザーが取得できませんでした uri=${actorUri} creator=${ldSignature.creator}`);
-				}
-				// 一応actorチェック
-				if (authUser.user.uri !== actorUri) {
-					throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${actorUri})`);
+					throw new Bull.UnrecoverableError('skip: LD-Signatureのユーザーが取得できませんでした');
 				}
+
 				if (authUser.key == null) {
-					throw new Bull.UnrecoverableError(`skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした uri=${actorUri} creator=${ldSignature.creator}`);
+					throw new Bull.UnrecoverableError('skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした');
 				}
 
 				const jsonLd = this.jsonLdService.use();
@@ -157,27 +142,13 @@ export class InboxProcessorService {
 					throw new Bull.UnrecoverableError('skip: LD-Signatureの検証に失敗しました');
 				}
 
-				// ブロックしてたら中断
-				const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
-				if (this.utilityService.isBlockedHost(meta.blockedHosts, ldHost)) {
-					throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`);
-				}
-
 				// アクティビティを正規化
-				// GHSA-2vxv-pv3m-3wvj
 				delete activity.signature;
 				try {
 					activity = await jsonLd.compact(activity) as IActivity;
 				} catch (e) {
 					throw new Bull.UnrecoverableError(`skip: failed to compact activity: ${e}`);
 				}
-
-				// actorが正規化前後で一致しているか確認
-				actorUri = getApId(activity.actor);
-				if (authUser.user.uri !== actorUri) {
-					throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity(after normalization).actor(${actorUri})`);
-				}
-
 				// TODO: 元のアクティビティと非互換な形に正規化される場合は転送をスキップする
 				// https://github.com/mastodon/mastodon/blob/664b0ca/app/services/activitypub/process_collection_service.rb#L24-L29
 				activity.signature = ldSignature;
@@ -187,8 +158,19 @@ export class InboxProcessorService {
 				delete compactedInfo['@context'];
 				this.logger.debug(`compacted: ${JSON.stringify(compactedInfo, null, 2)}`);
 				//#endregion
+
+				// もう一度actorチェック
+				if (authUser.user.uri !== activity.actor) {
+					throw new Bull.UnrecoverableError(`skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`);
+				}
+
+				// ブロックしてたら中断
+				const ldHost = this.utilityService.extractDbHost(authUser.user.uri);
+				if (this.utilityService.isBlockedHost(meta.blockedHosts, ldHost)) {
+					throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`);
+				}
 			} else {
-				throw new Bull.UnrecoverableError(`skip: http-signature verification failed and no LD-Signature. http_signature_keyId=${signature?.keyId}`);
+				throw new Bull.UnrecoverableError(`skip: http-signature verification failed and no LD-Signature. keyId=${signature.keyId}`);
 			}
 		}
 
diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts
index f2466f2e3d..a4077a0547 100644
--- a/packages/backend/src/queue/types.ts
+++ b/packages/backend/src/queue/types.ts
@@ -9,24 +9,7 @@ import type { MiNote } from '@/models/Note.js';
 import type { MiUser } from '@/models/User.js';
 import type { MiWebhook } from '@/models/Webhook.js';
 import type { IActivity } from '@/core/activitypub/type.js';
-import type { ParsedSignature, PrivateKeyWithPem } from '@misskey-dev/node-http-message-signatures';
-
-/**
- * @peertube/http-signature 時代の古いデータにも対応しておく
- * TODO: 2026年ぐらいには消す
- */
-export interface OldParsedSignature {
-	scheme: 'Signature';
-	params: {
-		keyId: string;
-		algorithm: string;
-		headers: string[];
-		signature: string;
-	};
-	signingString: string;
-	algorithm: string;
-	keyId: string;
-}
+import type httpSignature from '@peertube/http-signature';
 
 export type DeliverJobData = {
 	/** Actor */
@@ -39,13 +22,11 @@ export type DeliverJobData = {
 	to: string;
 	/** whether it is sharedInbox */
 	isSharedInbox: boolean;
-	/** force to use main (rsa) key */
-	privateKey?: PrivateKeyWithPem;
 };
 
 export type InboxJobData = {
 	activity: IActivity;
-	signature: ParsedSignature | OldParsedSignature | null;
+	signature: httpSignature.IParsedSignature;
 };
 
 export type RelationshipJobData = {
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 753eaad047..3255d64621 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -3,10 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import * as crypto from 'node:crypto';
 import { IncomingMessage } from 'node:http';
 import { Inject, Injectable } from '@nestjs/common';
 import fastifyAccepts from '@fastify/accepts';
-import { verifyDigestHeader, parseRequestSignature } from '@misskey-dev/node-http-message-signatures';
+import httpSignature from '@peertube/http-signature';
 import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
 import accepts from 'accepts';
 import vary from 'vary';
@@ -30,17 +31,12 @@ import { IActivity } from '@/core/activitypub/type.js';
 import { isQuote, isRenote } from '@/misc/is-renote.js';
 import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
 import type { FindOptionsWhere } from 'typeorm';
-import { LoggerService } from '@/core/LoggerService.js';
-import Logger from '@/logger.js';
 
 const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
 const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8';
 
 @Injectable()
 export class ActivityPubServerService {
-	private logger: Logger;
-	private inboxLogger: Logger;
-
 	constructor(
 		@Inject(DI.config)
 		private config: Config,
@@ -75,11 +71,8 @@ export class ActivityPubServerService {
 		private queueService: QueueService,
 		private userKeypairService: UserKeypairService,
 		private queryService: QueryService,
-		private loggerService: LoggerService,
 	) {
 		//this.createServer = this.createServer.bind(this);
-		this.logger = this.loggerService.getLogger('server-ap', 'gray');
-		this.inboxLogger = this.logger.createSubLogger('inbox', 'gray');
 	}
 
 	@bindThis
@@ -107,44 +100,70 @@ export class ActivityPubServerService {
 	}
 
 	@bindThis
-	private async inbox(request: FastifyRequest, reply: FastifyReply) {
-		if (request.body == null) {
-			this.inboxLogger.warn('request body is empty');
-			reply.code(400);
-			return;
-		}
+	private inbox(request: FastifyRequest, reply: FastifyReply) {
+		let signature;
 
-		let signature: ReturnType<typeof parseRequestSignature>;
-
-		const verifyDigest = await verifyDigestHeader(request.raw, request.rawBody || '', true);
-		if (verifyDigest !== true) {
-			this.inboxLogger.warn('digest verification failed');
+		try {
+			signature = httpSignature.parseRequest(request.raw, { 'headers': [] });
+		} catch (e) {
 			reply.code(401);
 			return;
 		}
 
-		try {
-			signature = parseRequestSignature(request.raw, {
-				requiredInputs: {
-					draft: ['(request-target)', 'digest', 'host', 'date'],
-				},
-			});
-		} catch (err) {
-			this.inboxLogger.warn('signature header parsing failed', { err });
+		if (signature.params.headers.indexOf('host') === -1
+			|| request.headers.host !== this.config.host) {
+			// Host not specified or not match.
+			reply.code(401);
+			return;
+		}
 
-			if (typeof request.body === 'object' && 'signature' in request.body) {
-				// LD SignatureがあればOK
-				this.queueService.inbox(request.body as IActivity, null);
-				reply.code(202);
+		if (signature.params.headers.indexOf('digest') === -1) {
+			// Digest not found.
+			reply.code(401);
+		} else {
+			const digest = request.headers.digest;
+
+			if (typeof digest !== 'string') {
+				// Huh?
+				reply.code(401);
 				return;
 			}
 
-			this.inboxLogger.warn('signature header parsing failed and LD signature not found');
-			reply.code(401);
-			return;
+			const re = /^([a-zA-Z0-9\-]+)=(.+)$/;
+			const match = digest.match(re);
+
+			if (match == null) {
+				// Invalid digest
+				reply.code(401);
+				return;
+			}
+
+			const algo = match[1].toUpperCase();
+			const digestValue = match[2];
+
+			if (algo !== 'SHA-256') {
+				// Unsupported digest algorithm
+				reply.code(401);
+				return;
+			}
+
+			if (request.rawBody == null) {
+				// Bad request
+				reply.code(400);
+				return;
+			}
+
+			const hash = crypto.createHash('sha256').update(request.rawBody).digest('base64');
+
+			if (hash !== digestValue) {
+				// Invalid digest
+				reply.code(401);
+				return;
+			}
 		}
 
 		this.queueService.inbox(request.body as IActivity, signature);
+
 		reply.code(202);
 	}
 
@@ -621,7 +640,7 @@ export class ActivityPubServerService {
 			if (this.userEntityService.isLocalUser(user)) {
 				reply.header('Cache-Control', 'public, max-age=180');
 				this.setResponseType(request, reply);
-				return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair.publicKey)));
+				return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair)));
 			} else {
 				reply.code(400);
 				return;
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index c0f8084768..cc18997fdc 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -94,13 +94,6 @@ export class NodeinfoServerService {
 					localComments: 0,
 				},
 				metadata: {
-					/**
-					 * '00': Draft, RSA only
-					 * '01': Draft, Ed25519 suported
-					 * '11': RFC 9421, Ed25519 supported
-					 */
-					httpMessageSignaturesImplementationLevel: '01',
-
 					nodeName: meta.name,
 					nodeDescription: meta.description,
 					nodeAdmins: [{
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
index bfe230da8d..305ae1af1d 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
@@ -56,8 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const res = [] as [string, number][];
 
 			for (const job of jobs) {
-				const signature = job.data.signature ? 'version' in job.data.signature ? job.data.signature.value : job.data.signature : null;
-				const host = signature ? Array.isArray(signature) ? 'TODO' : new URL(signature.keyId).host : new URL(job.data.activity.actor).host;
+				const host = new URL(job.data.signature.keyId).host;
 				if (res.find(x => x[0] === host)) {
 					res.find(x => x[0] === host)![1]++;
 				} else {
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index fce1eacf00..540b866b28 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -378,7 +378,7 @@ describe('Timelines', () => {
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
 			assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
-		});
+		}, 1000 * 10);
 
 		test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
@@ -672,7 +672,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		});
+		}, 1000 * 10);
 	});
 
 	describe('Social TL', () => {
@@ -812,7 +812,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		});
+		}, 1000 * 10);
 	});
 
 	describe('User List TL', () => {
@@ -1025,7 +1025,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		});
+		}, 1000 * 10);
 
 		test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
@@ -1184,7 +1184,7 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
 			assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
-		});
+		}, 1000 * 10);
 
 		test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts
index 485506ee64..3c7e796700 100644
--- a/packages/backend/test/misc/mock-resolver.ts
+++ b/packages/backend/test/misc/mock-resolver.ts
@@ -14,7 +14,6 @@ import type { InstanceActorService } from '@/core/InstanceActorService.js';
 import type { LoggerService } from '@/core/LoggerService.js';
 import type { MetaService } from '@/core/MetaService.js';
 import type { UtilityService } from '@/core/UtilityService.js';
-import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 import { bindThis } from '@/decorators.js';
 import type {
 	FollowRequestsRepository,
@@ -48,7 +47,6 @@ export class MockResolver extends Resolver {
 			{} as HttpRequestService,
 			{} as ApRendererService,
 			{} as ApDbResolverService,
-			{} as FederatedInstanceService,
 			loggerService,
 		);
 	}
diff --git a/packages/backend/test/unit/FetchInstanceMetadataService.ts b/packages/backend/test/unit/FetchInstanceMetadataService.ts
index 2e66b81fcd..bf8f3ab0e3 100644
--- a/packages/backend/test/unit/FetchInstanceMetadataService.ts
+++ b/packages/backend/test/unit/FetchInstanceMetadataService.ts
@@ -75,61 +75,62 @@ describe('FetchInstanceMetadataService', () => {
 	test('Lock and update', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date(now - 10 * 1000 * 60 * 60 * 24) } as any);
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => { return now - 10 * 1000 * 60 * 60 * 24; } } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
-		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
+		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
 		expect(httpRequestService.getJson).toHaveBeenCalled();
 	});
 
-	test('Don\'t lock and update if recently updated', async () => {
+	test('Lock and don\'t update', async () => {
 		redisClient.set = mockRedis();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date() } as any);
+		const now = Date.now();
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
+		expect(tryLockSpy).toHaveBeenCalledTimes(1);
+		expect(unlockSpy).toHaveBeenCalledTimes(1);
 		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
-		expect(tryLockSpy).toHaveBeenCalledTimes(0);
-		expect(unlockSpy).toHaveBeenCalledTimes(0);
 		expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
 	});
 
 	test('Do nothing when lock not acquired', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date(now - 10 * 1000 * 60 * 60 * 24) } as any);
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		await fetchInstanceMetadataService.tryLock('example.com');
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any);
-		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(1);
 		expect(tryLockSpy).toHaveBeenCalledTimes(1);
 		expect(unlockSpy).toHaveBeenCalledTimes(0);
+		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
 		expect(httpRequestService.getJson).toHaveBeenCalledTimes(0);
 	});
 
-	test('Do when forced', async () => {
+	test('Do when lock not acquired but forced', async () => {
 		redisClient.set = mockRedis();
 		const now = Date.now();
-		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: new Date(now - 10 * 1000 * 60 * 60 * 24) } as any);
+		federatedInstanceService.fetch.mockResolvedValue({ infoUpdatedAt: { getTime: () => now - 10 * 1000 * 60 * 60 * 24 } } as any);
 		httpRequestService.getJson.mockImplementation(() => { throw Error(); });
 		await fetchInstanceMetadataService.tryLock('example.com');
 		const tryLockSpy = jest.spyOn(fetchInstanceMetadataService, 'tryLock');
 		const unlockSpy = jest.spyOn(fetchInstanceMetadataService, 'unlock');
 
 		await fetchInstanceMetadataService.fetchInstanceMetadata({ host: 'example.com' } as any, true);
-		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
 		expect(tryLockSpy).toHaveBeenCalledTimes(0);
 		expect(unlockSpy).toHaveBeenCalledTimes(1);
+		expect(federatedInstanceService.fetch).toHaveBeenCalledTimes(0);
 		expect(httpRequestService.getJson).toHaveBeenCalled();
 	});
 });
diff --git a/packages/backend/test/unit/ap-request.ts b/packages/backend/test/unit/ap-request.ts
index 50894c8b81..d3d39240dc 100644
--- a/packages/backend/test/unit/ap-request.ts
+++ b/packages/backend/test/unit/ap-request.ts
@@ -4,8 +4,10 @@
  */
 
 import * as assert from 'assert';
-import { verifyDraftSignature, parseRequestSignature, genEd25519KeyPair, genRsaKeyPair, importPrivateKey } from '@misskey-dev/node-http-message-signatures';
-import { createSignedGet, createSignedPost } from '@/core/activitypub/ApRequestService.js';
+import httpSignature from '@peertube/http-signature';
+
+import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
+import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
 
 export const buildParsedSignature = (signingString: string, signature: string, algorithm: string) => {
 	return {
@@ -22,68 +24,38 @@ export const buildParsedSignature = (signingString: string, signature: string, a
 	};
 };
 
-async function getKeyPair(level: string) {
-	if (level === '00') {
-		return await genRsaKeyPair();
-	} else if (level === '01') {
-		return await genEd25519KeyPair();
-	}
-	throw new Error('Invalid level');
-}
+describe('ap-request', () => {
+	test('createSignedPost with verify', async () => {
+		const keypair = await genRsaKeyPair();
+		const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
+		const url = 'https://example.com/inbox';
+		const activity = { a: 1 };
+		const body = JSON.stringify(activity);
+		const headers = {
+			'User-Agent': 'UA',
+		};
 
-describe('ap-request post', () => {
-	const url = 'https://example.com/inbox';
-	const activity = { a: 1 };
-	const body = JSON.stringify(activity);
-	const headers = {
-		'User-Agent': 'UA',
-	};
+		const req = ApRequestCreator.createSignedPost({ key, url, body, additionalHeaders: headers });
 
-	describe.each(['00', '01'])('createSignedPost with verify', (level) => {
-		test('pem', async () => {
-			const keypair = await getKeyPair(level);
-			const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
+		const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256');
 
-			const req = await createSignedPost({ level, key, url, body, additionalHeaders: headers });
+		const result = httpSignature.verifySignature(parsed, keypair.publicKey);
+		assert.deepStrictEqual(result, true);
+	});
 
-			const parsed = parseRequestSignature(req.request);
-			expect(parsed.version).toBe('draft');
-			expect(Array.isArray(parsed.value)).toBe(false);
-			const verify = await verifyDraftSignature(parsed.value as any, keypair.publicKey);
-			assert.deepStrictEqual(verify, true);
-		});
-		test('imported', async () => {
-			const keypair = await getKeyPair(level);
-			const key = { keyId: 'x', 'privateKey': await importPrivateKey(keypair.privateKey) };
+	test('createSignedGet with verify', async () => {
+		const keypair = await genRsaKeyPair();
+		const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
+		const url = 'https://example.com/outbox';
+		const headers = {
+			'User-Agent': 'UA',
+		};
 
-			const req = await createSignedPost({ level, key, url, body, additionalHeaders: headers });
+		const req = ApRequestCreator.createSignedGet({ key, url, additionalHeaders: headers });
 
-			const parsed = parseRequestSignature(req.request);
-			expect(parsed.version).toBe('draft');
-			expect(Array.isArray(parsed.value)).toBe(false);
-			const verify = await verifyDraftSignature(parsed.value as any, keypair.publicKey);
-			assert.deepStrictEqual(verify, true);
-		});
-	});
-});
-
-describe('ap-request get', () => {
-	describe.each(['00', '01'])('createSignedGet with verify', (level) => {
-		test('pass', async () => {
-			const keypair = await getKeyPair(level);
-			const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey };
-			const url = 'https://example.com/outbox';
-			const headers = {
-				'User-Agent': 'UA',
-			};
-
-			const req = await createSignedGet({ level, key, url, additionalHeaders: headers });
-
-			const parsed = parseRequestSignature(req.request);
-			expect(parsed.version).toBe('draft');
-			expect(Array.isArray(parsed.value)).toBe(false);
-			const verify = await verifyDraftSignature(parsed.value as any, keypair.publicKey);
-			assert.deepStrictEqual(verify, true);
-		});
+		const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256');
+
+		const result = httpSignature.verifySignature(parsed, keypair.publicKey);
+		assert.deepStrictEqual(result, true);
 	});
 });
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 2a7e5a323d..d3c857219b 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4608,7 +4608,6 @@ export type components = {
       /** Format: date-time */
       latestRequestReceivedAt: string | null;
       moderationNote?: string | null;
-      httpMessageSignaturesImplementationLevel: string;
     };
     GalleryPost: {
       /**
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2d426c2fa8..7d3fec8596 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -125,9 +125,6 @@ importers:
       '@fastify/view':
         specifier: 9.1.0
         version: 9.1.0
-      '@misskey-dev/node-http-message-signatures':
-        specifier: 0.0.10
-        version: 0.0.10
       '@misskey-dev/sharp-read-bmp':
         specifier: 1.2.0
         version: 1.2.0
@@ -146,6 +143,9 @@ importers:
       '@nestjs/testing':
         specifier: 10.3.10
         version: 10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10))
+      '@peertube/http-signature':
+        specifier: 1.7.0
+        version: 1.7.0
       '@sentry/node':
         specifier: 8.13.0
         version: 8.13.0
@@ -3228,11 +3228,6 @@ packages:
   '@kurkle/color@0.3.2':
     resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
 
-  '@lapo/asn1js@2.0.4':
-    resolution: {integrity: sha512-KJD3wQAZxozcraJdWp3utDU6DEZgAVBGp9INCdptUpZaXCEYkpwNb7h7wyYh5y6DxtpvIud8k0suhWJ/z2rKvw==}
-    engines: {node: '>=12.20.0'}
-    hasBin: true
-
   '@levischuck/tiny-cbor@0.2.2':
     resolution: {integrity: sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==}
 
@@ -3286,10 +3281,6 @@ packages:
       eslint-plugin-import: '>= 2'
       globals: '>= 15'
 
-  '@misskey-dev/node-http-message-signatures@0.0.10':
-    resolution: {integrity: sha512-HiAuc//tOU077KFUJhHYLAPWku9enTpOFIqQiK6l2i2mIizRvv7HhV7Y+yuav5quDOAz+WZGK/i5C9OR5fkKIg==}
-    engines: {node: '>=18.4.0'}
-
   '@misskey-dev/sharp-read-bmp@1.2.0':
     resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
 
@@ -3680,6 +3671,10 @@ packages:
   '@peculiar/asn1-x509@2.3.8':
     resolution: {integrity: sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==}
 
+  '@peertube/http-signature@1.7.0':
+    resolution: {integrity: sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==}
+    engines: {node: '>=0.10'}
+
   '@pkgjs/parseargs@0.11.0':
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
@@ -10529,9 +10524,6 @@ packages:
     resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
     engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
 
-  rfc4648@1.5.3:
-    resolution: {integrity: sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ==}
-
   rfdc@1.3.0:
     resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
 
@@ -11083,10 +11075,6 @@ packages:
     resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==}
     engines: {node: '>=14.16'}
 
-  structured-headers@1.0.1:
-    resolution: {integrity: sha512-QYBxdBtA4Tl5rFPuqmbmdrS9kbtren74RTJTcs0VSQNVV5iRhJD4QlYTLD0+81SBwUQctjEQzjTRI3WG4DzICA==}
-    engines: {node: '>= 14', npm: '>=6'}
-
   stylehacks@6.1.1:
     resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==}
     engines: {node: ^14 || ^16 || >=18.0}
@@ -14654,8 +14642,6 @@ snapshots:
 
   '@kurkle/color@0.3.2': {}
 
-  '@lapo/asn1js@2.0.4': {}
-
   '@levischuck/tiny-cbor@0.2.2': {}
 
   '@lukeed/csprng@1.0.1': {}
@@ -14736,12 +14722,6 @@ snapshots:
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
       globals: 15.7.0
 
-  '@misskey-dev/node-http-message-signatures@0.0.10':
-    dependencies:
-      '@lapo/asn1js': 2.0.4
-      rfc4648: 1.5.3
-      structured-headers: 1.0.1
-
   '@misskey-dev/sharp-read-bmp@1.2.0':
     dependencies:
       decode-bmp: 0.2.1
@@ -15202,6 +15182,12 @@ snapshots:
       pvtsutils: 1.3.5
       tslib: 2.6.2
 
+  '@peertube/http-signature@1.7.0':
+    dependencies:
+      assert-plus: 1.0.0
+      jsprim: 1.4.2
+      sshpk: 1.17.0
+
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
@@ -23893,8 +23879,6 @@ snapshots:
 
   reusify@1.0.4: {}
 
-  rfc4648@1.5.3: {}
-
   rfdc@1.3.0: {}
 
   rimraf@2.6.3:
@@ -24490,8 +24474,6 @@ snapshots:
       '@tokenizer/token': 0.3.0
       peek-readable: 5.0.0
 
-  structured-headers@1.0.1: {}
-
   stylehacks@6.1.1(postcss@8.4.38):
     dependencies:
       browserslist: 4.23.0

From 32651aba677c957bcd9c734eef9806375413dbc5 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 23 Jul 2024 18:39:59 +0900
Subject: [PATCH 143/206] Update about-misskey.vue

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

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index cc0394f401..8459f0f9d5 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -243,6 +243,21 @@ const patronsWithIcon = [{
 }, {
 	name: '越貝鯛丸',
 	icon: 'https://assets.misskey-hub.net/patrons/86c7374de37849b882d8ebbc833dc968.jpg',
+}, {
+	name: '☔あめ🍬(灬˘╰╯˘灬)',
+	icon: 'https://assets.misskey-hub.net/patrons/676eea72d4884d3f89aababbb62533fb.jpg',
+}, {
+	name: '貯水よび',
+	icon: 'https://assets.misskey-hub.net/patrons/2974506d53244bbe94a67707b27099e2.jpg',
+}, {
+	name: 'はるかさ',
+	icon: 'https://assets.misskey-hub.net/patrons/26ce2432739a400aa3aa0de0ef67a107.jpg',
+}, {
+	name: '天鈴のあ',
+	icon: 'https://assets.misskey-hub.net/patrons/995cdbb00bd6421184461a883adfe1d9.jpg',
+}, {
+	name: 'えとゔぁす',
+	icon: 'https://assets.misskey-hub.net/patrons/2578f441b82a44cfaa55ba83a318b26e.jpg',
 }];
 
 const patrons = [
@@ -347,6 +362,7 @@ const patrons = [
 	'SHO SEKIGUCHI',
 	'塩キャベツ',
 	'はとぽぷさん',
+	'100の人 (エスパー・イーシア)',
 ];
 
 const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));

From aa3ea2b57a4f51a98449d6fc901037dcb1c3a23d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 25 Jul 2024 16:30:29 +0900
Subject: [PATCH 144/206] =?UTF-8?q?fix(frontend):=20=E5=88=9D=E6=9C=9F?=
 =?UTF-8?q?=E5=8C=96=E6=99=82=E3=81=A8route=E5=A4=89=E6=9B=B4=E6=99=82?=
 =?UTF-8?q?=E3=81=A7key=E3=81=AE=E6=B1=BA=E5=AE=9A=E6=96=B9=E6=B3=95?=
 =?UTF-8?q?=E3=81=8C=E9=81=95=E3=81=86=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#14283)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/global/RouterView.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 02a2edee3f..19bd794a5d 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -53,7 +53,7 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
 const current = resolveNested(router.current)!;
 const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
 const currentPageProps = ref(current.props);
-const key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
+const key = ref(router.getCurrentKey() + JSON.stringify(Object.fromEntries(current.props)));
 
 function onChange({ resolved, key: newKey }) {
 	const current = resolveNested(resolved);

From fc71bcc98e14f9c3c13ba74ade9245d64bd4b633 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 25 Jul 2024 16:32:07 +0900
Subject: [PATCH 145/206] fix(backend): avoid notifying to remote users on
 local (#13774)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): avoid notifying to remote users on local

* Update CHANGELOG.md

* refactor: check before calling method

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                          |  1 +
 packages/backend/src/core/RoleService.ts              |  5 +++--
 packages/backend/src/core/UserFollowingService.ts     |  6 ++++--
 .../EndedPollNotificationProcessorService.ts          | 11 ++++++++---
 4 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f429033aa0..787784ef3c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -68,6 +68,7 @@
 - Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
 - Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
+- Fix: 一部の通知がローカル上のリモートユーザーに対して行われていた問題を修正
 - Fix: エラーメッセージの誤字を修正 (#14213)
 - Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正
 - Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正  
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 94026fd503..7966774673 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -505,14 +505,15 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
 
 		this.globalEventService.publishInternalEvent('userRoleAssigned', created);
 
-		if (role.isPublic) {
+		const user = await this.usersRepository.findOneByOrFail({ id: userId });
+
+		if (role.isPublic && user.host === null) {
 			this.notificationService.createNotification(userId, 'roleAssigned', {
 				roleId: roleId,
 			});
 		}
 
 		if (moderator) {
-			const user = await this.usersRepository.findOneByOrFail({ id: userId });
 			this.moderationLogService.log(moderator, 'assignRole', {
 				roleId: roleId,
 				roleName: role.name,
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index 267a6a3f1b..6aab8fde70 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -279,8 +279,10 @@ export class UserFollowingService implements OnModuleInit {
 			});
 
 			// 通知を作成
-			this.notificationService.createNotification(follower.id, 'followRequestAccepted', {
-			}, followee.id);
+			if (follower.host === null) {
+				this.notificationService.createNotification(follower.id, 'followRequestAccepted', {
+				}, followee.id);
+			}
 		}
 
 		if (alreadyFollowed) return;
diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts
index 29c1f27bb1..34180e5f2b 100644
--- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts
+++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
 import type { PollVotesRepository, NotesRepository } from '@/models/_.js';
 import type Logger from '@/logger.js';
+import { CacheService } from '@/core/CacheService.js';
 import { NotificationService } from '@/core/NotificationService.js';
 import { bindThis } from '@/decorators.js';
 import { QueueLoggerService } from '../QueueLoggerService.js';
@@ -24,6 +25,7 @@ export class EndedPollNotificationProcessorService {
 		@Inject(DI.pollVotesRepository)
 		private pollVotesRepository: PollVotesRepository,
 
+		private cacheService: CacheService,
 		private notificationService: NotificationService,
 		private queueLoggerService: QueueLoggerService,
 	) {
@@ -47,9 +49,12 @@ export class EndedPollNotificationProcessorService {
 		const userIds = [...new Set([note.userId, ...votes.map(v => v.userId)])];
 
 		for (const userId of userIds) {
-			this.notificationService.createNotification(userId, 'pollEnded', {
-				noteId: note.id,
-			});
+			const profile = await this.cacheService.userProfileCache.fetch(userId);
+			if (profile.userHost === null) {
+				this.notificationService.createNotification(userId, 'pollEnded', {
+					noteId: note.id,
+				});
+			}
 		}
 	}
 }

From befa8e4a7f91ee6f13ea6179a8a45dc84764b1f7 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 25 Jul 2024 16:37:46 +0900
Subject: [PATCH 146/206] fix(backend): avoid caching remote user's HTL when
 receiving Note (#13772)

* fix(backend): avoid caching remote user's HTL when receiving Note

* test(backend): add test for FFT

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 .../backend/src/core/NoteCreateService.ts     | 11 +++--
 packages/backend/test/e2e/timelines.ts        | 40 ++++++++++++++++++-
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 787784ef3c..21d25fe0de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -68,6 +68,7 @@
 - Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
 - Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正
+- Fix: FTT有効時にリモートユーザーのノートがHTLにキャッシュされる問題を修正
 - Fix: 一部の通知がローカル上のリモートユーザーに対して行われていた問題を修正
 - Fix: エラーメッセージの誤字を修正 (#14213)
 - Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index a2c3aaa701..fd9fac357f 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -933,10 +933,13 @@ export class NoteCreateService implements OnApplicationShutdown {
 				}
 			}
 
-			if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL
-				this.fanoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r);
-				if (note.fileIds.length > 0) {
-					this.fanoutTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
+			// 自分自身のHTL
+			if (note.userHost == null) {
+				if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) {
+					this.fanoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r);
+					if (note.fileIds.length > 0) {
+						this.fanoutTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
+					}
 				}
 			}
 
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index 540b866b28..ab65781f70 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -9,8 +9,8 @@
 import * as assert from 'assert';
 import { setTimeout } from 'node:timers/promises';
 import { Redis } from 'ioredis';
-import { loadConfig } from '@/config.js';
 import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js';
+import { loadConfig } from '@/config.js';
 
 function genHost() {
 	return randomString() + '.example.com';
@@ -492,6 +492,44 @@ describe('Timelines', () => {
 
 			assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
 		});
+
+		test.concurrent('FTT: ローカルユーザーの HTL にはプッシュされる', async () => {
+			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+			await api('following/create', {
+				userId: alice.id,
+			}, bob);
+
+			const aliceNote = await post(alice, { text: 'I\'m Alice.' });
+			const bobNote = await post(bob, { text: 'I\'m Bob.' });
+			const carolNote = await post(carol, { text: 'I\'m Carol.' });
+
+			await waitForPushToTl();
+
+			// NOTE: notes/timeline だと DB へのフォールバックが効くので Redis を直接見て確かめる
+			assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 1);
+
+			const bobHTL = await redisForTimelines.lrange(`list:homeTimeline:${bob.id}`, 0, -1);
+			assert.strictEqual(bobHTL.includes(aliceNote.id), true);
+			assert.strictEqual(bobHTL.includes(bobNote.id), true);
+			assert.strictEqual(bobHTL.includes(carolNote.id), false);
+		});
+
+		test.concurrent('FTT: リモートユーザーの HTL にはプッシュされない', async () => {
+			const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]);
+
+			await api('following/create', {
+				userId: alice.id,
+			}, bob);
+
+			await post(alice, { text: 'I\'m Alice.' });
+			await post(bob, { text: 'I\'m Bob.' });
+
+			await waitForPushToTl();
+
+			// NOTE: notes/timeline だと DB へのフォールバックが効くので Redis を直接見て確かめる
+			assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0);
+		});
 	});
 
 	describe('Local TL', () => {

From 3d8eda14a299dbf97cd135f7087c1797bd1ebb37 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Thu, 25 Jul 2024 16:40:14 +0900
Subject: [PATCH 147/206] =?UTF-8?q?[Re]=20refactor(misskey-js):=20?=
 =?UTF-8?q?=E8=AD=A6=E5=91=8A=E3=82=92=E3=81=99=E3=81=B9=E3=81=A6=E8=A7=A3?=
 =?UTF-8?q?=E6=B1=BA=20(#14277)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore(misskey-js): Unchanged files with check annotationsで紛らわしい部分の警告を抑制 ロジック面は後で直す

* dummy change to see if the feature do not report them (to be reverted after the check)

* refactor: 型合わせ

* refactor: fix warnings from c22dd6358ba4e068c49be033a07d9fbb001f2347

* lint

* 型合わせ

* キャスト

* pnpm build-misskey-js-with-types

* Revert "dummy change to see if the feature do not report them (to be reverted after the check)"

This reverts commit 67072e3ca6e3e16342ca3b35feadcb41afcbe04f.

* eliminate reversiGame any

* move reversiGame types

* lint

* Update packages/misskey-js/src/streaming.ts

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>

* Update acct.ts

* run api extractor

* re-run api extractor

---------

Co-authored-by: Kisaragi Marine <kisaragi.effective@gmail.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
---
 packages/misskey-js/eslint.config.js       |   1 +
 packages/misskey-js/etc/misskey-js.api.md  |  27 +++---
 packages/misskey-js/src/acct.ts            |   3 +-
 packages/misskey-js/src/api.ts             |   3 +
 packages/misskey-js/src/api.types.ts       |   2 +
 packages/misskey-js/src/consts.ts          | 104 +++++++++++++--------
 packages/misskey-js/src/entities.ts        |   3 +-
 packages/misskey-js/src/streaming.ts       |  30 +++---
 packages/misskey-js/src/streaming.types.ts |  18 ++--
 9 files changed, 118 insertions(+), 73 deletions(-)

diff --git a/packages/misskey-js/eslint.config.js b/packages/misskey-js/eslint.config.js
index e34e7510b2..d8173f30e9 100644
--- a/packages/misskey-js/eslint.config.js
+++ b/packages/misskey-js/eslint.config.js
@@ -1,6 +1,7 @@
 import tsParser from '@typescript-eslint/parser';
 import sharedConfig from '../shared/eslint.config.js';
 
+// eslint-disable-next-line import/no-default-export
 export default [
 	...sharedConfig,
 	{
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index d11d2a4f06..377dd6f658 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -551,7 +551,7 @@ type Channel = components['schemas']['Channel'];
 // Warning: (ae-forgotten-export) The symbol "AnyOf" needs to be exported by the entry point index.d.ts
 //
 // @public (undocumented)
-export abstract class ChannelConnection<Channel extends AnyOf<Channels> = any> extends EventEmitter<Channel['events']> {
+export abstract class ChannelConnection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends EventEmitter<Channel['events']> {
     constructor(stream: Stream, channel: string, name?: string);
     // (undocumented)
     channel: string;
@@ -771,12 +771,12 @@ export type Channels = {
                 user1: boolean;
                 user2: boolean;
             }) => void;
-            updateSettings: (payload: {
+            updateSettings: <K extends ReversiUpdateKey>(payload: {
                 userId: User['id'];
-                key: string;
-                value: any;
+                key: K;
+                value: ReversiGameDetailed[K];
             }) => void;
-            log: (payload: Record<string, any>) => void;
+            log: (payload: Record<string, unknown>) => void;
         };
         receives: {
             putStone: {
@@ -785,10 +785,7 @@ export type Channels = {
             };
             ready: boolean;
             cancel: null | Record<string, never>;
-            updateSettings: {
-                key: string;
-                value: any;
-            };
+            updateSettings: ReversiUpdateSettings<ReversiUpdateKey>;
             claimTimeIsUp: null | Record<string, never>;
         };
     };
@@ -2730,7 +2727,7 @@ type PagesUnlikeRequest = operations['pages___unlike']['requestBody']['content']
 type PagesUpdateRequest = operations['pages___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-function parse(acct: string): Acct;
+function parse(_acct: string): Acct;
 
 // Warning: (ae-forgotten-export) The symbol "Values" needs to be exported by the entry point index.d.ts
 //
@@ -2969,7 +2966,7 @@ export class Stream extends EventEmitter<StreamEvents> {
     constructor(origin: string, user: {
         token: string;
     } | null, options?: {
-        WebSocket?: any;
+        WebSocket?: WebSocket;
     });
     // (undocumented)
     close(): void;
@@ -2992,9 +2989,9 @@ export class Stream extends EventEmitter<StreamEvents> {
     // (undocumented)
     send(typeOrPayload: string): void;
     // (undocumented)
-    send(typeOrPayload: string, payload: any): void;
+    send(typeOrPayload: string, payload: unknown): void;
     // (undocumented)
-    send(typeOrPayload: Record<string, any> | any[]): void;
+    send(typeOrPayload: Record<string, unknown> | unknown[]): void;
     // (undocumented)
     state: 'initializing' | 'reconnecting' | 'connected';
     // (undocumented)
@@ -3229,7 +3226,9 @@ type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['
 
 // Warnings were encountered during analysis:
 //
-// src/entities.ts:34:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/entities.ts:35:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/streaming.types.ts:220:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts
+// src/streaming.types.ts:230:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts
 
 // (No @packageDocumentation comment for this package)
 
diff --git a/packages/misskey-js/src/acct.ts b/packages/misskey-js/src/acct.ts
index b25bc564ea..aa8658cdbd 100644
--- a/packages/misskey-js/src/acct.ts
+++ b/packages/misskey-js/src/acct.ts
@@ -3,7 +3,8 @@ export type Acct = {
 	host: string | null;
 };
 
-export function parse(acct: string): Acct {
+export function parse(_acct: string): Acct {
+	let acct = _acct;
 	if (acct.startsWith('@')) acct = acct.substring(1);
 	const split = acct.split('@', 2);
 	return { username: split[0], host: split[1] || null };
diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts
index 76d055cbe4..ea1df57f3d 100644
--- a/packages/misskey-js/src/api.ts
+++ b/packages/misskey-js/src/api.ts
@@ -14,6 +14,7 @@ export type APIError = {
 	code: string;
 	message: string;
 	kind: 'client' | 'server';
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	info: Record<string, any>;
 };
 
@@ -29,6 +30,7 @@ export type FetchLike = (input: string, init?: {
 	headers: { [key in string]: string }
 }) => Promise<{
 	status: number;
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	json(): Promise<any>;
 }>;
 
@@ -49,6 +51,7 @@ export class APIClient {
 		this.fetch = opts.fetch ?? ((...args) => fetch(...args));
 	}
 
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	private assertIsRecord<T>(obj: T): obj is T & Record<string, any> {
 		return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
 	}
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index 8c403639b7..5ee4194db2 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -28,11 +28,13 @@ type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
 
 type IsCaseMatched<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
 	Endpoints[E]['res'] extends SwitchCase
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
 		? IsNeverType<StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>> extends false ? true : false
 		: false
 
 type GetCaseResult<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
 	Endpoints[E]['res'] extends SwitchCase
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
 		? StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1]
 		: never
 
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 03b9069290..b509d3280c 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -1,3 +1,13 @@
+import type { operations } from './autogen/types.js';
+import type {
+	AbuseReportNotificationRecipient, Ad,
+	Announcement,
+	EmojiDetailed, InviteCode,
+	MetaDetailed,
+	Note,
+	Role, SystemWebhook, UserLite,
+} from './autogen/models.js';
+
 export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned'] as const;
 
 export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
@@ -135,10 +145,30 @@ export const moderationLogTypes = [
 	'unsetUserBanner',
 ] as const;
 
+// See: packages/backend/src/core/ReversiService.ts@L410
+export const reversiUpdateKeys = [
+	'map',
+	'bw',
+	'isLlotheo',
+	'canPutEverywhere',
+	'loopedBoard',
+	'timeLimitForEachTurn',
+] as const;
+
+export type ReversiUpdateKey = typeof reversiUpdateKeys[number];
+
+type AvatarDecoration = UserLite['avatarDecorations'][number];
+
+type ReceivedAbuseReport = {
+	reportId: AbuseReportNotificationRecipient['id'];
+	report: operations['admin___abuse-user-reports']['responses'][200]['content']['application/json'];
+	forwarded: boolean;
+};
+
 export type ModerationLogPayloads = {
 	updateServerSettings: {
-		before: any | null;
-		after: any | null;
+		before: MetaDetailed | null;
+		after: MetaDetailed | null;
 	};
 	suspend: {
 		userId: string;
@@ -159,16 +189,16 @@ export type ModerationLogPayloads = {
 	};
 	addCustomEmoji: {
 		emojiId: string;
-		emoji: any;
+		emoji: EmojiDetailed;
 	};
 	updateCustomEmoji: {
 		emojiId: string;
-		before: any;
-		after: any;
+		before: EmojiDetailed;
+		after: EmojiDetailed;
 	};
 	deleteCustomEmoji: {
 		emojiId: string;
-		emoji: any;
+		emoji: EmojiDetailed;
 	};
 	assignRole: {
 		userId: string;
@@ -187,16 +217,16 @@ export type ModerationLogPayloads = {
 	};
 	createRole: {
 		roleId: string;
-		role: any;
+		role: Role;
 	};
 	updateRole: {
 		roleId: string;
-		before: any;
-		after: any;
+		before: Role;
+		after: Role;
 	};
 	deleteRole: {
 		roleId: string;
-		role: any;
+		role: Role;
 	};
 	clearQueue: Record<string, never>;
 	promoteQueue: Record<string, never>;
@@ -211,39 +241,39 @@ export type ModerationLogPayloads = {
 		noteUserId: string;
 		noteUserUsername: string;
 		noteUserHost: string | null;
-		note: any;
+		note: Note;
 	};
 	createGlobalAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 	};
 	createUserAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 		userId: string;
 		userUsername: string;
 		userHost: string | null;
 	};
 	updateGlobalAnnouncement: {
 		announcementId: string;
-		before: any;
-		after: any;
+		before: Announcement;
+		after: Announcement;
 	};
 	updateUserAnnouncement: {
 		announcementId: string;
-		before: any;
-		after: any;
+		before: Announcement;
+		after: Announcement;
 		userId: string;
 		userUsername: string;
 		userHost: string | null;
 	};
 	deleteGlobalAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 	};
 	deleteUserAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 		userId: string;
 		userUsername: string;
 		userHost: string | null;
@@ -281,37 +311,37 @@ export type ModerationLogPayloads = {
 	};
 	resolveAbuseReport: {
 		reportId: string;
-		report: any;
+		report: ReceivedAbuseReport;
 		forwarded: boolean;
 	};
 	createInvitation: {
-		invitations: any[];
+		invitations: InviteCode[];
 	};
 	createAd: {
 		adId: string;
-		ad: any;
+		ad: Ad;
 	};
 	updateAd: {
 		adId: string;
-		before: any;
-		after: any;
+		before: Ad;
+		after: Ad;
 	};
 	deleteAd: {
 		adId: string;
-		ad: any;
+		ad: Ad;
 	};
 	createAvatarDecoration: {
 		avatarDecorationId: string;
-		avatarDecoration: any;
+		avatarDecoration: AvatarDecoration;
 	};
 	updateAvatarDecoration: {
 		avatarDecorationId: string;
-		before: any;
-		after: any;
+		before: AvatarDecoration;
+		after: AvatarDecoration;
 	};
 	deleteAvatarDecoration: {
 		avatarDecorationId: string;
-		avatarDecoration: any;
+		avatarDecoration: AvatarDecoration;
 	};
 	unsetUserAvatar: {
 		userId: string;
@@ -327,28 +357,28 @@ export type ModerationLogPayloads = {
 	};
 	createSystemWebhook: {
 		systemWebhookId: string;
-		webhook: any;
+		webhook: SystemWebhook;
 	};
 	updateSystemWebhook: {
 		systemWebhookId: string;
-		before: any;
-		after: any;
+		before: SystemWebhook;
+		after: SystemWebhook;
 	};
 	deleteSystemWebhook: {
 		systemWebhookId: string;
-		webhook: any;
+		webhook: SystemWebhook;
 	};
 	createAbuseReportNotificationRecipient: {
 		recipientId: string;
-		recipient: any;
+		recipient: AbuseReportNotificationRecipient;
 	};
 	updateAbuseReportNotificationRecipient: {
 		recipientId: string;
-		before: any;
-		after: any;
+		before: AbuseReportNotificationRecipient;
+		after: AbuseReportNotificationRecipient;
 	};
 	deleteAbuseReportNotificationRecipient: {
 		recipientId: string;
-		recipient: any;
+		recipient: AbuseReportNotificationRecipient;
 	};
 };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 7331a55a1c..ce58fb2970 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -7,7 +7,7 @@ import {
 	Role,
 	RolePolicies,
 	User,
-	UserDetailedNotMe
+	UserDetailedNotMe,
 } from './autogen/models.js';
 
 export * from './autogen/entities.js';
@@ -19,6 +19,7 @@ export type DateString = string;
 export type PageEvent = {
 	pageId: Page['id'];
 	event: string;
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	var: any;
 	userId: User['id'];
 	user: User;
diff --git a/packages/misskey-js/src/streaming.ts b/packages/misskey-js/src/streaming.ts
index 83930e621c..d1d131cfc1 100644
--- a/packages/misskey-js/src/streaming.ts
+++ b/packages/misskey-js/src/streaming.ts
@@ -15,7 +15,7 @@ export function urlQuery(obj: Record<string, string | number | boolean | undefin
 		.join('&');
 }
 
-type AnyOf<T extends Record<any, any>> = T[keyof T];
+type AnyOf<T extends Record<PropertyKey, unknown>> = T[keyof T];
 
 type StreamEvents = {
 	_connected_: void;
@@ -25,6 +25,7 @@ type StreamEvents = {
 /**
  * Misskey stream connection
  */
+// eslint-disable-next-line import/no-default-export
 export default class Stream extends EventEmitter<StreamEvents> {
 	private stream: _ReconnectingWebsocket.default;
 	public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing';
@@ -34,7 +35,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
 	private idCounter = 0;
 
 	constructor(origin: string, user: { token: string; } | null, options?: {
-		WebSocket?: any;
+		WebSocket?: WebSocket;
 	}) {
 		super();
 
@@ -51,6 +52,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
 		this.send = this.send.bind(this);
 		this.close = this.close.bind(this);
 
+		// eslint-disable-next-line no-param-reassign
 		options = options ?? { };
 
 		const query = urlQuery({
@@ -91,8 +93,8 @@ export default class Stream extends EventEmitter<StreamEvents> {
 			this.sharedConnectionPools.push(pool);
 		}
 
-		const connection = new SharedConnection(this, channel, pool, name);
-		this.sharedConnections.push(connection);
+		const connection = new SharedConnection<Channels[C]>(this, channel, pool, name);
+		this.sharedConnections.push(connection as unknown as SharedConnection);
 		return connection;
 	}
 
@@ -106,7 +108,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
 
 	private connectToChannel<C extends keyof Channels>(channel: C, params: Channels[C]['params']): NonSharedConnection<Channels[C]> {
 		const connection = new NonSharedConnection(this, channel, this.genId(), params);
-		this.nonSharedConnections.push(connection);
+		this.nonSharedConnections.push(connection as unknown as NonSharedConnection);
 		return connection;
 	}
 
@@ -174,9 +176,9 @@ export default class Stream extends EventEmitter<StreamEvents> {
 	 * ! ストリーム上のやり取りはすべてJSONで行われます !
 	 */
 	public send(typeOrPayload: string): void
-	public send(typeOrPayload: string, payload: any): void
-	public send(typeOrPayload: Record<string, any> | any[]): void
-	public send(typeOrPayload: string | Record<string, any> | any[], payload?: any): void {
+	public send(typeOrPayload: string, payload: unknown): void
+	public send(typeOrPayload: Record<string, unknown> | unknown[]): void
+	public send(typeOrPayload: string | Record<string, unknown> | unknown[], payload?: unknown): void {
 		if (typeof typeOrPayload === 'string') {
 			this.stream.send(JSON.stringify({
 				type: typeOrPayload,
@@ -211,7 +213,7 @@ class Pool {
 	public id: string;
 	protected stream: Stream;
 	public users = 0;
-	private disposeTimerId: any;
+	private disposeTimerId: ReturnType<typeof setTimeout> | null = null;
 	private isConnected = false;
 
 	constructor(stream: Stream, channel: string, id: string) {
@@ -275,7 +277,7 @@ class Pool {
 	}
 }
 
-export abstract class Connection<Channel extends AnyOf<Channels> = any> extends EventEmitter<Channel['events']> {
+export abstract class Connection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends EventEmitter<Channel['events']> {
 	public channel: string;
 	protected stream: Stream;
 	public abstract id: string;
@@ -309,7 +311,7 @@ export abstract class Connection<Channel extends AnyOf<Channels> = any> extends
 	public abstract dispose(): void;
 }
 
-class SharedConnection<Channel extends AnyOf<Channels> = any> extends Connection<Channel> {
+class SharedConnection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends Connection<Channel> {
 	private pool: Pool;
 
 	public get id(): string {
@@ -328,11 +330,11 @@ class SharedConnection<Channel extends AnyOf<Channels> = any> extends Connection
 	public dispose(): void {
 		this.pool.dec();
 		this.removeAllListeners();
-		this.stream.removeSharedConnection(this);
+		this.stream.removeSharedConnection(this as unknown as SharedConnection);
 	}
 }
 
-class NonSharedConnection<Channel extends AnyOf<Channels> = any> extends Connection<Channel> {
+class NonSharedConnection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends Connection<Channel> {
 	public id: string;
 	protected params: Channel['params'];
 
@@ -359,6 +361,6 @@ class NonSharedConnection<Channel extends AnyOf<Channels> = any> extends Connect
 	public dispose(): void {
 		this.removeAllListeners();
 		this.stream.send('disconnect', { id: this.id });
-		this.stream.disconnectToChannel(this);
+		this.stream.disconnectToChannel(this as unknown as NonSharedConnection);
 	}
 }
diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts
index 9a86e03d69..4447a2e8fc 100644
--- a/packages/misskey-js/src/streaming.types.ts
+++ b/packages/misskey-js/src/streaming.types.ts
@@ -21,6 +21,14 @@ import {
 	ServerStatsLog,
 	ReversiGameDetailed,
 } from './entities.js';
+import {
+	ReversiUpdateKey,
+} from './consts.js';
+
+type ReversiUpdateSettings<K extends ReversiUpdateKey> = {
+	key: K;
+	value: ReversiGameDetailed[K];
+};
 
 export type Channels = {
 	main: {
@@ -51,6 +59,7 @@ export type Channels = {
 			registryUpdated: (payload: {
 				scope?: string[];
 				key: string;
+				// eslint-disable-next-line @typescript-eslint/no-explicit-any
 				value: any | null;
 			}) => void;
 			driveFileCreated: (payload: DriveFile) => void;
@@ -208,8 +217,8 @@ export type Channels = {
 			ended: (payload: { winnerId: User['id'] | null; game: ReversiGameDetailed; }) => void;
 			canceled: (payload: { userId: User['id']; }) => void;
 			changeReadyStates: (payload: { user1: boolean; user2: boolean; }) => void;
-			updateSettings: (payload: { userId: User['id']; key: string; value: any; }) => void;
-			log: (payload: Record<string, any>) => void;
+			updateSettings: <K extends ReversiUpdateKey>(payload: { userId: User['id']; key: K; value: ReversiGameDetailed[K]; }) => void;
+			log: (payload: Record<string, unknown>) => void;
 		};
 		receives: {
 			putStone: {
@@ -218,10 +227,7 @@ export type Channels = {
 			};
 			ready: boolean;
 			cancel: null | Record<string, never>;
-			updateSettings: {
-				key: string;
-				value: any;
-			};
+			updateSettings: ReversiUpdateSettings<ReversiUpdateKey>;
 			claimTimeIsUp: null | Record<string, never>;
 		}
 	}

From 7c67d3a5aae29317902d73ced1fe1ec28a209e32 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 25 Jul 2024 16:44:38 +0900
Subject: [PATCH 148/206] fix(frontend): emoji picker not opening on `/share`
 page (#14295)

* fix(frontend): emoji picker not opening on `/share` page

* Update CHANGELOG.md
---
 CHANGELOG.md                           | 1 +
 packages/frontend/src/boot/sub-boot.ts | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21d25fe0de..1521e88fcb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@
 - Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正
 - Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正
 - Fix: 一部の画像がセンシティブ指定されているときに画面に何も表示されないことがあるのを修正
+- Fix: `/share`ページにおいて絵文字ピッカーを開くことができない問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts
index 017457822b..35c84d5568 100644
--- a/packages/frontend/src/boot/sub-boot.ts
+++ b/packages/frontend/src/boot/sub-boot.ts
@@ -5,9 +5,12 @@
 
 import { createApp, defineAsyncComponent } from 'vue';
 import { common } from './common.js';
+import { emojiPicker } from '@/scripts/emoji-picker.js';
 
 export async function subBoot() {
 	const { isClientUpdated } = await common(() => createApp(
 		defineAsyncComponent(() => import('@/ui/minimum.vue')),
 	));
+
+	emojiPicker.init();
 }

From ed6dc84c5f92c00c92bb2fa17b5f5aa81c21429e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 25 Jul 2024 17:03:00 +0900
Subject: [PATCH 149/206] =?UTF-8?q?fix(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=97=E3=81=9F=E3=83=A6?=
 =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E4=B8=80=E8=A6=A7=E3=81=AE=E3=83=A6?=
 =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E5=90=8D=E3=81=8C=E3=81=AF=E3=81=BF?=
 =?UTF-8?q?=E5=87=BA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#14294)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* pnpm dev で絵文字が表示されない問題を解決

(cherry picked from commit 22fcafbf55830922efe75d129f48b4d8c11724e6)

* リアクションしたユーザー一覧のユーザーネームがはみ出る問題を解決

(cherry picked from commit 46458b190e2b4ccfc8b50b6857ee9a5a6fd09fe9)

* Update Changelog

---------

Co-authored-by: 6wFh3kVo <yukikum57@gmail.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                                   | 2 ++
 packages/frontend/src/components/MkReactionsViewer.details.vue | 1 +
 packages/frontend/vite.config.local-dev.ts                     | 1 +
 3 files changed, 4 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1521e88fcb..7d48d14c71 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,8 @@
 - Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正
 - Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正
 - Fix: 一部の画像がセンシティブ指定されているときに画面に何も表示されないことがあるのを修正
+- Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正  
+  (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/672)
 - Fix: `/share`ページにおいて絵文字ピッカーを開くことができない問題を修正
 
 ### Server
diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index 8b5e6efdf3..60118fadd2 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -81,6 +81,7 @@ function getReactionName(reaction: string): string {
 }
 
 .user {
+	display: flex;
 	line-height: 24px;
 	padding-top: 4px;
 	white-space: nowrap;
diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
index 0a88e489c1..887ab7927e 100644
--- a/packages/frontend/vite.config.local-dev.ts
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -62,6 +62,7 @@ const devConfig: UserConfig = {
 			'/bios': httpUrl,
 			'/cli': httpUrl,
 			'/inbox': httpUrl,
+			'/emoji/': httpUrl,
 			'/notes': {
 				target: httpUrl,
 				bypass: varyHandler,

From ee2f0f3a21bbe978483ebe28a38806431c972405 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Thu, 25 Jul 2024 17:03:55 +0900
Subject: [PATCH 150/206] =?UTF-8?q?fix(frontend):=20=E3=81=84=E3=81=8F?=
 =?UTF-8?q?=E3=81=A4=E3=81=8B=E3=81=AE`number`=20input=E3=81=AB=E6=9C=80?=
 =?UTF-8?q?=E5=B0=8F=E5=80=A4=E3=82=92=E8=A8=AD=E5=AE=9A=20(#14284)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkPollEditor.vue            | 2 +-
 packages/frontend/src/pages/admin/invites.vue                | 2 +-
 packages/frontend/src/pages/settings/statusbar.statusbar.vue | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue
index db74354bbb..3726ddf822 100644
--- a/packages/frontend/src/components/MkPollEditor.vue
+++ b/packages/frontend/src/components/MkPollEditor.vue
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</MkInput>
 			</section>
 			<section v-else-if="expiration === 'after'">
-				<MkInput v-model="after" small type="number" class="input">
+				<MkInput v-model="after" small type="number" min="1" class="input">
 					<template #label>{{ i18n.ts._poll.duration }}</template>
 				</MkInput>
 				<MkSelect v-model="unit" small>
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index 95727fb14c..9cb430b0fe 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkInput v-if="!noExpirationDate" v-model="expiresAt" type="datetime-local">
 						<template #label>{{ i18n.ts.expirationDate }}</template>
 					</MkInput>
-					<MkInput v-model="createCount" type="number">
+					<MkInput v-model="createCount" type="number" min="1">
 						<template #label>{{ i18n.ts.createCount }}</template>
 					</MkInput>
 					<MkButton primary rounded @click="createWithOptions">{{ i18n.ts.create }}</MkButton>
diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
index 92e389a288..67943524ef 100644
--- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkSwitch v-model="statusbar.props.shuffle">
 			<template #label>{{ i18n.ts.shuffle }}</template>
 		</MkSwitch>
-		<MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number">
+		<MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number" min="1">
 			<template #label>{{ i18n.ts.refreshInterval }}</template>
 		</MkInput>
 		<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</MkSwitch>
 	</template>
 	<template v-else-if="statusbar.type === 'federation'">
-		<MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number">
+		<MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number" min="1">
 			<template #label>{{ i18n.ts.refreshInterval }}</template>
 		</MkInput>
 		<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">

From 8959ff89d033d20ca1ab75e6d314be7953ed60ae Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Thu, 25 Jul 2024 17:12:06 +0900
Subject: [PATCH 151/206] chore: reflect actual policy about Committers' rights
 (#14267)

* Update CONTRIBUTING.md

* member -> commiter

* apply suggestions

Co-authored-by: Marie <robloxfilmcam@gmail.com>

* Update CONTRIBUTING.md

---------

Co-authored-by: Marie <robloxfilmcam@gmail.com>
---
 CONTRIBUTING.md | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index afc21ea594..784d56d08c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -20,13 +20,29 @@ Before creating an issue, please check the following:
 > **Warning**
 > Do not close issues that are about to be resolved. It should remain open until a commit that actually resolves it is merged.
 
-## Before implementation
+
+### Recommended discussing before implementation
+We welcome your purposal.
+
 When you want to add a feature or fix a bug, **first have the design and policy reviewed in an Issue** (if it is not there, please make one). Without this step, there is a high possibility that the PR will not be merged even if it is implemented.
 
 At this point, you also need to clarify the goals of the PR you will create, and make sure that the other members of the team are aware of them.
 PRs that do not have a clear set of do's and don'ts tend to be bloated and difficult to review.
 
-Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask another member to assign you). By expressing your intention to work the Issue, you can prevent conflicts in the work.
+Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask Commiter to assign you). 
+By expressing your intention to work on the Issue, you can prevent conflicts in the work.
+
+To the Committers: you should not assign someone on it before the Final Decision.
+
+### How issues are triaged
+
+The Commiters may:
+* close an issue that is not reproducible on latest stable release,
+* merge an issue into another issue,
+* split an issue into multiple issues,
+* or re-open that has been closed for some reason which is not applicable anymore.
+
+@syuilo reserves the Final Desicion rights including whether the project will implement feature and how to implement, these rights are not always exercised.
 
 ## Well-known branches
 - **`master`** branch is tracking the latest release and used for production purposes.

From 908d3ecb5cb9e510e06818c145aaba6664829773 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Thu, 25 Jul 2024 12:02:12 +0000
Subject: [PATCH 152/206] Bump version to 2024.7.0-beta.2

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

diff --git a/package.json b/package.json
index 44466aaae6..510b96aa01 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-beta.1",
+	"version": "2024.7.0-beta.2",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index d494b6b58e..aa2e0e781b 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-beta.1",
+	"version": "2024.7.0-beta.2",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 0d76842abefa32cb22ed2e224122337bccce693d Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Fri, 26 Jul 2024 10:16:27 +0900
Subject: [PATCH 153/206] docs: format `CONTRIBUTING.md` (#14302)

* fix: correct typos

* chore: convert indentation to tabs

* fix: missing lang

* chore: trim unnecessary whitespaces and newlines

* chore: use local path

* chore: use GFM alerts

* fix: missing use GFM alerts
---
 CONTRIBUTING.md | 45 +++++++++++++++++++++------------------------
 1 file changed, 21 insertions(+), 24 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 784d56d08c..72c84c2f18 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,7 +1,7 @@
 # Contribution guide
 We're glad you're interested in contributing Misskey! In this document you will find the information you need to contribute to the project.
 
-> **Note**
+> [!NOTE]
 > This project uses Japanese as its major language, **but you do not need to translate and write the Issues/PRs in Japanese.**
 > Also, you might receive comments on your Issue/PR in Japanese, but you do not need to reply to them in Japanese as well.\
 > The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language.
@@ -17,32 +17,31 @@ Before creating an issue, please check the following:
 	- Issues should only be used to feature requests, suggestions, and bug tracking.
 	- Please ask questions or troubleshooting in [GitHub Discussions](https://github.com/misskey-dev/misskey/discussions) or [Discord](https://discord.gg/Wp8gVStHW3).
 
-> **Warning**
+> [!WARNING]
 > Do not close issues that are about to be resolved. It should remain open until a commit that actually resolves it is merged.
 
-
 ### Recommended discussing before implementation
-We welcome your purposal.
+We welcome your proposal.
 
 When you want to add a feature or fix a bug, **first have the design and policy reviewed in an Issue** (if it is not there, please make one). Without this step, there is a high possibility that the PR will not be merged even if it is implemented.
 
 At this point, you also need to clarify the goals of the PR you will create, and make sure that the other members of the team are aware of them.
 PRs that do not have a clear set of do's and don'ts tend to be bloated and difficult to review.
 
-Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask Commiter to assign you). 
+Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask Committer to assign you).
 By expressing your intention to work on the Issue, you can prevent conflicts in the work.
 
 To the Committers: you should not assign someone on it before the Final Decision.
 
 ### How issues are triaged
 
-The Commiters may:
+The Committers may:
 * close an issue that is not reproducible on latest stable release,
 * merge an issue into another issue,
 * split an issue into multiple issues,
 * or re-open that has been closed for some reason which is not applicable anymore.
 
-@syuilo reserves the Final Desicion rights including whether the project will implement feature and how to implement, these rights are not always exercised.
+@syuilo reserves the Final Decision rights including whether the project will implement feature and how to implement, these rights are not always exercised.
 
 ## Well-known branches
 - **`master`** branch is tracking the latest release and used for production purposes.
@@ -53,14 +52,14 @@ The Commiters may:
 ## Creating a PR
 Thank you for your PR! Before creating a PR, please check the following:
 - If possible, prefix the title with a keyword that identifies the type of this PR, as shown below.
-  - `fix` / `refactor` / `feat` / `enhance` / `perf` / `chore` etc
-  - Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR.
+	- `fix` / `refactor` / `feat` / `enhance` / `perf` / `chore` etc
+	- Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR.
 - If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text.
 - Please add the summary of the changes to [`CHANGELOG.md`](/CHANGELOG.md). However, this is not necessary for changes that do not affect the users, such as refactoring.
 - Check if there are any documents that need to be created or updated due to this change.
 - If you have added a feature or fixed a bug, please add a test case if possible.
 - Please make sure that tests and Lint are passed in advance.
-  - You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing)
+	- You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing)
 - If this PR includes UI changes, please attach a screenshot in the text.
 
 Thanks for your cooperation 🤗
@@ -70,8 +69,8 @@ Be willing to comment on the good points and not just the things you want fixed
 
 ### Review perspective
 - Scope
-  - Are the goals of the PR clear?
-  - Is the granularity of the PR appropriate?
+	- Are the goals of the PR clear?
+	- Is the granularity of the PR appropriate?
 - Security
 	- Does merging this PR create a vulnerability?
 - Performance
@@ -93,7 +92,7 @@ An actual domain will be assigned so you can test the federation.
 
 ## Release
 ### Release Instructions
-1. Commit version changes in the `develop` branch ([package.json](https://github.com/misskey-dev/misskey/blob/develop/package.json))
+1. Commit version changes in the `develop` branch ([package.json](package.json))
 2. Create a release PR.
 	- Into `master` from `develop` branch.
 	- The title must be in the format `Release: x.y.z`.
@@ -104,7 +103,7 @@ An actual domain will be assigned so you can test the federation.
 	- The target branch must be `master`
 	- The tag name must be the version
 
-> **Note**
+> [!NOTE]
 > Why this instruction is necessary:
 > - To perform final QA checks
 > - To distribute responsibility
@@ -139,7 +138,7 @@ You could obtain middleware container by typing `docker compose -f $PROJECT_ROOT
 Devcontainer also has necessary setting. This method can be done by connecting from VSCode.
 
 Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
-To use Dev Container, open the project directory on VSCode with Dev Containers installed.  
+To use Dev Container, open the project directory on VSCode with Dev Containers installed.
 **Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled.
 
 It will run the following command automatically inside the container.
@@ -155,11 +154,9 @@ After finishing the migration, you can proceed.
 
 ### Start developing
 During development, it is useful to use the
-
 ```
 pnpm dev
 ```
-
 command.
 
 - Server-side source files and automatically builds them if they are modified. Automatically start the server process(es).
@@ -232,7 +229,7 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド
 ### ルート定義
 ルート定義は、以下の形式のオブジェクトの配列です。
 
-``` ts
+```ts
 {
 	name?: string;
 	path: string;
@@ -245,7 +242,7 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド
 }
 ```
 
-> **Warning**
+> [!WARNING]
 > 現状、ルートは定義された順に評価されます。
 > たとえば、`/foo/:id`ルート定義の次に`/foo/bar`ルート定義がされていた場合、後者がマッチすることはありません。
 
@@ -307,7 +304,7 @@ export const Default = {
 	parameters: {
 		layout: 'centered',
 	},
-} satisfies StoryObj<typeof MkAvatar>;
+} satisfies StoryObj<typeof MyComponent>;
 ```
 
 If you want to opt-out from the automatic generation, create a `MyComponent.stories.impl.ts` file and add the following line to the file.
@@ -418,7 +415,7 @@ describe('test', () => {
 		})
 			.useMocker(...
 			.compile();
-	
+
 		fooService = app.get<FooService>(FooService);
 		barService = app.get<BarService>(BarService) as jest.Mocked<BarService>;
 
@@ -539,13 +536,13 @@ pnpm dlx typeorm migration:generate -d ormconfig.js -o <migration name>
 - 作成されたスクリプトは不必要な変更を含むため除去してください
 
 ### JSON SchemaのobjectでanyOfを使うとき
-JSON Schemaで、objectに対してanyOfを使う場合、anyOfの中でpropertiesを定義しないこと。  
-バリデーションが効かないため。(SchemaTypeもそのように作られており、objectのanyOf内のpropertiesは捨てられます)  
+JSON Schemaで、objectに対してanyOfを使う場合、anyOfの中でpropertiesを定義しないこと。
+バリデーションが効かないため。(SchemaTypeもそのように作られており、objectのanyOf内のpropertiesは捨てられます)
 https://github.com/misskey-dev/misskey/pull/10082
 
 テキストhogeおよびfugaについて、片方を必須としつつ両方の指定もありうる場合:
 
-```
+```ts
 export const paramDef = {
 	type: 'object',
 	properties: {

From 46d96c7412f85990d01006fbb548178c8cfdf827 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 27 Jul 2024 18:09:15 +0900
Subject: [PATCH 154/206] =?UTF-8?q?fix(build):=20autogen=E7=94=9F=E6=88=90?=
 =?UTF-8?q?=E6=99=82=E3=81=ABbackend=E3=82=922=E5=BA=A6build=E3=81=97?=
 =?UTF-8?q?=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#14309)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(build): autogen生成時にbackendを2度buildしているのを修正

* fix

* fix
---
 package.json                                  |  2 +-
 packages/backend/package.json                 |  2 +-
 packages/backend/scripts/generate_api_json.js | 35 +++++++++++++++----
 3 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/package.json b/package.json
index 510b96aa01..ecf2de39d3 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
 		"build-assets": "node ./scripts/build-assets.mjs",
 		"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
 		"build-storybook": "pnpm --filter frontend build-storybook",
-		"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
+		"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
 		"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
 		"init": "pnpm migrate",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 22fdc5cf16..b99717d15c 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -31,7 +31,7 @@
 		"test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e",
 		"test-and-coverage": "pnpm jest-and-coverage",
 		"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
-		"generate-api-json": "pnpm build && node ./scripts/generate_api_json.js"
+		"generate-api-json": "node ./scripts/generate_api_json.js"
 	},
 	"optionalDependencies": {
 		"@swc/core-android-arm64": "1.3.11",
diff --git a/packages/backend/scripts/generate_api_json.js b/packages/backend/scripts/generate_api_json.js
index b4769ef801..798e243004 100644
--- a/packages/backend/scripts/generate_api_json.js
+++ b/packages/backend/scripts/generate_api_json.js
@@ -3,11 +3,34 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { loadConfig } from '../built/config.js'
-import { genOpenapiSpec } from '../built/server/api/openapi/gen-spec.js'
-import { writeFileSync } from "node:fs";
+import { execa } from 'execa';
+import { writeFileSync, existsSync } from "node:fs";
 
-const config = loadConfig();
-const spec = genOpenapiSpec(config, true);
+async function main() {
+	if (!process.argv.includes('--no-build')) {
+		await execa('pnpm', ['run', 'build'], {
+			stdout: process.stdout,
+			stderr: process.stderr,
+		});
+	}
 
-writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8');
+	if (!existsSync('./built')) {
+		throw new Error('`built` directory does not exist.');
+	}
+
+	/** @type {import('../src/config.js')} */
+	const { loadConfig } = await import('../built/config.js');
+
+	/** @type {import('../src/server/api/openapi/gen-spec.js')} */
+	const { genOpenapiSpec } = await import('../built/server/api/openapi/gen-spec.js');
+
+	const config = loadConfig();
+	const spec = genOpenapiSpec(config, true);
+
+	writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8');
+}
+
+main().catch(e => {
+	console.error(e);
+	process.exit(1);
+});

From 22c4e9d7ecf5a659832ea32244e46a40627bfc8f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 27 Jul 2024 18:09:57 +0900
Subject: [PATCH 155/206] =?UTF-8?q?fix(frontend):=20modal=E3=81=8C?=
 =?UTF-8?q?=E6=AD=A3=E3=81=97=E3=81=8F=E9=96=89=E3=81=98=E3=82=89=E3=82=8C?=
 =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#14307)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): modalが正しく閉じられていないのを修正

* Update packages/frontend/src/components/MkSystemWebhookEditor.vue

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

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 .../components/MkSystemWebhookEditor.impl.ts  | 13 +++++----
 .../src/components/MkSystemWebhookEditor.vue  | 22 +++++++++-----
 .../src/components/MkUserSelectDialog.vue     |  8 ++---
 .../notification-recipient.editor.vue         | 29 ++++++++++++-------
 .../abuse-report/notification-recipient.vue   | 15 +++++-----
 5 files changed, 53 insertions(+), 34 deletions(-)

diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
index 76f54e8d37..69b8edd85a 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
@@ -24,22 +24,23 @@ export type MkSystemWebhookResult = {
 };
 
 export async function showSystemWebhookEditorDialog(props: MkSystemWebhookEditorProps): Promise<MkSystemWebhookResult | null> {
-	const { dispose, result } = await new Promise<{ dispose: () => void, result: MkSystemWebhookResult | null }>(async resolve => {
-		const { dispose: _dispose } = os.popup(
+	const { result } = await new Promise<{ result: MkSystemWebhookResult | null }>(async resolve => {
+		const { dispose } = os.popup(
 			defineAsyncComponent(() => import('@/components/MkSystemWebhookEditor.vue')),
 			props,
 			{
 				submitted: (ev: MkSystemWebhookResult) => {
-					resolve({ dispose: _dispose, result: ev });
+					resolve({ result: ev });
+				},
+				canceled: () => {
+					resolve({ result: null });
 				},
 				closed: () => {
-					resolve({ dispose: _dispose, result: null });
+					dispose();
 				},
 			},
 		);
 	});
 
-	dispose();
-
 	return result;
 }
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index 007d841f00..3e6a015018 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkModalWindow
+	ref="dialogEl"
 	:width="450"
 	:height="590"
 	:canClose="true"
@@ -12,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:okButtonDisabled="false"
 	@click="onCancelClicked"
 	@close="onCancelClicked"
-	@closed="onCancelClicked"
+	@closed="emit('closed')"
 >
 	<template #header>
 		{{ mode === 'create' ? i18n.ts._webhookSettings.createWebhook : i18n.ts._webhookSettings.modifyWebhook }}
@@ -59,8 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script setup lang="ts">
-import { computed, onMounted, ref, toRefs } from 'vue';
-import FormSection from '@/components/form/section.vue';
+import { computed, onMounted, ref, shallowRef, toRefs } from 'vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import {
@@ -82,9 +82,12 @@ type EventType = {
 
 const emit = defineEmits<{
 	(ev: 'submitted', result: MkSystemWebhookResult): void;
+	(ev: 'canceled'): void;
 	(ev: 'closed'): void;
 }>();
 
+const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
+
 const props = defineProps<MkSystemWebhookEditorProps>();
 
 const { mode, id, requiredEvents } = toRefs(props);
@@ -133,12 +136,14 @@ async function onSubmitClicked() {
 			switch (mode.value) {
 				case 'create': {
 					const result = await misskeyApi('admin/system-webhook/create', params);
+					dialogEl.value?.close();
 					emit('submitted', result);
 					break;
 				}
 				case 'edit': {
 					// eslint-disable-next-line
 					const result = await misskeyApi('admin/system-webhook/update', { id: id.value!, ...params });
+					dialogEl.value?.close();
 					emit('submitted', result);
 					break;
 				}
@@ -147,13 +152,15 @@ async function onSubmitClicked() {
 		} catch (ex: any) {
 			const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
 			await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
-			emit('closed');
+			dialogEl.value?.close();
+			emit('canceled');
 		}
 	});
 }
 
 function onCancelClicked() {
-	emit('closed');
+	dialogEl.value?.close();
+	emit('canceled');
 }
 
 async function loadingScope<T>(fn: () => Promise<T>): Promise<T> {
@@ -183,11 +190,12 @@ onMounted(async () => {
 					for (const ev of Object.keys(events.value)) {
 						events.value[ev] = res.on.includes(ev as SystemWebhookEventType);
 					}
-					// eslint-disable-next-line
+					// eslint-disable-next-line @typescript-eslint/no-explicit-any
 				} catch (ex: any) {
 					const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
 					await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
-					emit('closed');
+					dialogEl.value?.close();
+					emit('canceled');
 				}
 				break;
 			}
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index cc3ea78ffe..cbb40924f6 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref } from 'vue';
+import { onMounted, ref, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkInput from '@/components/MkInput.vue';
 import FormSplit from '@/components/form/split.vue';
@@ -91,7 +91,7 @@ const host = ref('');
 const users = ref<Misskey.entities.UserLite[]>([]);
 const recentUsers = ref<Misskey.entities.UserDetailed[]>([]);
 const selected = ref<Misskey.entities.UserLite | null>(null);
-const dialogEl = ref();
+const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
 
 function search() {
 	if (username.value === '' && host.value === '') {
@@ -122,7 +122,7 @@ async function ok() {
 	});
 	emit('ok', user);
 
-	dialogEl.value.close();
+	dialogEl.value?.close();
 
 	// 最近使ったユーザー更新
 	let recents = defaultStore.state.recentlyUsedUsers;
@@ -133,7 +133,7 @@ async function ok() {
 
 function cancel() {
 	emit('cancel');
-	dialogEl.value.close();
+	dialogEl.value?.close();
 }
 
 onMounted(() => {
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
index ffe9c620d6..015109f54e 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkModalWindow
-	ref="dialog"
+	ref="dialogEl"
 	:width="400"
 	:height="490"
 	:withOkButton="false"
@@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted, ref, toRefs } from 'vue';
+import { computed, onMounted, ref, shallowRef, toRefs } from 'vue';
 import { entities } from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
@@ -88,6 +88,7 @@ type NotificationRecipientMethod = 'email' | 'webhook';
 
 const emit = defineEmits<{
 	(ev: 'submitted'): void;
+	(ev: 'canceled'): void;
 	(ev: 'closed'): void;
 }>();
 
@@ -98,6 +99,8 @@ const props = defineProps<{
 
 const { mode, id } = toRefs(props);
 
+const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
+
 const loading = ref<number>(0);
 
 const title = ref<string>('');
@@ -166,18 +169,21 @@ async function onSubmitClicked() {
 				}
 			}
 
+			dialogEl.value?.close();
 			emit('submitted');
-			// eslint-disable-next-line
+			// eslint-disable-next-line @typescript-eslint/no-explicit-any
 		} catch (ex: any) {
 			const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
 			await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
-			emit('closed');
+			dialogEl.value?.close();
+			emit('canceled');
 		}
 	});
 }
 
 function onCancelClicked() {
-	emit('closed');
+	dialogEl.value?.close();
+	emit('canceled');
 }
 
 async function onEditSystemWebhookClicked() {
@@ -262,7 +268,8 @@ onMounted(async () => {
 			} catch (ex: any) {
 				const msg = ex.message ?? i18n.ts.internalServerErrorDescription;
 				await os.alert({ type: 'error', title: i18n.ts.error, text: msg });
-				emit('closed');
+				dialogEl.value?.close();
+				emit('canceled');
 			}
 		} else {
 			userId.value = moderators.value[0]?.id ?? null;
@@ -296,11 +303,13 @@ onMounted(async () => {
 	gap: 8px;
 
 	button {
-		width: 2.5em;
-		height: 2.5em;
-		min-width: 2.5em;
-		min-height: 2.5em;
+		min-width: 0;
+		min-height: 0;
+		width: 34px;
+		height: 34px;
+		flex-shrink: 0;
 		box-sizing: border-box;
+		margin: 1px 0;
 		padding: 6px;
 	}
 }
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
index 93800873f9..f5249261be 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
@@ -108,26 +108,27 @@ async function onDeleteButtonClicked(id: string) {
 }
 
 async function showEditor(mode: 'create' | 'edit', id?: string) {
-	const { dispose, needLoad } = await new Promise<{ dispose: () => void, needLoad: boolean }>(async resolve => {
-		const { dispose: _dispose } = os.popup(
+	const { needLoad } = await new Promise<{ needLoad: boolean }>(async resolve => {
+		const { dispose } = os.popup(
 			defineAsyncComponent(() => import('./notification-recipient.editor.vue')),
 			{
 				mode,
 				id,
 			},
 			{
-				submitted: async () => {
-					resolve({ dispose: _dispose, needLoad: true });
+				submitted: () => {
+					resolve({ needLoad: true });
+				},
+				canceled: () => {
+					resolve({ needLoad: false });
 				},
 				closed: () => {
-					resolve({ dispose: _dispose, needLoad: false });
+					dispose();
 				},
 			},
 		);
 	});
 
-	dispose();
-
 	if (needLoad) {
 		await fetchRecipients();
 	}

From 02ecd1b3712cd6fe011a00cefec6ce3d1cc9db36 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 28 Jul 2024 10:36:12 +0900
Subject: [PATCH 156/206] refactor

---
 packages/frontend/src/components/MkModalWindow.vue | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue
index 78053c8cfd..9dcdf3dcfd 100644
--- a/packages/frontend/src/components/MkModalWindow.vue
+++ b/packages/frontend/src/components/MkModalWindow.vue
@@ -7,12 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkModal ref="modal" :preferType="'dialog'" @click="onBgClick" @closed="emit('closed')" @esc="emit('esc')">
 	<div ref="rootEl" :class="$style.root" :style="{ width: `${width}px`, height: `min(${height}px, 100%)` }">
 		<div ref="headerEl" :class="$style.header">
-			<button v-if="withOkButton" :class="$style.headerButton" class="_button" @click="$emit('close')"><i class="ti ti-x"></i></button>
+			<button v-if="withOkButton" :class="$style.headerButton" class="_button" @click="emit('close')"><i class="ti ti-x"></i></button>
 			<span :class="$style.title">
 				<slot name="header"></slot>
 			</span>
-			<button v-if="!withOkButton" :class="$style.headerButton" class="_button" data-cy-modal-window-close @click="$emit('close')"><i class="ti ti-x"></i></button>
-			<button v-if="withOkButton" :class="$style.headerButton" class="_button" :disabled="okButtonDisabled" @click="$emit('ok')"><i class="ti ti-check"></i></button>
+			<button v-if="!withOkButton" :class="$style.headerButton" class="_button" data-cy-modal-window-close @click="emit('close')"><i class="ti ti-x"></i></button>
+			<button v-if="withOkButton" :class="$style.headerButton" class="_button" :disabled="okButtonDisabled" @click="emit('ok')"><i class="ti ti-check"></i></button>
 		</div>
 		<div :class="$style.body">
 			<slot :width="bodyWidth" :height="bodyHeight"></slot>

From 5df85b8be140171c90057b6af4c8827f6e2a93bc Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 28 Jul 2024 10:39:31 +0900
Subject: [PATCH 157/206] enhance(frontend): add withCloseButton option for
 MkModalWindow

---
 packages/frontend/src/components/MkModalWindow.vue | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue
index 9dcdf3dcfd..c3c7812036 100644
--- a/packages/frontend/src/components/MkModalWindow.vue
+++ b/packages/frontend/src/components/MkModalWindow.vue
@@ -7,11 +7,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <MkModal ref="modal" :preferType="'dialog'" @click="onBgClick" @closed="emit('closed')" @esc="emit('esc')">
 	<div ref="rootEl" :class="$style.root" :style="{ width: `${width}px`, height: `min(${height}px, 100%)` }">
 		<div ref="headerEl" :class="$style.header">
-			<button v-if="withOkButton" :class="$style.headerButton" class="_button" @click="emit('close')"><i class="ti ti-x"></i></button>
+			<button v-if="withOkButton && withCloseButton" :class="$style.headerButton" class="_button" @click="emit('close')"><i class="ti ti-x"></i></button>
 			<span :class="$style.title">
 				<slot name="header"></slot>
 			</span>
-			<button v-if="!withOkButton" :class="$style.headerButton" class="_button" data-cy-modal-window-close @click="emit('close')"><i class="ti ti-x"></i></button>
+			<button v-if="!withOkButton && withCloseButton" :class="$style.headerButton" class="_button" data-cy-modal-window-close @click="emit('close')"><i class="ti ti-x"></i></button>
 			<button v-if="withOkButton" :class="$style.headerButton" class="_button" :disabled="okButtonDisabled" @click="emit('ok')"><i class="ti ti-check"></i></button>
 		</div>
 		<div :class="$style.body">
@@ -27,11 +27,13 @@ import MkModal from './MkModal.vue';
 
 const props = withDefaults(defineProps<{
 	withOkButton: boolean;
+	withCloseButton: boolean;
 	okButtonDisabled: boolean;
 	width: number;
 	height: number;
 }>(), {
 	withOkButton: false,
+	withCloseButton: true,
 	okButtonDisabled: false,
 	width: 400,
 	height: 500,
@@ -51,13 +53,13 @@ const headerEl = shallowRef<HTMLElement>();
 const bodyWidth = ref(0);
 const bodyHeight = ref(0);
 
-const close = () => {
+function close() {
 	modal.value?.close();
-};
+}
 
-const onBgClick = () => {
+function onBgClick() {
 	emit('click');
-};
+}
 
 const ro = new ResizeObserver((entries, observer) => {
 	if (rootEl.value == null || headerEl.value == null) return;

From 085b3abf26d0a7f6de80746a6b3c935814b48836 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 28 Jul 2024 11:14:31 +0900
Subject: [PATCH 158/206] update deps (#14312)

---
 package.json                     |   26 +-
 packages/backend/package.json    |   44 +-
 packages/frontend/package.json   |   88 +-
 packages/misskey-js/package.json |   16 +-
 packages/sw/package.json         |    6 +-
 pnpm-lock.yaml                   | 5436 ++++++++++++++----------------
 6 files changed, 2557 insertions(+), 3059 deletions(-)

diff --git a/package.json b/package.json
index ecf2de39d3..8c04c6a806 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
 		"type": "git",
 		"url": "https://github.com/misskey-dev/misskey.git"
 	},
-	"packageManager": "pnpm@9.5.0",
+	"packageManager": "pnpm@9.6.0",
 	"workspaces": [
 		"packages/frontend",
 		"packages/backend",
@@ -51,24 +51,24 @@
 		"cssnano": "6.1.2",
 		"execa": "8.0.1",
 		"fast-glob": "3.3.2",
-		"ignore-walk": "6.0.4",
+		"ignore-walk": "6.0.5",
 		"js-yaml": "4.1.0",
-		"postcss": "8.4.38",
+		"postcss": "8.4.40",
 		"tar": "6.2.1",
-		"terser": "5.31.1",
-		"typescript": "5.5.3",
-		"esbuild": "0.22.0",
-		"glob": "10.3.12"
+		"terser": "5.31.3",
+		"typescript": "5.5.4",
+		"esbuild": "0.23.0",
+		"glob": "11.0.0"
 	},
 	"devDependencies": {
 		"@misskey-dev/eslint-plugin": "2.0.2",
-		"@types/node": "20.14.9",
-		"@typescript-eslint/eslint-plugin": "7.15.0",
-		"@typescript-eslint/parser": "7.15.0",
+		"@types/node": "20.14.12",
+		"@typescript-eslint/eslint-plugin": "7.17.0",
+		"@typescript-eslint/parser": "7.17.0",
 		"cross-env": "7.0.3",
-		"cypress": "13.13.0",
-		"eslint": "9.6.0",
-		"globals": "15.7.0",
+		"cypress": "13.13.1",
+		"eslint": "9.8.0",
+		"globals": "15.8.0",
 		"ncp": "2.0.0",
 		"start-server-and-test": "2.0.4"
 	},
diff --git a/packages/backend/package.json b/packages/backend/package.json
index b99717d15c..f497610af9 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -65,11 +65,11 @@
 		"utf-8-validate": "6.0.3"
 	},
 	"dependencies": {
-		"@aws-sdk/client-s3": "3.600.0",
-		"@aws-sdk/lib-storage": "3.600.0",
-		"@bull-board/api": "5.20.5",
-		"@bull-board/fastify": "5.20.5",
-		"@bull-board/ui": "5.20.5",
+		"@aws-sdk/client-s3": "3.620.0",
+		"@aws-sdk/lib-storage": "3.620.0",
+		"@bull-board/api": "5.21.1",
+		"@bull-board/fastify": "5.21.1",
+		"@bull-board/ui": "5.21.1",
 		"@discordapp/twemoji": "15.0.3",
 		"@fastify/accepts": "4.3.0",
 		"@fastify/cookie": "9.3.1",
@@ -86,22 +86,22 @@
 		"@nestjs/core": "10.3.10",
 		"@nestjs/testing": "10.3.10",
 		"@peertube/http-signature": "1.7.0",
-		"@sentry/node": "8.13.0",
-		"@sentry/profiling-node": "8.13.0",
-		"@simplewebauthn/server": "10.0.0",
+		"@sentry/node": "8.20.0",
+		"@sentry/profiling-node": "8.20.0",
+		"@simplewebauthn/server": "10.0.1",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.5.0",
 		"@swc/cli": "0.3.12",
 		"@swc/core": "1.6.6",
 		"@twemoji/parser": "15.1.1",
 		"accepts": "1.3.8",
-		"ajv": "8.16.0",
+		"ajv": "8.17.1",
 		"archiver": "7.0.1",
 		"async-mutex": "0.5.0",
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "5.8.3",
+		"bullmq": "5.10.4",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.2",
 		"chalk": "5.3.0",
@@ -115,10 +115,10 @@
 		"fastify": "4.28.1",
 		"fastify-raw-body": "4.3.0",
 		"feed": "4.2.2",
-		"file-type": "19.0.0",
+		"file-type": "19.3.0",
 		"fluent-ffmpeg": "2.1.3",
 		"form-data": "4.0.0",
-		"got": "14.4.1",
+		"got": "14.4.2",
 		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
 		"htmlescape": "1.1.1",
@@ -128,7 +128,7 @@
 		"ipaddr.js": "2.2.0",
 		"is-svg": "5.0.1",
 		"js-yaml": "4.1.0",
-		"jsdom": "24.1.0",
+		"jsdom": "24.1.1",
 		"json5": "2.2.3",
 		"jsonld": "8.3.2",
 		"jsrsasign": "11.1.0",
@@ -177,11 +177,11 @@
 		"tsc-alias": "1.8.10",
 		"tsconfig-paths": "4.2.0",
 		"typeorm": "0.3.20",
-		"typescript": "5.5.3",
+		"typescript": "5.5.4",
 		"ulid": "2.3.0",
 		"vary": "1.1.2",
 		"web-push": "3.6.7",
-		"ws": "8.17.1",
+		"ws": "8.18.0",
 		"xev": "3.0.2"
 	},
 	"devDependencies": {
@@ -201,11 +201,11 @@
 		"@types/jest": "29.5.12",
 		"@types/js-yaml": "4.0.9",
 		"@types/jsdom": "21.1.7",
-		"@types/jsonld": "1.5.14",
+		"@types/jsonld": "1.5.15",
 		"@types/jsrsasign": "10.5.14",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.14.9",
+		"@types/node": "20.14.12",
 		"@types/nodemailer": "6.4.15",
 		"@types/oauth": "0.9.5",
 		"@types/oauth2orize": "1.11.5",
@@ -225,18 +225,18 @@
 		"@types/tmp": "0.2.6",
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
-		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "7.15.0",
-		"@typescript-eslint/parser": "7.15.0",
+		"@types/ws": "8.5.11",
+		"@typescript-eslint/eslint-plugin": "7.17.0",
+		"@typescript-eslint/parser": "7.17.0",
 		"aws-sdk-client-mock": "4.0.1",
 		"cross-env": "7.0.3",
 		"eslint-plugin-import": "2.29.1",
-		"execa": "9.2.0",
+		"execa": "9.3.0",
 		"fkill": "9.0.0",
 		"jest": "29.7.0",
 		"jest-mock": "29.7.0",
 		"nodemon": "3.1.4",
 		"pid-port": "1.0.0",
-		"simple-oauth2": "5.0.1"
+		"simple-oauth2": "5.1.0"
 	}
 }
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index fbeae08aa0..244d117de2 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -27,8 +27,8 @@
 		"@syuilo/aiscript": "0.19.0",
 		"@tabler/icons-webfont": "3.3.0",
 		"@twemoji/parser": "15.1.1",
-		"@vitejs/plugin-vue": "5.0.5",
-		"@vue/compiler-sfc": "3.4.31",
+		"@vitejs/plugin-vue": "5.1.0",
+		"@vue/compiler-sfc": "3.4.34",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.11",
 		"astring": "1.8.6",
 		"broadcast-channel": "7.0.0",
@@ -39,9 +39,9 @@
 		"chartjs-chart-matrix": "2.0.1",
 		"chartjs-plugin-gradient": "0.6.1",
 		"chartjs-plugin-zoom": "2.0.1",
-		"chromatic": "11.5.4",
-		"compare-versions": "6.1.0",
-		"cropperjs": "2.0.0-beta.5",
+		"chromatic": "11.5.6",
+		"compare-versions": "6.1.1",
+		"cropperjs": "2.0.0-rc.1",
 		"date-fns": "2.30.0",
 		"escape-regexp": "0.0.1",
 		"estree-walker": "3.0.3",
@@ -57,85 +57,85 @@
 		"misskey-reversi": "workspace:*",
 		"photoswipe": "5.4.4",
 		"punycode": "2.3.1",
-		"rollup": "4.18.0",
+		"rollup": "4.19.1",
 		"sanitize-html": "2.13.0",
-		"sass": "1.77.6",
-		"shiki": "1.10.0",
+		"sass": "1.77.8",
+		"shiki": "1.12.0",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
-		"three": "0.165.0",
+		"three": "0.167.0",
 		"throttle-debounce": "5.0.2",
 		"tinycolor2": "1.6.0",
 		"tsc-alias": "1.8.10",
 		"tsconfig-paths": "4.2.0",
-		"typescript": "5.5.3",
+		"typescript": "5.5.4",
 		"uuid": "10.0.0",
 		"v-code-diff": "1.12.0",
-		"vite": "5.3.2",
-		"vue": "3.4.31",
+		"vite": "5.3.5",
+		"vue": "3.4.34",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
 		"@misskey-dev/summaly": "5.1.0",
-		"@storybook/addon-actions": "8.1.11",
-		"@storybook/addon-essentials": "8.1.11",
-		"@storybook/addon-interactions": "8.1.11",
-		"@storybook/addon-links": "8.1.11",
-		"@storybook/addon-mdx-gfm": "8.1.11",
-		"@storybook/addon-storysource": "8.1.11",
-		"@storybook/blocks": "8.1.11",
-		"@storybook/components": "8.1.11",
-		"@storybook/core-events": "8.1.11",
-		"@storybook/manager-api": "8.1.11",
-		"@storybook/preview-api": "8.1.11",
-		"@storybook/react": "8.1.11",
-		"@storybook/react-vite": "8.1.11",
-		"@storybook/test": "8.1.11",
-		"@storybook/theming": "8.1.11",
-		"@storybook/types": "8.1.11",
-		"@storybook/vue3": "8.1.11",
+		"@storybook/addon-actions": "8.2.6",
+		"@storybook/addon-essentials": "8.2.6",
+		"@storybook/addon-interactions": "8.2.6",
+		"@storybook/addon-links": "8.2.6",
+		"@storybook/addon-mdx-gfm": "8.2.6",
+		"@storybook/addon-storysource": "8.2.6",
+		"@storybook/blocks": "8.2.6",
+		"@storybook/components": "8.2.6",
+		"@storybook/core-events": "8.2.6",
+		"@storybook/manager-api": "8.2.6",
+		"@storybook/preview-api": "8.2.6",
+		"@storybook/react": "8.2.6",
+		"@storybook/react-vite": "8.2.6",
+		"@storybook/test": "8.2.6",
+		"@storybook/theming": "8.2.6",
+		"@storybook/types": "8.2.6",
+		"@storybook/vue3": "8.2.6",
 		"@storybook/vue3-vite": "8.1.11",
 		"@testing-library/vue": "8.1.0",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
-		"@types/matter-js": "0.19.6",
+		"@types/matter-js": "0.19.7",
 		"@types/micromatch": "4.0.9",
-		"@types/node": "20.14.9",
+		"@types/node": "20.14.12",
 		"@types/punycode": "2.1.4",
 		"@types/sanitize-html": "2.11.0",
 		"@types/seedrandom": "3.0.8",
 		"@types/throttle-debounce": "5.0.2",
 		"@types/tinycolor2": "1.4.6",
 		"@types/uuid": "10.0.0",
-		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "7.15.0",
-		"@typescript-eslint/parser": "7.15.0",
+		"@types/ws": "8.5.11",
+		"@typescript-eslint/eslint-plugin": "7.17.0",
+		"@typescript-eslint/parser": "7.17.0",
 		"@vitest/coverage-v8": "1.6.0",
-		"@vue/runtime-core": "3.4.31",
-		"acorn": "8.12.0",
+		"@vue/runtime-core": "3.4.34",
+		"acorn": "8.12.1",
 		"cross-env": "7.0.3",
-		"cypress": "13.13.0",
+		"cypress": "13.13.1",
 		"eslint-plugin-import": "2.29.1",
-		"eslint-plugin-vue": "9.26.0",
+		"eslint-plugin-vue": "9.27.0",
 		"fast-glob": "3.3.2",
 		"happy-dom": "10.0.3",
 		"intersection-observer": "0.12.2",
 		"micromatch": "4.0.7",
-		"msw": "2.3.1",
-		"msw-storybook-addon": "2.0.2",
+		"msw": "2.3.4",
+		"msw-storybook-addon": "2.0.3",
 		"nodemon": "3.1.4",
-		"prettier": "3.3.2",
+		"prettier": "3.3.3",
 		"react": "18.3.1",
 		"react-dom": "18.3.1",
 		"seedrandom": "3.0.5",
 		"start-server-and-test": "2.0.4",
-		"storybook": "8.1.11",
+		"storybook": "8.2.6",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
 		"vite-plugin-turbosnap": "1.0.3",
 		"vitest": "1.6.0",
 		"vitest-fetch-mock": "0.2.2",
-		"vue-component-type-helpers": "2.0.24",
+		"vue-component-type-helpers": "2.0.29",
 		"vue-eslint-parser": "9.4.3",
-		"vue-tsc": "2.0.24"
+		"vue-tsc": "2.0.29"
 	}
 }
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index aa2e0e781b..a047e50063 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -35,23 +35,23 @@
 		"directory": "packages/misskey-js"
 	},
 	"devDependencies": {
-		"@microsoft/api-extractor": "7.47.0",
+		"@microsoft/api-extractor": "7.47.4",
 		"@swc/jest": "0.2.36",
 		"@types/jest": "29.5.12",
-		"@types/node": "20.14.9",
-		"@typescript-eslint/eslint-plugin": "7.15.0",
-		"@typescript-eslint/parser": "7.15.0",
+		"@types/node": "20.14.12",
+		"@typescript-eslint/eslint-plugin": "7.17.0",
+		"@typescript-eslint/parser": "7.17.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
 		"mock-socket": "9.3.1",
 		"ncp": "2.0.0",
 		"nodemon": "3.1.4",
-		"execa": "9.2.0",
+		"execa": "9.3.0",
 		"tsd": "0.31.1",
-		"typescript": "5.5.3",
-		"esbuild": "0.22.0",
-		"glob": "10.4.2"
+		"typescript": "5.5.4",
+		"esbuild": "0.23.0",
+		"glob": "11.0.0"
 	},
 	"files": [
 		"built"
diff --git a/packages/sw/package.json b/packages/sw/package.json
index bcd642ffc4..9174f50ae3 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,16 +9,16 @@
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"dependencies": {
-		"esbuild": "0.22.0",
+		"esbuild": "0.23.0",
 		"idb-keyval": "6.2.1",
 		"misskey-js": "workspace:*"
 	},
 	"devDependencies": {
-		"@typescript-eslint/parser": "7.15.0",
+		"@typescript-eslint/parser": "7.17.0",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
 		"eslint-plugin-import": "2.29.1",
 		"nodemon": "3.1.4",
-		"typescript": "5.5.3"
+		"typescript": "5.5.4"
 	},
 	"type": "module"
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7d3fec8596..eb8047cde5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,10 +14,10 @@ importers:
     dependencies:
       cssnano:
         specifier: 6.1.2
-        version: 6.1.2(postcss@8.4.38)
+        version: 6.1.2(postcss@8.4.40)
       esbuild:
-        specifier: 0.22.0
-        version: 0.22.0
+        specifier: 0.23.0
+        version: 0.23.0
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -25,55 +25,55 @@ importers:
         specifier: 3.3.2
         version: 3.3.2
       glob:
-        specifier: 10.3.12
-        version: 10.3.12
+        specifier: 11.0.0
+        version: 11.0.0
       ignore-walk:
-        specifier: 6.0.4
-        version: 6.0.4
+        specifier: 6.0.5
+        version: 6.0.5
       js-yaml:
         specifier: 4.1.0
         version: 4.1.0
       postcss:
-        specifier: 8.4.38
-        version: 8.4.38
+        specifier: 8.4.40
+        version: 8.4.40
       tar:
         specifier: 6.2.1
         version: 6.2.1
       terser:
-        specifier: 5.31.1
-        version: 5.31.1
+        specifier: 5.31.3
+        version: 5.31.3
       typescript:
-        specifier: 5.5.3
-        version: 5.5.3
+        specifier: 5.5.4
+        version: 5.5.4
     optionalDependencies:
       '@tensorflow/tfjs-core':
         specifier: 4.4.0
-        version: 4.20.0(encoding@0.1.13)
+        version: 4.4.0(encoding@0.1.13)
     devDependencies:
       '@misskey-dev/eslint-plugin':
         specifier: 2.0.2
-        version: 2.0.2(@eslint/compat@1.1.1)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)
+        version: 2.0.2(@eslint/compat@1.1.1)(@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4))(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0))(eslint@9.8.0)(globals@15.8.0)
       '@types/node':
-        specifier: 20.14.9
-        version: 20.14.9
+        specifier: 20.14.12
+        version: 20.14.12
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.15.0
-        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)
       '@typescript-eslint/parser':
-        specifier: 7.15.0
-        version: 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(eslint@9.8.0)(typescript@5.5.4)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.13.0
-        version: 13.13.0
+        specifier: 13.13.1
+        version: 13.13.1
       eslint:
-        specifier: 9.6.0
-        version: 9.6.0
+        specifier: 9.8.0
+        version: 9.8.0
       globals:
-        specifier: 15.7.0
-        version: 15.7.0
+        specifier: 15.8.0
+        version: 15.8.0
       ncp:
         specifier: 2.0.0
         version: 2.0.0
@@ -84,20 +84,20 @@ importers:
   packages/backend:
     dependencies:
       '@aws-sdk/client-s3':
-        specifier: 3.600.0
-        version: 3.600.0
+        specifier: 3.620.0
+        version: 3.620.0
       '@aws-sdk/lib-storage':
-        specifier: 3.600.0
-        version: 3.600.0(@aws-sdk/client-s3@3.600.0)
+        specifier: 3.620.0
+        version: 3.620.0(@aws-sdk/client-s3@3.620.0)
       '@bull-board/api':
-        specifier: 5.20.5
-        version: 5.20.5(@bull-board/ui@5.20.5)
+        specifier: 5.21.1
+        version: 5.21.1(@bull-board/ui@5.21.1)
       '@bull-board/fastify':
-        specifier: 5.20.5
-        version: 5.20.5
+        specifier: 5.21.1
+        version: 5.21.1
       '@bull-board/ui':
-        specifier: 5.20.5
-        version: 5.20.5
+        specifier: 5.21.1
+        version: 5.21.1
       '@discordapp/twemoji':
         specifier: 15.0.3
         version: 15.0.3
@@ -115,7 +115,7 @@ importers:
         version: 3.0.0
       '@fastify/http-proxy':
         specifier: 9.5.0
-        version: 9.5.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+        version: 9.5.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       '@fastify/multipart':
         specifier: 8.3.0
         version: 8.3.0
@@ -147,14 +147,14 @@ importers:
         specifier: 1.7.0
         version: 1.7.0
       '@sentry/node':
-        specifier: 8.13.0
-        version: 8.13.0
+        specifier: 8.20.0
+        version: 8.20.0
       '@sentry/profiling-node':
-        specifier: 8.13.0
-        version: 8.13.0
+        specifier: 8.20.0
+        version: 8.20.0
       '@simplewebauthn/server':
-        specifier: 10.0.0
-        version: 10.0.0(encoding@0.1.13)
+        specifier: 10.0.1
+        version: 10.0.1(encoding@0.1.13)
       '@sinonjs/fake-timers':
         specifier: 11.2.2
         version: 11.2.2
@@ -174,8 +174,8 @@ importers:
         specifier: 1.3.8
         version: 1.3.8
       ajv:
-        specifier: 8.16.0
-        version: 8.16.0
+        specifier: 8.17.1
+        version: 8.17.1
       archiver:
         specifier: 7.0.1
         version: 7.0.1
@@ -192,8 +192,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 5.8.3
-        version: 5.8.3
+        specifier: 5.10.4
+        version: 5.10.4
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -234,8 +234,8 @@ importers:
         specifier: 4.2.2
         version: 4.2.2
       file-type:
-        specifier: 19.0.0
-        version: 19.0.0
+        specifier: 19.3.0
+        version: 19.3.0
       fluent-ffmpeg:
         specifier: 2.1.3
         version: 2.1.3
@@ -243,8 +243,8 @@ importers:
         specifier: 4.0.0
         version: 4.0.0
       got:
-        specifier: 14.4.1
-        version: 14.4.1
+        specifier: 14.4.2
+        version: 14.4.2
       happy-dom:
         specifier: 10.0.3
         version: 10.0.3
@@ -273,8 +273,8 @@ importers:
         specifier: 4.1.0
         version: 4.1.0
       jsdom:
-        specifier: 24.1.0
-        version: 24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+        specifier: 24.1.1
+        version: 24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       json5:
         specifier: 2.2.3
         version: 2.2.3
@@ -319,7 +319,7 @@ importers:
         version: 6.9.14
       nsfwjs:
         specifier: 2.4.2
-        version: 2.4.2(@tensorflow/tfjs@4.20.0(encoding@0.1.13)(seedrandom@3.0.5))
+        version: 2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5))
       oauth:
         specifier: 0.10.0
         version: 0.10.0
@@ -420,8 +420,8 @@ importers:
         specifier: 0.3.20
         version: 0.3.20(ioredis@5.4.1)(pg@8.12.0)
       typescript:
-        specifier: 5.5.3
-        version: 5.5.3
+        specifier: 5.5.4
+        version: 5.5.4
       ulid:
         specifier: 2.3.0
         version: 2.3.0
@@ -432,8 +432,8 @@ importers:
         specifier: 3.6.7
         version: 3.6.7
       ws:
-        specifier: 8.17.1
-        version: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+        specifier: 8.18.0
+        version: 8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xev:
         specifier: 3.0.2
         version: 3.0.2
@@ -443,46 +443,46 @@ importers:
         version: 1.3.11
       '@swc/core-darwin-arm64':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-darwin-x64':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-freebsd-x64':
         specifier: 1.3.11
         version: 1.3.11
       '@swc/core-linux-arm-gnueabihf':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-linux-arm64-gnu':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-linux-arm64-musl':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-linux-x64-gnu':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-linux-x64-musl':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-win32-arm64-msvc':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-win32-ia32-msvc':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@swc/core-win32-x64-msvc':
         specifier: 1.3.56
-        version: 1.7.0-nightly-20240715.2
+        version: 1.3.56
       '@tensorflow/tfjs':
         specifier: 4.4.0
-        version: 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
+        version: 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
       '@tensorflow/tfjs-node':
         specifier: 4.4.0
-        version: 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
+        version: 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
       bufferutil:
         specifier: 4.0.7
-        version: 4.0.8
+        version: 4.0.7
       slacc-android-arm-eabi:
         specifier: 0.0.10
         version: 0.0.10
@@ -524,7 +524,7 @@ importers:
         version: 0.0.10
       utf-8-validate:
         specifier: 6.0.3
-        version: 6.0.4
+        version: 6.0.3
     devDependencies:
       '@jest/globals':
         specifier: 29.7.0
@@ -575,8 +575,8 @@ importers:
         specifier: 21.1.7
         version: 21.1.7
       '@types/jsonld':
-        specifier: 1.5.14
-        version: 1.5.14
+        specifier: 1.5.15
+        version: 1.5.15
       '@types/jsrsasign':
         specifier: 10.5.14
         version: 10.5.14
@@ -587,8 +587,8 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.14.9
-        version: 20.14.9
+        specifier: 20.14.12
+        version: 20.14.12
       '@types/nodemailer':
         specifier: 6.4.15
         version: 6.4.15
@@ -647,14 +647,14 @@ importers:
         specifier: 3.6.3
         version: 3.6.3
       '@types/ws':
-        specifier: 8.5.10
-        version: 8.5.10
+        specifier: 8.5.11
+        version: 8.5.11
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.15.0
-        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)
       '@typescript-eslint/parser':
-        specifier: 7.15.0
-        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(eslint@9.8.0)(typescript@5.5.4)
       aws-sdk-client-mock:
         specifier: 4.0.1
         version: 4.0.1
@@ -663,16 +663,16 @@ importers:
         version: 7.0.3
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)
+        version: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)
       execa:
-        specifier: 9.2.0
-        version: 9.2.0
+        specifier: 9.3.0
+        version: 9.3.0
       fkill:
         specifier: 9.0.0
         version: 9.0.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.14.9)
+        version: 29.7.0(@types/node@20.14.12)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
@@ -683,8 +683,8 @@ importers:
         specifier: 1.0.0
         version: 1.0.0
       simple-oauth2:
-        specifier: 5.0.1
-        version: 5.0.1
+        specifier: 5.1.0
+        version: 5.1.0
 
   packages/frontend:
     dependencies:
@@ -702,13 +702,13 @@ importers:
         version: 2024.1.0
       '@rollup/plugin-json':
         specifier: 6.1.0
-        version: 6.1.0(rollup@4.18.0)
+        version: 6.1.0(rollup@4.19.1)
       '@rollup/plugin-replace':
         specifier: 5.0.7
-        version: 5.0.7(rollup@4.18.0)
+        version: 5.0.7(rollup@4.19.1)
       '@rollup/pluginutils':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.18.0)
+        version: 5.1.0(rollup@4.19.1)
       '@syuilo/aiscript':
         specifier: 0.19.0
         version: 0.19.0
@@ -719,11 +719,11 @@ importers:
         specifier: 15.1.1
         version: 15.1.1
       '@vitejs/plugin-vue':
-        specifier: 5.0.5
-        version: 5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))
+        specifier: 5.1.0
+        version: 5.1.0(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))
       '@vue/compiler-sfc':
-        specifier: 3.4.31
-        version: 3.4.31
+        specifier: 3.4.34
+        version: 3.4.34
       aiscript-vscode:
         specifier: github:aiscript-dev/aiscript-vscode#v0.1.11
         version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9
@@ -755,14 +755,14 @@ importers:
         specifier: 2.0.1
         version: 2.0.1(chart.js@4.4.3)
       chromatic:
-        specifier: 11.5.4
-        version: 11.5.4
+        specifier: 11.5.6
+        version: 11.5.6
       compare-versions:
-        specifier: 6.1.0
-        version: 6.1.0
+        specifier: 6.1.1
+        version: 6.1.1
       cropperjs:
-        specifier: 2.0.0-beta.5
-        version: 2.0.0-beta.5
+        specifier: 2.0.0-rc.1
+        version: 2.0.0-rc.1
       date-fns:
         specifier: 2.30.0
         version: 2.30.0
@@ -809,17 +809,17 @@ importers:
         specifier: 2.3.1
         version: 2.3.1
       rollup:
-        specifier: 4.18.0
-        version: 4.18.0
+        specifier: 4.19.1
+        version: 4.19.1
       sanitize-html:
         specifier: 2.13.0
         version: 2.13.0
       sass:
-        specifier: 1.77.6
-        version: 1.77.6
+        specifier: 1.77.8
+        version: 1.77.8
       shiki:
-        specifier: 1.10.0
-        version: 1.10.0
+        specifier: 1.12.0
+        version: 1.12.0
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -827,8 +827,8 @@ importers:
         specifier: 3.1.0
         version: 3.1.0
       three:
-        specifier: 0.165.0
-        version: 0.165.0
+        specifier: 0.167.0
+        version: 0.167.0
       throttle-debounce:
         specifier: 5.0.2
         version: 5.0.2
@@ -842,84 +842,84 @@ importers:
         specifier: 4.2.0
         version: 4.2.0
       typescript:
-        specifier: 5.5.3
-        version: 5.5.3
+        specifier: 5.5.4
+        version: 5.5.4
       uuid:
         specifier: 10.0.0
         version: 10.0.0
       v-code-diff:
         specifier: 1.12.0
-        version: 1.12.0(vue@3.4.31(typescript@5.5.3))
+        version: 1.12.0(vue@3.4.34(typescript@5.5.4))
       vite:
-        specifier: 5.3.2
-        version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
+        specifier: 5.3.5
+        version: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
       vue:
-        specifier: 3.4.31
-        version: 3.4.31(typescript@5.5.3)
+        specifier: 3.4.34
+        version: 3.4.34(typescript@5.5.4)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.4.31(typescript@5.5.3))
+        version: 4.1.0(vue@3.4.34(typescript@5.5.4))
     devDependencies:
       '@misskey-dev/summaly':
         specifier: 5.1.0
         version: 5.1.0
       '@storybook/addon-actions':
-        specifier: 8.1.11
-        version: 8.1.11
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/addon-essentials':
-        specifier: 8.1.11
-        version: 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/addon-interactions':
-        specifier: 8.1.11
-        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
+        specifier: 8.2.6
+        version: 8.2.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.12))(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))
       '@storybook/addon-links':
-        specifier: 8.1.11
-        version: 8.1.11(react@18.3.1)
+        specifier: 8.2.6
+        version: 8.2.6(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/addon-mdx-gfm':
-        specifier: 8.1.11
-        version: 8.1.11
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/addon-storysource':
-        specifier: 8.1.11
-        version: 8.1.11
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/blocks':
-        specifier: 8.1.11
-        version: 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.2.6
+        version: 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/components':
-        specifier: 8.1.11
-        version: 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/core-events':
-        specifier: 8.1.11
-        version: 8.1.11
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/manager-api':
-        specifier: 8.1.11
-        version: 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/preview-api':
-        specifier: 8.1.11
-        version: 8.1.11
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/react':
-        specifier: 8.1.11
-        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)
+        specifier: 8.2.6
+        version: 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.4)
       '@storybook/react-vite':
-        specifier: 8.1.11
-        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
+        specifier: 8.2.6
+        version: 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.19.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))
       '@storybook/test':
-        specifier: 8.1.11
-        version: 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
+        specifier: 8.2.6
+        version: 8.2.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.12))(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))
       '@storybook/theming':
-        specifier: 8.1.11
-        version: 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/types':
-        specifier: 8.1.11
-        version: 8.1.11
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/vue3':
-        specifier: 8.1.11
-        version: 8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))
+        specifier: 8.2.6
+        version: 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(vue@3.4.34(typescript@5.5.4))
       '@storybook/vue3-vite':
         specifier: 8.1.11
-        version: 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))
+        version: 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))
       '@testing-library/vue':
         specifier: 8.1.0
-        version: 8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
+        version: 8.1.0(@vue/compiler-sfc@3.4.34)(@vue/server-renderer@3.4.34(vue@3.4.34(typescript@5.5.4)))(vue@3.4.34(typescript@5.5.4))
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -927,14 +927,14 @@ importers:
         specifier: 1.0.5
         version: 1.0.5
       '@types/matter-js':
-        specifier: 0.19.6
-        version: 0.19.6
+        specifier: 0.19.7
+        version: 0.19.7
       '@types/micromatch':
         specifier: 4.0.9
         version: 4.0.9
       '@types/node':
-        specifier: 20.14.9
-        version: 20.14.9
+        specifier: 20.14.12
+        version: 20.14.12
       '@types/punycode':
         specifier: 2.1.4
         version: 2.1.4
@@ -954,35 +954,35 @@ importers:
         specifier: 10.0.0
         version: 10.0.0
       '@types/ws':
-        specifier: 8.5.10
-        version: 8.5.10
+        specifier: 8.5.11
+        version: 8.5.11
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.15.0
-        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)
       '@typescript-eslint/parser':
-        specifier: 7.15.0
-        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(eslint@9.8.0)(typescript@5.5.4)
       '@vitest/coverage-v8':
         specifier: 1.6.0
-        version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
+        version: 1.6.0(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))
       '@vue/runtime-core':
-        specifier: 3.4.31
-        version: 3.4.31
+        specifier: 3.4.34
+        version: 3.4.34
       acorn:
-        specifier: 8.12.0
-        version: 8.12.0
+        specifier: 8.12.1
+        version: 8.12.1
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.13.0
-        version: 13.13.0
+        specifier: 13.13.1
+        version: 13.13.1
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)
+        version: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)
       eslint-plugin-vue:
-        specifier: 9.26.0
-        version: 9.26.0(eslint@9.7.0)
+        specifier: 9.27.0
+        version: 9.27.0(eslint@9.8.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
@@ -996,17 +996,17 @@ importers:
         specifier: 4.0.7
         version: 4.0.7
       msw:
-        specifier: 2.3.1
-        version: 2.3.1(typescript@5.5.3)
+        specifier: 2.3.4
+        version: 2.3.4(typescript@5.5.4)
       msw-storybook-addon:
-        specifier: 2.0.2
-        version: 2.0.2(msw@2.3.1(typescript@5.5.3))
+        specifier: 2.0.3
+        version: 2.0.3(msw@2.3.4(typescript@5.5.4))
       nodemon:
         specifier: 3.1.4
         version: 3.1.4
       prettier:
-        specifier: 3.3.2
-        version: 3.3.2
+        specifier: 3.3.3
+        version: 3.3.3
       react:
         specifier: 18.3.1
         version: 18.3.1
@@ -1020,29 +1020,29 @@ importers:
         specifier: 2.0.4
         version: 2.0.4
       storybook:
-        specifier: 8.1.11
-        version: 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
+        specifier: 8.2.6
+        version: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
-        version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.1.11)(@storybook/manager-api@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.1.11)(@storybook/theming@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.1.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+        version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(3rvqj7p7l43ansgshs3zbslm7u)
       vite-plugin-turbosnap:
         specifier: 1.0.3
         version: 1.0.3
       vitest:
         specifier: 1.6.0
-        version: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
+        version: 1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3)
       vitest-fetch-mock:
         specifier: 0.2.2
-        version: 0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
+        version: 0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))
       vue-component-type-helpers:
-        specifier: 2.0.24
-        version: 2.0.24
+        specifier: 2.0.29
+        version: 2.0.29
       vue-eslint-parser:
         specifier: 9.4.3
-        version: 9.4.3(eslint@9.7.0)
+        version: 9.4.3(eslint@9.8.0)
       vue-tsc:
-        specifier: 2.0.24
-        version: 2.0.24(typescript@5.5.3)
+        specifier: 2.0.29
+        version: 2.0.29(typescript@5.5.4)
 
   packages/misskey-bubble-game:
     dependencies:
@@ -1067,10 +1067,10 @@ importers:
         version: 3.0.8
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.8.0)(typescript@5.3.3))(eslint@9.8.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
-        version: 7.1.0(eslint@9.7.0)(typescript@5.3.3)
+        version: 7.1.0(eslint@9.8.0)(typescript@5.3.3)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
@@ -1097,8 +1097,8 @@ importers:
         version: 4.4.0
     devDependencies:
       '@microsoft/api-extractor':
-        specifier: 7.47.0
-        version: 7.47.0(@types/node@20.14.9)
+        specifier: 7.47.4
+        version: 7.47.4(@types/node@20.14.12)
       '@swc/jest':
         specifier: 0.2.36
         version: 0.2.36(@swc/core@1.6.13)
@@ -1106,26 +1106,26 @@ importers:
         specifier: 29.5.12
         version: 29.5.12
       '@types/node':
-        specifier: 20.14.9
-        version: 20.14.9
+        specifier: 20.14.12
+        version: 20.14.12
       '@typescript-eslint/eslint-plugin':
-        specifier: 7.15.0
-        version: 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)
       '@typescript-eslint/parser':
-        specifier: 7.15.0
-        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(eslint@9.8.0)(typescript@5.5.4)
       esbuild:
-        specifier: 0.22.0
-        version: 0.22.0
+        specifier: 0.23.0
+        version: 0.23.0
       execa:
-        specifier: 9.2.0
-        version: 9.2.0
+        specifier: 9.3.0
+        version: 9.3.0
       glob:
-        specifier: 10.4.2
-        version: 10.4.2
+        specifier: 11.0.0
+        version: 11.0.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.14.9)
+        version: 29.7.0(@types/node@20.14.12)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3(encoding@0.1.13)
@@ -1145,8 +1145,8 @@ importers:
         specifier: 0.31.1
         version: 0.31.1
       typescript:
-        specifier: 5.5.3
-        version: 5.5.3
+        specifier: 5.5.4
+        version: 5.5.4
 
   packages/misskey-js/generator:
     devDependencies:
@@ -1158,10 +1158,10 @@ importers:
         version: 20.9.1
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.8.0)(typescript@5.3.3))(eslint@9.8.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@9.7.0)(typescript@5.3.3)
+        version: 6.11.0(eslint@9.8.0)(typescript@5.3.3)
       openapi-types:
         specifier: 12.1.3
         version: 12.1.3
@@ -1189,10 +1189,10 @@ importers:
         version: 20.11.5
       '@typescript-eslint/eslint-plugin':
         specifier: 7.1.0
-        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)
+        version: 7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.8.0)(typescript@5.3.3))(eslint@9.8.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 7.1.0
-        version: 7.1.0(eslint@9.7.0)(typescript@5.3.3)
+        version: 7.1.0(eslint@9.8.0)(typescript@5.3.3)
       esbuild:
         specifier: 0.19.11
         version: 0.19.11
@@ -1212,8 +1212,8 @@ importers:
   packages/sw:
     dependencies:
       esbuild:
-        specifier: 0.22.0
-        version: 0.22.0
+        specifier: 0.23.0
+        version: 0.23.0
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -1222,27 +1222,23 @@ importers:
         version: link:../misskey-js
     devDependencies:
       '@typescript-eslint/parser':
-        specifier: 7.15.0
-        version: 7.15.0(eslint@9.7.0)(typescript@5.5.3)
+        specifier: 7.17.0
+        version: 7.17.0(eslint@9.8.0)(typescript@5.5.4)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: '@types/serviceworker@0.0.67'
       eslint-plugin-import:
         specifier: 2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)
+        version: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)
       nodemon:
         specifier: 3.1.4
         version: 3.1.4
       typescript:
-        specifier: 5.5.3
-        version: 5.5.3
+        specifier: 5.5.4
+        version: 5.5.4
 
 packages:
 
-  '@aashutoshrathi/word-wrap@1.2.6':
-    resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
-    engines: {node: '>=0.10.0'}
-
   '@adobe/css-tools@4.3.3':
     resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==}
 
@@ -1289,143 +1285,145 @@ packages:
   '@aws-crypto/util@5.2.0':
     resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
 
-  '@aws-sdk/client-s3@3.600.0':
-    resolution: {integrity: sha512-iYoKbJTputbf+ubkX6gSK/y/4uJEBRaXZ18jykLdBQ8UJuGrk2gqvV8h7OlGAhToCeysmmMqM0vDWyLt6lP8nw==}
+  '@aws-sdk/client-s3@3.620.0':
+    resolution: {integrity: sha512-kf3Lqvuq/ciUn4myQjd1a9nhVg95+FEWkIq7pdkgxFoKow8HKj3nuiwI7zYBRTfk0RKXRkJca3GE+3RXpeZSiA==}
     engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/client-sso-oidc@3.600.0':
-    resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/client-sso@3.598.0':
-    resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/client-sts@3.600.0':
-    resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/core@3.598.0':
-    resolution: {integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/credential-provider-env@3.598.0':
-    resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/credential-provider-http@3.598.0':
-    resolution: {integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/credential-provider-ini@3.598.0':
-    resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==}
+  '@aws-sdk/client-sso-oidc@3.620.0':
+    resolution: {integrity: sha512-CWL8aJa6rrNaQXNsLhblGZzbFBrRz4BXAsFBbyqAZEmryr9q/IC7z/ww3nq8CD2UsW+bn89U/XcoP5r1KWUHuQ==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
-      '@aws-sdk/client-sts': ^3.598.0
+      '@aws-sdk/client-sts': ^3.620.0
 
-  '@aws-sdk/credential-provider-node@3.600.0':
-    resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==}
+  '@aws-sdk/client-sso@3.620.0':
+    resolution: {integrity: sha512-J1CvF7u39XwtCK9rPlkW2AA631EPqkb4PjOOj9aZ9LjQmkJ0DkL+9tEqU2XIWcjDd2Z3hS3LBuS8uN7upIkEnQ==}
     engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/credential-provider-process@3.598.0':
-    resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==}
+  '@aws-sdk/client-sts@3.620.0':
+    resolution: {integrity: sha512-pG4SqDHZV/ZbpoVoVtpxo6ZZoqVDbVItC3QUO73UJ3Gemxznd/Ck7kAsyb6/dJkV/Aqm3gt2O5UL7vzQLNHSjw==}
     engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/credential-provider-sso@3.598.0':
-    resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==}
+  '@aws-sdk/core@3.620.0':
+    resolution: {integrity: sha512-5D9tMahxIDDFLULS9/ULa0HuIu7CZSshfj6wmDSmigXzkWyUvHoVIrme2z6eM3Icat/MO3d4WEy3445Vk385gQ==}
     engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/credential-provider-web-identity@3.598.0':
-    resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==}
+  '@aws-sdk/credential-provider-env@3.609.0':
+    resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/credential-provider-http@3.620.0':
+    resolution: {integrity: sha512-BI2BdrSKDmB/2ouB/NJR0PT0x/+5fmoF6XOE78hFBb4F5w/yynGgcJY936dF+oREfpME6ehjB2b0okGg78Scpw==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/credential-provider-ini@3.620.0':
+    resolution: {integrity: sha512-P9fYi6dzZIl8ITC7qAPf5DX9omI3LfA91g3KH+0OUmS3ctP7tN+gNo3HmqlzoqnwPe0pXn1FumYAe1qFl6Yjjg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
-      '@aws-sdk/client-sts': ^3.598.0
+      '@aws-sdk/client-sts': ^3.620.0
 
-  '@aws-sdk/lib-storage@3.600.0':
-    resolution: {integrity: sha512-jjgGMmFykXBAs8YO3ghgnVSjM/uf99jvVQqKJfDjwXUCLPrsZqk14v2WcDCWAXzeAroDvIOVQO1V/RR8fK18Pw==}
+  '@aws-sdk/credential-provider-node@3.620.0':
+    resolution: {integrity: sha512-or8ahy4ysURcWgKX00367DMDTTyMynDEl+FQh4wce66fMyePhFVuoPcRgXzWsi8KYmL95sPCfJFNqBMyFNcgvQ==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/credential-provider-process@3.614.0':
+    resolution: {integrity: sha512-Q0SI0sTRwi8iNODLs5+bbv8vgz8Qy2QdxbCHnPk/6Cx6LMf7i3dqmWquFbspqFRd8QiqxStrblwxrUYZi09tkA==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/credential-provider-sso@3.620.0':
+    resolution: {integrity: sha512-xtIj2hmq3jcKwvGmqhoYapbWeQfFyoQgKBtwD6nx0M6oS5lbFH4rzHhj0gBwatZDjMa35cWtcYVUJCv2/9mWvA==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/credential-provider-web-identity@3.609.0':
+    resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
-      '@aws-sdk/client-s3': ^3.600.0
+      '@aws-sdk/client-sts': ^3.609.0
 
-  '@aws-sdk/middleware-bucket-endpoint@3.598.0':
-    resolution: {integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-expect-continue@3.598.0':
-    resolution: {integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-flexible-checksums@3.598.0':
-    resolution: {integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-host-header@3.598.0':
-    resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-location-constraint@3.598.0':
-    resolution: {integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-logger@3.598.0':
-    resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-recursion-detection@3.598.0':
-    resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-sdk-s3@3.598.0':
-    resolution: {integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-signing@3.598.0':
-    resolution: {integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-ssec@3.598.0':
-    resolution: {integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/middleware-user-agent@3.598.0':
-    resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/region-config-resolver@3.598.0':
-    resolution: {integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/signature-v4-multi-region@3.598.0':
-    resolution: {integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA==}
-    engines: {node: '>=16.0.0'}
-
-  '@aws-sdk/token-providers@3.598.0':
-    resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==}
+  '@aws-sdk/lib-storage@3.620.0':
+    resolution: {integrity: sha512-xUeKH8RrPQqE6J49Yun0qCdu5SGaN5LgyyjWutLeElqR0G3jhmPVrRzFXsHDXbr9S0QVE4V8DjeC17bkEdG4dw==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
-      '@aws-sdk/client-sso-oidc': ^3.598.0
+      '@aws-sdk/client-s3': ^3.620.0
 
-  '@aws-sdk/types@3.598.0':
-    resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==}
+  '@aws-sdk/middleware-bucket-endpoint@3.620.0':
+    resolution: {integrity: sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-expect-continue@3.620.0':
+    resolution: {integrity: sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-flexible-checksums@3.620.0':
+    resolution: {integrity: sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-host-header@3.620.0':
+    resolution: {integrity: sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-location-constraint@3.609.0':
+    resolution: {integrity: sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-logger@3.609.0':
+    resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-recursion-detection@3.620.0':
+    resolution: {integrity: sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-sdk-s3@3.620.0':
+    resolution: {integrity: sha512-AAZ6NLVOx/bP97PYj/afCMeySzxOHocgJG3ZXh6f8MnJcGpZgx8NyRm0vtiYUTFrS2JtU4xV05Dl3j4afV3s4A==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-signing@3.620.0':
+    resolution: {integrity: sha512-gxI7rubiaanUXaLfJ4NybERa9MGPNg2Ycl/OqANsozrBnR3Pw8vqy3EuVImQOyn2pJ2IFvl8ZPoSMHf4pX56FQ==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-ssec@3.609.0':
+    resolution: {integrity: sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/middleware-user-agent@3.620.0':
+    resolution: {integrity: sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/region-config-resolver@3.614.0':
+    resolution: {integrity: sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/signature-v4-multi-region@3.620.0':
+    resolution: {integrity: sha512-yu1pTCqIbkSdaOvmyfW9vV9jWe3pDApkQPZLg4VEN5dXDWRtgQ/amv88myyCEoG14irUN1tsbvytcKzGyEXnhA==}
+    engines: {node: '>=16.0.0'}
+
+  '@aws-sdk/token-providers@3.614.0':
+    resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      '@aws-sdk/client-sso-oidc': ^3.614.0
+
+  '@aws-sdk/types@3.609.0':
+    resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==}
     engines: {node: '>=16.0.0'}
 
   '@aws-sdk/util-arn-parser@3.568.0':
     resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==}
     engines: {node: '>=16.0.0'}
 
-  '@aws-sdk/util-endpoints@3.598.0':
-    resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==}
+  '@aws-sdk/util-endpoints@3.614.0':
+    resolution: {integrity: sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==}
     engines: {node: '>=16.0.0'}
 
   '@aws-sdk/util-locate-window@3.208.0':
     resolution: {integrity: sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==}
     engines: {node: '>=14.0.0'}
 
-  '@aws-sdk/util-user-agent-browser@3.598.0':
-    resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==}
+  '@aws-sdk/util-user-agent-browser@3.609.0':
+    resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==}
 
-  '@aws-sdk/util-user-agent-node@3.598.0':
-    resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==}
+  '@aws-sdk/util-user-agent-node@3.614.0':
+    resolution: {integrity: sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       aws-crt: '>=1.0.0'
@@ -1433,8 +1431,8 @@ packages:
       aws-crt:
         optional: true
 
-  '@aws-sdk/xml-builder@3.598.0':
-    resolution: {integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q==}
+  '@aws-sdk/xml-builder@3.609.0':
+    resolution: {integrity: sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA==}
     engines: {node: '>=16.0.0'}
 
   '@babel/code-frame@7.23.5':
@@ -2176,16 +2174,16 @@ packages:
   '@bcoe/v8-coverage@0.2.3':
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
 
-  '@bull-board/api@5.20.5':
-    resolution: {integrity: sha512-YI95JK5A4/K4KB5VWbQn/CYNB+AO5cZ/BnZ77LxAhsaJ3ssHBN3Au0n3Z4wD7O+78+W3ON9uqGjKnHV6rXBGcQ==}
+  '@bull-board/api@5.21.1':
+    resolution: {integrity: sha512-anzTfhOJ93eraT/GYeyxWpxRMarHwuevn6pPBfdOj0LC2eg98OPnkfdMSjcrpL3qrqsxON0RslS7kuPfCEnX6A==}
     peerDependencies:
-      '@bull-board/ui': 5.20.5
+      '@bull-board/ui': 5.21.1
 
-  '@bull-board/fastify@5.20.5':
-    resolution: {integrity: sha512-tdMR97xbzEzBbMJiJQreJHGdhfOocQn61K/WqM9I038Dk1dBHM5phQJxRJhspvwEJV4jwAayNOZbzuETI7QKwA==}
+  '@bull-board/fastify@5.21.1':
+    resolution: {integrity: sha512-We33yolc70SALjDdF3cjEaLn1L/vrw85eFCsrviESaW3dFVIdB+xn0fdqMFK6NnaC0JjBa3Ypfev4Co+eaZ+1A==}
 
-  '@bull-board/ui@5.20.5':
-    resolution: {integrity: sha512-RV9VlW4qVL1A0Dewpsor4z7ZL9D56OW9LcRYjvXrIU5FSzvTvYKofmrUYoVrNQDs6jGMwJic+dMiW9K8GUU15A==}
+  '@bull-board/ui@5.21.1':
+    resolution: {integrity: sha512-JBDeCqG7j/c3WE0uGMN9snPkRJz9/D6MpTZzyVj7KOxIJwNKPOICNFZbCrCNi7bcJYHDJ2xGTN9OO1mw7i43BQ==}
 
   '@bundled-es-modules/cookie@2.0.0':
     resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==}
@@ -2193,6 +2191,9 @@ packages:
   '@bundled-es-modules/statuses@1.0.1':
     resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==}
 
+  '@bundled-es-modules/tough-cookie@0.1.6':
+    resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==}
+
   '@canvas/image-data@1.0.0':
     resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==}
 
@@ -2200,38 +2201,38 @@ packages:
     resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
     engines: {node: '>=0.1.90'}
 
-  '@cropper/element-canvas@2.0.0-beta.5':
-    resolution: {integrity: sha512-TS+NTVQAyLKBFLIjzFcaFK6V5GaNCNSp8FBjApTD/AosV/dPRlNCsgmdJ/BugwJTJUSowVnLrPmulI35z4npAg==}
+  '@cropper/element-canvas@2.0.0-rc.1':
+    resolution: {integrity: sha512-jRt9OM7cls+zch8U2m7pA9wp8dNOz0EtedGKkqH+DInUYw1+UtonEJirrxyl1YHRgOme5M5DTsDmkUSrUiZN6g==}
 
-  '@cropper/element-crosshair@2.0.0-beta.5':
-    resolution: {integrity: sha512-en3EjiqS/O/uVPVLUanx2ZxvE2n3J5VxGABvBTwQimX4c3kNixq8TUVlsaLdcG7jbehxFpT3S19+tiuZudHqxg==}
+  '@cropper/element-crosshair@2.0.0-rc.1':
+    resolution: {integrity: sha512-xfLelqM8EnRZUf7xEE88RWQQx5erUv7jrzni52bAw3/Ua8HXIz3uAMnkrGKOTBj8K4Rv/mNJY8k1DVAEfHY6Lg==}
 
-  '@cropper/element-grid@2.0.0-beta.5':
-    resolution: {integrity: sha512-uKQExNTOMOGo5d6Tv1NJDbjJHRR/0NgqeROUSt2J8g9ymPP+/MoFdCCf+Nj/KM5pk7/fBEV3HhzUnO8jh1hZfQ==}
+  '@cropper/element-grid@2.0.0-rc.1':
+    resolution: {integrity: sha512-U/BYPl76upd9sXT+pZTFoQzUqWyNxdGs4YR2UtaVCfTMHLDTrssPAedmqEEnHgbqVcr325sIEfVmwWVA+v+8Dg==}
 
-  '@cropper/element-handle@2.0.0-beta.5':
-    resolution: {integrity: sha512-SlaV5/qbEBQLHnuaGD8J0EqSp797m/MMB8V10EUZpv6cznSRxg/SXOj6ROE0ePzo5+0i96Dw+8ZukLilDgc1Xw==}
+  '@cropper/element-handle@2.0.0-rc.1':
+    resolution: {integrity: sha512-GuOHbjkg5CP1+oFzWQeD7VZffUE86dp4gKv5egLxkBEwnQp1VQxjO7L1Wkgj+KsQymoDczsl+x4bF12KDyDg2g==}
 
-  '@cropper/element-image@2.0.0-beta.5':
-    resolution: {integrity: sha512-369ztVaoRS2DN8SaiHZ/bRCz0Snw9ss7PZrX1OQK86fwVhCoeRqCHj48ayfLMdchx+J3RbM5f2g8ePf7ejwOKw==}
+  '@cropper/element-image@2.0.0-rc.1':
+    resolution: {integrity: sha512-ttzawKbUkR2A9U3bc2AN/jbNdszBP/yb83PIc5jekjOs+Z7kUBVdOo1SLIewpQ0DjUzhfCRXWUowP1McVQUXZw==}
 
-  '@cropper/element-selection@2.0.0-beta.5':
-    resolution: {integrity: sha512-l8DvOBAZYytTarpkfhCglhxD+zDQ2acVwIzGwp5r9xR+ERleJHxr2rHYVhowRHT/JZRd94DJBlye91c1uO/XGg==}
+  '@cropper/element-selection@2.0.0-rc.1':
+    resolution: {integrity: sha512-AcRHRbsyt9xRfBD1QRyNDTS+vaYg6uAeuqhk/Ra58pqxlhtoimAV3oQ7uc/edwOlK60f/DxtKCc8rSOYFQ85bQ==}
 
-  '@cropper/element-shade@2.0.0-beta.5':
-    resolution: {integrity: sha512-LGSVLAD1lasFrS+Pd7JnQSJRCMSNnc40UCcjLhscDuRcRHK/ViMglnwCfFxeGnS26kugbDLF5IbYDCLCbykUog==}
+  '@cropper/element-shade@2.0.0-rc.1':
+    resolution: {integrity: sha512-nHv2WujETENoIfxWQn7TYiOnXm5YUnZsoG4r6njK5cxj0gIUfPudUSbjWCQSuB2oxxpeEK8oyTdfOZtP9cxK4g==}
 
-  '@cropper/element-viewer@2.0.0-beta.5':
-    resolution: {integrity: sha512-i4cc+L+j8Gq1L8g1BQWfQ842QxH5T9v2EkIeGuW66SVSBglafxu8gxmSOyRD3hDAMHM3wbJ+XVmFwBHZzlYCvQ==}
+  '@cropper/element-viewer@2.0.0-rc.1':
+    resolution: {integrity: sha512-xTj0BObCygbVWXc7t7FYZ9k2eFyWN360it5uGeAkImXcwINRQGTFcLLOjs6i3SwedI7F1a1yNcTBfoT1B/sNAg==}
 
-  '@cropper/element@2.0.0-beta.5':
-    resolution: {integrity: sha512-+pHX/iYw+Y/HxgpcjvSPBc3+hvJaycznbZdWifnChmDkpLStd6Xu9gO2ful9sSL0uGSjQxUYV4xPyikYJOnfug==}
+  '@cropper/element@2.0.0-rc.1':
+    resolution: {integrity: sha512-OPKgjUgYC2Xmv77vEqtAR6bdfKOW+v9FrSjr4re3u95rcVj6NJ0JidIta41Ipp8KydHTXSmLetq4XDrA+vuIJQ==}
 
-  '@cropper/elements@2.0.0-beta.5':
-    resolution: {integrity: sha512-KWa5/dmJpLcKDJpNlbEQzO9Shz+f4aB0I3y97CqqTf8JSGS6CEKOd9uLywd1eow1r4O0Hwo65ktXPwAEhMWDZg==}
+  '@cropper/elements@2.0.0-rc.1':
+    resolution: {integrity: sha512-6qbtCq3iL3dETVav2XA03a8iLkHXWMIqHFxViMjlLr9CSuDjjaS5wp0JDuGtPv5FHxjsjyQ8Yayt8Ak5p09Zxg==}
 
-  '@cropper/utils@2.0.0-beta.5':
-    resolution: {integrity: sha512-xE7Klel/4WSjhLUeldfROwbWqV/1A3YKrQLqTrs5/X0ath7B05Fmvhr3TNFvN51v2KSx46Ug6xDJzmbg772m1g==}
+  '@cropper/utils@2.0.0-rc.1':
+    resolution: {integrity: sha512-kreB3wdrAhmTEscfB8/j7ksGBgYSKN+28t37CAI0Vb5DvX/aUDPDH+3e2kyD7YE+DIZgdnuY2FsMYJAQ9sTThg==}
 
   '@cypress/request@3.0.0':
     resolution: {integrity: sha512-GKFCqwZwMYmL3IBoNeR2MM1SnxRIGERsQOTWeQKoYBt2JLqcqiy7JXqO894FLrpjZYqGxW92MNwRH2BN56obdQ==}
@@ -2271,8 +2272,8 @@ packages:
     cpu: [ppc64]
     os: [aix]
 
-  '@esbuild/aix-ppc64@0.22.0':
-    resolution: {integrity: sha512-uvQR2crZ/zgzSHDvdygHyNI+ze9zwS8mqz0YtGXotSqvEE0UkYE9s+FZKQNTt1VtT719mfP3vHrUdCpxBNQZhQ==}
+  '@esbuild/aix-ppc64@0.23.0':
+    resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==}
     engines: {node: '>=18'}
     cpu: [ppc64]
     os: [aix]
@@ -2295,8 +2296,8 @@ packages:
     cpu: [arm64]
     os: [android]
 
-  '@esbuild/android-arm64@0.22.0':
-    resolution: {integrity: sha512-UKhPb3o2gAB/bfXcl58ZXTn1q2oVu1rEu/bKrCtmm+Nj5MKUbrOwR5WAixE2v+lk0amWuwPvhnPpBRLIGiq7ig==}
+  '@esbuild/android-arm64@0.23.0':
+    resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==}
     engines: {node: '>=18'}
     cpu: [arm64]
     os: [android]
@@ -2319,8 +2320,8 @@ packages:
     cpu: [arm]
     os: [android]
 
-  '@esbuild/android-arm@0.22.0':
-    resolution: {integrity: sha512-PBnyP+r8vJE4ifxsWys9l+Mc2UY/yYZOpX82eoyGISXXb3dRr0M21v+s4fgRKWMFPMSf/iyowqPW/u7ScSUkjQ==}
+  '@esbuild/android-arm@0.23.0':
+    resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==}
     engines: {node: '>=18'}
     cpu: [arm]
     os: [android]
@@ -2343,8 +2344,8 @@ packages:
     cpu: [x64]
     os: [android]
 
-  '@esbuild/android-x64@0.22.0':
-    resolution: {integrity: sha512-IjTYtvIrjhR41Ijy2dDPgYjQHWG/x/A4KXYbs1fiU3efpRdoxMChK3oEZV6GPzVEzJqxFgcuBaiX1kwEvWUxSw==}
+  '@esbuild/android-x64@0.23.0':
+    resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [android]
@@ -2367,8 +2368,8 @@ packages:
     cpu: [arm64]
     os: [darwin]
 
-  '@esbuild/darwin-arm64@0.22.0':
-    resolution: {integrity: sha512-mqt+Go4y9wRvEz81bhKd9RpHsQR1LwU8Xm6jZRUV/xpM7cIQFbFH6wBCLPTNsdELBvfoHeumud7X78jQQJv2TA==}
+  '@esbuild/darwin-arm64@0.23.0':
+    resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==}
     engines: {node: '>=18'}
     cpu: [arm64]
     os: [darwin]
@@ -2391,8 +2392,8 @@ packages:
     cpu: [x64]
     os: [darwin]
 
-  '@esbuild/darwin-x64@0.22.0':
-    resolution: {integrity: sha512-vTaTQ9OgYc3VTaWtOE5pSuDT6H3d/qSRFRfSBbnxFfzAvYoB3pqKXA0LEbi/oT8GUOEAutspfRMqPj2ezdFaMw==}
+  '@esbuild/darwin-x64@0.23.0':
+    resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [darwin]
@@ -2415,8 +2416,8 @@ packages:
     cpu: [arm64]
     os: [freebsd]
 
-  '@esbuild/freebsd-arm64@0.22.0':
-    resolution: {integrity: sha512-0e1ZgoobJzaGnR4reD7I9rYZ7ttqdh1KPvJWnquUoDJhL0rYwdneeLailBzd2/4g/U5p4e5TIHEWa68NF2hFpQ==}
+  '@esbuild/freebsd-arm64@0.23.0':
+    resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==}
     engines: {node: '>=18'}
     cpu: [arm64]
     os: [freebsd]
@@ -2439,8 +2440,8 @@ packages:
     cpu: [x64]
     os: [freebsd]
 
-  '@esbuild/freebsd-x64@0.22.0':
-    resolution: {integrity: sha512-BFgyYwlCwRWyPQJtkzqq2p6pJbiiWgp0P9PNf7a5FQ1itKY4czPuOMAlFVItirSmEpRPCeImuwePNScZS0pL5Q==}
+  '@esbuild/freebsd-x64@0.23.0':
+    resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [freebsd]
@@ -2463,8 +2464,8 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@esbuild/linux-arm64@0.22.0':
-    resolution: {integrity: sha512-V/K2rctCUgC0PCXpN7AqT4hoazXKgIYugFGu/myk2+pfe6jTW2guz/TBwq4cZ7ESqusR/IzkcQaBkcjquuBWsw==}
+  '@esbuild/linux-arm64@0.23.0':
+    resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==}
     engines: {node: '>=18'}
     cpu: [arm64]
     os: [linux]
@@ -2487,8 +2488,8 @@ packages:
     cpu: [arm]
     os: [linux]
 
-  '@esbuild/linux-arm@0.22.0':
-    resolution: {integrity: sha512-KEMWiA9aGuPUD4BH5yjlhElLgaRXe+Eri6gKBoDazoPBTo1BXc/e6IW5FcJO9DoL19FBeCxgONyh95hLDNepIg==}
+  '@esbuild/linux-arm@0.23.0':
+    resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==}
     engines: {node: '>=18'}
     cpu: [arm]
     os: [linux]
@@ -2511,8 +2512,8 @@ packages:
     cpu: [ia32]
     os: [linux]
 
-  '@esbuild/linux-ia32@0.22.0':
-    resolution: {integrity: sha512-r2ZZqkOMOrpUhzNwxI7uLAHIDwkfeqmTnrv1cjpL/rjllPWszgqmprd/om9oviKXUBpMqHbXmppvjAYgISb26Q==}
+  '@esbuild/linux-ia32@0.23.0':
+    resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==}
     engines: {node: '>=18'}
     cpu: [ia32]
     os: [linux]
@@ -2535,8 +2536,8 @@ packages:
     cpu: [loong64]
     os: [linux]
 
-  '@esbuild/linux-loong64@0.22.0':
-    resolution: {integrity: sha512-qaowLrV/YOMAL2RfKQ4C/VaDzAuLDuylM2sd/LH+4OFirMl6CuDpRlCq4u49ZBaVV8pkI/Y+hTdiibvQRhojCA==}
+  '@esbuild/linux-loong64@0.23.0':
+    resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==}
     engines: {node: '>=18'}
     cpu: [loong64]
     os: [linux]
@@ -2559,8 +2560,8 @@ packages:
     cpu: [mips64el]
     os: [linux]
 
-  '@esbuild/linux-mips64el@0.22.0':
-    resolution: {integrity: sha512-hgrezzjQTRxjkQ5k08J6rtZN5PNnkWx/Rz6Kmj9gnsdCAX1I4Dn4ZPqvFRkXo55Q3pnVQJBwbdtrTO7tMGtyVA==}
+  '@esbuild/linux-mips64el@0.23.0':
+    resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==}
     engines: {node: '>=18'}
     cpu: [mips64el]
     os: [linux]
@@ -2583,8 +2584,8 @@ packages:
     cpu: [ppc64]
     os: [linux]
 
-  '@esbuild/linux-ppc64@0.22.0':
-    resolution: {integrity: sha512-ewxg6FLLUio883XgSjfULEmDl3VPv/TYNnRprVAS3QeGFLdCYdx1tIudBcd7n9jIdk82v1Ajov4jx87qW7h9+g==}
+  '@esbuild/linux-ppc64@0.23.0':
+    resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==}
     engines: {node: '>=18'}
     cpu: [ppc64]
     os: [linux]
@@ -2607,8 +2608,8 @@ packages:
     cpu: [riscv64]
     os: [linux]
 
-  '@esbuild/linux-riscv64@0.22.0':
-    resolution: {integrity: sha512-Az5XbgSJC2lE8XK8pdcutsf9RgdafWdTpUK/+6uaDdfkviw/B4JCwAfh1qVeRWwOohwdsl4ywZrWBNWxwrPLFg==}
+  '@esbuild/linux-riscv64@0.23.0':
+    resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==}
     engines: {node: '>=18'}
     cpu: [riscv64]
     os: [linux]
@@ -2631,8 +2632,8 @@ packages:
     cpu: [s390x]
     os: [linux]
 
-  '@esbuild/linux-s390x@0.22.0':
-    resolution: {integrity: sha512-8j4a2ChT9+V34NNNY9c/gMldutaJFmfMacTPq4KfNKwv2fitBCLYjee7c+Vxaha2nUhPK7cXcZpJtJ3+Y7ZdVQ==}
+  '@esbuild/linux-s390x@0.23.0':
+    resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==}
     engines: {node: '>=18'}
     cpu: [s390x]
     os: [linux]
@@ -2655,8 +2656,8 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@esbuild/linux-x64@0.22.0':
-    resolution: {integrity: sha512-JUQyOnpbAkkRFOk/AhsEemz5TfWN4FJZxVObUlnlNCbe7QBl61ZNfM4cwBXayQA6laMJMUcqLHaYQHAB6YQ95Q==}
+  '@esbuild/linux-x64@0.23.0':
+    resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [linux]
@@ -2679,14 +2680,14 @@ packages:
     cpu: [x64]
     os: [netbsd]
 
-  '@esbuild/netbsd-x64@0.22.0':
-    resolution: {integrity: sha512-11PoCoHXo4HFNbLsXuMB6bpMPWGDiw7xETji6COdJss4SQZLvcgNoeSqWtATRm10Jj1uEHiaIk4N0PiN6x4Fcg==}
+  '@esbuild/netbsd-x64@0.23.0':
+    resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [netbsd]
 
-  '@esbuild/openbsd-arm64@0.22.0':
-    resolution: {integrity: sha512-Ezlhu/YyITmXwKSB+Zu/QqD7cxrjrpiw85cc0Rbd3AWr2wsgp+dWbWOE8MqHaLW9NKMZvuL0DhbJbvzR7F6Zvg==}
+  '@esbuild/openbsd-arm64@0.23.0':
+    resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==}
     engines: {node: '>=18'}
     cpu: [arm64]
     os: [openbsd]
@@ -2709,8 +2710,8 @@ packages:
     cpu: [x64]
     os: [openbsd]
 
-  '@esbuild/openbsd-x64@0.22.0':
-    resolution: {integrity: sha512-ufjdW5tFJGUjlH9j/5cCE9lrwRffyZh+T4vYvoDKoYsC6IXbwaFeV/ENxeNXcxotF0P8CDzoICXVSbJaGBhkrw==}
+  '@esbuild/openbsd-x64@0.23.0':
+    resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [openbsd]
@@ -2733,8 +2734,8 @@ packages:
     cpu: [x64]
     os: [sunos]
 
-  '@esbuild/sunos-x64@0.22.0':
-    resolution: {integrity: sha512-zY6ly/AoSmKnmNTowDJsK5ehra153/5ZhqxNLfq9NRsTTltetr+yHHcQ4RW7QDqw4JC8A1uC1YmeSfK9NRcK1w==}
+  '@esbuild/sunos-x64@0.23.0':
+    resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [sunos]
@@ -2757,8 +2758,8 @@ packages:
     cpu: [arm64]
     os: [win32]
 
-  '@esbuild/win32-arm64@0.22.0':
-    resolution: {integrity: sha512-Kml5F7tv/1Maam0pbbCrvkk9vj046dPej30kFzlhXnhuCtYYBP6FGy/cLbc5yUT1lkZznGLf2OvuvmLjscO5rw==}
+  '@esbuild/win32-arm64@0.23.0':
+    resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==}
     engines: {node: '>=18'}
     cpu: [arm64]
     os: [win32]
@@ -2781,8 +2782,8 @@ packages:
     cpu: [ia32]
     os: [win32]
 
-  '@esbuild/win32-ia32@0.22.0':
-    resolution: {integrity: sha512-IOgwn+mYTM3RrcydP4Og5IpXh+ftN8oF+HELTXSmbWBlujuci4Qa3DTeO+LEErceisI7KUSfEIiX+WOUlpELkw==}
+  '@esbuild/win32-ia32@0.23.0':
+    resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==}
     engines: {node: '>=18'}
     cpu: [ia32]
     os: [win32]
@@ -2805,8 +2806,8 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@esbuild/win32-x64@0.22.0':
-    resolution: {integrity: sha512-4bDHJrk2WHBXJPhy1y80X7/5b5iZTZP3LGcKIlAP1J+KqZ4zQAPMLEzftGyjjfcKbA4JDlPt/+2R/F1ZTeRgrw==}
+  '@esbuild/win32-x64@0.23.0':
+    resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==}
     engines: {node: '>=18'}
     cpu: [x64]
     os: [win32]
@@ -2817,10 +2818,6 @@ packages:
     peerDependencies:
       eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
 
-  '@eslint-community/regexpp@4.10.0':
-    resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
-    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
-
   '@eslint-community/regexpp@4.11.0':
     resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==}
     engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@@ -2833,20 +2830,16 @@ packages:
     resolution: {integrity: sha512-lpHyRyplhGPL5mGEh6M9O5nnKk0Gz4bFI+Zu6tKlPpDUN7XshWvH9C/px4UVm87IAANE0W81CEsNGbS1KlzXpA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@eslint/config-array@0.17.0':
-    resolution: {integrity: sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==}
+  '@eslint/config-array@0.17.1':
+    resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@eslint/eslintrc@3.1.0':
     resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  '@eslint/js@9.6.0':
-    resolution: {integrity: sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
-  '@eslint/js@9.7.0':
-    resolution: {integrity: sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==}
+  '@eslint/js@9.8.0':
+    resolution: {integrity: sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
   '@eslint/object-schema@2.1.4':
@@ -3204,9 +3197,6 @@ packages:
     resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
     engines: {node: '>=6.0.0'}
 
-  '@jridgewell/source-map@0.3.5':
-    resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
-
   '@jridgewell/source-map@0.3.6':
     resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
 
@@ -3255,11 +3245,11 @@ packages:
       '@types/react': '>=16'
       react: '>=16'
 
-  '@microsoft/api-extractor-model@7.29.2':
-    resolution: {integrity: sha512-hAYajOjQan3uslhKJRwvvHIdLJ+ZByKqdSsJ/dgHFxPtEbdKpzMDO8zuW4K5gkSMYl5D0LbNwxkhxr51P2zsmw==}
+  '@microsoft/api-extractor-model@7.29.4':
+    resolution: {integrity: sha512-LHOMxmT8/tU1IiiiHOdHFF83Qsi+V8d0kLfscG4EvQE9cafiR8blOYr8SfkQKWB1wgEilQgXJX3MIA4vetDLZw==}
 
-  '@microsoft/api-extractor@7.47.0':
-    resolution: {integrity: sha512-LT8yvcWNf76EpDC+8/ArTVSYePvuDQ+YbAUrsTcpg3ptiZ93HIcMCozP/JOxDt+rrsFfFHcpfoselKfPyRI0GQ==}
+  '@microsoft/api-extractor@7.47.4':
+    resolution: {integrity: sha512-HKm+P4VNzWwvq1Ey+Jfhhj/3MjsD+ka2hbt8L5AcRM95lu1MFOYnz3XlU7Gr79Q/ZhOb7W/imAKeYrOI0bFydg==}
     hasBin: true
 
   '@microsoft/tsdoc-config@0.17.0':
@@ -3321,10 +3311,6 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@mswjs/cookies@1.1.0':
-    resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==}
-    engines: {node: '>=18'}
-
   '@mswjs/interceptors@0.29.1':
     resolution: {integrity: sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==}
     engines: {node: '>=18'}
@@ -3387,9 +3373,6 @@ packages:
     resolution: {integrity: sha512-XsEZi97+kKykmAiPpY+IpZoHxJY1srqFZp8jDt1/RySzC0kB0iZYt/VMIFqQKpLCARZjD7SOAz2AULtwYlesCA==}
     engines: {node: '>= 10'}
 
-  '@ndelangen/get-tarball@3.0.7':
-    resolution: {integrity: sha512-NqGfTZIZpRFef1GoVaShSSRwDC3vde3ThtTeqFdcYd6ipKqnfEVhjK2hUeHjCQUcptyZr2TONqcloFXM+5QBrQ==}
-
   '@nestjs/common@10.3.10':
     resolution: {integrity: sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg==}
     peerDependencies:
@@ -3512,32 +3495,32 @@ packages:
     peerDependencies:
       '@opentelemetry/api': '>=1.0.0 <1.10.0'
 
-  '@opentelemetry/instrumentation-connect@0.37.0':
-    resolution: {integrity: sha512-SeQktDIH5rNzjiEiazWiJAIXkmnLOnNV7wwHpahrqE0Ph+Z3heqMfxRtoMtbdJSIYLfcNZYO51AjxZ00IXufdw==}
+  '@opentelemetry/instrumentation-connect@0.38.0':
+    resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-express@0.40.1':
-    resolution: {integrity: sha512-+RKMvVe2zw3kIXRup9c1jFu3T4d0fs5aKy015TpiMyoCKX1UMu3Z0lfgYtuyiSTANvg5hZnDbWmQmqSPj9VTvg==}
+  '@opentelemetry/instrumentation-express@0.41.0':
+    resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-fastify@0.37.0':
-    resolution: {integrity: sha512-WRjwzNZgupSzbEYvo9s+QuHJRqZJjVdNxSEpGBwWK8RKLlHGwGVAu0gcc2gPamJWUJsGqPGvahAPWM18ZkWj6A==}
+  '@opentelemetry/instrumentation-fastify@0.38.0':
+    resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-graphql@0.41.0':
-    resolution: {integrity: sha512-R/gXeljgIhaRDKquVkKYT5QHPnFouM8ooyePZEP0kqyaVAedtR1V7NfAUJbxfTG5fBQa5wdmLjvu63+tzRXZCA==}
+  '@opentelemetry/instrumentation-graphql@0.42.0':
+    resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-hapi@0.39.0':
-    resolution: {integrity: sha512-ik2nA9Yj2s2ay+aNY+tJsKCsEx6Tsc2g/MK0iWBW5tibwrWKTy1pdVt5sB3kd5Gkimqj23UV5+FH2JFcQLeKug==}
+  '@opentelemetry/instrumentation-hapi@0.40.0':
+    resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
@@ -3548,62 +3531,62 @@ packages:
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-ioredis@0.41.0':
-    resolution: {integrity: sha512-rxiLloU8VyeJGm5j2fZS8ShVdB82n7VNP8wTwfUQqDwRfHCnkzGr+buKoxuhGD91gtwJ91RHkjHA1Eg6RqsUTg==}
+  '@opentelemetry/instrumentation-ioredis@0.42.0':
+    resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-koa@0.41.0':
-    resolution: {integrity: sha512-mbPnDt7ELvpM2S0vixYUsde7122lgegLOJQxx8iJQbB8YHal/xnTh9v7IfArSVzIDo+E+080hxZyUZD4boOWkw==}
+  '@opentelemetry/instrumentation-koa@0.42.0':
+    resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mongodb@0.45.0':
-    resolution: {integrity: sha512-xnZP9+ayeB1JJyNE9cIiwhOJTzNEsRhXVdLgfzmrs48Chhhk026mQdM5CITfyXSCfN73FGAIB8d91+pflJEfWQ==}
+  '@opentelemetry/instrumentation-mongodb@0.46.0':
+    resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mongoose@0.39.0':
-    resolution: {integrity: sha512-J1r66A7zJklPPhMtrFOO7/Ud2p0Pv5u8+r23Cd1JUH6fYPmftNJVsLp2urAt6PHK4jVqpP/YegN8wzjJ2mZNPQ==}
+  '@opentelemetry/instrumentation-mongoose@0.40.0':
+    resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mysql2@0.39.0':
-    resolution: {integrity: sha512-Iypuq2z6TCfriAXCIZjRq8GTFCKhQv5SpXbmI+e60rYdXw8NHtMH4NXcGF0eKTuoCsC59IYSTUvDQYDKReaszA==}
+  '@opentelemetry/instrumentation-mysql2@0.40.0':
+    resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-mysql@0.39.0':
-    resolution: {integrity: sha512-8snHPh83rhrDf31v9Kq0Nf+ts8hdr7NguuszRqZomZBHgE0+UyXZSkXHAAFZoBPPRMGyM68uaFE5hVtFl+wOcA==}
+  '@opentelemetry/instrumentation-mysql@0.40.0':
+    resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-nestjs-core@0.38.0':
-    resolution: {integrity: sha512-M381Df1dM8aqihZz2yK+ugvMFK5vlHG/835dc67Sx2hH4pQEQYDA2PpFPTgc9AYYOydQaj7ClFQunESimjXDgg==}
+  '@opentelemetry/instrumentation-nestjs-core@0.39.0':
+    resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-pg@0.42.0':
-    resolution: {integrity: sha512-sjgcM8CswYy8zxHgXv4RAZ09DlYhQ+9TdlourUs63Df/ek5RrB1ZbjznqW7PB6c3TyJJmX6AVtPTjAsROovEjA==}
+  '@opentelemetry/instrumentation-pg@0.43.0':
+    resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation-redis-4@0.40.0':
-    resolution: {integrity: sha512-0ieQYJb6yl35kXA75LQUPhHtGjtQU9L85KlWa7d4ohBbk/iQKZ3X3CFl5jC5vNMq/GGPB3+w3IxNvALlHtrp7A==}
+  '@opentelemetry/instrumentation-redis-4@0.41.0':
+    resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
 
-  '@opentelemetry/instrumentation@0.43.0':
-    resolution: {integrity: sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ==}
+  '@opentelemetry/instrumentation@0.46.0':
+    resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==}
     engines: {node: '>=14'}
     peerDependencies:
       '@opentelemetry/api': ^1.3.0
@@ -3679,170 +3662,8 @@ packages:
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
-  '@prisma/instrumentation@5.16.0':
-    resolution: {integrity: sha512-MVzNRW2ikWvVNnMIEgQMcwWxpFD+XF2U2h0Qz7MjutRqJxrhWexWV2aSi2OXRaU8UL5wzWw7pnjdKUzYhWauLg==}
-
-  '@radix-ui/primitive@1.1.0':
-    resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==}
-
-  '@radix-ui/react-compose-refs@1.1.0':
-    resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-context@1.1.0':
-    resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-dialog@1.1.1':
-    resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==}
-    peerDependencies:
-      '@types/react': '*'
-      '@types/react-dom': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-      '@types/react-dom':
-        optional: true
-
-  '@radix-ui/react-dismissable-layer@1.1.0':
-    resolution: {integrity: sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==}
-    peerDependencies:
-      '@types/react': '*'
-      '@types/react-dom': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-      '@types/react-dom':
-        optional: true
-
-  '@radix-ui/react-focus-guards@1.1.0':
-    resolution: {integrity: sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-focus-scope@1.1.0':
-    resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==}
-    peerDependencies:
-      '@types/react': '*'
-      '@types/react-dom': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-      '@types/react-dom':
-        optional: true
-
-  '@radix-ui/react-id@1.1.0':
-    resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-portal@1.1.1':
-    resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==}
-    peerDependencies:
-      '@types/react': '*'
-      '@types/react-dom': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-      '@types/react-dom':
-        optional: true
-
-  '@radix-ui/react-presence@1.1.0':
-    resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==}
-    peerDependencies:
-      '@types/react': '*'
-      '@types/react-dom': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-      '@types/react-dom':
-        optional: true
-
-  '@radix-ui/react-primitive@2.0.0':
-    resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
-    peerDependencies:
-      '@types/react': '*'
-      '@types/react-dom': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-      '@types/react-dom':
-        optional: true
-
-  '@radix-ui/react-slot@1.1.0':
-    resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-use-callback-ref@1.1.0':
-    resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-use-controllable-state@1.1.0':
-    resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-use-escape-keydown@1.1.0':
-    resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  '@radix-ui/react-use-layout-effect@1.1.0':
-    resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
+  '@prisma/instrumentation@5.17.0':
+    resolution: {integrity: sha512-c1Sle4ji8aasMcYfBBHFM56We4ljfenVtRmS8aY06BllS7SoU6SmJBwG7vil+GHiR0Yrh+t9iBwt4AY0Jr4KNQ==}
 
   '@readme/better-ajv-errors@1.6.0':
     resolution: {integrity: sha512-9gO9rld84Jgu13kcbKRU+WHseNhaVt76wYMeRDGsUGYxwJtI3RmEJ9LY9dZCYQGI8eUZLuxb5qDja0nqklpFjQ==}
@@ -3886,121 +3707,121 @@ packages:
       rollup:
         optional: true
 
-  '@rollup/rollup-android-arm-eabi@4.18.0':
-    resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==}
+  '@rollup/rollup-android-arm-eabi@4.19.1':
+    resolution: {integrity: sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==}
     cpu: [arm]
     os: [android]
 
-  '@rollup/rollup-android-arm64@4.18.0':
-    resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==}
+  '@rollup/rollup-android-arm64@4.19.1':
+    resolution: {integrity: sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==}
     cpu: [arm64]
     os: [android]
 
-  '@rollup/rollup-darwin-arm64@4.18.0':
-    resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==}
+  '@rollup/rollup-darwin-arm64@4.19.1':
+    resolution: {integrity: sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==}
     cpu: [arm64]
     os: [darwin]
 
-  '@rollup/rollup-darwin-x64@4.18.0':
-    resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==}
+  '@rollup/rollup-darwin-x64@4.19.1':
+    resolution: {integrity: sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==}
     cpu: [x64]
     os: [darwin]
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.18.0':
-    resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==}
+  '@rollup/rollup-linux-arm-gnueabihf@4.19.1':
+    resolution: {integrity: sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm-musleabihf@4.18.0':
-    resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==}
+  '@rollup/rollup-linux-arm-musleabihf@4.19.1':
+    resolution: {integrity: sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-gnu@4.18.0':
-    resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==}
+  '@rollup/rollup-linux-arm64-gnu@4.19.1':
+    resolution: {integrity: sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-musl@4.18.0':
-    resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==}
+  '@rollup/rollup-linux-arm64-musl@4.19.1':
+    resolution: {integrity: sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.18.0':
-    resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==}
+  '@rollup/rollup-linux-powerpc64le-gnu@4.19.1':
+    resolution: {integrity: sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==}
     cpu: [ppc64]
     os: [linux]
 
-  '@rollup/rollup-linux-riscv64-gnu@4.18.0':
-    resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==}
+  '@rollup/rollup-linux-riscv64-gnu@4.19.1':
+    resolution: {integrity: sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==}
     cpu: [riscv64]
     os: [linux]
 
-  '@rollup/rollup-linux-s390x-gnu@4.18.0':
-    resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==}
+  '@rollup/rollup-linux-s390x-gnu@4.19.1':
+    resolution: {integrity: sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==}
     cpu: [s390x]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-gnu@4.18.0':
-    resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==}
+  '@rollup/rollup-linux-x64-gnu@4.19.1':
+    resolution: {integrity: sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-musl@4.18.0':
-    resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==}
+  '@rollup/rollup-linux-x64-musl@4.19.1':
+    resolution: {integrity: sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-win32-arm64-msvc@4.18.0':
-    resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==}
+  '@rollup/rollup-win32-arm64-msvc@4.19.1':
+    resolution: {integrity: sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==}
     cpu: [arm64]
     os: [win32]
 
-  '@rollup/rollup-win32-ia32-msvc@4.18.0':
-    resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==}
+  '@rollup/rollup-win32-ia32-msvc@4.19.1':
+    resolution: {integrity: sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==}
     cpu: [ia32]
     os: [win32]
 
-  '@rollup/rollup-win32-x64-msvc@4.18.0':
-    resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==}
+  '@rollup/rollup-win32-x64-msvc@4.19.1':
+    resolution: {integrity: sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==}
     cpu: [x64]
     os: [win32]
 
-  '@rushstack/node-core-library@5.4.1':
-    resolution: {integrity: sha512-WNnwdS8r9NZ/2K3u29tNoSRldscFa7SxU0RT+82B6Dy2I4Hl2MeCSKm4EXLXPKeNzLGvJ1cqbUhTLviSF8E6iA==}
+  '@rushstack/node-core-library@5.5.1':
+    resolution: {integrity: sha512-ZutW56qIzH8xIOlfyaLQJFx+8IBqdbVCZdnj+XT1MorQ1JqqxHse8vbCpEM+2MjsrqcbxcgDIbfggB1ZSQ2A3g==}
     peerDependencies:
       '@types/node': '*'
     peerDependenciesMeta:
       '@types/node':
         optional: true
 
-  '@rushstack/rig-package@0.5.2':
-    resolution: {integrity: sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==}
+  '@rushstack/rig-package@0.5.3':
+    resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==}
 
-  '@rushstack/terminal@0.13.0':
-    resolution: {integrity: sha512-Ou44Q2s81BqJu3dpYedAX54am9vn245F0HzqVrfJCMQk5pGgoKKOBOjkbfZC9QKcGNaECh6pwH2s5noJt7X6ew==}
+  '@rushstack/terminal@0.13.3':
+    resolution: {integrity: sha512-fc3zjXOw8E0pXS5t9vTiIPx9gHA0fIdTXsu9mT4WbH+P3mYvnrX0iAQ5a6NvyK1+CqYWBTw/wVNx7SDJkI+WYQ==}
     peerDependencies:
       '@types/node': '*'
     peerDependenciesMeta:
       '@types/node':
         optional: true
 
-  '@rushstack/ts-command-line@4.22.0':
-    resolution: {integrity: sha512-Qj28t6MO3HRgAZ72FDeFsrpdE6wBWxF3VENgvrXh7JF2qIT+CrXiOJIesW80VFZB9QwObSpkB1ilx794fGQg6g==}
+  '@rushstack/ts-command-line@4.22.3':
+    resolution: {integrity: sha512-edMpWB3QhFFZ4KtSzS8WNjBgR4PXPPOVrOHMbb7kNpmQ1UFS9HdVtjCXg1H5fG+xYAbeE+TMPcVPUyX2p84STA==}
 
   '@sec-ant/readable-stream@0.4.1':
     resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
 
-  '@sentry/core@8.13.0':
-    resolution: {integrity: sha512-N9Qg4ZGxZWp8eb2eUUHVVKgjBLtFIjS805nG92s6yJmkvOpKm6mLtcUaT/iDf3Hta6nG+xRkhbE3r+Z4cbXG8w==}
+  '@sentry/core@8.20.0':
+    resolution: {integrity: sha512-R81snuw+67VT4aCxr6ShST/s0Y6FlwN2YczhDwaGyzumn5rlvA6A4JtQDeExduNoDDyv4T3LrmW8wlYZn3CJJw==}
     engines: {node: '>=14.18'}
 
-  '@sentry/node@8.13.0':
-    resolution: {integrity: sha512-OeZ7K90RhyxfwfreerIi4cszzHrPRRH36STJno2+p3sIGbG5VScOccqXzYEOAqHpByxnti4KQN34BLAT2BFOEA==}
+  '@sentry/node@8.20.0':
+    resolution: {integrity: sha512-i4ywT2m0Gw65U3uwI4NwiNcyqp9YF6/RsusfH1pg4YkiL/RYp7FS0MPVgMggfvoue9S3KjCgRVlzTLwFATyPXQ==}
     engines: {node: '>=14.18'}
 
-  '@sentry/opentelemetry@8.13.0':
-    resolution: {integrity: sha512-NYn/HNE/SxFXe8pfnxJknhrrRzYRMHNssCoi5M1CeR5G7F2BGxxVmaGsd8j0WyTCpUS4i97G4vhYtDGxHvWN6w==}
+  '@sentry/opentelemetry@8.20.0':
+    resolution: {integrity: sha512-NFcLK6+t9wUc4HlGKeuDn6W4KjZxZfZmWlrK2/tgC5KzG1cnVeOnWUrJzGHTa+YDDdIijpjiFUcpXGPkX3rmIg==}
     engines: {node: '>=14.18'}
     peerDependencies:
       '@opentelemetry/api': ^1.9.0
@@ -4009,21 +3830,21 @@ packages:
       '@opentelemetry/sdk-trace-base': ^1.25.1
       '@opentelemetry/semantic-conventions': ^1.25.1
 
-  '@sentry/profiling-node@8.13.0':
-    resolution: {integrity: sha512-6qirN71xlMahcm4m25XZLnjQdvs0EaFym/9MdLqcsAa3gAHZtw6h+IDapUzBWRXVOrU1OR5oQdh2tlFthsDtew==}
+  '@sentry/profiling-node@8.20.0':
+    resolution: {integrity: sha512-vQaMYjPM7o0qvmj4atxXZywIDhnxMwTlc6x24eKqT8zN0OFBuIc1nYIacT7pEmd7R6e/mXdiG04GH1Vg0bHfOQ==}
     engines: {node: '>=14.18'}
     hasBin: true
 
-  '@sentry/types@8.13.0':
-    resolution: {integrity: sha512-r63s/H5gvQnQM9tTGBXz2xErUbxZALh4e2Lg/1aHj4zIvGLBjA2z5qWsh6TEZYbpmgAyGShLDr6+rWeUVf9yBQ==}
+  '@sentry/types@8.20.0':
+    resolution: {integrity: sha512-6IP278KojOpiAA7vrd1hjhUyn26cl0n0nGsShzic5ztCVs92sTeVRnh7MTB9irDVtAbOEyt/YH6go3h+Jia1pA==}
     engines: {node: '>=14.18'}
 
-  '@sentry/utils@8.13.0':
-    resolution: {integrity: sha512-PxV0v9VbGWH9zP37P5w2msLUFDr287nYjoY2XVF+RSolyiTs1CQNI5ZMUO3o4MsSac/dpXxjyrZXQd72t/jRYA==}
+  '@sentry/utils@8.20.0':
+    resolution: {integrity: sha512-+1I5H8dojURiEUGPliDwheQk8dhjp8uV1sMccR/W/zjFrt4wZyPs+Ttp/V7gzm9LDJoNek9tmELert/jQqWTgg==}
     engines: {node: '>=14.18'}
 
-  '@shikijs/core@1.10.0':
-    resolution: {integrity: sha512-BZcr6FCmPfP6TXaekvujZcnkFmJHZ/Yglu97r/9VjzVndQA56/F4WjUKtJRQUnK59Wi7p/UTAOekMfCJv7jnYg==}
+  '@shikijs/core@1.12.0':
+    resolution: {integrity: sha512-mc1cLbm6UQ8RxLc0dZES7v5rkH+99LxQp/ZvTqV3NLyYsO/fD6JhEflP1H5b2SDq9gI0+0G36AVZWxvounfR9w==}
 
   '@sideway/address@4.1.4':
     resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
@@ -4034,8 +3855,8 @@ packages:
   '@sideway/pinpoint@2.0.0':
     resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
 
-  '@simplewebauthn/server@10.0.0':
-    resolution: {integrity: sha512-w5eIoiF7ltg1sgggjY5Tx654j+DBuyEx2B3869jjmPp0xl2Z4BUP4kJ3yJ6DnZIv+ZYYntT3E6nZXNjPOQbrtw==}
+  '@simplewebauthn/server@10.0.1':
+    resolution: {integrity: sha512-djNWcRn+H+6zvihBFJSpG3fzb0NQS9c/Mw5dYOtZ9H+oDw8qn9Htqxt4cpqRvSOAfwqP7rOvE9rwqVaoGGc3hg==}
     engines: {node: '>=20.0.0'}
 
   '@simplewebauthn/types@10.0.0':
@@ -4052,9 +3873,9 @@ packages:
     resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
     engines: {node: '>=14.16'}
 
-  '@sindresorhus/is@6.3.1':
-    resolution: {integrity: sha512-FX4MfcifwJyFOI2lPoX7PQxCqx8BG1HCho7WdiXwpEQx1Ycij0JxkfYtGK7yqNScrZGSlt6RE6sw8QYoH7eKnQ==}
-    engines: {node: '>=16'}
+  '@sindresorhus/is@7.0.0':
+    resolution: {integrity: sha512-WDTlVTyvFivSOuyvMeedzg2hdoBLZ3f1uNVuEida2Rl9BrfjrIRjWA/VZIrMRLvSwJYCAlCRA3usDt1THytxWQ==}
+    engines: {node: '>=18'}
 
   '@sindresorhus/merge-streams@2.3.0':
     resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
@@ -4096,23 +3917,23 @@ packages:
   '@smithy/chunked-blob-reader@3.0.0':
     resolution: {integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==}
 
-  '@smithy/config-resolver@3.0.4':
-    resolution: {integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==}
+  '@smithy/config-resolver@3.0.5':
+    resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/core@2.2.4':
-    resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==}
+  '@smithy/core@2.3.1':
+    resolution: {integrity: sha512-BC7VMXx/1BCmRPCVzzn4HGWAtsrb7/0758EtwOGFJQrlSwJBEjCcDLNZLFoL/68JexYa2s+KmgL/UfmXdG6v1w==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/credential-provider-imds@3.1.3':
-    resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==}
+  '@smithy/credential-provider-imds@3.2.0':
+    resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/eventstream-codec@3.1.2':
     resolution: {integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==}
 
-  '@smithy/eventstream-serde-browser@3.0.4':
-    resolution: {integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==}
+  '@smithy/eventstream-serde-browser@3.0.5':
+    resolution: {integrity: sha512-dEyiUYL/ekDfk+2Ra4GxV+xNnFoCmk1nuIXg+fMChFTrM2uI/1r9AdiTYzPqgb72yIv/NtAj6C3dG//1wwgakQ==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/eventstream-serde-config-resolver@3.0.3':
@@ -4127,8 +3948,8 @@ packages:
     resolution: {integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/fetch-http-handler@3.2.0':
-    resolution: {integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==}
+  '@smithy/fetch-http-handler@3.2.4':
+    resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==}
 
   '@smithy/hash-blob-browser@3.1.2':
     resolution: {integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==}
@@ -4155,16 +3976,16 @@ packages:
   '@smithy/md5-js@3.0.3':
     resolution: {integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==}
 
-  '@smithy/middleware-content-length@3.0.3':
-    resolution: {integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==}
+  '@smithy/middleware-content-length@3.0.5':
+    resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/middleware-endpoint@3.0.4':
-    resolution: {integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==}
+  '@smithy/middleware-endpoint@3.1.0':
+    resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/middleware-retry@3.0.7':
-    resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==}
+  '@smithy/middleware-retry@3.0.13':
+    resolution: {integrity: sha512-zvCLfaRYCaUmjbF2yxShGZdolSHft7NNCTA28HVN9hKcEbOH+g5irr1X9s+in8EpambclGnevZY4A3lYpvDCFw==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/middleware-serde@3.0.3':
@@ -4175,16 +3996,16 @@ packages:
     resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/node-config-provider@3.1.3':
-    resolution: {integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==}
+  '@smithy/node-config-provider@3.1.4':
+    resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/node-http-handler@2.5.0':
     resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/node-http-handler@3.1.1':
-    resolution: {integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==}
+  '@smithy/node-http-handler@3.1.4':
+    resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/property-provider@3.1.3':
@@ -4195,8 +4016,8 @@ packages:
     resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==}
     engines: {node: '>=14.0.0'}
 
-  '@smithy/protocol-http@4.0.3':
-    resolution: {integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==}
+  '@smithy/protocol-http@4.1.0':
+    resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/querystring-builder@2.2.0':
@@ -4215,16 +4036,16 @@ packages:
     resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/shared-ini-file-loader@3.1.3':
-    resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==}
+  '@smithy/shared-ini-file-loader@3.1.4':
+    resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/signature-v4@3.1.2':
-    resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==}
+  '@smithy/signature-v4@4.1.0':
+    resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/smithy-client@3.1.5':
-    resolution: {integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==}
+  '@smithy/smithy-client@3.1.11':
+    resolution: {integrity: sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/types@2.12.0':
@@ -4261,16 +4082,16 @@ packages:
     resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/util-defaults-mode-browser@3.0.7':
-    resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==}
+  '@smithy/util-defaults-mode-browser@3.0.13':
+    resolution: {integrity: sha512-ZIRSUsnnMRStOP6OKtW+gCSiVFkwnfQF2xtf32QKAbHR6ACjhbAybDvry+3L5qQYdh3H6+7yD/AiUE45n8mTTw==}
     engines: {node: '>= 10.0.0'}
 
-  '@smithy/util-defaults-mode-node@3.0.7':
-    resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==}
+  '@smithy/util-defaults-mode-node@3.0.13':
+    resolution: {integrity: sha512-voUa8TFJGfD+U12tlNNLCDlXibt9vRdNzRX45Onk/WxZe7TS+hTOZouEZRa7oARGicdgeXvt1A0W45qLGYdy+g==}
     engines: {node: '>= 10.0.0'}
 
-  '@smithy/util-endpoints@2.0.4':
-    resolution: {integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==}
+  '@smithy/util-endpoints@2.0.5':
+    resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/util-hex-encoding@3.0.0':
@@ -4285,8 +4106,8 @@ packages:
     resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==}
     engines: {node: '>=16.0.0'}
 
-  '@smithy/util-stream@3.0.5':
-    resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==}
+  '@smithy/util-stream@3.1.3':
+    resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==}
     engines: {node: '>=16.0.0'}
 
   '@smithy/util-uri-escape@2.2.0':
@@ -4312,58 +4133,86 @@ packages:
   '@sqltools/formatter@1.2.5':
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
 
-  '@storybook/addon-actions@8.1.11':
-    resolution: {integrity: sha512-jqYXgBgOVInStOCk//AA+dGkrfN8R7rDXA4lyu82zM59kvICtG9iqgmkSRDn0Z3zUkM+lIHZGoz0aLVQ8pxsgw==}
+  '@storybook/addon-actions@8.2.6':
+    resolution: {integrity: sha512-iCsf3V28/jJ95w2zd8aSvR4denoA2UYV3fpNCTGOURqICyKOG3cyVxvqKp8Hhcwn7trNOsK+HlL6q5gpv56ViA==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-backgrounds@8.1.11':
-    resolution: {integrity: sha512-naGf1ovmsU2pSWb270yRO1IidnO+0YCZ5Tcb8I4rPhZ0vsdXNURYKS1LPSk1OZkvaUXdeB4Im9HhHfUBJOW9oQ==}
+  '@storybook/addon-backgrounds@8.2.6':
+    resolution: {integrity: sha512-61NFowA6EmCw+Eyzp0U4fat9MlPDdnT7aoDyzqSImLwWLITY9IvmWuTeo7XKJZN3fe22z1r7cZseKdYrtaHcKw==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-controls@8.1.11':
-    resolution: {integrity: sha512-q/Vt4meNVlFlBWIMCJhx6r+bqiiYocCta2RoUK5nyIZUiLzHncKHX6JnCU36EmJzRyah9zkwjfCb2G1r9cjnoQ==}
+  '@storybook/addon-controls@8.2.6':
+    resolution: {integrity: sha512-EHUwHy+oZZv3pXzN7fuXWrS/meHFjqcELY3RBvOyEkGf21agl6co6R1tnf6d5N5QoYAGfIbDO7dkauSL2RfNAw==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-docs@8.1.11':
-    resolution: {integrity: sha512-69dv+CE4R5wFU7xnJmhuyEbLN2PEVDV3N/BbgJqeucIYPmm6zDV83Q66teCHKYtRln3BFUqPH5mxsjiHobxfJQ==}
+  '@storybook/addon-docs@8.2.6':
+    resolution: {integrity: sha512-qe7hxntaezqjKdU9QS+Q9NFL6i/uNdBxdvOnCKgPhBAY/zY6yhk5t3sOvonynPK5nkaNAowfSNPIzNxAXlJ1sA==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-essentials@8.1.11':
-    resolution: {integrity: sha512-uRTpcIZQnflML8H+2onicUNIIssKfuviW8Lyrs/KFwSZ1rMcYzhwzCNbGlIbAv04tgHe5NqEyNhb+DVQcZQBzg==}
+  '@storybook/addon-essentials@8.2.6':
+    resolution: {integrity: sha512-diGjGZcZNov+RCAVQBTm8JKP2kUtMRuJIQFBeXdPWpu6hYBk6lw1FlAf2GywWGCvdny1pJT90hfoD33qUMNuDg==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-highlight@8.1.11':
-    resolution: {integrity: sha512-Iu8FCAd4ETsB6QF4xDE/OLLZY3HOFopuLM5KE0f58jnccF5zAVGr1Rj/54p6TeK0PEou0tLRPFuZs+LPlEzrSw==}
+  '@storybook/addon-highlight@8.2.6':
+    resolution: {integrity: sha512-03cV9USsfP3bS4wYV06DYcIaGPfoheQe53Q0Jr1B2yJUVyIPKvmO2nGjLBsqzeL3Wl7vSfLQn0/dUdxCcbqLsw==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-interactions@8.1.11':
-    resolution: {integrity: sha512-nkc01z61mYM1kxf0ncBQLlFnnwW4RAVPfRSxK9BdbFN3AAvFiHCwVZdn71mi+C3L8oTqYR6o32e0RlXk+AjhHA==}
+  '@storybook/addon-interactions@8.2.6':
+    resolution: {integrity: sha512-YXpHf8jWPz9HJV+Fw4GaunaCWeE6uqF24aLXdAd8xuhN1UfWJeNV6AwAvFQ0hTLqvmz0yMhX/5JXDKeKESoYDA==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-links@8.1.11':
-    resolution: {integrity: sha512-HlV2RQSrZyi+55W1B1a9eWNuJdNpWx0g3j7s2arNlNmbd6/kfWAp84axBstI1tL0nW4svut7bWlCsMSOIden+A==}
+  '@storybook/addon-links@8.2.6':
+    resolution: {integrity: sha512-CUuU3nk8wyZ3bljCmOG/OCKazan+bPuNbCph8N763zyzdEx5M/CbBxV9d3pi3zjYpix7txlqrl2/YdMCejfyFw==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      storybook: ^8.2.6
     peerDependenciesMeta:
       react:
         optional: true
 
-  '@storybook/addon-mdx-gfm@8.1.11':
-    resolution: {integrity: sha512-0/4Xaisvmoi26iK1ezTOB9dN2b0JbgWKzO2PO6att2Jh7lplLCf1QeoE8Y4SgCh0brage+mA8mKI8NrT7d18pg==}
+  '@storybook/addon-mdx-gfm@8.2.6':
+    resolution: {integrity: sha512-PFVfJeuydxlV1VmxEuKNQ7z2vCDvQtHa2GB0ANM11ahxDSUz8QxsO0Y/L3LOn2JjJGYiVFrsHAaC+8NW43iArQ==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-measure@8.1.11':
-    resolution: {integrity: sha512-LkQD3SiLWaWt53aLB3EnmhD9Im8EOO+HKSUE+XGnIJRUcHHRqHfvDkN9KX7T1DCWbfRE5WzMHF5o23b3UiAANw==}
+  '@storybook/addon-measure@8.2.6':
+    resolution: {integrity: sha512-neI8YeSOAtOmzasLxo6O8ZLr2ebMaD7XVF+kYatl5+SpyuwwvUGcP9NkKe5S+mB8V2zxFUIsXS74XrhmQhRoaQ==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-outline@8.1.11':
-    resolution: {integrity: sha512-vco3RLVjkcS25dNtj1lxmjq4fC0Nq08KNLMS5cbNPVJWNTuSUi/2EthSTQQCdpfMV/p6u+D5uF20A9Pl0xJFXw==}
+  '@storybook/addon-outline@8.2.6':
+    resolution: {integrity: sha512-uAlPtqDWlq7MQQ4zJT80qdjbSdLF/zsvtPhidX6h9cjLKNPWAv79xJQ14AJHaMv+Hzy5xKnM4wdEhgPbzKabQg==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-storysource@8.1.11':
-    resolution: {integrity: sha512-b2K3+ZzfANDTTeN1jnqNgAQ5ZIhnhIAv89gC/36cOhSK5NLyKmyVKLGQmR3fVqX3URpnz9xccst2JNXopvtccw==}
+  '@storybook/addon-storysource@8.2.6':
+    resolution: {integrity: sha512-8H2kvRIM12oXN4kO/oowABu88IOY9Je7PphCUUs/nIfTIB+Ck1GrLxx5fCNNCSwUrTBEZY8bDOfxmkf9d4qngw==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-toolbars@8.1.11':
-    resolution: {integrity: sha512-reIKB0+JTiP+GNzynlDcRf4xmv9+j/DQ94qiXl2ZG5+ufKilH8DiRZpVA/i0x+4+TxdGdOJr1/pOf8tAmhNEoQ==}
+  '@storybook/addon-toolbars@8.2.6':
+    resolution: {integrity: sha512-0JmRirMpxHS6VZzBk0kY871xWTpkk3TN4S1sxoFf5fcnCfVTHDjEJ5Ws/QWru1RJlIZHuJKRdQIA6Vuq5X+KfQ==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/addon-viewport@8.1.11':
-    resolution: {integrity: sha512-qk4IcGnAgiAUQxt8l5PIQ293Za+w6wxlJQIpxr7+QM8OVkADPzXY0MmQfYWU9EQplrxAC2MSx3/C1gZeq+MDOQ==}
+  '@storybook/addon-viewport@8.2.6':
+    resolution: {integrity: sha512-IAxH9H8tVFzSmZhKf5E+EALiAdkp19RzGqP/rWluD8LH7oW5HumQE/4oN0ZhVMy1RxYsCKFYjWyAp7AuxeMRSw==}
+    peerDependencies:
+      storybook: ^8.2.6
 
-  '@storybook/blocks@8.1.11':
-    resolution: {integrity: sha512-eMed7PpL/hAVM6tBS7h70bEAyzbiSU9I/kye4jZ7DkCbAsrX6OKmC7pcHSDn712WTcf3vVqxy5jOKUmOXpc0eg==}
+  '@storybook/blocks@8.2.6':
+    resolution: {integrity: sha512-nMlZJjVTyfOJ6xwORptsNuS1AZZlDbJUVXc2R8uukGd5GIXxxCdrPk4NvUsjfQslMT9LhYuFld3z62FATsM2rw==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      storybook: ^8.2.6
     peerDependenciesMeta:
       react:
         optional: true
@@ -4388,24 +4237,35 @@ packages:
       vite-plugin-glimmerx:
         optional: true
 
+  '@storybook/builder-vite@8.2.6':
+    resolution: {integrity: sha512-3PrsPZAedpQUbzRBEl23Fi1zG5bkQD76JsygVwmfiSm4Est4K8kW2AIB2ht9cIfKXh3mfQkyQlxXKHeQEHeQwQ==}
+    peerDependencies:
+      '@preact/preset-vite': '*'
+      storybook: ^8.2.6
+      typescript: '>= 4.3.x'
+      vite: ^4.0.0 || ^5.0.0
+      vite-plugin-glimmerx: '*'
+    peerDependenciesMeta:
+      '@preact/preset-vite':
+        optional: true
+      typescript:
+        optional: true
+      vite-plugin-glimmerx:
+        optional: true
+
   '@storybook/channels@8.1.11':
     resolution: {integrity: sha512-fu5FTqo6duOqtJFa6gFzKbiSLJoia+8Tibn3xFfB6BeifWrH81hc+AZq0lTmHo5qax2G5t8ZN8JooHjMw6k2RA==}
 
-  '@storybook/cli@8.1.11':
-    resolution: {integrity: sha512-4U48w9C7mVEKrykcPcfHwJkRyCqJ28XipbElACbjIIkQEqaHaOVtP3GeKIrgkoOXe/HK3O4zKWRP2SqlVS0r4A==}
-    hasBin: true
-
   '@storybook/client-logger@8.1.11':
     resolution: {integrity: sha512-DVMh2usz3yYmlqCLCiCKy5fT8/UR9aTh+gSqwyNFkGZrIM4otC5A8eMXajXifzotQLT5SaOEnM3WzHwmpvMIEA==}
 
-  '@storybook/codemod@8.1.11':
-    resolution: {integrity: sha512-/LCozjH1IQ1TOs9UQV59BE0X6UZ9q+C0NEUz7qmJZPrwAii3FkW4l7D/fwxblpMExaoxv0oE8NQfUz49U/5Ymg==}
+  '@storybook/codemod@8.2.6':
+    resolution: {integrity: sha512-+mFJ6R+JhJLpU7VPDlXU5Yn6nqIBq745GaEosnIiFOdNo3jaxJ58wq/sGhbQvoCHPUxMA+sDQvR7pS62YFoLRQ==}
 
-  '@storybook/components@8.1.11':
-    resolution: {integrity: sha512-iXKsNu7VmrLBtjMfPj7S4yJ6T13GU6joKcVcrcw8wfrQJGlPFp4YaURPBUEDxvCt1XWi5JkaqJBvb48kIrROEQ==}
+  '@storybook/components@8.2.6':
+    resolution: {integrity: sha512-H8ckH1AnLkHtMtvJ3J8LxnmDtHxkJ7NJacGctHMRrsBIvdKTVwlT4su5nAVVJlan/PrEou+jESfw+OjjBYE5PA==}
     peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      storybook: ^8.2.6
 
   '@storybook/core-common@8.1.11':
     resolution: {integrity: sha512-Ix0nplD4I4DrV2t9B+62jaw1baKES9UbR/Jz9LVKFF9nsua3ON0aVe73dOjMxFWBngpzBYWe+zYBTZ7aQtDH4Q==}
@@ -4418,15 +4278,31 @@ packages:
   '@storybook/core-events@8.1.11':
     resolution: {integrity: sha512-vXaNe2KEW9BGlLrg0lzmf5cJ0xt+suPjWmEODH5JqBbrdZ67X6ApA2nb6WcxDQhykesWCuFN5gp1l+JuDOBi7A==}
 
+  '@storybook/core-events@8.2.6':
+    resolution: {integrity: sha512-bmtm7sHBExKCSGiCIyhwfHKFIsdrRQqd8ZEb/iNWsR93AxHszcf/adYAVynencdWKipw1haIWBNaiDhnsOBVPA==}
+    peerDependencies:
+      storybook: ^8.2.6
+
   '@storybook/core-server@8.1.11':
     resolution: {integrity: sha512-L6dzQTmR0np/kagNONvvlm6lSvF1FNc9js3vxsEEPnEypLbhx8bDZaHmuhmBpYUzKyUMpRVQTE/WgjHLuBBuxA==}
 
+  '@storybook/core@8.2.6':
+    resolution: {integrity: sha512-XY71g3AcpD6IiER9k9Lt+vlUMYfPIYgWekd7e0Ggzz2gJkPuLunKEdQccLGDSHf5OFAobHhrTJc7ZsvWhmDMag==}
+
   '@storybook/csf-plugin@8.1.11':
     resolution: {integrity: sha512-hkA8gjFtSN/tabG0cuvmEqanMXtxPr3qTkp4UNSt1R6jBEgFHRG2y/KYLl367kDwOSFTT987ZgRfJJruU66Fvw==}
 
+  '@storybook/csf-plugin@8.2.6':
+    resolution: {integrity: sha512-USn7E/bMQYVqvFBuW6d9rKoSuCImjk0BAmc/0wIOuMQ/yQNp2Xze0m8eVkNHUIUDokyx0TXDjRjwq10Xxk16ag==}
+    peerDependencies:
+      storybook: ^8.2.6
+
   '@storybook/csf-tools@8.1.11':
     resolution: {integrity: sha512-6qMWAg/dBwCVIHzANM9lSHoirwqSS+wWmv+NwAs0t9S94M75IttHYxD3IyzwaSYCC5llp0EQFvtXXAuSfFbibg==}
 
+  '@storybook/csf@0.1.11':
+    resolution: {integrity: sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==}
+
   '@storybook/csf@0.1.9':
     resolution: {integrity: sha512-JlZ6v/iFn+iKohKGpYXnMeNeTiiAMeFoDhYnPLIC8GnyyIWqEI9wJYrOK9i9rxlJ8NZAH/ojGC/u/xVC41qSgQ==}
 
@@ -4446,12 +4322,19 @@ packages:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
 
-  '@storybook/instrumenter@8.1.11':
-    resolution: {integrity: sha512-r/U9hcqnodNMHuzRt1g56mWrVsDazR85Djz64M3KOwBhrTj5d46DF4/EE80w/5zR5JOrT7p8WmjJRowiVteOCQ==}
+  '@storybook/instrumenter@8.2.6':
+    resolution: {integrity: sha512-RxtpcMTUSq8/wPM6cR6EXVrPEiNuRbC71cIFVFZagOFYvnnOKwSPV+GOLPK0wxMbGB4c5/+Xe8ADefmZTvxOsA==}
+    peerDependencies:
+      storybook: ^8.2.6
 
   '@storybook/manager-api@8.1.11':
     resolution: {integrity: sha512-QSgwKfAw01K9YvvZj30iGBMgQ4YaCT3vojmttuqdH5ukyXkiO7pENLJj4Y+alwUeSi0g+SJeadCI3PXySBHOGg==}
 
+  '@storybook/manager-api@8.2.6':
+    resolution: {integrity: sha512-uv36h/b5RhlajWtEg4cVPBYV8gZs6juux0nIE+6G9i7vt8Ild6gM9tW1KNabgZcaHFiyWJYCNWxJZoKjgUmXDg==}
+    peerDependencies:
+      storybook: ^8.2.6
+
   '@storybook/manager@8.1.11':
     resolution: {integrity: sha512-e02y9dmxowo7cTKYm9am7UO6NOHoHy6Xi7xZf/UA932qLwFZUtk5pnwIEFaZWI3OQsRUCGhP+FL5zizU7uVZeg==}
 
@@ -4461,29 +4344,37 @@ packages:
   '@storybook/preview-api@8.1.11':
     resolution: {integrity: sha512-8ZChmFV56GKppCJ0hnBd/kNTfGn2gWVq1242kuet13pbJtBpvOhyq4W01e/Yo14tAPXvgz8dSnMvWLbJx4QfhQ==}
 
+  '@storybook/preview-api@8.2.6':
+    resolution: {integrity: sha512-5vTj2ndX5ng4nDntZYe+r8UwLjCIGFymhq5/r2adAvRKL+Bo4zQDWGO7bhvGJk16do2THb2JvPz49ComW9LLZw==}
+    peerDependencies:
+      storybook: ^8.2.6
+
   '@storybook/preview@8.1.11':
     resolution: {integrity: sha512-K/9NZmjnL0D1BROkTNWNoPqgL2UaocALRSqCARmkBLgU2Rn/FuZgEclHkWlYo6pUrmLNK+bZ+XzpNMu12iTbpg==}
 
-  '@storybook/react-dom-shim@8.1.11':
-    resolution: {integrity: sha512-KVDSuipqkFjpGfldoRM5xR/N1/RNmbr+sVXqMmelr0zV2jGnexEZnoa7wRHk7IuXuivLWe8BxMxzvQWqjIa4GA==}
+  '@storybook/react-dom-shim@8.2.6':
+    resolution: {integrity: sha512-B+x8UAEQPDp1yhN3tMh09NvSL38QNfJB7PAyLgKrfE7xIAzvewq+RLW2DfGkoZCy+Zr7QSHm1p7NOgud8+sQCg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      storybook: ^8.2.6
 
-  '@storybook/react-vite@8.1.11':
-    resolution: {integrity: sha512-QqkE6QKsIDthXtps9+YSBQ39O4VvU7Uu3y6WSA3IPgKTtGnmIvhwXtapjf7WQ2cNb5KY1JksFxHXbDe0i5IL4g==}
+  '@storybook/react-vite@8.2.6':
+    resolution: {integrity: sha512-BpbteaIzsJZL1QN3iR7uuslrPfdtbZYXPhcU9awpfl5pW5MOQThuvl7728mwT8V7KdANeikJPgsnlETOb/afDA==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      storybook: ^8.2.6
       vite: ^4.0.0 || ^5.0.0
 
-  '@storybook/react@8.1.11':
-    resolution: {integrity: sha512-t+EYXOkgwg3ropLGS9y8gGvX5/Okffu/6JYL3YWksrBGAZSqVV4NkxCnVJZepS717SyhR0tN741gv/SxxFPJMg==}
+  '@storybook/react@8.2.6':
+    resolution: {integrity: sha512-awJlzfiAMrf8l9AgiLhjXEJ+HvS3VKPxNNQaRwBELGq/vigjJe656tMrhvg4OIlJXtlS+6XPshd2knLwjIWNLw==}
     engines: {node: '>=18.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+      storybook: ^8.2.6
       typescript: '>= 4.2.x'
     peerDependenciesMeta:
       typescript:
@@ -4492,14 +4383,18 @@ packages:
   '@storybook/router@8.1.11':
     resolution: {integrity: sha512-nU5lsBvy0L8wBYOkjagh29ztZicDATpZNYrHuavlhQ2jznmmHdJvXKYk+VrMAbthjQ6ZBqfeeMNPR1UlnqR5Rw==}
 
-  '@storybook/source-loader@8.1.11':
-    resolution: {integrity: sha512-4cfJ7aPjtniIdDGiFjdFpO47byHOl4RKYCJEHf9t+j0xHmlXe4B9aAinxuFfv3GKAXfLvSbbwGO0cDZQRj+brw==}
+  '@storybook/source-loader@8.2.6':
+    resolution: {integrity: sha512-mOVf+TJhlQywCymFMs7l604CxEZRKZRKVQojrrgU6CH6EhhLx/q6BT8tf1CakY9JO3Ey+PhUMBBCerYiDaHLcQ==}
+    peerDependencies:
+      storybook: ^8.2.6
 
   '@storybook/telemetry@8.1.11':
     resolution: {integrity: sha512-Jqvm7HcZismKzPuebhyLECO6KjGiSk4ycbca1WUM/TUvifxCXqgoUPlHHQEEfaRdHS63/MSqtMNjLsQRLC/vNQ==}
 
-  '@storybook/test@8.1.11':
-    resolution: {integrity: sha512-k+V3HemF2/I8fkRxRqM8uH8ULrpBSAAdBOtWSHWLvHguVcb2YA4g4kKo6tXBB9256QfyDW4ZiaAj0/9TMxmJPQ==}
+  '@storybook/test@8.2.6':
+    resolution: {integrity: sha512-nTzNxReBcMRlX1+8PNU/MuA9ArFbeQhfZXMBIwJJoHOhnNe1knYpyn1++xINxAHKOh0BBhQ0NIMoKdcGmW3V6w==}
+    peerDependencies:
+      storybook: ^8.2.6
 
   '@storybook/theming@8.1.11':
     resolution: {integrity: sha512-Chn/opjO6Rl1isNobutYqAH2PjKNkj09YBw/8noomk6gElSa3JbUTyaG/+JCHA6OG/9kUsqoKDb5cZmAKNq/jA==}
@@ -4512,9 +4407,19 @@ packages:
       react-dom:
         optional: true
 
+  '@storybook/theming@8.2.6':
+    resolution: {integrity: sha512-ICnYuLIVsYifVCMQljdHgrp+5vAquNybHxDGWiPeOxBicotwHF8rLhTckD2CdVQbMp0jk6r6jetvjXbFJ2MbvQ==}
+    peerDependencies:
+      storybook: ^8.2.6
+
   '@storybook/types@8.1.11':
     resolution: {integrity: sha512-k9N5iRuY2+t7lVRL6xeu6diNsxO3YI3lS4Juv3RZ2K4QsE/b3yG5ElfJB8DjHDSHwRH4ORyrU71KkOCUVfvtnw==}
 
+  '@storybook/types@8.2.6':
+    resolution: {integrity: sha512-9Kb5+nui8M7TP/EDGwiuOAHYQPg9U6iQl0OWwgbDIYGBpldwlCwVKAoQWzXz/LlhQijULXIpe1cLvEvJN2Uwhg==}
+    peerDependencies:
+      storybook: ^8.2.6
+
   '@storybook/vue3-vite@8.1.11':
     resolution: {integrity: sha512-q0bqh8XEEunaTmp4YiDqM2+YZLwEIevTb5PnNe7G7f2qOiSCE1ncBDnBK717UlCd+iYr34NTztgV2/jIhz1i5w==}
     engines: {node: '>=18.0.0'}
@@ -4527,6 +4432,13 @@ packages:
     peerDependencies:
       vue: ^3.0.0
 
+  '@storybook/vue3@8.2.6':
+    resolution: {integrity: sha512-j4gMuWc1ZDzqWSdf79YswcZmcbhmbByq/6upqxwqXtjv1mHAiBnEs8bbnnylDrzg4GOvBC8w+FjArkzlFA7uXg==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      storybook: ^8.2.6
+      vue: ^3.0.0
+
   '@swc/cli@0.3.12':
     resolution: {integrity: sha512-h7bvxT+4+UDrLWJLFHt6V+vNAcUNii2G4aGSSotKz1ECEk4MyEh5CWxmeSscwuz5K3i+4DWTgm4+4EyMCQKn+g==}
     engines: {node: '>= 16.14.0'}
@@ -4544,6 +4456,12 @@ packages:
     cpu: [arm64]
     os: [android]
 
+  '@swc/core-darwin-arm64@1.3.56':
+    resolution: {integrity: sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [darwin]
+
   '@swc/core-darwin-arm64@1.6.13':
     resolution: {integrity: sha512-SOF4buAis72K22BGJ3N8y88mLNfxLNprTuJUpzikyMGrvkuBFNcxYtMhmomO0XHsgLDzOJ+hWzcgjRNzjMsUcQ==}
     engines: {node: '>=10'}
@@ -4556,10 +4474,10 @@ packages:
     cpu: [arm64]
     os: [darwin]
 
-  '@swc/core-darwin-arm64@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-tOMR9yVA1MD320KySitaRT5yUvdzdmuUjX4+sOw2UKHqgk2s68j6JlBHZjdmqpviXXH5qwXWH3ui8ElcHcpN0Q==}
+  '@swc/core-darwin-x64@1.3.56':
+    resolution: {integrity: sha512-VH5saqYFasdRXJy6RAT+MXm0+IjkMZvOkohJwUei+oA65cKJofQwrJ1jZro8yOJFYvUSI3jgNRGsdBkmo/4hMw==}
     engines: {node: '>=10'}
-    cpu: [arm64]
+    cpu: [x64]
     os: [darwin]
 
   '@swc/core-darwin-x64@1.6.13':
@@ -4574,18 +4492,18 @@ packages:
     cpu: [x64]
     os: [darwin]
 
-  '@swc/core-darwin-x64@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-GVQqC/vSOS/LPXYEF00/+JMwXIFBquIhBy4HEdfybkoRaoeLUHxdqkUhI6dW0lRe85QUrSkg69ylAXwD4foU8Q==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [darwin]
-
   '@swc/core-freebsd-x64@1.3.11':
     resolution: {integrity: sha512-02uqYktPp6WmZfZ2Crc/yIVOcgANtjo8ciHcT7yLHvz7v+S7gx1I2tyNGUFtTX5hcR2IFNGrL8Yj4DvpTABFHg==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [freebsd]
 
+  '@swc/core-linux-arm-gnueabihf@1.3.56':
+    resolution: {integrity: sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==}
+    engines: {node: '>=10'}
+    cpu: [arm]
+    os: [linux]
+
   '@swc/core-linux-arm-gnueabihf@1.6.13':
     resolution: {integrity: sha512-f4gxxvDXVUm2HLYXRd311mSrmbpQF2MZ4Ja6XCQz1hWAxXdhRl1gpnZ+LH/xIfGSwQChrtLLVrkxdYUCVuIjFg==}
     engines: {node: '>=10'}
@@ -4598,10 +4516,10 @@ packages:
     cpu: [arm]
     os: [linux]
 
-  '@swc/core-linux-arm-gnueabihf@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-r2VnlO7ABL+fUAEJa/qJeqBKG2lxQV9XcgHIyeWHzcyf9G9frcevcXSHbJSGBIIxiADRuoyrYGfRwzsLXDt7jA==}
+  '@swc/core-linux-arm64-gnu@1.3.56':
+    resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==}
     engines: {node: '>=10'}
-    cpu: [arm]
+    cpu: [arm64]
     os: [linux]
 
   '@swc/core-linux-arm64-gnu@1.6.13':
@@ -4616,8 +4534,8 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@swc/core-linux-arm64-gnu@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-Pk+RwrvsY1fB2PE3b/RcIbVfvzc3pO8WcTvZjmXQENQ0Af0R+pIR+tnnXweZB48ZdfoBE0sPE3Du1SntoZOcnA==}
+  '@swc/core-linux-arm64-musl@1.3.56':
+    resolution: {integrity: sha512-9gxL09BIiAv8zY0DjfnFf19bo8+P4T9tdhzPwcm+1yPJcY5yr1+YFWLNFzz01agtOj6VlZ2/wUJTaOfdjjtc+A==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -4634,10 +4552,10 @@ packages:
     cpu: [arm64]
     os: [linux]
 
-  '@swc/core-linux-arm64-musl@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-spizj6AJ3xxVz/+7sbeXeqkiGrks4u451gGf4pzlhYqWZzxsWjSS9lZXVGJ6vHslofYsQEK0pIsN+F/wOIspUg==}
+  '@swc/core-linux-x64-gnu@1.3.56':
+    resolution: {integrity: sha512-n0ORNknl50vMRkll3BDO1E4WOqY6iISlPV1ZQCRLWQ6YQ2q8/WAryBxc2OAybcGHBUFkxyACpJukeU1QZ/9tNw==}
     engines: {node: '>=10'}
-    cpu: [arm64]
+    cpu: [x64]
     os: [linux]
 
   '@swc/core-linux-x64-gnu@1.6.13':
@@ -4652,8 +4570,8 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@swc/core-linux-x64-gnu@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-08zfcePPoBBBcoSHvNWK2OZk4EQtwB/tgFstDf5HROOKr9YqCxVYemWCwBFuqVHr6ALAoEeizPOqCilvk0jQnw==}
+  '@swc/core-linux-x64-musl@1.3.56':
+    resolution: {integrity: sha512-r+D34WLAOAlJtfw1gaVWpHRwCncU9nzW9i7w9kSw4HpWYnHJOz54jLGSEmNsrhdTCz1VK2ar+V2ktFUsrlGlDA==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -4670,11 +4588,11 @@ packages:
     cpu: [x64]
     os: [linux]
 
-  '@swc/core-linux-x64-musl@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-hrkT5htUfC4DWHI8tI6DxBHQpw6LU6AuWjMuEJcHtGnHA3BeXxXrZsCPpuxKfUHEEQbdijK67pWWh5SCfrX+Lw==}
+  '@swc/core-win32-arm64-msvc@1.3.56':
+    resolution: {integrity: sha512-29Yt75Is6X24z3x8h/xZC1HnDPkPpyLH9mDQiM6Cuc0I9mVr1XSriPEUB2N/awf5IE4SA8c+3IVq1DtKWbkJIw==}
     engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
+    cpu: [arm64]
+    os: [win32]
 
   '@swc/core-win32-arm64-msvc@1.6.13':
     resolution: {integrity: sha512-ap6uNmYjwk9M/+bFEuWRNl3hq4VqgQ/Lk+ID/F5WGqczNr0L7vEf+pOsRAn0F6EV+o/nyb3ePt8rLhE/wjHpPg==}
@@ -4688,10 +4606,10 @@ packages:
     cpu: [arm64]
     os: [win32]
 
-  '@swc/core-win32-arm64-msvc@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-3SQj5cKo91+cNorNvOne2cqHW3Wcg+/irYts0lCGy3pruZXcgUKeTQ3kmV8pRPtXCs17YLysZOSyCF9DQIbacw==}
+  '@swc/core-win32-ia32-msvc@1.3.56':
+    resolution: {integrity: sha512-mplp0zbYDrcHtfvkniXlXdB04e2qIjz2Gq/XHKr4Rnc6xVORJjjXF91IemXKpavx2oZYJws+LNJL7UFQ8jyCdQ==}
     engines: {node: '>=10'}
-    cpu: [arm64]
+    cpu: [ia32]
     os: [win32]
 
   '@swc/core-win32-ia32-msvc@1.6.13':
@@ -4706,10 +4624,10 @@ packages:
     cpu: [ia32]
     os: [win32]
 
-  '@swc/core-win32-ia32-msvc@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-BiHjqoPhodpgrNXoPaflvpC98NXLa3jI6vV5ZSTLkpJ1JR+T9RcB+unkllr/mjqUZaqafW9AE784A9+wHzja3Q==}
+  '@swc/core-win32-x64-msvc@1.3.56':
+    resolution: {integrity: sha512-zp8MBnrw/bjdLenO/ifYzHrImSjKunqL0C2IF4LXYNRfcbYFh2NwobsVQMZ20IT0474lKRdlP8Oxdt+bHuXrzA==}
     engines: {node: '>=10'}
-    cpu: [ia32]
+    cpu: [x64]
     os: [win32]
 
   '@swc/core-win32-x64-msvc@1.6.13':
@@ -4724,12 +4642,6 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@swc/core-win32-x64-msvc@1.7.0-nightly-20240715.2':
-    resolution: {integrity: sha512-bfSnQxYLOKRakwQo/sJA8I/krU/TfxdlER41OlvEiBXVYEjJFMb7z9NyPsk/DBbqeaA1ywHun8ohBhuEwJWpDQ==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [win32]
-
   '@swc/core@1.6.13':
     resolution: {integrity: sha512-eailUYex6fkfaQTev4Oa3mwn0/e3mQU4H8y1WPuImYQESOQDtVrowwUGDSc19evpBbHpKtwM+hw8nLlhIsF+Tw==}
     engines: {node: '>=10'}
@@ -4780,44 +4692,44 @@ packages:
   '@tabler/icons@3.3.0':
     resolution: {integrity: sha512-PLVe9d7b59sKytbx00KgeGhQG3N176Ezv8YMmsnSz4s0ifDzMWlp/h2wEfQZ0ZNe8e377GY2OW6kovUe3Rnd0g==}
 
-  '@tensorflow/tfjs-backend-cpu@4.20.0':
-    resolution: {integrity: sha512-1QRQ6AqAa/VB8JOArf5nY3Dc/QQHXbfuxgdIdQhKrABEHgvlaWt2Vv696UhIlVl75YoNY+vWlCwBdGQIKYfFGw==}
+  '@tensorflow/tfjs-backend-cpu@4.4.0':
+    resolution: {integrity: sha512-d4eln500/qNym78z9IrUUzF0ITBoJGLrxV8xd92kLVoXhg35Mm+zqUXShjFcrH8joOHOFuST0qZ0TbDDqcPzPA==}
     engines: {yarn: '>= 1.3.2'}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.20.0
+      '@tensorflow/tfjs-core': 4.4.0
 
-  '@tensorflow/tfjs-backend-webgl@4.20.0':
-    resolution: {integrity: sha512-M03fJonJGxm2u3SCzRNA2JLh0gxaAye64SEmGAXOehizowxy42l+lMsPWU8xU7r7mN6PEilBNkuKAf5YJ7Xumg==}
+  '@tensorflow/tfjs-backend-webgl@4.4.0':
+    resolution: {integrity: sha512-TzQKvfAPgGt9cMG+5bVoTckoG1xr/PVJM/uODkPvzcMqi3j97kuWDXwkYJIgXldStmfiKkU7f5CmyD3Cq3E6BA==}
     engines: {yarn: '>= 1.3.2'}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.20.0
+      '@tensorflow/tfjs-core': 4.4.0
 
-  '@tensorflow/tfjs-converter@4.20.0':
-    resolution: {integrity: sha512-UJ2ntQ1TNtVHB5qGMwB0j306bs3KH1E1HKJ9Dxvrc6PUaivOV+CPKqmbidOFG5LylXeRC36JBdhe+gVT2nFHNw==}
+  '@tensorflow/tfjs-converter@4.4.0':
+    resolution: {integrity: sha512-JUjpRStrAuw37tgPd5UENu0UjQVuJT09yF7KpOur4BriJ0uQqrbEZHMPHmvUtr5nYzkqlXJTuXIyxvEY/olNpg==}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.20.0
+      '@tensorflow/tfjs-core': 4.4.0
 
-  '@tensorflow/tfjs-core@4.20.0':
-    resolution: {integrity: sha512-m/cc9qDc63al9UhdbXRUYTLGfJJlhuN5tylAX/2pJMLj32c8a6ThGDJYoKzpf32n5g3MQGYLchjClDxeGdXMPQ==}
+  '@tensorflow/tfjs-core@4.4.0':
+    resolution: {integrity: sha512-Anxpc7cAOA0Q7EUXdTbQKMg3reFvrdkgDlaYzH9ZfkMq2CgLV4Au6E/s6HmbYn/VrAtWy9mLY5c/lLJqh4764g==}
     engines: {yarn: '>= 1.3.2'}
 
-  '@tensorflow/tfjs-data@4.20.0':
-    resolution: {integrity: sha512-k6S8joXhoXkatcoT6mYCxBzRCsnrLfnl6xjLe46SnXO0oEEy4Vuzbmp5Ydl1uU2hHr73zL91EdAC1k8Hng/+oA==}
+  '@tensorflow/tfjs-data@4.4.0':
+    resolution: {integrity: sha512-aY4eq4cgrsrXeBU6ABZAAN3tV0fG4YcHd0z+cYuNXnCo+VEQLJnPmhn+xymZ4VQZQH4GXbVS4dV9pXMclFNRFw==}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.20.0
+      '@tensorflow/tfjs-core': 4.4.0
       seedrandom: ^3.0.5
 
-  '@tensorflow/tfjs-layers@4.20.0':
-    resolution: {integrity: sha512-SCHZH29Vyw+Y9eoaJHiaNo6yqM9vD3XCKncoczonRRywejm3FFqddg1AuWAfSE9XoNPE21o9PsknvKLl/Uh+Cg==}
+  '@tensorflow/tfjs-layers@4.4.0':
+    resolution: {integrity: sha512-OGC7shfiD9Gc698hINHK4y9slOJvu5m54tVNm4xf+WSNrw/avvgpar6yyoL5bakYIZNQvFNK75Yr8VRPR7oPeQ==}
     peerDependencies:
-      '@tensorflow/tfjs-core': 4.20.0
+      '@tensorflow/tfjs-core': 4.4.0
 
-  '@tensorflow/tfjs-node@4.20.0':
-    resolution: {integrity: sha512-pVSOlzsVqh5ck3aiNPJCltB3ASKjsLqNPvJ28lXn9Xg648U4eHDk8G47m9w4uf0FdVcWDfjPM3hDCbBZ/E2KXg==}
+  '@tensorflow/tfjs-node@4.4.0':
+    resolution: {integrity: sha512-+JSAddsupjSQUDZeb7QGOFkL3Tty3kjPHx8ethiYFzwTZJHCMvM7wZJd0Fqnjxym6A0KpsmB7SPZgwRRXVIlPA==}
     engines: {node: '>=8.11.0'}
 
-  '@tensorflow/tfjs@4.20.0':
-    resolution: {integrity: sha512-+ZLfJq2jyIOE2/+yKPoyD/gfy3RZypbfMrlzvBDgodTK5jnexprihhX38hxilh9HPWvWQXJqiUjKJP5ECCikrw==}
+  '@tensorflow/tfjs@4.4.0':
+    resolution: {integrity: sha512-EmCsnzdvawyk4b+4JKaLLuicHcJQRZtL1zSy9AWJLiiHTbDDseYgLxfaCEfLk8v2bUe7SBXwl3n3B7OjgvH11Q==}
     hasBin: true
 
   '@testing-library/dom@10.1.0':
@@ -4936,9 +4848,6 @@ packages:
   '@types/cookie@0.6.0':
     resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
 
-  '@types/cookies@0.9.0':
-    resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==}
-
   '@types/cross-spawn@6.0.2':
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
 
@@ -4987,6 +4896,9 @@ packages:
   '@types/express@4.17.17':
     resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==}
 
+  '@types/express@4.17.21':
+    resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
+
   '@types/find-cache-dir@3.2.1':
     resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==}
 
@@ -5005,15 +4917,9 @@ packages:
   '@types/htmlescape@1.1.3':
     resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==}
 
-  '@types/http-assert@1.5.5':
-    resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==}
-
   '@types/http-cache-semantics@4.0.4':
     resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
 
-  '@types/http-errors@2.0.4':
-    resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
-
   '@types/http-link-header@1.0.7':
     resolution: {integrity: sha512-snm5oLckop0K3cTDAiBnZDy6ncx9DJ3mCRDvs42C884MbVYPP74Tiq2hFsSDRTyjK6RyDYDIulPiW23ge+g5Lw==}
 
@@ -5044,27 +4950,15 @@ packages:
   '@types/json5@0.0.29':
     resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
 
-  '@types/jsonld@1.5.14':
-    resolution: {integrity: sha512-z4IRf5oRgjPTkazDDv94sjzI5iK3DrDEW7Y5Gk4VO4+ANymgtHtNaXWi93+BmiAoG3PB9QTv5DgSpKWGYVvysA==}
+  '@types/jsonld@1.5.15':
+    resolution: {integrity: sha512-PlAFPZjL+AuGYmwlqwKEL0IMP8M8RexH0NIPGfCVWSQ041H2rR/8OlyZSD7KsCVoN8vCfWdtWDBxX8yBVP+xow==}
 
   '@types/jsrsasign@10.5.14':
     resolution: {integrity: sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==}
 
-  '@types/keygrip@1.0.6':
-    resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==}
-
   '@types/keyv@3.1.4':
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
 
-  '@types/koa-compose@3.2.8':
-    resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==}
-
-  '@types/koa@2.14.0':
-    resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==}
-
-  '@types/koa__router@12.0.3':
-    resolution: {integrity: sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==}
-
   '@types/lodash@4.14.191':
     resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
 
@@ -5074,6 +4968,9 @@ packages:
   '@types/matter-js@0.19.6':
     resolution: {integrity: sha512-ffk6tqJM5scla+ThXmnox+mdfCo3qYk6yMjQsNcrbo6eQ5DqorVdtnaL+1agCoYzxUjmHeiNB7poBMAmhuLY7w==}
 
+  '@types/matter-js@0.19.7':
+    resolution: {integrity: sha512-dlh50YEh1lQS4fiCDGBnK75ocHQIq/1E371Qk6hASJImICIivdZQC2GkOqnfBm0Hac2xLk5+yrqRFDAEfj/yLA==}
+
   '@types/mdast@4.0.3':
     resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
 
@@ -5113,8 +5010,8 @@ packages:
   '@types/node@20.11.5':
     resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==}
 
-  '@types/node@20.14.9':
-    resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
+  '@types/node@20.14.12':
+    resolution: {integrity: sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==}
 
   '@types/node@20.9.1':
     resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==}
@@ -5197,9 +5094,6 @@ packages:
   '@types/scheduler@0.16.2':
     resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
 
-  '@types/seedrandom@2.4.30':
-    resolution: {integrity: sha512-AnxLHewubLVzoF/A4qdxBGHCKifw8cY32iro3DQX9TPcetE95zBeVt3jnsvtvAUf1vwzMfwzp4t/L2yqPlnjkQ==}
-
   '@types/seedrandom@2.4.34':
     resolution: {integrity: sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==}
 
@@ -5251,6 +5145,9 @@ packages:
   '@types/tough-cookie@4.0.2':
     resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==}
 
+  '@types/tough-cookie@4.0.5':
+    resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
+
   '@types/unist@3.0.2':
     resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
 
@@ -5266,11 +5163,14 @@ packages:
   '@types/web-push@3.6.3':
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
 
+  '@types/webgl-ext@0.0.30':
+    resolution: {integrity: sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==}
+
   '@types/wrap-ansi@3.0.0':
     resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==}
 
-  '@types/ws@8.5.10':
-    resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
+  '@types/ws@8.5.11':
+    resolution: {integrity: sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==}
 
   '@types/yargs-parser@21.0.0':
     resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
@@ -5303,8 +5203,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/eslint-plugin@7.15.0':
-    resolution: {integrity: sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==}
+  '@typescript-eslint/eslint-plugin@7.17.0':
+    resolution: {integrity: sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^7.0.0
@@ -5334,8 +5234,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/parser@7.15.0':
-    resolution: {integrity: sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==}
+  '@typescript-eslint/parser@7.17.0':
+    resolution: {integrity: sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -5352,8 +5252,8 @@ packages:
     resolution: {integrity: sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==}
     engines: {node: ^16.0.0 || >=18.0.0}
 
-  '@typescript-eslint/scope-manager@7.15.0':
-    resolution: {integrity: sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==}
+  '@typescript-eslint/scope-manager@7.17.0':
+    resolution: {integrity: sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
   '@typescript-eslint/type-utils@6.11.0':
@@ -5376,8 +5276,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/type-utils@7.15.0':
-    resolution: {integrity: sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==}
+  '@typescript-eslint/type-utils@7.17.0':
+    resolution: {integrity: sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -5394,8 +5294,8 @@ packages:
     resolution: {integrity: sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==}
     engines: {node: ^16.0.0 || >=18.0.0}
 
-  '@typescript-eslint/types@7.15.0':
-    resolution: {integrity: sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==}
+  '@typescript-eslint/types@7.17.0':
+    resolution: {integrity: sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
   '@typescript-eslint/typescript-estree@6.11.0':
@@ -5416,8 +5316,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/typescript-estree@7.15.0':
-    resolution: {integrity: sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==}
+  '@typescript-eslint/typescript-estree@7.17.0':
+    resolution: {integrity: sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       typescript: '*'
@@ -5437,8 +5337,8 @@ packages:
     peerDependencies:
       eslint: ^8.56.0
 
-  '@typescript-eslint/utils@7.15.0':
-    resolution: {integrity: sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==}
+  '@typescript-eslint/utils@7.17.0':
+    resolution: {integrity: sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -5451,15 +5351,15 @@ packages:
     resolution: {integrity: sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==}
     engines: {node: ^16.0.0 || >=18.0.0}
 
-  '@typescript-eslint/visitor-keys@7.15.0':
-    resolution: {integrity: sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==}
+  '@typescript-eslint/visitor-keys@7.17.0':
+    resolution: {integrity: sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
   '@ungap/structured-clone@1.2.0':
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
 
-  '@vitejs/plugin-vue@5.0.5':
-    resolution: {integrity: sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==}
+  '@vitejs/plugin-vue@5.1.0':
+    resolution: {integrity: sha512-QMRxARyrdiwi1mj3AW4fLByoHTavreXq0itdEW696EihXglf1MB3D4C2gBvE0jMPH29ZjC3iK8aIaUMLf4EOGA==}
     engines: {node: ^18.0.0 || >=20.0.0}
     peerDependencies:
       vite: ^5.0.0
@@ -5488,20 +5388,20 @@ packages:
   '@volar/language-core@2.2.0':
     resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==}
 
-  '@volar/language-core@2.4.0-alpha.11':
-    resolution: {integrity: sha512-DtftH0DtpksK1y+de/kLnu8CHcFQ7huKXi7cyxH9R0PbOOTSGXd31kijBeKNzyoXRp8dqGpu/7WhOlCWXQR62w==}
+  '@volar/language-core@2.4.0-alpha.18':
+    resolution: {integrity: sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==}
 
   '@volar/source-map@2.2.0':
     resolution: {integrity: sha512-HQlPRlHOVqCCHK8wI76ZldHkEwKsjp7E6idUc36Ekni+KJDNrqgSqPvyHQixybXPHNU7CI9Uxd9/IkxO7LuNBw==}
 
-  '@volar/source-map@2.4.0-alpha.11':
-    resolution: {integrity: sha512-yyjmv8KUkTcxXzwme9qUMl6Szdji9JUQa8eadE4ib/spFXXZGq6QOX8cgSu5UQ0ooyBJFO1zdVH5otBJyZE3Ew==}
+  '@volar/source-map@2.4.0-alpha.18':
+    resolution: {integrity: sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g==}
 
   '@volar/typescript@2.2.0':
     resolution: {integrity: sha512-wC6l4zLiiCLxF+FGaHCbWlQYf4vMsnRxYhcI6WgvaNppOD6r1g+Ef1RKRJUApALWU46Yy/JDU/TbdV6w/X6Liw==}
 
-  '@volar/typescript@2.4.0-alpha.11':
-    resolution: {integrity: sha512-N/v+wSddhtsNtfv2w0Bxj2QQWURN5budGzpyBTrlcXxz2dnvB0eAMqrEQbBi6rCOVHlRaXbh+wyTRdAcB/FHrg==}
+  '@volar/typescript@2.4.0-alpha.18':
+    resolution: {integrity: sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==}
 
   '@vue/compiler-core@3.4.29':
     resolution: {integrity: sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==}
@@ -5509,17 +5409,26 @@ packages:
   '@vue/compiler-core@3.4.31':
     resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==}
 
+  '@vue/compiler-core@3.4.34':
+    resolution: {integrity: sha512-Z0izUf32+wAnQewjHu+pQf1yw00EGOmevl1kE+ljjjMe7oEfpQ+BI3/JNK7yMB4IrUsqLDmPecUrpj3mCP+yJQ==}
+
   '@vue/compiler-dom@3.4.29':
     resolution: {integrity: sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==}
 
   '@vue/compiler-dom@3.4.31':
     resolution: {integrity: sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==}
 
-  '@vue/compiler-sfc@3.4.31':
-    resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==}
+  '@vue/compiler-dom@3.4.34':
+    resolution: {integrity: sha512-3PUOTS1h5cskdOJMExCu2TInXuM0j60DRPpSCJDqOCupCfUZCJoyQmKtRmA8EgDNZ5kcEE7vketamRZfrEuVDw==}
 
-  '@vue/compiler-ssr@3.4.31':
-    resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==}
+  '@vue/compiler-sfc@3.4.34':
+    resolution: {integrity: sha512-x6lm0UrM03jjDXTPZgD9Ad8bIVD1ifWNit2EaWQIZB5CULr46+FbLQ5RpK7AXtDHGjx9rmvC7QRCTjsiGkAwRw==}
+
+  '@vue/compiler-ssr@3.4.34':
+    resolution: {integrity: sha512-8TDBcLaTrFm5rnF+Qm4BlliaopJgqJ28Nsrc80qazynm5aJO+Emu7y0RWw34L8dNnTRdcVBpWzJxhGYzsoVu4g==}
+
+  '@vue/compiler-vue2@2.7.16':
+    resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
 
   '@vue/devtools-api@6.6.1':
     resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
@@ -5532,27 +5441,27 @@ packages:
       typescript:
         optional: true
 
-  '@vue/language-core@2.0.24':
-    resolution: {integrity: sha512-997YD6Lq/66LXr3ZOLNxDCmyn13z9NP8LU1UZn9hGCDWhzlbXAIP0hOgL3w3x4RKEaWTaaRtsHP9DzHvmduruQ==}
+  '@vue/language-core@2.0.29':
+    resolution: {integrity: sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
 
-  '@vue/reactivity@3.4.31':
-    resolution: {integrity: sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==}
+  '@vue/reactivity@3.4.34':
+    resolution: {integrity: sha512-ua+Lo+wBRlBEX9TtgPOShE2JwIO7p6BTZ7t1KZVPoaBRfqbC7N3c8Mpzicx173fXxx5VXeU6ykiHo7WgLzJQDA==}
 
-  '@vue/runtime-core@3.4.31':
-    resolution: {integrity: sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==}
+  '@vue/runtime-core@3.4.34':
+    resolution: {integrity: sha512-PXhkiRPwcPGJ1BnyBZFI96GfInCVskd0HPNIAZn7i3YOmLbtbTZpB7/kDTwC1W7IqdGPkTVC63IS7J2nZs4Ebg==}
 
-  '@vue/runtime-dom@3.4.31':
-    resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==}
+  '@vue/runtime-dom@3.4.34':
+    resolution: {integrity: sha512-dXqIe+RqFAK2Euak4UsvbIupalrhc67OuQKpD7HJ3W2fv8jlqvI7szfBCsAEcE8o/wyNpkloxB6J8viuF/E3gw==}
 
-  '@vue/server-renderer@3.4.31':
-    resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==}
+  '@vue/server-renderer@3.4.34':
+    resolution: {integrity: sha512-GeyEUfMVRZMD/mZcNONEqg7MiU10QQ1DB3O/Qr6+8uXpbwdlmVgQ5Qs1/ZUAFX1X2UUtqMoGrDRbxdWfOJFT7Q==}
     peerDependencies:
-      vue: 3.4.31
+      vue: 3.4.34
 
   '@vue/shared@3.4.29':
     resolution: {integrity: sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==}
@@ -5560,6 +5469,9 @@ packages:
   '@vue/shared@3.4.31':
     resolution: {integrity: sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==}
 
+  '@vue/shared@3.4.34':
+    resolution: {integrity: sha512-x5LmiRLpRsd9KTjAB8MPKf0CDPMcuItjP0gbNqFCIgL1I8iYp4zglhj9w9FPCdIbHG2M91RVeIbArFfFTz9I3A==}
+
   '@vue/test-utils@2.4.1':
     resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
     peerDependencies:
@@ -5569,8 +5481,8 @@ packages:
       '@vue/server-renderer':
         optional: true
 
-  '@webgpu/types@0.1.38':
-    resolution: {integrity: sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==}
+  '@webgpu/types@0.1.30':
+    resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==}
 
   '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15':
     resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==}
@@ -5632,8 +5544,8 @@ packages:
     engines: {node: '>=0.4.0'}
     hasBin: true
 
-  acorn@8.12.0:
-    resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
+  acorn@8.12.1:
+    resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
     engines: {node: '>=0.4.0'}
     hasBin: true
 
@@ -5703,8 +5615,8 @@ packages:
   ajv@8.13.0:
     resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
 
-  ajv@8.16.0:
-    resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==}
+  ajv@8.17.1:
+    resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
 
   ansi-colors@4.1.3:
     resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
@@ -5775,6 +5687,7 @@ packages:
   are-we-there-yet@2.0.0:
     resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
     engines: {node: '>=10'}
+    deprecated: This package is no longer supported.
 
   arg@5.0.2:
     resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
@@ -5785,10 +5698,6 @@ packages:
   argparse@2.0.1:
     resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
 
-  aria-hidden@1.2.4:
-    resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
-    engines: {node: '>=10'}
-
   aria-query@5.1.3:
     resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
 
@@ -6049,9 +5958,6 @@ packages:
   browser-assert@1.2.1:
     resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==}
 
-  browserify-zlib@0.1.4:
-    resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==}
-
   browserslist@4.22.2:
     resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -6087,12 +5993,16 @@ packages:
   buffer@6.0.3:
     resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
 
+  bufferutil@4.0.7:
+    resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
+    engines: {node: '>=6.14.2'}
+
   bufferutil@4.0.8:
     resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==}
     engines: {node: '>=6.14.2'}
 
-  bullmq@5.8.3:
-    resolution: {integrity: sha512-RJgQu/vgSZqjOYrZ7F1UJsSAzveNx7FFpR3Tp/1TxOMXXN9TtZMSly5MT+vjzOhQX//3+YWNRbMWpC1mkqBc9w==}
+  bullmq@5.10.4:
+    resolution: {integrity: sha512-YEssEbWBbPXvSW2YMjIBKZdkIPZsOaTGWo1y2wpCFv/wUY+tRLKiSVuHgv09x0QEieybx844f9//UWuarG1JHg==}
 
   buraha@0.0.1:
     resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==}
@@ -6272,8 +6182,8 @@ packages:
     resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
     engines: {node: '>=10'}
 
-  chromatic@11.5.4:
-    resolution: {integrity: sha512-+J+CopeUSyGUIQJsU6X7CfvSmeVBs0j6LZ9AgF4+XTjI4pFmUiUXsTc00rH9x9W1jCppOaqDXv2kqJJXGDK3mA==}
+  chromatic@11.5.6:
+    resolution: {integrity: sha512-ycX/hlZLs69BltwwBNsEXr+As6x5/0rlwp6W/CiHMZ3tpm7dmkd+hQCsb8JGHb1h49W3qPOKQ/Lh9evqcJ1yeQ==}
     hasBin: true
     peerDependencies:
       '@chromatic-com/cypress': ^0.*.* || ^1.0.0
@@ -6424,8 +6334,8 @@ packages:
   commondir@1.0.1:
     resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
 
-  compare-versions@6.1.0:
-    resolution: {integrity: sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==}
+  compare-versions@6.1.1:
+    resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
 
   compress-commons@6.0.2:
     resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==}
@@ -6521,8 +6431,8 @@ packages:
     resolution: {integrity: sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==}
     engines: {node: '>=12.0.0'}
 
-  cropperjs@2.0.0-beta.5:
-    resolution: {integrity: sha512-8RIynsyHV7KyCxbjV4fCQubGiM6sHMgYvRPKkzuUQSTYHK6shoUNvdvbBekwAwS8QRLsxEBcJ5lvl0W3dvkDQA==}
+  cropperjs@2.0.0-rc.1:
+    resolution: {integrity: sha512-Y9ciurIuK6G1vy0ErHC8Gt6wHWvsHWJ5fgE60GL6vsuF2WzHwDpH7F1yof40XAEheeSN4v3rD09D1VZ7kiiSOA==}
 
   cross-env@7.0.3:
     resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
@@ -6607,8 +6517,8 @@ packages:
   cwise-compiler@1.1.3:
     resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==}
 
-  cypress@13.13.0:
-    resolution: {integrity: sha512-ou/MQUDq4tcDJI2FsPaod2FZpex4kpIK43JJlcBgWrX8WX7R/05ZxGTuxedOuZBfxjZxja+fbijZGyxiLP6CFA==}
+  cypress@13.13.1:
+    resolution: {integrity: sha512-8F9UjL5MDUdgC/S5hr8CGLHbS5gGht5UOV184qc2pFny43fnkoaKxlzH/U6//zmGu/xRTaKimNfjknLT8+UDFg==}
     engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
     hasBin: true
 
@@ -6771,10 +6681,6 @@ packages:
     resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
     engines: {node: '>=8'}
 
-  detect-libc@2.0.2:
-    resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
-    engines: {node: '>=8'}
-
   detect-libc@2.0.3:
     resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
     engines: {node: '>=8'}
@@ -6783,9 +6689,6 @@ packages:
     resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
     engines: {node: '>=8'}
 
-  detect-node-es@1.1.0:
-    resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
-
   detect-package-manager@2.0.1:
     resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==}
     engines: {node: '>=12'}
@@ -6863,9 +6766,6 @@ packages:
   duplexer@0.1.2:
     resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
 
-  duplexify@3.7.1:
-    resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==}
-
   eastasianwidth@0.2.0:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
 
@@ -6993,8 +6893,8 @@ packages:
     engines: {node: '>=12'}
     hasBin: true
 
-  esbuild@0.22.0:
-    resolution: {integrity: sha512-zNYA6bFZsVnsU481FnGAQjLDW0Pl/8BGG7EvAp15RzUvGC+ME7hf1q7LvIfStEQBz/iEHuBJCYcOwPmNCf1Tlw==}
+  esbuild@0.23.0:
+    resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -7067,8 +6967,8 @@ packages:
       '@typescript-eslint/parser':
         optional: true
 
-  eslint-plugin-vue@9.26.0:
-    resolution: {integrity: sha512-eTvlxXgd4ijE1cdur850G6KalZqk65k1JKoOI2d1kT3hr8sPD07j1q98FRFdNnpxBELGPWxZmInxeHGF/GxtqQ==}
+  eslint-plugin-vue@9.27.0:
+    resolution: {integrity: sha512-5Dw3yxEyuBSXTzT5/Ge1X5kIkRTQ3nvBn/VwPwInNiZBSJOO/timWMUaflONnFBzU6NhB68lxnCda7ULV5N7LA==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
@@ -7080,10 +6980,6 @@ packages:
     resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
-  eslint-scope@8.0.1:
-    resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
   eslint-scope@8.0.2:
     resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -7096,13 +6992,8 @@ packages:
     resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
-  eslint@9.6.0:
-    resolution: {integrity: sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==}
-    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-    hasBin: true
-
-  eslint@9.7.0:
-    resolution: {integrity: sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==}
+  eslint@9.8.0:
+    resolution: {integrity: sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     hasBin: true
 
@@ -7123,10 +7014,6 @@ packages:
     resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==}
     engines: {node: '>=0.10'}
 
-  esquery@1.5.0:
-    resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
-    engines: {node: '>=0.10'}
-
   esquery@1.6.0:
     resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
     engines: {node: '>=0.10'}
@@ -7193,8 +7080,8 @@ packages:
     resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
     engines: {node: '>=16.17'}
 
-  execa@9.2.0:
-    resolution: {integrity: sha512-vpOyYg7UAVKLAWWtRS2gAdgkT7oJbCn0me3gmUmxZih4kd3MF/oo8kNTBTIbkO3yuuF5uB4ZCZfn8BOolITYhg==}
+  execa@9.3.0:
+    resolution: {integrity: sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==}
     engines: {node: ^18.19.0 || >=20.5.0}
 
   executable@4.1.1:
@@ -7274,6 +7161,9 @@ packages:
   fast-uri@2.2.0:
     resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==}
 
+  fast-uri@3.0.1:
+    resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==}
+
   fast-xml-parser@4.2.5:
     resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==}
     hasBin: true
@@ -7294,6 +7184,9 @@ packages:
   fb-watchman@2.0.2:
     resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
 
+  fd-package-json@1.2.0:
+    resolution: {integrity: sha512-45LSPmWf+gC5tdCQMNH4s9Sr00bIkiD9aN7dc5hqkrEw1geRYyDQS1v1oMHAW3ysfxfndqGsrDREHHjNNbKUfA==}
+
   fd-slicer@1.1.0:
     resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
 
@@ -7327,8 +7220,8 @@ packages:
     resolution: {integrity: sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
-  file-type@19.0.0:
-    resolution: {integrity: sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==}
+  file-type@19.3.0:
+    resolution: {integrity: sha512-mROwiKLZf/Kwa/2Rol+OOZQn1eyTkPB3ZTwC0ExY6OLFCbgxHYZvBm7xI77NvfZFMKBsmuXfmLJnD4eEftEhrA==}
     engines: {node: '>=18'}
 
   filelist@1.0.4:
@@ -7454,9 +7347,6 @@ packages:
   from@0.1.7:
     resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
 
-  fs-constants@1.0.0:
-    resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
-
   fs-extra@11.1.1:
     resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
     engines: {node: '>=14.14'}
@@ -7473,6 +7363,9 @@ packages:
     resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
     engines: {node: '>=10'}
 
+  fs-minipass@1.2.7:
+    resolution: {integrity: sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==}
+
   fs-minipass@2.1.0:
     resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
     engines: {node: '>= 8'}
@@ -7502,6 +7395,7 @@ packages:
   gauge@3.0.2:
     resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
     engines: {node: '>=10'}
+    deprecated: This package is no longer supported.
 
   gensync@1.0.0-beta.2:
     resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
@@ -7517,14 +7411,6 @@ packages:
   get-intrinsic@1.2.1:
     resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
 
-  get-nonce@1.0.1:
-    resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
-    engines: {node: '>=6'}
-
-  get-npm-tarball-url@2.0.3:
-    resolution: {integrity: sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw==}
-    engines: {node: '>=12.17'}
-
   get-package-type@0.1.0:
     resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
     engines: {node: '>=8.0.0'}
@@ -7598,16 +7484,16 @@ packages:
     engines: {node: '>=16 || 14 >=14.17'}
     hasBin: true
 
-  glob@10.3.12:
-    resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    hasBin: true
-
   glob@10.4.2:
     resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==}
     engines: {node: '>=16 || 14 >=14.18'}
     hasBin: true
 
+  glob@11.0.0:
+    resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==}
+    engines: {node: 20 || >=22}
+    hasBin: true
+
   glob@7.2.3:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
     deprecated: Glob versions prior to v9 are no longer supported
@@ -7633,8 +7519,8 @@ packages:
     resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
     engines: {node: '>=18'}
 
-  globals@15.7.0:
-    resolution: {integrity: sha512-ivatRXWwKC6ImcdKO7dOwXuXR5XFrdwo45qFwD7D0qOkEPzzJdLXC3BHceBdyrPOD3p1suPaWi4Y4NMm2D++AQ==}
+  globals@15.8.0:
+    resolution: {integrity: sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==}
     engines: {node: '>=18'}
 
   globalthis@1.0.3:
@@ -7663,8 +7549,8 @@ packages:
     resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==}
     engines: {node: '>=14.16'}
 
-  got@14.4.1:
-    resolution: {integrity: sha512-IvDJbJBUeexX74xNQuMIVgCRRuNOm5wuK+OC3Dc2pnSoh1AOmgc7JVj7WC+cJ4u0aPcO9KZ2frTXcqK4W/5qTQ==}
+  got@14.4.2:
+    resolution: {integrity: sha512-+Te/qEZ6hr7i+f0FNgXx/6WQteSM/QqueGvxeYQQFm0GDfoxLVJ/oiwUKYMTeioColWUTdewZ06hmrBjw6F7tw==}
     engines: {node: '>=20'}
 
   graceful-fs@4.2.11:
@@ -7680,10 +7566,6 @@ packages:
     resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==}
     engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
 
-  gunzip-maybe@1.4.2:
-    resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==}
-    hasBin: true
-
   hammerjs@2.0.8:
     resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
     engines: {node: '>=0.8.0'}
@@ -7818,10 +7700,6 @@ packages:
     resolution: {integrity: sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==}
     engines: {node: '>=6.0.0'}
 
-  http-proxy-agent@7.0.0:
-    resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
-    engines: {node: '>= 14'}
-
   http-proxy-agent@7.0.2:
     resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
     engines: {node: '>= 14'}
@@ -7862,6 +7740,10 @@ packages:
     resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
     engines: {node: '>= 14'}
 
+  https-proxy-agent@7.0.5:
+    resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
+    engines: {node: '>= 14'}
+
   human-signals@1.1.1:
     resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
     engines: {node: '>=8.12.0'}
@@ -7899,8 +7781,8 @@ packages:
   ignore-by-default@1.0.1:
     resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
 
-  ignore-walk@6.0.4:
-    resolution: {integrity: sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==}
+  ignore-walk@6.0.5:
+    resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
   ignore@5.2.4:
@@ -7918,11 +7800,11 @@ packages:
     resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
     engines: {node: '>=6'}
 
-  import-in-the-middle@1.4.2:
-    resolution: {integrity: sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==}
+  import-in-the-middle@1.10.0:
+    resolution: {integrity: sha512-Z1jumVdF2GwnnYfM0a/y2ts7mZbwFMgt5rRuVmLgobgahC6iKgN5MBuXjzfTIOUpq5LSU10vJIPpVKe0X89fIw==}
 
-  import-in-the-middle@1.8.1:
-    resolution: {integrity: sha512-yhRwoHtiLGvmSozNOALgjRPFI6uYsds60EoMqqnXyyv+JOIW/BrrLejuTGBt+bq0T5tLzOHrN0T7xYTm4Qt/ng==}
+  import-in-the-middle@1.7.1:
+    resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==}
 
   import-lazy@4.0.0:
     resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
@@ -7972,9 +7854,6 @@ packages:
   intersection-observer@0.12.2:
     resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
 
-  invariant@2.2.4:
-    resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
-
   ioredis@5.4.1:
     resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==}
     engines: {node: '>=12.22.0'}
@@ -8055,9 +7934,6 @@ packages:
     resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
     engines: {node: '>= 0.4'}
 
-  is-deflate@1.0.0:
-    resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==}
-
   is-docker@2.2.1:
     resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
     engines: {node: '>=8'}
@@ -8089,10 +7965,6 @@ packages:
     resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
     engines: {node: '>=0.10.0'}
 
-  is-gzip@1.0.0:
-    resolution: {integrity: sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==}
-    engines: {node: '>=0.10.0'}
-
   is-installed-globally@0.4.0:
     resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
     engines: {node: '>=10'}
@@ -8285,6 +8157,10 @@ packages:
     resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==}
     engines: {node: '>=14'}
 
+  jackspeak@4.0.1:
+    resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==}
+    engines: {node: 20 || >=22}
+
   jake@10.8.5:
     resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
     engines: {node: '>=10'}
@@ -8475,8 +8351,8 @@ packages:
       '@babel/preset-env':
         optional: true
 
-  jsdom@24.1.0:
-    resolution: {integrity: sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==}
+  jsdom@24.1.1:
+    resolution: {integrity: sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ==}
     engines: {node: '>=18'}
     peerDependencies:
       canvas: ^2.11.2
@@ -8714,6 +8590,10 @@ packages:
     resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
     engines: {node: 14 || >=16.14}
 
+  lru-cache@11.0.0:
+    resolution: {integrity: sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==}
+    engines: {node: 20 || >=22}
+
   lru-cache@4.1.5:
     resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
 
@@ -8785,8 +8665,8 @@ packages:
   markdown-table@3.0.3:
     resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
 
-  markdown-to-jsx@7.3.2:
-    resolution: {integrity: sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==}
+  markdown-to-jsx@7.4.7:
+    resolution: {integrity: sha512-0+ls1IQZdU6cwM1yu0ZjjiVWYtkbExSyUIFU2ZeDIFuZM1W42Mh4OlJ4nb4apX4H8smxDHRdFaoIVJGwfv5hkg==}
     engines: {node: '>= 10'}
     peerDependencies:
       react: '>= 0.14.0'
@@ -9001,6 +8881,10 @@ packages:
   minimalistic-assert@1.0.1:
     resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
 
+  minimatch@10.0.1:
+    resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
+    engines: {node: 20 || >=22}
+
   minimatch@3.0.8:
     resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==}
 
@@ -9050,6 +8934,9 @@ packages:
     resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==}
     engines: {node: '>=8'}
 
+  minipass@2.9.0:
+    resolution: {integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==}
+
   minipass@3.3.6:
     resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
     engines: {node: '>=8'}
@@ -9066,13 +8953,13 @@ packages:
     resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
     engines: {node: '>=16 || 14 >=14.17'}
 
+  minizlib@1.3.3:
+    resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==}
+
   minizlib@2.1.2:
     resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
     engines: {node: '>= 8'}
 
-  mkdirp-classic@0.5.3:
-    resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
-
   mkdirp@0.5.6:
     resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
     hasBin: true
@@ -9124,13 +9011,13 @@ packages:
   msgpackr@1.10.1:
     resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==}
 
-  msw-storybook-addon@2.0.2:
-    resolution: {integrity: sha512-sdw++X+AoUbaG2ku493ViVqCA/LfqnybXsKXyPUrF3ZS/x8BqGBnkBLmT/0SHCC5zIO3Vfm5zlclAxnhqOOikQ==}
+  msw-storybook-addon@2.0.3:
+    resolution: {integrity: sha512-CzHmGO32JeOPnyUnRWnB0PFTXCY1HKfHiEB/6fYoUYiFm2NYosLjzs9aBd3XJUryYEN0avJqMNh7nCRDxE5JjQ==}
     peerDependencies:
       msw: ^2.0.0
 
-  msw@2.3.1:
-    resolution: {integrity: sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==}
+  msw@2.3.4:
+    resolution: {integrity: sha512-sHMlwrajgmZSA2l1o7qRSe+azm/I+x9lvVVcOxAzi4vCtH8uVPJk1K5BQYDkzGl+tt0RvM9huEXXdeGrgcc79g==}
     engines: {node: '>=18'}
     hasBin: true
     peerDependencies:
@@ -9243,15 +9130,6 @@ packages:
   node-fetch-native@1.0.2:
     resolution: {integrity: sha512-KIkvH1jl6b3O7es/0ShyCgWLcfXxlBrLBbP3rOr23WArC66IMcU4DeZEeYEOwnopYhawLTn7/y+YtmASe8DFVQ==}
 
-  node-fetch@2.6.11:
-    resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==}
-    engines: {node: 4.x || >=6.0.0}
-    peerDependencies:
-      encoding: ^0.1.0
-    peerDependenciesMeta:
-      encoding:
-        optional: true
-
   node-fetch@2.6.13:
     resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==}
     engines: {node: 4.x || >=6.0.0}
@@ -9371,6 +9249,7 @@ packages:
 
   npmlog@5.0.1:
     resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
+    deprecated: This package is no longer supported.
 
   nsfwjs@2.4.2:
     resolution: {integrity: sha512-i4Pp2yt59qPQgeZFyg3wXFBX52uSeu/hkDoqdZfe+sILRxNBUu0VDogj7Lmqak0GlrXviS/wLiVeIx40IDUu7A==}
@@ -9380,8 +9259,8 @@ packages:
   nth-check@2.1.1:
     resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
 
-  nwsapi@2.2.10:
-    resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==}
+  nwsapi@2.2.12:
+    resolution: {integrity: sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==}
 
   oauth-sign@0.9.0:
     resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
@@ -9472,13 +9351,11 @@ packages:
     resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==}
     hasBin: true
 
-  opentelemetry-instrumentation-fetch-node@1.2.0:
-    resolution: {integrity: sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA==}
+  opentelemetry-instrumentation-fetch-node@1.2.3:
+    resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==}
     engines: {node: '>18.0.0'}
-
-  optionator@0.9.3:
-    resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
-    engines: {node: '>= 0.8.0'}
+    peerDependencies:
+      '@opentelemetry/api': ^1.6.0
 
   optionator@0.9.4:
     resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
@@ -9563,9 +9440,6 @@ packages:
   package-json-from-dist@1.0.0:
     resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
 
-  pako@0.2.9:
-    resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
-
   parent-module@1.0.1:
     resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
     engines: {node: '>=6'}
@@ -9637,14 +9511,14 @@ packages:
     resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
     engines: {node: '>=16 || 14 >=14.17'}
 
-  path-scurry@1.10.2:
-    resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==}
-    engines: {node: '>=16 || 14 >=14.17'}
-
   path-scurry@1.11.1:
     resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
     engines: {node: '>=16 || 14 >=14.18'}
 
+  path-scurry@2.0.0:
+    resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==}
+    engines: {node: 20 || >=22}
+
   path-to-regexp@0.1.7:
     resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
 
@@ -9678,8 +9552,9 @@ packages:
     resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==}
     engines: {node: '>=14.16'}
 
-  peek-stream@1.1.3:
-    resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==}
+  peek-readable@5.1.3:
+    resolution: {integrity: sha512-kCsc9HwH5RgVA3H3VqkWFyGQwsxUxLdiSX1d5nqAm7hnMFjNFX1VhBLmJoUY0hZNc8gmDNgBkLjfhiWPsziXWA==}
+    engines: {node: '>=14.16'}
 
   pend@1.2.0:
     resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
@@ -9736,6 +9611,9 @@ packages:
   picocolors@1.0.0:
     resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
 
+  picocolors@1.0.1:
+    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
+
   picomatch@2.3.1:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
     engines: {node: '>=8.6'}
@@ -9988,6 +9866,10 @@ packages:
     resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
     engines: {node: ^10 || ^12 || >=14}
 
+  postcss@8.4.40:
+    resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==}
+    engines: {node: ^10 || ^12 || >=14}
+
   postgres-array@2.0.0:
     resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
     engines: {node: '>=4'}
@@ -10027,8 +9909,8 @@ packages:
     resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
     engines: {node: '>= 0.8.0'}
 
-  prettier@3.3.2:
-    resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
+  prettier@3.3.3:
+    resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
     engines: {node: '>=14'}
     hasBin: true
 
@@ -10173,15 +10055,9 @@ packages:
   pug@3.0.3:
     resolution: {integrity: sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==}
 
-  pump@2.0.1:
-    resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
-
   pump@3.0.0:
     resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
 
-  pumpify@1.5.1:
-    resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
-
   punycode@2.3.1:
     resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
     engines: {node: '>=6'}
@@ -10303,36 +10179,6 @@ packages:
   react-is@18.2.0:
     resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
 
-  react-remove-scroll-bar@2.3.6:
-    resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  react-remove-scroll@2.5.7:
-    resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  react-style-singleton@2.2.1:
-    resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
   react@18.3.1:
     resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
     engines: {node: '>=0.10.0'}
@@ -10534,14 +10380,16 @@ packages:
 
   rimraf@2.7.1:
     resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+    deprecated: Rimraf versions prior to v4 are no longer supported
     hasBin: true
 
   rimraf@3.0.2:
     resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    deprecated: Rimraf versions prior to v4 are no longer supported
     hasBin: true
 
-  rollup@4.18.0:
-    resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==}
+  rollup@4.19.1:
+    resolution: {integrity: sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
 
@@ -10586,8 +10434,8 @@ packages:
   sanitize-html@2.13.0:
     resolution: {integrity: sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==}
 
-  sass@1.77.6:
-    resolution: {integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==}
+  sass@1.77.8:
+    resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==}
     engines: {node: '>=14.0.0'}
     hasBin: true
 
@@ -10681,8 +10529,8 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
-  shiki@1.10.0:
-    resolution: {integrity: sha512-YD2sXQ+TMD/F9BimV9Jn0wj35pqOvywvOG/3PB6hGHyGKlM7TJ9tyJ02jOb2kF8F0HfJwKNYrh3sW7jEcuRlXA==}
+  shiki@1.12.0:
+    resolution: {integrity: sha512-BuAxWOm5JhRcbSOl7XCei8wGjgJJonnV0oipUupPY58iULxUGyHhW5CF+9FRMuM1pcJ5cGEJGll1LusX6FwpPA==}
 
   shimmer@1.2.1:
     resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
@@ -10700,8 +10548,8 @@ packages:
     resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
     engines: {node: '>=14'}
 
-  simple-oauth2@5.0.1:
-    resolution: {integrity: sha512-JcmGdzvbHKU3GegF3BK6zNi46DqFTxPMjwYddu2bgYqZuy7Gtm8U8wdedkVE4lI4LEqXocmPBLAvC4BIiiBc5w==}
+  simple-oauth2@5.1.0:
+    resolution: {integrity: sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw==}
 
   simple-swizzle@0.2.2:
     resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
@@ -10946,8 +10794,8 @@ packages:
       react-dom:
         optional: true
 
-  storybook@8.1.11:
-    resolution: {integrity: sha512-3KjIhF8lczXhKKHyHbOqV30dvuRYJSxc0d1as/C8kybuwE7cLaydhWGma7VBv5bTSPv0rDzucx7KcO+achArPg==}
+  storybook@8.2.6:
+    resolution: {integrity: sha512-8j30wDxQmkcqI0fWcSYFsUCjErsY1yTWbTW+yjbwM8DyW18Cud6CwbFRCxjFsH+2M0CjP6Pqs/m1PGI0vcQscQ==}
     hasBin: true
 
   stream-browserify@3.0.0:
@@ -10959,9 +10807,6 @@ packages:
   stream-parser@0.3.1:
     resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==}
 
-  stream-shift@1.0.1:
-    resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
-
   stream-wormhole@1.1.0:
     resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
     engines: {node: '>=4.0.0'}
@@ -11075,6 +10920,10 @@ packages:
     resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==}
     engines: {node: '>=14.16'}
 
+  strtok3@8.0.1:
+    resolution: {integrity: sha512-HNkTAnNWQj2YBzfTtoC5OQyu1QwPsMwiB7VyQmNvQKCrmEDSvFB857Vh97UY9InGLNRAB91sdS1ztifRo/3hdA==}
+    engines: {node: '>=16'}
+
   stylehacks@6.1.1:
     resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==}
     engines: {node: ^14 || ^16 || >=18.0}
@@ -11119,16 +10968,13 @@ packages:
     os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
     hasBin: true
 
-  tar-fs@2.1.1:
-    resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
-
-  tar-stream@2.2.0:
-    resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
-    engines: {node: '>=6'}
-
   tar-stream@3.1.6:
     resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==}
 
+  tar@4.4.19:
+    resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==}
+    engines: {node: '>=4.5'}
+
   tar@6.2.1:
     resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     engines: {node: '>=10'}
@@ -11152,13 +10998,8 @@ packages:
     resolution: {integrity: sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==}
     engines: {node: '>=14.16'}
 
-  terser@5.31.1:
-    resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==}
-    engines: {node: '>=10'}
-    hasBin: true
-
-  terser@5.31.2:
-    resolution: {integrity: sha512-LGyRZVFm/QElZHy/CPr/O4eNZOZIzsrQ92y4v9UJe/pFJjypje2yI3C2FmPtvUEnhadlSbmG2nXtdcjHOjCfxw==}
+  terser@5.31.3:
+    resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==}
     engines: {node: '>=10'}
     hasBin: true
 
@@ -11182,8 +11023,8 @@ packages:
   thread-stream@3.1.0:
     resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
 
-  three@0.165.0:
-    resolution: {integrity: sha512-cc96IlVYGydeceu0e5xq70H8/yoVT/tXBxV/W8A/U6uOq7DXc4/s1Mkmnu6SqoYGhSRWWYFOhVwvq6V0VtbplA==}
+  three@0.167.0:
+    resolution: {integrity: sha512-9Y1a66fpjqF3rhq7ivKTaKtjQLZ97Hj/lZ00DmZWaKHaQFH4uzYT1znwRDWQOcgMmCcOloQzo61gDmqO8l9xmA==}
 
   throttle-debounce@5.0.2:
     resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
@@ -11192,9 +11033,6 @@ packages:
   throttleit@1.0.0:
     resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==}
 
-  through2@2.0.5:
-    resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
-
   through@2.3.4:
     resolution: {integrity: sha512-DwbmSAcABsMazNkLOJJSLRC3gfh4cPxUxJCn9npmvbcI6undhgoJ2ShvEOgZrW8BH62Gyr9jKboGbfFcmY5VsQ==}
 
@@ -11244,9 +11082,6 @@ packages:
     resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
     engines: {node: '>=12'}
 
-  tocbot@4.21.1:
-    resolution: {integrity: sha512-IfajhBTeg0HlMXu1f+VMbPef05QpDTsZ9X2Yn1+8npdaXsXg/+wrm9Ze1WG5OS1UDC3qJ5EQN/XOZ3gfXjPFCw==}
-
   toidentifier@1.0.1:
     resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
     engines: {node: '>=0.6'}
@@ -11258,6 +11093,10 @@ packages:
     resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==}
     engines: {node: '>=14.16'}
 
+  token-types@6.0.0:
+    resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==}
+    engines: {node: '>=14.16'}
+
   touch@3.1.0:
     resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==}
     hasBin: true
@@ -11390,10 +11229,6 @@ packages:
     resolution: {integrity: sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg==}
     engines: {node: '>=16'}
 
-  type-fest@4.9.0:
-    resolution: {integrity: sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==}
-    engines: {node: '>=16'}
-
   type-is@1.6.18:
     resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
     engines: {node: '>= 0.6'}
@@ -11484,8 +11319,8 @@ packages:
     engines: {node: '>=14.17'}
     hasBin: true
 
-  typescript@5.5.3:
-    resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==}
+  typescript@5.5.4:
+    resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -11504,6 +11339,10 @@ packages:
     resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
     engines: {node: '>=8'}
 
+  uint8array-extras@1.4.0:
+    resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==}
+    engines: {node: '>=18'}
+
   ulid@2.3.0:
     resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==}
     hasBin: true
@@ -11609,25 +11448,9 @@ packages:
   url-parse@1.5.10:
     resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
 
-  use-callback-ref@1.3.2:
-    resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-
-  use-sidecar@1.1.2:
-    resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
+  utf-8-validate@6.0.3:
+    resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==}
+    engines: {node: '>=6.14.2'}
 
   utf-8-validate@6.0.4:
     resolution: {integrity: sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==}
@@ -11698,8 +11521,8 @@ packages:
   vite-plugin-turbosnap@1.0.3:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
 
-  vite@5.3.2:
-    resolution: {integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==}
+  vite@5.3.5:
+    resolution: {integrity: sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -11799,11 +11622,8 @@ packages:
   vue-component-type-helpers@2.0.16:
     resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
 
-  vue-component-type-helpers@2.0.24:
-    resolution: {integrity: sha512-Jr5N8QVYEcbQuMN1LRgvg61758G8HTnzUlQsAFOxx6Y6X8kmhJ7C+jOvWsQruYxi3uHhhS6BghyRlyiwO99DBg==}
-
-  vue-component-type-helpers@2.0.26:
-    resolution: {integrity: sha512-sO9qQ8oC520SW6kqlls0iqDak53gsTVSrYylajgjmkt1c0vcgjsGSy1KzlDrbEx8pm02IEYhlUkU5hCYf8rwtg==}
+  vue-component-type-helpers@2.0.29:
+    resolution: {integrity: sha512-58i+ZhUAUpwQ+9h5Hck0D+jr1qbYl4voRt5KffBx8qzELViQ4XdT/Tuo+mzq8u63teAG8K0lLaOiL5ofqW38rg==}
 
   vue-demi@0.14.7:
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
@@ -11841,14 +11661,14 @@ packages:
   vue-template-compiler@2.7.14:
     resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
 
-  vue-tsc@2.0.24:
-    resolution: {integrity: sha512-1qi4P8L7yS78A7OJ7CDDxUIZPD6nVxoQEgX3DkRZNi1HI1qOfzOJwQlNpmwkogSVD6S/XcanbW9sktzpSxz6rA==}
+  vue-tsc@2.0.29:
+    resolution: {integrity: sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==}
     hasBin: true
     peerDependencies:
       typescript: '>=5.0.0'
 
-  vue@3.4.31:
-    resolution: {integrity: sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==}
+  vue@3.4.34:
+    resolution: {integrity: sha512-VZze05HWlA3ItreQ/ka7Sx7PoD0/3St8FEiSlSTVgb6l4hL+RjtP2/8g5WQBzZgyf8WG2f+g1bXzC7zggLhAJA==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
@@ -11869,6 +11689,9 @@ packages:
     engines: {node: '>=12.0.0'}
     hasBin: true
 
+  walk-up-path@3.0.1:
+    resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==}
+
   walker@1.0.8:
     resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
 
@@ -11997,8 +11820,8 @@ packages:
     resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
 
-  ws@8.17.1:
-    resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
+  ws@8.18.0:
+    resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
     engines: {node: '>=10.0.0'}
     peerDependencies:
       bufferutil: ^4.0.1
@@ -12103,8 +11926,6 @@ packages:
 
 snapshots:
 
-  '@aashutoshrathi/word-wrap@1.2.6': {}
-
   '@adobe/css-tools@4.3.3': {}
 
   '@aiscript-dev/aiscript-languageserver@https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz':
@@ -12131,508 +11952,509 @@ snapshots:
   '@aws-crypto/crc32@5.2.0':
     dependencies:
       '@aws-crypto/util': 5.2.0
-      '@aws-sdk/types': 3.598.0
-      tslib: 2.6.2
+      '@aws-sdk/types': 3.609.0
+      tslib: 2.6.3
 
   '@aws-crypto/crc32c@5.2.0':
     dependencies:
       '@aws-crypto/util': 5.2.0
-      '@aws-sdk/types': 3.598.0
-      tslib: 2.6.2
+      '@aws-sdk/types': 3.609.0
+      tslib: 2.6.3
 
   '@aws-crypto/sha1-browser@5.2.0':
     dependencies:
       '@aws-crypto/supports-web-crypto': 5.2.0
       '@aws-crypto/util': 5.2.0
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@aws-sdk/util-locate-window': 3.208.0
       '@smithy/util-utf8': 2.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@aws-crypto/sha256-browser@5.2.0':
     dependencies:
       '@aws-crypto/sha256-js': 5.2.0
       '@aws-crypto/supports-web-crypto': 5.2.0
       '@aws-crypto/util': 5.2.0
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@aws-sdk/util-locate-window': 3.208.0
       '@smithy/util-utf8': 2.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@aws-crypto/sha256-js@5.2.0':
     dependencies:
       '@aws-crypto/util': 5.2.0
-      '@aws-sdk/types': 3.598.0
-      tslib: 2.6.2
+      '@aws-sdk/types': 3.609.0
+      tslib: 2.6.3
 
   '@aws-crypto/supports-web-crypto@5.2.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@aws-crypto/util@5.2.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/util-utf8': 2.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/client-s3@3.600.0':
+  '@aws-sdk/client-s3@3.620.0':
     dependencies:
       '@aws-crypto/sha1-browser': 5.2.0
       '@aws-crypto/sha256-browser': 5.2.0
       '@aws-crypto/sha256-js': 5.2.0
-      '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/client-sts': 3.600.0
-      '@aws-sdk/core': 3.598.0
-      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/middleware-bucket-endpoint': 3.598.0
-      '@aws-sdk/middleware-expect-continue': 3.598.0
-      '@aws-sdk/middleware-flexible-checksums': 3.598.0
-      '@aws-sdk/middleware-host-header': 3.598.0
-      '@aws-sdk/middleware-location-constraint': 3.598.0
-      '@aws-sdk/middleware-logger': 3.598.0
-      '@aws-sdk/middleware-recursion-detection': 3.598.0
-      '@aws-sdk/middleware-sdk-s3': 3.598.0
-      '@aws-sdk/middleware-signing': 3.598.0
-      '@aws-sdk/middleware-ssec': 3.598.0
-      '@aws-sdk/middleware-user-agent': 3.598.0
-      '@aws-sdk/region-config-resolver': 3.598.0
-      '@aws-sdk/signature-v4-multi-region': 3.598.0
-      '@aws-sdk/types': 3.598.0
-      '@aws-sdk/util-endpoints': 3.598.0
-      '@aws-sdk/util-user-agent-browser': 3.598.0
-      '@aws-sdk/util-user-agent-node': 3.598.0
-      '@aws-sdk/xml-builder': 3.598.0
-      '@smithy/config-resolver': 3.0.4
-      '@smithy/core': 2.2.4
-      '@smithy/eventstream-serde-browser': 3.0.4
+      '@aws-sdk/client-sso-oidc': 3.620.0(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/client-sts': 3.620.0
+      '@aws-sdk/core': 3.620.0
+      '@aws-sdk/credential-provider-node': 3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/middleware-bucket-endpoint': 3.620.0
+      '@aws-sdk/middleware-expect-continue': 3.620.0
+      '@aws-sdk/middleware-flexible-checksums': 3.620.0
+      '@aws-sdk/middleware-host-header': 3.620.0
+      '@aws-sdk/middleware-location-constraint': 3.609.0
+      '@aws-sdk/middleware-logger': 3.609.0
+      '@aws-sdk/middleware-recursion-detection': 3.620.0
+      '@aws-sdk/middleware-sdk-s3': 3.620.0
+      '@aws-sdk/middleware-signing': 3.620.0
+      '@aws-sdk/middleware-ssec': 3.609.0
+      '@aws-sdk/middleware-user-agent': 3.620.0
+      '@aws-sdk/region-config-resolver': 3.614.0
+      '@aws-sdk/signature-v4-multi-region': 3.620.0
+      '@aws-sdk/types': 3.609.0
+      '@aws-sdk/util-endpoints': 3.614.0
+      '@aws-sdk/util-user-agent-browser': 3.609.0
+      '@aws-sdk/util-user-agent-node': 3.614.0
+      '@aws-sdk/xml-builder': 3.609.0
+      '@smithy/config-resolver': 3.0.5
+      '@smithy/core': 2.3.1
+      '@smithy/eventstream-serde-browser': 3.0.5
       '@smithy/eventstream-serde-config-resolver': 3.0.3
       '@smithy/eventstream-serde-node': 3.0.4
-      '@smithy/fetch-http-handler': 3.2.0
+      '@smithy/fetch-http-handler': 3.2.4
       '@smithy/hash-blob-browser': 3.1.2
       '@smithy/hash-node': 3.0.3
       '@smithy/hash-stream-node': 3.1.2
       '@smithy/invalid-dependency': 3.0.3
       '@smithy/md5-js': 3.0.3
-      '@smithy/middleware-content-length': 3.0.3
-      '@smithy/middleware-endpoint': 3.0.4
-      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-content-length': 3.0.5
+      '@smithy/middleware-endpoint': 3.1.0
+      '@smithy/middleware-retry': 3.0.13
       '@smithy/middleware-serde': 3.0.3
       '@smithy/middleware-stack': 3.0.3
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/node-http-handler': 3.1.1
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/node-http-handler': 3.1.4
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       '@smithy/url-parser': 3.0.3
       '@smithy/util-base64': 3.0.0
       '@smithy/util-body-length-browser': 3.0.0
       '@smithy/util-body-length-node': 3.0.0
-      '@smithy/util-defaults-mode-browser': 3.0.7
-      '@smithy/util-defaults-mode-node': 3.0.7
-      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-defaults-mode-browser': 3.0.13
+      '@smithy/util-defaults-mode-node': 3.0.13
+      '@smithy/util-endpoints': 2.0.5
       '@smithy/util-retry': 3.0.3
-      '@smithy/util-stream': 3.0.5
+      '@smithy/util-stream': 3.1.3
       '@smithy/util-utf8': 3.0.0
       '@smithy/util-waiter': 3.1.2
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
       - aws-crt
 
-  '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)':
+  '@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0)':
     dependencies:
       '@aws-crypto/sha256-browser': 5.2.0
       '@aws-crypto/sha256-js': 5.2.0
-      '@aws-sdk/client-sts': 3.600.0
-      '@aws-sdk/core': 3.598.0
-      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/middleware-host-header': 3.598.0
-      '@aws-sdk/middleware-logger': 3.598.0
-      '@aws-sdk/middleware-recursion-detection': 3.598.0
-      '@aws-sdk/middleware-user-agent': 3.598.0
-      '@aws-sdk/region-config-resolver': 3.598.0
-      '@aws-sdk/types': 3.598.0
-      '@aws-sdk/util-endpoints': 3.598.0
-      '@aws-sdk/util-user-agent-browser': 3.598.0
-      '@aws-sdk/util-user-agent-node': 3.598.0
-      '@smithy/config-resolver': 3.0.4
-      '@smithy/core': 2.2.4
-      '@smithy/fetch-http-handler': 3.2.0
+      '@aws-sdk/client-sts': 3.620.0
+      '@aws-sdk/core': 3.620.0
+      '@aws-sdk/credential-provider-node': 3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/middleware-host-header': 3.620.0
+      '@aws-sdk/middleware-logger': 3.609.0
+      '@aws-sdk/middleware-recursion-detection': 3.620.0
+      '@aws-sdk/middleware-user-agent': 3.620.0
+      '@aws-sdk/region-config-resolver': 3.614.0
+      '@aws-sdk/types': 3.609.0
+      '@aws-sdk/util-endpoints': 3.614.0
+      '@aws-sdk/util-user-agent-browser': 3.609.0
+      '@aws-sdk/util-user-agent-node': 3.614.0
+      '@smithy/config-resolver': 3.0.5
+      '@smithy/core': 2.3.1
+      '@smithy/fetch-http-handler': 3.2.4
       '@smithy/hash-node': 3.0.3
       '@smithy/invalid-dependency': 3.0.3
-      '@smithy/middleware-content-length': 3.0.3
-      '@smithy/middleware-endpoint': 3.0.4
-      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-content-length': 3.0.5
+      '@smithy/middleware-endpoint': 3.1.0
+      '@smithy/middleware-retry': 3.0.13
       '@smithy/middleware-serde': 3.0.3
       '@smithy/middleware-stack': 3.0.3
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/node-http-handler': 3.1.1
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/node-http-handler': 3.1.4
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       '@smithy/url-parser': 3.0.3
       '@smithy/util-base64': 3.0.0
       '@smithy/util-body-length-browser': 3.0.0
       '@smithy/util-body-length-node': 3.0.0
-      '@smithy/util-defaults-mode-browser': 3.0.7
-      '@smithy/util-defaults-mode-node': 3.0.7
-      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-defaults-mode-browser': 3.0.13
+      '@smithy/util-defaults-mode-node': 3.0.13
+      '@smithy/util-endpoints': 2.0.5
       '@smithy/util-middleware': 3.0.3
       '@smithy/util-retry': 3.0.3
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
-      - '@aws-sdk/client-sts'
       - aws-crt
 
-  '@aws-sdk/client-sso@3.598.0':
+  '@aws-sdk/client-sso@3.620.0':
     dependencies:
       '@aws-crypto/sha256-browser': 5.2.0
       '@aws-crypto/sha256-js': 5.2.0
-      '@aws-sdk/core': 3.598.0
-      '@aws-sdk/middleware-host-header': 3.598.0
-      '@aws-sdk/middleware-logger': 3.598.0
-      '@aws-sdk/middleware-recursion-detection': 3.598.0
-      '@aws-sdk/middleware-user-agent': 3.598.0
-      '@aws-sdk/region-config-resolver': 3.598.0
-      '@aws-sdk/types': 3.598.0
-      '@aws-sdk/util-endpoints': 3.598.0
-      '@aws-sdk/util-user-agent-browser': 3.598.0
-      '@aws-sdk/util-user-agent-node': 3.598.0
-      '@smithy/config-resolver': 3.0.4
-      '@smithy/core': 2.2.4
-      '@smithy/fetch-http-handler': 3.2.0
+      '@aws-sdk/core': 3.620.0
+      '@aws-sdk/middleware-host-header': 3.620.0
+      '@aws-sdk/middleware-logger': 3.609.0
+      '@aws-sdk/middleware-recursion-detection': 3.620.0
+      '@aws-sdk/middleware-user-agent': 3.620.0
+      '@aws-sdk/region-config-resolver': 3.614.0
+      '@aws-sdk/types': 3.609.0
+      '@aws-sdk/util-endpoints': 3.614.0
+      '@aws-sdk/util-user-agent-browser': 3.609.0
+      '@aws-sdk/util-user-agent-node': 3.614.0
+      '@smithy/config-resolver': 3.0.5
+      '@smithy/core': 2.3.1
+      '@smithy/fetch-http-handler': 3.2.4
       '@smithy/hash-node': 3.0.3
       '@smithy/invalid-dependency': 3.0.3
-      '@smithy/middleware-content-length': 3.0.3
-      '@smithy/middleware-endpoint': 3.0.4
-      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-content-length': 3.0.5
+      '@smithy/middleware-endpoint': 3.1.0
+      '@smithy/middleware-retry': 3.0.13
       '@smithy/middleware-serde': 3.0.3
       '@smithy/middleware-stack': 3.0.3
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/node-http-handler': 3.1.1
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/node-http-handler': 3.1.4
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       '@smithy/url-parser': 3.0.3
       '@smithy/util-base64': 3.0.0
       '@smithy/util-body-length-browser': 3.0.0
       '@smithy/util-body-length-node': 3.0.0
-      '@smithy/util-defaults-mode-browser': 3.0.7
-      '@smithy/util-defaults-mode-node': 3.0.7
-      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-defaults-mode-browser': 3.0.13
+      '@smithy/util-defaults-mode-node': 3.0.13
+      '@smithy/util-endpoints': 2.0.5
       '@smithy/util-middleware': 3.0.3
       '@smithy/util-retry': 3.0.3
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
       - aws-crt
 
-  '@aws-sdk/client-sts@3.600.0':
+  '@aws-sdk/client-sts@3.620.0':
     dependencies:
       '@aws-crypto/sha256-browser': 5.2.0
       '@aws-crypto/sha256-js': 5.2.0
-      '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/core': 3.598.0
-      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/middleware-host-header': 3.598.0
-      '@aws-sdk/middleware-logger': 3.598.0
-      '@aws-sdk/middleware-recursion-detection': 3.598.0
-      '@aws-sdk/middleware-user-agent': 3.598.0
-      '@aws-sdk/region-config-resolver': 3.598.0
-      '@aws-sdk/types': 3.598.0
-      '@aws-sdk/util-endpoints': 3.598.0
-      '@aws-sdk/util-user-agent-browser': 3.598.0
-      '@aws-sdk/util-user-agent-node': 3.598.0
-      '@smithy/config-resolver': 3.0.4
-      '@smithy/core': 2.2.4
-      '@smithy/fetch-http-handler': 3.2.0
+      '@aws-sdk/client-sso-oidc': 3.620.0(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/core': 3.620.0
+      '@aws-sdk/credential-provider-node': 3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/middleware-host-header': 3.620.0
+      '@aws-sdk/middleware-logger': 3.609.0
+      '@aws-sdk/middleware-recursion-detection': 3.620.0
+      '@aws-sdk/middleware-user-agent': 3.620.0
+      '@aws-sdk/region-config-resolver': 3.614.0
+      '@aws-sdk/types': 3.609.0
+      '@aws-sdk/util-endpoints': 3.614.0
+      '@aws-sdk/util-user-agent-browser': 3.609.0
+      '@aws-sdk/util-user-agent-node': 3.614.0
+      '@smithy/config-resolver': 3.0.5
+      '@smithy/core': 2.3.1
+      '@smithy/fetch-http-handler': 3.2.4
       '@smithy/hash-node': 3.0.3
       '@smithy/invalid-dependency': 3.0.3
-      '@smithy/middleware-content-length': 3.0.3
-      '@smithy/middleware-endpoint': 3.0.4
-      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-content-length': 3.0.5
+      '@smithy/middleware-endpoint': 3.1.0
+      '@smithy/middleware-retry': 3.0.13
       '@smithy/middleware-serde': 3.0.3
       '@smithy/middleware-stack': 3.0.3
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/node-http-handler': 3.1.1
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/node-http-handler': 3.1.4
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       '@smithy/url-parser': 3.0.3
       '@smithy/util-base64': 3.0.0
       '@smithy/util-body-length-browser': 3.0.0
       '@smithy/util-body-length-node': 3.0.0
-      '@smithy/util-defaults-mode-browser': 3.0.7
-      '@smithy/util-defaults-mode-node': 3.0.7
-      '@smithy/util-endpoints': 2.0.4
+      '@smithy/util-defaults-mode-browser': 3.0.13
+      '@smithy/util-defaults-mode-node': 3.0.13
+      '@smithy/util-endpoints': 2.0.5
       '@smithy/util-middleware': 3.0.3
       '@smithy/util-retry': 3.0.3
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
       - aws-crt
 
-  '@aws-sdk/core@3.598.0':
+  '@aws-sdk/core@3.620.0':
     dependencies:
-      '@smithy/core': 2.2.4
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/signature-v4': 3.1.2
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/core': 2.3.1
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/signature-v4': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       fast-xml-parser: 4.2.5
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/credential-provider-env@3.598.0':
+  '@aws-sdk/credential-provider-env@3.609.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/property-provider': 3.1.3
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/credential-provider-http@3.598.0':
+  '@aws-sdk/credential-provider-http@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
-      '@smithy/fetch-http-handler': 3.2.0
-      '@smithy/node-http-handler': 3.1.1
+      '@aws-sdk/types': 3.609.0
+      '@smithy/fetch-http-handler': 3.2.4
+      '@smithy/node-http-handler': 3.1.4
       '@smithy/property-provider': 3.1.3
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
-      '@smithy/util-stream': 3.0.5
-      tslib: 2.6.2
+      '@smithy/util-stream': 3.1.3
+      tslib: 2.6.3
 
-  '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)':
+  '@aws-sdk/credential-provider-ini@3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))(@aws-sdk/client-sts@3.620.0)':
     dependencies:
-      '@aws-sdk/client-sts': 3.600.0
-      '@aws-sdk/credential-provider-env': 3.598.0
-      '@aws-sdk/credential-provider-http': 3.598.0
-      '@aws-sdk/credential-provider-process': 3.598.0
-      '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
-      '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/types': 3.598.0
-      '@smithy/credential-provider-imds': 3.1.3
+      '@aws-sdk/client-sts': 3.620.0
+      '@aws-sdk/credential-provider-env': 3.609.0
+      '@aws-sdk/credential-provider-http': 3.620.0
+      '@aws-sdk/credential-provider-process': 3.614.0
+      '@aws-sdk/credential-provider-sso': 3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))
+      '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/types': 3.609.0
+      '@smithy/credential-provider-imds': 3.2.0
       '@smithy/property-provider': 3.1.3
-      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
       - '@aws-sdk/client-sso-oidc'
       - aws-crt
 
-  '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)':
+  '@aws-sdk/credential-provider-node@3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))(@aws-sdk/client-sts@3.620.0)':
     dependencies:
-      '@aws-sdk/credential-provider-env': 3.598.0
-      '@aws-sdk/credential-provider-http': 3.598.0
-      '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/credential-provider-process': 3.598.0
-      '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
-      '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/types': 3.598.0
-      '@smithy/credential-provider-imds': 3.1.3
+      '@aws-sdk/credential-provider-env': 3.609.0
+      '@aws-sdk/credential-provider-http': 3.620.0
+      '@aws-sdk/credential-provider-ini': 3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/credential-provider-process': 3.614.0
+      '@aws-sdk/credential-provider-sso': 3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))
+      '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/types': 3.609.0
+      '@smithy/credential-provider-imds': 3.2.0
       '@smithy/property-provider': 3.1.3
-      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
       - '@aws-sdk/client-sso-oidc'
       - '@aws-sdk/client-sts'
       - aws-crt
 
-  '@aws-sdk/credential-provider-process@3.598.0':
+  '@aws-sdk/credential-provider-process@3.614.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/property-provider': 3.1.3
-      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)':
+  '@aws-sdk/credential-provider-sso@3.620.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))':
     dependencies:
-      '@aws-sdk/client-sso': 3.598.0
-      '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/client-sso': 3.620.0
+      '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))
+      '@aws-sdk/types': 3.609.0
       '@smithy/property-provider': 3.1.3
-      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
     transitivePeerDependencies:
       - '@aws-sdk/client-sso-oidc'
       - aws-crt
 
-  '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)':
+  '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.620.0)':
     dependencies:
-      '@aws-sdk/client-sts': 3.600.0
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/client-sts': 3.620.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/property-provider': 3.1.3
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/lib-storage@3.600.0(@aws-sdk/client-s3@3.600.0)':
+  '@aws-sdk/lib-storage@3.620.0(@aws-sdk/client-s3@3.620.0)':
     dependencies:
-      '@aws-sdk/client-s3': 3.600.0
+      '@aws-sdk/client-s3': 3.620.0
       '@smithy/abort-controller': 3.1.1
-      '@smithy/middleware-endpoint': 3.0.4
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/middleware-endpoint': 3.1.0
+      '@smithy/smithy-client': 3.1.11
       buffer: 5.6.0
       events: 3.3.0
       stream-browserify: 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-bucket-endpoint@3.598.0':
+  '@aws-sdk/middleware-bucket-endpoint@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@aws-sdk/util-arn-parser': 3.568.0
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/protocol-http': 4.0.3
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
       '@smithy/util-config-provider': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-expect-continue@3.598.0':
+  '@aws-sdk/middleware-expect-continue@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
-      '@smithy/protocol-http': 4.0.3
+      '@aws-sdk/types': 3.609.0
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-flexible-checksums@3.598.0':
+  '@aws-sdk/middleware-flexible-checksums@3.620.0':
     dependencies:
       '@aws-crypto/crc32': 5.2.0
       '@aws-crypto/crc32c': 5.2.0
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/is-array-buffer': 3.0.0
-      '@smithy/protocol-http': 4.0.3
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-host-header@3.598.0':
+  '@aws-sdk/middleware-host-header@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
-      '@smithy/protocol-http': 4.0.3
+      '@aws-sdk/types': 3.609.0
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-location-constraint@3.598.0':
+  '@aws-sdk/middleware-location-constraint@3.609.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-logger@3.598.0':
+  '@aws-sdk/middleware-logger@3.609.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-recursion-detection@3.598.0':
+  '@aws-sdk/middleware-recursion-detection@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
-      '@smithy/protocol-http': 4.0.3
+      '@aws-sdk/types': 3.609.0
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-sdk-s3@3.598.0':
+  '@aws-sdk/middleware-sdk-s3@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@aws-sdk/util-arn-parser': 3.568.0
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/signature-v4': 3.1.2
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/signature-v4': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       '@smithy/util-config-provider': 3.0.0
-      tslib: 2.6.2
+      '@smithy/util-stream': 3.1.3
+      '@smithy/util-utf8': 3.0.0
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-signing@3.598.0':
+  '@aws-sdk/middleware-signing@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/property-provider': 3.1.3
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/signature-v4': 3.1.2
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/signature-v4': 4.1.0
       '@smithy/types': 3.3.0
       '@smithy/util-middleware': 3.0.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-ssec@3.598.0':
+  '@aws-sdk/middleware-ssec@3.609.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/middleware-user-agent@3.598.0':
+  '@aws-sdk/middleware-user-agent@3.620.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
-      '@aws-sdk/util-endpoints': 3.598.0
-      '@smithy/protocol-http': 4.0.3
+      '@aws-sdk/types': 3.609.0
+      '@aws-sdk/util-endpoints': 3.614.0
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/region-config-resolver@3.598.0':
+  '@aws-sdk/region-config-resolver@3.614.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
-      '@smithy/node-config-provider': 3.1.3
+      '@aws-sdk/types': 3.609.0
+      '@smithy/node-config-provider': 3.1.4
       '@smithy/types': 3.3.0
       '@smithy/util-config-provider': 3.0.0
       '@smithy/util-middleware': 3.0.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/signature-v4-multi-region@3.598.0':
+  '@aws-sdk/signature-v4-multi-region@3.620.0':
     dependencies:
-      '@aws-sdk/middleware-sdk-s3': 3.598.0
-      '@aws-sdk/types': 3.598.0
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/signature-v4': 3.1.2
+      '@aws-sdk/middleware-sdk-s3': 3.620.0
+      '@aws-sdk/types': 3.609.0
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/signature-v4': 4.1.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)':
+  '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.0(@aws-sdk/client-sts@3.620.0))':
     dependencies:
-      '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0)
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/client-sso-oidc': 3.620.0(@aws-sdk/client-sts@3.620.0)
+      '@aws-sdk/types': 3.609.0
       '@smithy/property-provider': 3.1.3
-      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/types@3.598.0':
+  '@aws-sdk/types@3.609.0':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@aws-sdk/util-arn-parser@3.568.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/util-endpoints@3.598.0':
+  '@aws-sdk/util-endpoints@3.614.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/types': 3.3.0
-      '@smithy/util-endpoints': 2.0.4
-      tslib: 2.6.2
+      '@smithy/util-endpoints': 2.0.5
+      tslib: 2.6.3
 
   '@aws-sdk/util-locate-window@3.208.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/util-user-agent-browser@3.598.0':
+  '@aws-sdk/util-user-agent-browser@3.609.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
+      '@aws-sdk/types': 3.609.0
       '@smithy/types': 3.3.0
       bowser: 2.11.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/util-user-agent-node@3.598.0':
+  '@aws-sdk/util-user-agent-node@3.614.0':
     dependencies:
-      '@aws-sdk/types': 3.598.0
-      '@smithy/node-config-provider': 3.1.3
+      '@aws-sdk/types': 3.609.0
+      '@smithy/node-config-provider': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@aws-sdk/xml-builder@3.598.0':
+  '@aws-sdk/xml-builder@3.609.0':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@babel/code-frame@7.23.5':
     dependencies:
@@ -13677,22 +13499,22 @@ snapshots:
 
   '@bcoe/v8-coverage@0.2.3': {}
 
-  '@bull-board/api@5.20.5(@bull-board/ui@5.20.5)':
+  '@bull-board/api@5.21.1(@bull-board/ui@5.21.1)':
     dependencies:
-      '@bull-board/ui': 5.20.5
+      '@bull-board/ui': 5.21.1
       redis-info: 3.1.0
 
-  '@bull-board/fastify@5.20.5':
+  '@bull-board/fastify@5.21.1':
     dependencies:
-      '@bull-board/api': 5.20.5(@bull-board/ui@5.20.5)
-      '@bull-board/ui': 5.20.5
+      '@bull-board/api': 5.21.1(@bull-board/ui@5.21.1)
+      '@bull-board/ui': 5.21.1
       '@fastify/static': 6.12.0
       '@fastify/view': 8.2.0
       ejs: 3.1.10
 
-  '@bull-board/ui@5.20.5':
+  '@bull-board/ui@5.21.1':
     dependencies:
-      '@bull-board/api': 5.20.5(@bull-board/ui@5.20.5)
+      '@bull-board/api': 5.21.1(@bull-board/ui@5.21.1)
 
   '@bundled-es-modules/cookie@2.0.0':
     dependencies:
@@ -13702,76 +13524,81 @@ snapshots:
     dependencies:
       statuses: 2.0.1
 
+  '@bundled-es-modules/tough-cookie@0.1.6':
+    dependencies:
+      '@types/tough-cookie': 4.0.5
+      tough-cookie: 4.1.4
+
   '@canvas/image-data@1.0.0': {}
 
   '@colors/colors@1.5.0':
     optional: true
 
-  '@cropper/element-canvas@2.0.0-beta.5':
+  '@cropper/element-canvas@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element-crosshair@2.0.0-beta.5':
+  '@cropper/element-crosshair@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element-grid@2.0.0-beta.5':
+  '@cropper/element-grid@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element-handle@2.0.0-beta.5':
+  '@cropper/element-handle@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element-image@2.0.0-beta.5':
+  '@cropper/element-image@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/element-canvas': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/element-canvas': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element-selection@2.0.0-beta.5':
+  '@cropper/element-selection@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/element-canvas': 2.0.0-beta.5
-      '@cropper/element-image': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/element-canvas': 2.0.0-rc.1
+      '@cropper/element-image': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element-shade@2.0.0-beta.5':
+  '@cropper/element-shade@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/element-canvas': 2.0.0-beta.5
-      '@cropper/element-selection': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/element-canvas': 2.0.0-rc.1
+      '@cropper/element-selection': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element-viewer@2.0.0-beta.5':
+  '@cropper/element-viewer@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/element-canvas': 2.0.0-beta.5
-      '@cropper/element-image': 2.0.0-beta.5
-      '@cropper/element-selection': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/element-canvas': 2.0.0-rc.1
+      '@cropper/element-image': 2.0.0-rc.1
+      '@cropper/element-selection': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/element@2.0.0-beta.5':
+  '@cropper/element@2.0.0-rc.1':
     dependencies:
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/utils': 2.0.0-rc.1
 
-  '@cropper/elements@2.0.0-beta.5':
+  '@cropper/elements@2.0.0-rc.1':
     dependencies:
-      '@cropper/element': 2.0.0-beta.5
-      '@cropper/element-canvas': 2.0.0-beta.5
-      '@cropper/element-crosshair': 2.0.0-beta.5
-      '@cropper/element-grid': 2.0.0-beta.5
-      '@cropper/element-handle': 2.0.0-beta.5
-      '@cropper/element-image': 2.0.0-beta.5
-      '@cropper/element-selection': 2.0.0-beta.5
-      '@cropper/element-shade': 2.0.0-beta.5
-      '@cropper/element-viewer': 2.0.0-beta.5
+      '@cropper/element': 2.0.0-rc.1
+      '@cropper/element-canvas': 2.0.0-rc.1
+      '@cropper/element-crosshair': 2.0.0-rc.1
+      '@cropper/element-grid': 2.0.0-rc.1
+      '@cropper/element-handle': 2.0.0-rc.1
+      '@cropper/element-image': 2.0.0-rc.1
+      '@cropper/element-selection': 2.0.0-rc.1
+      '@cropper/element-shade': 2.0.0-rc.1
+      '@cropper/element-viewer': 2.0.0-rc.1
 
-  '@cropper/utils@2.0.0-beta.5': {}
+  '@cropper/utils@2.0.0-rc.1': {}
 
   '@cypress/request@3.0.0':
     dependencies:
@@ -13820,7 +13647,7 @@ snapshots:
 
   '@emnapi/runtime@1.1.1':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
     optional: true
 
   '@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.3.1)':
@@ -13833,7 +13660,7 @@ snapshots:
   '@esbuild/aix-ppc64@0.21.5':
     optional: true
 
-  '@esbuild/aix-ppc64@0.22.0':
+  '@esbuild/aix-ppc64@0.23.0':
     optional: true
 
   '@esbuild/android-arm64@0.18.20':
@@ -13845,7 +13672,7 @@ snapshots:
   '@esbuild/android-arm64@0.21.5':
     optional: true
 
-  '@esbuild/android-arm64@0.22.0':
+  '@esbuild/android-arm64@0.23.0':
     optional: true
 
   '@esbuild/android-arm@0.18.20':
@@ -13857,7 +13684,7 @@ snapshots:
   '@esbuild/android-arm@0.21.5':
     optional: true
 
-  '@esbuild/android-arm@0.22.0':
+  '@esbuild/android-arm@0.23.0':
     optional: true
 
   '@esbuild/android-x64@0.18.20':
@@ -13869,7 +13696,7 @@ snapshots:
   '@esbuild/android-x64@0.21.5':
     optional: true
 
-  '@esbuild/android-x64@0.22.0':
+  '@esbuild/android-x64@0.23.0':
     optional: true
 
   '@esbuild/darwin-arm64@0.18.20':
@@ -13881,7 +13708,7 @@ snapshots:
   '@esbuild/darwin-arm64@0.21.5':
     optional: true
 
-  '@esbuild/darwin-arm64@0.22.0':
+  '@esbuild/darwin-arm64@0.23.0':
     optional: true
 
   '@esbuild/darwin-x64@0.18.20':
@@ -13893,7 +13720,7 @@ snapshots:
   '@esbuild/darwin-x64@0.21.5':
     optional: true
 
-  '@esbuild/darwin-x64@0.22.0':
+  '@esbuild/darwin-x64@0.23.0':
     optional: true
 
   '@esbuild/freebsd-arm64@0.18.20':
@@ -13905,7 +13732,7 @@ snapshots:
   '@esbuild/freebsd-arm64@0.21.5':
     optional: true
 
-  '@esbuild/freebsd-arm64@0.22.0':
+  '@esbuild/freebsd-arm64@0.23.0':
     optional: true
 
   '@esbuild/freebsd-x64@0.18.20':
@@ -13917,7 +13744,7 @@ snapshots:
   '@esbuild/freebsd-x64@0.21.5':
     optional: true
 
-  '@esbuild/freebsd-x64@0.22.0':
+  '@esbuild/freebsd-x64@0.23.0':
     optional: true
 
   '@esbuild/linux-arm64@0.18.20':
@@ -13929,7 +13756,7 @@ snapshots:
   '@esbuild/linux-arm64@0.21.5':
     optional: true
 
-  '@esbuild/linux-arm64@0.22.0':
+  '@esbuild/linux-arm64@0.23.0':
     optional: true
 
   '@esbuild/linux-arm@0.18.20':
@@ -13941,7 +13768,7 @@ snapshots:
   '@esbuild/linux-arm@0.21.5':
     optional: true
 
-  '@esbuild/linux-arm@0.22.0':
+  '@esbuild/linux-arm@0.23.0':
     optional: true
 
   '@esbuild/linux-ia32@0.18.20':
@@ -13953,7 +13780,7 @@ snapshots:
   '@esbuild/linux-ia32@0.21.5':
     optional: true
 
-  '@esbuild/linux-ia32@0.22.0':
+  '@esbuild/linux-ia32@0.23.0':
     optional: true
 
   '@esbuild/linux-loong64@0.18.20':
@@ -13965,7 +13792,7 @@ snapshots:
   '@esbuild/linux-loong64@0.21.5':
     optional: true
 
-  '@esbuild/linux-loong64@0.22.0':
+  '@esbuild/linux-loong64@0.23.0':
     optional: true
 
   '@esbuild/linux-mips64el@0.18.20':
@@ -13977,7 +13804,7 @@ snapshots:
   '@esbuild/linux-mips64el@0.21.5':
     optional: true
 
-  '@esbuild/linux-mips64el@0.22.0':
+  '@esbuild/linux-mips64el@0.23.0':
     optional: true
 
   '@esbuild/linux-ppc64@0.18.20':
@@ -13989,7 +13816,7 @@ snapshots:
   '@esbuild/linux-ppc64@0.21.5':
     optional: true
 
-  '@esbuild/linux-ppc64@0.22.0':
+  '@esbuild/linux-ppc64@0.23.0':
     optional: true
 
   '@esbuild/linux-riscv64@0.18.20':
@@ -14001,7 +13828,7 @@ snapshots:
   '@esbuild/linux-riscv64@0.21.5':
     optional: true
 
-  '@esbuild/linux-riscv64@0.22.0':
+  '@esbuild/linux-riscv64@0.23.0':
     optional: true
 
   '@esbuild/linux-s390x@0.18.20':
@@ -14013,7 +13840,7 @@ snapshots:
   '@esbuild/linux-s390x@0.21.5':
     optional: true
 
-  '@esbuild/linux-s390x@0.22.0':
+  '@esbuild/linux-s390x@0.23.0':
     optional: true
 
   '@esbuild/linux-x64@0.18.20':
@@ -14025,7 +13852,7 @@ snapshots:
   '@esbuild/linux-x64@0.21.5':
     optional: true
 
-  '@esbuild/linux-x64@0.22.0':
+  '@esbuild/linux-x64@0.23.0':
     optional: true
 
   '@esbuild/netbsd-x64@0.18.20':
@@ -14037,10 +13864,10 @@ snapshots:
   '@esbuild/netbsd-x64@0.21.5':
     optional: true
 
-  '@esbuild/netbsd-x64@0.22.0':
+  '@esbuild/netbsd-x64@0.23.0':
     optional: true
 
-  '@esbuild/openbsd-arm64@0.22.0':
+  '@esbuild/openbsd-arm64@0.23.0':
     optional: true
 
   '@esbuild/openbsd-x64@0.18.20':
@@ -14052,7 +13879,7 @@ snapshots:
   '@esbuild/openbsd-x64@0.21.5':
     optional: true
 
-  '@esbuild/openbsd-x64@0.22.0':
+  '@esbuild/openbsd-x64@0.23.0':
     optional: true
 
   '@esbuild/sunos-x64@0.18.20':
@@ -14064,7 +13891,7 @@ snapshots:
   '@esbuild/sunos-x64@0.21.5':
     optional: true
 
-  '@esbuild/sunos-x64@0.22.0':
+  '@esbuild/sunos-x64@0.23.0':
     optional: true
 
   '@esbuild/win32-arm64@0.18.20':
@@ -14076,7 +13903,7 @@ snapshots:
   '@esbuild/win32-arm64@0.21.5':
     optional: true
 
-  '@esbuild/win32-arm64@0.22.0':
+  '@esbuild/win32-arm64@0.23.0':
     optional: true
 
   '@esbuild/win32-ia32@0.18.20':
@@ -14088,7 +13915,7 @@ snapshots:
   '@esbuild/win32-ia32@0.21.5':
     optional: true
 
-  '@esbuild/win32-ia32@0.22.0':
+  '@esbuild/win32-ia32@0.23.0':
     optional: true
 
   '@esbuild/win32-x64@0.18.20':
@@ -14100,28 +13927,21 @@ snapshots:
   '@esbuild/win32-x64@0.21.5':
     optional: true
 
-  '@esbuild/win32-x64@0.22.0':
+  '@esbuild/win32-x64@0.23.0':
     optional: true
 
-  '@eslint-community/eslint-utils@4.4.0(eslint@9.6.0)':
+  '@eslint-community/eslint-utils@4.4.0(eslint@9.8.0)':
     dependencies:
-      eslint: 9.6.0
+      eslint: 9.8.0
       eslint-visitor-keys: 3.4.3
 
-  '@eslint-community/eslint-utils@4.4.0(eslint@9.7.0)':
-    dependencies:
-      eslint: 9.7.0
-      eslint-visitor-keys: 3.4.3
-
-  '@eslint-community/regexpp@4.10.0': {}
-
   '@eslint-community/regexpp@4.11.0': {}
 
   '@eslint-community/regexpp@4.6.2': {}
 
   '@eslint/compat@1.1.1': {}
 
-  '@eslint/config-array@0.17.0':
+  '@eslint/config-array@0.17.1':
     dependencies:
       '@eslint/object-schema': 2.1.4
       debug: 4.3.5(supports-color@8.1.1)
@@ -14143,9 +13963,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@eslint/js@9.6.0': {}
-
-  '@eslint/js@9.7.0': {}
+  '@eslint/js@9.8.0': {}
 
   '@eslint/object-schema@2.1.4': {}
 
@@ -14160,8 +13978,8 @@ snapshots:
 
   '@fastify/ajv-compiler@3.5.0':
     dependencies:
-      ajv: 8.16.0
-      ajv-formats: 2.1.1(ajv@8.16.0)
+      ajv: 8.17.1
+      ajv-formats: 2.1.1(ajv@8.17.1)
       fast-uri: 2.2.0
 
   '@fastify/busboy@2.1.0': {}
@@ -14191,12 +14009,12 @@ snapshots:
     dependencies:
       fast-json-stringify: 5.8.0
 
-  '@fastify/http-proxy@9.5.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
+  '@fastify/http-proxy@9.5.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)':
     dependencies:
       '@fastify/reply-from': 9.0.1
       fast-querystring: 1.1.2
       fastify-plugin: 4.5.0
-      ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      ws: 8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - bufferutil
       - utf-8-validate
@@ -14371,7 +14189,7 @@ snapshots:
       '@inquirer/figures': 1.0.1
       '@inquirer/type': 1.3.1
       '@types/mute-stream': 0.0.4
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       '@types/wrap-ansi': 3.0.0
       ansi-escapes: 4.3.2
       chalk: 4.1.2
@@ -14422,7 +14240,7 @@ snapshots:
   '@jest/console@29.7.0':
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -14435,14 +14253,14 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.7.1
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.14.9)
+      jest-config: 29.7.0(@types/node@20.14.12)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -14471,7 +14289,7 @@ snapshots:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       jest-mock: 29.7.0
 
   '@jest/expect-utils@29.7.0':
@@ -14489,7 +14307,7 @@ snapshots:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -14511,7 +14329,7 @@ snapshots:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -14558,7 +14376,7 @@ snapshots:
 
   '@jest/transform@29.7.0':
     dependencies:
-      '@babel/core': 7.23.5
+      '@babel/core': 7.24.7
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
       babel-plugin-istanbul: 6.1.1
@@ -14581,19 +14399,19 @@ snapshots:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       '@types/yargs': 17.0.19
       chalk: 4.1.2
 
-  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))':
+  '@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))':
     dependencies:
       glob: 7.2.3
       glob-promise: 4.2.2(glob@7.2.3)
       magic-string: 0.27.0
-      react-docgen-typescript: 2.2.2(typescript@5.5.3)
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
+      react-docgen-typescript: 2.2.2(typescript@5.5.4)
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
   '@jridgewell/gen-mapping@0.3.2':
     dependencies:
@@ -14613,16 +14431,10 @@ snapshots:
 
   '@jridgewell/set-array@1.2.1': {}
 
-  '@jridgewell/source-map@0.3.5':
-    dependencies:
-      '@jridgewell/gen-mapping': 0.3.5
-      '@jridgewell/trace-mapping': 0.3.25
-
   '@jridgewell/source-map@0.3.6':
     dependencies:
       '@jridgewell/gen-mapping': 0.3.5
       '@jridgewell/trace-mapping': 0.3.25
-    optional: true
 
   '@jridgewell/sourcemap-codec@1.4.14': {}
 
@@ -14650,14 +14462,14 @@ snapshots:
 
   '@mapbox/node-pre-gyp@1.0.9(encoding@0.1.13)':
     dependencies:
-      detect-libc: 2.0.2
+      detect-libc: 2.0.3
       https-proxy-agent: 5.0.1
       make-dir: 3.1.0
       node-fetch: 2.7.0(encoding@0.1.13)
       nopt: 5.0.0
       npmlog: 5.0.1
       rimraf: 3.0.2
-      semver: 7.5.4
+      semver: 7.6.0
       tar: 6.2.1
     transitivePeerDependencies:
       - encoding
@@ -14676,23 +14488,23 @@ snapshots:
       '@types/react': 18.0.28
       react: 18.3.1
 
-  '@microsoft/api-extractor-model@7.29.2(@types/node@20.14.9)':
+  '@microsoft/api-extractor-model@7.29.4(@types/node@20.14.12)':
     dependencies:
       '@microsoft/tsdoc': 0.15.0
       '@microsoft/tsdoc-config': 0.17.0
-      '@rushstack/node-core-library': 5.4.1(@types/node@20.14.9)
+      '@rushstack/node-core-library': 5.5.1(@types/node@20.14.12)
     transitivePeerDependencies:
       - '@types/node'
 
-  '@microsoft/api-extractor@7.47.0(@types/node@20.14.9)':
+  '@microsoft/api-extractor@7.47.4(@types/node@20.14.12)':
     dependencies:
-      '@microsoft/api-extractor-model': 7.29.2(@types/node@20.14.9)
+      '@microsoft/api-extractor-model': 7.29.4(@types/node@20.14.12)
       '@microsoft/tsdoc': 0.15.0
       '@microsoft/tsdoc-config': 0.17.0
-      '@rushstack/node-core-library': 5.4.1(@types/node@20.14.9)
-      '@rushstack/rig-package': 0.5.2
-      '@rushstack/terminal': 0.13.0(@types/node@20.14.9)
-      '@rushstack/ts-command-line': 4.22.0(@types/node@20.14.9)
+      '@rushstack/node-core-library': 5.5.1(@types/node@20.14.12)
+      '@rushstack/rig-package': 0.5.3
+      '@rushstack/terminal': 0.13.3(@types/node@20.14.12)
+      '@rushstack/ts-command-line': 4.22.3(@types/node@20.14.12)
       lodash: 4.17.21
       minimatch: 3.0.8
       resolve: 1.22.8
@@ -14713,14 +14525,14 @@ snapshots:
 
   '@misskey-dev/browser-image-resizer@2024.1.0': {}
 
-  '@misskey-dev/eslint-plugin@2.0.2(@eslint/compat@1.1.1)(@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3))(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0))(eslint@9.6.0)(globals@15.7.0)':
+  '@misskey-dev/eslint-plugin@2.0.2(@eslint/compat@1.1.1)(@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4))(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0))(eslint@9.8.0)(globals@15.8.0)':
     dependencies:
       '@eslint/compat': 1.1.1
-      '@typescript-eslint/eslint-plugin': 7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)
-      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
-      eslint: 9.6.0
-      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)
-      globals: 15.7.0
+      '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)
+      '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4)
+      eslint: 9.8.0
+      eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)
+      globals: 15.8.0
 
   '@misskey-dev/sharp-read-bmp@1.2.0':
     dependencies:
@@ -14768,8 +14580,6 @@ snapshots:
   '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2':
     optional: true
 
-  '@mswjs/cookies@1.1.0': {}
-
   '@mswjs/interceptors@0.29.1':
     dependencies:
       '@open-draft/deferred-promise': 2.2.0
@@ -14818,12 +14628,6 @@ snapshots:
       '@napi-rs/canvas-linux-x64-musl': 0.1.53
       '@napi-rs/canvas-win32-x64-msvc': 0.1.53
 
-  '@ndelangen/get-tarball@3.0.7':
-    dependencies:
-      gunzip-maybe: 1.4.2
-      pump: 3.0.0
-      tar-fs: 2.1.1
-
   '@nestjs/common@10.3.10(reflect-metadata@0.2.2)(rxjs@7.8.1)':
     dependencies:
       iterare: 1.2.1
@@ -14885,8 +14689,8 @@ snapshots:
   '@npmcli/agent@2.2.0':
     dependencies:
       agent-base: 7.1.0
-      http-proxy-agent: 7.0.0
-      https-proxy-agent: 7.0.2
+      http-proxy-agent: 7.0.2
+      https-proxy-agent: 7.0.4
       lru-cache: 10.2.2
       socks-proxy-agent: 8.0.2
     transitivePeerDependencies:
@@ -14949,7 +14753,7 @@ snapshots:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/semantic-conventions': 1.25.1
 
-  '@opentelemetry/instrumentation-connect@0.37.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
@@ -14959,7 +14763,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-express@0.40.1(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
@@ -14968,7 +14772,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-fastify@0.37.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
@@ -14977,14 +14781,14 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-graphql@0.41.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-hapi@0.39.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
@@ -15003,7 +14807,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-ioredis@0.41.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15012,18 +14816,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-koa@0.41.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/semantic-conventions': 1.25.1
-      '@types/koa': 2.14.0
-      '@types/koa__router': 12.0.3
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mongodb@0.45.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15032,7 +14834,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mongoose@0.39.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
@@ -15041,7 +14843,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mysql2@0.39.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15050,7 +14852,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-mysql@0.39.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15059,7 +14861,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-nestjs-core@0.38.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15067,7 +14869,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-pg@0.42.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15078,7 +14880,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation-redis-4@0.40.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15087,11 +14889,11 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.9.0)':
+  '@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@types/shimmer': 1.0.5
-      import-in-the-middle: 1.4.2
+      import-in-the-middle: 1.7.1
       require-in-the-middle: 7.3.0
       semver: 7.6.0
       shimmer: 1.2.1
@@ -15104,7 +14906,7 @@ snapshots:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/api-logs': 0.52.1
       '@types/shimmer': 1.0.5
-      import-in-the-middle: 1.8.1
+      import-in-the-middle: 1.10.0
       require-in-the-middle: 7.3.0
       semver: 7.6.0
       shimmer: 1.2.1
@@ -15152,27 +14954,27 @@ snapshots:
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       asn1js: 3.0.5
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@peculiar/asn1-ecc@2.3.8':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       '@peculiar/asn1-x509': 2.3.8
       asn1js: 3.0.5
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@peculiar/asn1-rsa@2.3.8':
     dependencies:
       '@peculiar/asn1-schema': 2.3.8
       '@peculiar/asn1-x509': 2.3.8
       asn1js: 3.0.5
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@peculiar/asn1-schema@2.3.8':
     dependencies:
       asn1js: 3.0.5
       pvtsutils: 1.3.5
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@peculiar/asn1-x509@2.3.8':
     dependencies:
@@ -15180,7 +14982,7 @@ snapshots:
       asn1js: 3.0.5
       ipaddr.js: 2.2.0
       pvtsutils: 1.3.5
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@peertube/http-signature@1.7.0':
     dependencies:
@@ -15191,7 +14993,7 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
-  '@prisma/instrumentation@5.16.0':
+  '@prisma/instrumentation@5.17.0':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
@@ -15199,141 +15001,12 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@radix-ui/primitive@1.1.0': {}
-
-  '@radix-ui/react-compose-refs@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-context@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-dialog@1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
-    dependencies:
-      '@radix-ui/primitive': 1.1.0
-      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-context': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-dismissable-layer': 1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-focus-scope': 1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-id': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-portal': 1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-presence': 1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-slot': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      aria-hidden: 1.2.4
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-      react-remove-scroll: 2.5.7(@types/react@18.0.28)(react@18.3.1)
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-dismissable-layer@1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
-    dependencies:
-      '@radix-ui/primitive': 1.1.0
-      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-focus-guards@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-focus-scope@1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-id@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-portal@1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-primitive': 2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-presence@1.1.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-primitive@2.0.0(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-slot': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-slot@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.0.28)(react@18.3.1)':
-    dependencies:
-      react: 18.3.1
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  '@readme/better-ajv-errors@1.6.0(ajv@8.16.0)':
+  '@readme/better-ajv-errors@1.6.0(ajv@8.17.1)':
     dependencies:
       '@babel/code-frame': 7.23.5
       '@babel/runtime': 7.23.4
       '@humanwhocodes/momoa': 2.0.4
-      ajv: 8.16.0
+      ajv: 8.17.1
       chalk: 4.1.2
       json-to-ast: 2.1.0
       jsonpointer: 5.0.1
@@ -15351,83 +15024,83 @@ snapshots:
       '@apidevtools/openapi-schemas': 2.1.0
       '@apidevtools/swagger-methods': 3.0.2
       '@jsdevtools/ono': 7.1.3
-      '@readme/better-ajv-errors': 1.6.0(ajv@8.16.0)
+      '@readme/better-ajv-errors': 1.6.0(ajv@8.17.1)
       '@readme/json-schema-ref-parser': 1.2.0
-      ajv: 8.16.0
-      ajv-draft-04: 1.0.0(ajv@8.16.0)
+      ajv: 8.17.1
+      ajv-draft-04: 1.0.0(ajv@8.17.1)
       call-me-maybe: 1.0.2
       openapi-types: 12.1.3
 
-  '@rollup/plugin-json@6.1.0(rollup@4.18.0)':
+  '@rollup/plugin-json@6.1.0(rollup@4.19.1)':
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
+      '@rollup/pluginutils': 5.1.0(rollup@4.19.1)
     optionalDependencies:
-      rollup: 4.18.0
+      rollup: 4.19.1
 
-  '@rollup/plugin-replace@5.0.7(rollup@4.18.0)':
+  '@rollup/plugin-replace@5.0.7(rollup@4.19.1)':
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
+      '@rollup/pluginutils': 5.1.0(rollup@4.19.1)
       magic-string: 0.30.10
     optionalDependencies:
-      rollup: 4.18.0
+      rollup: 4.19.1
 
-  '@rollup/pluginutils@5.1.0(rollup@4.18.0)':
+  '@rollup/pluginutils@5.1.0(rollup@4.19.1)':
     dependencies:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
     optionalDependencies:
-      rollup: 4.18.0
+      rollup: 4.19.1
 
-  '@rollup/rollup-android-arm-eabi@4.18.0':
+  '@rollup/rollup-android-arm-eabi@4.19.1':
     optional: true
 
-  '@rollup/rollup-android-arm64@4.18.0':
+  '@rollup/rollup-android-arm64@4.19.1':
     optional: true
 
-  '@rollup/rollup-darwin-arm64@4.18.0':
+  '@rollup/rollup-darwin-arm64@4.19.1':
     optional: true
 
-  '@rollup/rollup-darwin-x64@4.18.0':
+  '@rollup/rollup-darwin-x64@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.18.0':
+  '@rollup/rollup-linux-arm-gnueabihf@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-arm-musleabihf@4.18.0':
+  '@rollup/rollup-linux-arm-musleabihf@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-arm64-gnu@4.18.0':
+  '@rollup/rollup-linux-arm64-gnu@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-arm64-musl@4.18.0':
+  '@rollup/rollup-linux-arm64-musl@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.18.0':
+  '@rollup/rollup-linux-powerpc64le-gnu@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-riscv64-gnu@4.18.0':
+  '@rollup/rollup-linux-riscv64-gnu@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-s390x-gnu@4.18.0':
+  '@rollup/rollup-linux-s390x-gnu@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-x64-gnu@4.18.0':
+  '@rollup/rollup-linux-x64-gnu@4.19.1':
     optional: true
 
-  '@rollup/rollup-linux-x64-musl@4.18.0':
+  '@rollup/rollup-linux-x64-musl@4.19.1':
     optional: true
 
-  '@rollup/rollup-win32-arm64-msvc@4.18.0':
+  '@rollup/rollup-win32-arm64-msvc@4.19.1':
     optional: true
 
-  '@rollup/rollup-win32-ia32-msvc@4.18.0':
+  '@rollup/rollup-win32-ia32-msvc@4.19.1':
     optional: true
 
-  '@rollup/rollup-win32-x64-msvc@4.18.0':
+  '@rollup/rollup-win32-x64-msvc@4.19.1':
     optional: true
 
-  '@rushstack/node-core-library@5.4.1(@types/node@20.14.9)':
+  '@rushstack/node-core-library@5.5.1(@types/node@20.14.12)':
     dependencies:
       ajv: 8.13.0
       ajv-draft-04: 1.0.0(ajv@8.13.0)
@@ -15438,23 +15111,23 @@ snapshots:
       resolve: 1.22.8
       semver: 7.5.4
     optionalDependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
-  '@rushstack/rig-package@0.5.2':
+  '@rushstack/rig-package@0.5.3':
     dependencies:
       resolve: 1.22.8
       strip-json-comments: 3.1.1
 
-  '@rushstack/terminal@0.13.0(@types/node@20.14.9)':
+  '@rushstack/terminal@0.13.3(@types/node@20.14.12)':
     dependencies:
-      '@rushstack/node-core-library': 5.4.1(@types/node@20.14.9)
+      '@rushstack/node-core-library': 5.5.1(@types/node@20.14.12)
       supports-color: 8.1.1
     optionalDependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
-  '@rushstack/ts-command-line@4.22.0(@types/node@20.14.9)':
+  '@rushstack/ts-command-line@4.22.3(@types/node@20.14.12)':
     dependencies:
-      '@rushstack/terminal': 0.13.0(@types/node@20.14.9)
+      '@rushstack/terminal': 0.13.3(@types/node@20.14.12)
       '@types/argparse': 1.0.38
       argparse: 1.0.10
       string-argv: 0.3.1
@@ -15463,74 +15136,77 @@ snapshots:
 
   '@sec-ant/readable-stream@0.4.1': {}
 
-  '@sentry/core@8.13.0':
+  '@sentry/core@8.20.0':
     dependencies:
-      '@sentry/types': 8.13.0
-      '@sentry/utils': 8.13.0
+      '@sentry/types': 8.20.0
+      '@sentry/utils': 8.20.0
 
-  '@sentry/node@8.13.0':
+  '@sentry/node@8.20.0':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-connect': 0.37.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-express': 0.40.1(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-fastify': 0.37.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-graphql': 0.41.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-hapi': 0.39.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-graphql': 0.42.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-hapi': 0.40.0(@opentelemetry/api@1.9.0)
       '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-ioredis': 0.41.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-koa': 0.41.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-mongodb': 0.45.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-mongoose': 0.39.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-mysql': 0.39.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-mysql2': 0.39.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-nestjs-core': 0.38.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-pg': 0.42.0(@opentelemetry/api@1.9.0)
-      '@opentelemetry/instrumentation-redis-4': 0.40.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-ioredis': 0.42.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-koa': 0.42.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mongodb': 0.46.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mongoose': 0.40.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mysql': 0.40.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-mysql2': 0.40.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-nestjs-core': 0.39.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0)
       '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/semantic-conventions': 1.25.1
-      '@prisma/instrumentation': 5.16.0
-      '@sentry/core': 8.13.0
-      '@sentry/opentelemetry': 8.13.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)
-      '@sentry/types': 8.13.0
-      '@sentry/utils': 8.13.0
+      '@prisma/instrumentation': 5.17.0
+      '@sentry/core': 8.20.0
+      '@sentry/opentelemetry': 8.20.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)
+      '@sentry/types': 8.20.0
+      '@sentry/utils': 8.20.0
+      import-in-the-middle: 1.10.0
     optionalDependencies:
-      opentelemetry-instrumentation-fetch-node: 1.2.0
+      opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@sentry/opentelemetry@8.13.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)':
+  '@sentry/opentelemetry@8.20.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)':
     dependencies:
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0)
       '@opentelemetry/semantic-conventions': 1.25.1
-      '@sentry/core': 8.13.0
-      '@sentry/types': 8.13.0
-      '@sentry/utils': 8.13.0
+      '@sentry/core': 8.20.0
+      '@sentry/types': 8.20.0
+      '@sentry/utils': 8.20.0
 
-  '@sentry/profiling-node@8.13.0':
+  '@sentry/profiling-node@8.20.0':
     dependencies:
-      '@sentry/core': 8.13.0
-      '@sentry/node': 8.13.0
-      '@sentry/types': 8.13.0
-      '@sentry/utils': 8.13.0
+      '@sentry/core': 8.20.0
+      '@sentry/node': 8.20.0
+      '@sentry/types': 8.20.0
+      '@sentry/utils': 8.20.0
       detect-libc: 2.0.3
       node-abi: 3.62.0
     transitivePeerDependencies:
       - supports-color
 
-  '@sentry/types@8.13.0': {}
+  '@sentry/types@8.20.0': {}
 
-  '@sentry/utils@8.13.0':
+  '@sentry/utils@8.20.0':
     dependencies:
-      '@sentry/types': 8.13.0
+      '@sentry/types': 8.20.0
 
-  '@shikijs/core@1.10.0': {}
+  '@shikijs/core@1.12.0':
+    dependencies:
+      '@types/hast': 3.0.4
 
   '@sideway/address@4.1.4':
     dependencies:
@@ -15540,7 +15216,7 @@ snapshots:
 
   '@sideway/pinpoint@2.0.0': {}
 
-  '@simplewebauthn/server@10.0.0(encoding@0.1.13)':
+  '@simplewebauthn/server@10.0.1(encoding@0.1.13)':
     dependencies:
       '@hexagon/base64': 1.1.27
       '@levischuck/tiny-cbor': 0.2.2
@@ -15562,7 +15238,7 @@ snapshots:
 
   '@sindresorhus/is@5.3.0': {}
 
-  '@sindresorhus/is@6.3.1': {}
+  '@sindresorhus/is@7.0.0': {}
 
   '@sindresorhus/merge-streams@2.3.0': {}
 
@@ -15600,165 +15276,165 @@ snapshots:
   '@smithy/abort-controller@3.1.1':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/chunked-blob-reader-native@3.0.0':
     dependencies:
       '@smithy/util-base64': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/chunked-blob-reader@3.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/config-resolver@3.0.4':
+  '@smithy/config-resolver@3.0.5':
     dependencies:
-      '@smithy/node-config-provider': 3.1.3
+      '@smithy/node-config-provider': 3.1.4
       '@smithy/types': 3.3.0
       '@smithy/util-config-provider': 3.0.0
       '@smithy/util-middleware': 3.0.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/core@2.2.4':
+  '@smithy/core@2.3.1':
     dependencies:
-      '@smithy/middleware-endpoint': 3.0.4
-      '@smithy/middleware-retry': 3.0.7
+      '@smithy/middleware-endpoint': 3.1.0
+      '@smithy/middleware-retry': 3.0.13
       '@smithy/middleware-serde': 3.0.3
-      '@smithy/protocol-http': 4.0.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/protocol-http': 4.1.0
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       '@smithy/util-middleware': 3.0.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/credential-provider-imds@3.1.3':
+  '@smithy/credential-provider-imds@3.2.0':
     dependencies:
-      '@smithy/node-config-provider': 3.1.3
+      '@smithy/node-config-provider': 3.1.4
       '@smithy/property-provider': 3.1.3
       '@smithy/types': 3.3.0
       '@smithy/url-parser': 3.0.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/eventstream-codec@3.1.2':
     dependencies:
       '@aws-crypto/crc32': 5.2.0
       '@smithy/types': 3.3.0
       '@smithy/util-hex-encoding': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/eventstream-serde-browser@3.0.4':
+  '@smithy/eventstream-serde-browser@3.0.5':
     dependencies:
       '@smithy/eventstream-serde-universal': 3.0.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/eventstream-serde-config-resolver@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/eventstream-serde-node@3.0.4':
     dependencies:
       '@smithy/eventstream-serde-universal': 3.0.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/eventstream-serde-universal@3.0.4':
     dependencies:
       '@smithy/eventstream-codec': 3.1.2
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/fetch-http-handler@3.2.0':
+  '@smithy/fetch-http-handler@3.2.4':
     dependencies:
-      '@smithy/protocol-http': 4.0.3
+      '@smithy/protocol-http': 4.1.0
       '@smithy/querystring-builder': 3.0.3
       '@smithy/types': 3.3.0
       '@smithy/util-base64': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/hash-blob-browser@3.1.2':
     dependencies:
       '@smithy/chunked-blob-reader': 3.0.0
       '@smithy/chunked-blob-reader-native': 3.0.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/hash-node@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
       '@smithy/util-buffer-from': 3.0.0
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/hash-stream-node@3.1.2':
     dependencies:
       '@smithy/types': 3.3.0
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/invalid-dependency@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/is-array-buffer@2.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/is-array-buffer@3.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/md5-js@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/middleware-content-length@3.0.3':
+  '@smithy/middleware-content-length@3.0.5':
     dependencies:
-      '@smithy/protocol-http': 4.0.3
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/middleware-endpoint@3.0.4':
+  '@smithy/middleware-endpoint@3.1.0':
     dependencies:
       '@smithy/middleware-serde': 3.0.3
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/shared-ini-file-loader': 3.1.4
       '@smithy/types': 3.3.0
       '@smithy/url-parser': 3.0.3
       '@smithy/util-middleware': 3.0.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/middleware-retry@3.0.7':
+  '@smithy/middleware-retry@3.0.13':
     dependencies:
-      '@smithy/node-config-provider': 3.1.3
-      '@smithy/protocol-http': 4.0.3
+      '@smithy/node-config-provider': 3.1.4
+      '@smithy/protocol-http': 4.1.0
       '@smithy/service-error-classification': 3.0.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       '@smithy/util-middleware': 3.0.3
       '@smithy/util-retry': 3.0.3
-      tslib: 2.6.2
+      tslib: 2.6.3
       uuid: 9.0.1
 
   '@smithy/middleware-serde@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/middleware-stack@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/node-config-provider@3.1.3':
+  '@smithy/node-config-provider@3.1.4':
     dependencies:
       '@smithy/property-provider': 3.1.3
-      '@smithy/shared-ini-file-loader': 3.1.3
+      '@smithy/shared-ini-file-loader': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/node-http-handler@2.5.0':
     dependencies:
@@ -15768,28 +15444,28 @@ snapshots:
       '@smithy/types': 2.12.0
       tslib: 2.6.2
 
-  '@smithy/node-http-handler@3.1.1':
+  '@smithy/node-http-handler@3.1.4':
     dependencies:
       '@smithy/abort-controller': 3.1.1
-      '@smithy/protocol-http': 4.0.3
+      '@smithy/protocol-http': 4.1.0
       '@smithy/querystring-builder': 3.0.3
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/property-provider@3.1.3':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/protocol-http@3.3.0':
     dependencies:
       '@smithy/types': 2.12.0
       tslib: 2.6.2
 
-  '@smithy/protocol-http@4.0.3':
+  '@smithy/protocol-http@4.1.0':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/querystring-builder@2.2.0':
     dependencies:
@@ -15801,40 +15477,41 @@ snapshots:
     dependencies:
       '@smithy/types': 3.3.0
       '@smithy/util-uri-escape': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/querystring-parser@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/service-error-classification@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
 
-  '@smithy/shared-ini-file-loader@3.1.3':
+  '@smithy/shared-ini-file-loader@3.1.4':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/signature-v4@3.1.2':
+  '@smithy/signature-v4@4.1.0':
     dependencies:
       '@smithy/is-array-buffer': 3.0.0
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
       '@smithy/util-hex-encoding': 3.0.0
       '@smithy/util-middleware': 3.0.3
       '@smithy/util-uri-escape': 3.0.0
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/smithy-client@3.1.5':
+  '@smithy/smithy-client@3.1.11':
     dependencies:
-      '@smithy/middleware-endpoint': 3.0.4
+      '@smithy/middleware-endpoint': 3.1.0
       '@smithy/middleware-stack': 3.0.3
-      '@smithy/protocol-http': 4.0.3
+      '@smithy/protocol-http': 4.1.0
       '@smithy/types': 3.3.0
-      '@smithy/util-stream': 3.0.5
-      tslib: 2.6.2
+      '@smithy/util-stream': 3.1.3
+      tslib: 2.6.3
 
   '@smithy/types@2.12.0':
     dependencies:
@@ -15842,91 +15519,91 @@ snapshots:
 
   '@smithy/types@3.3.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/url-parser@3.0.3':
     dependencies:
       '@smithy/querystring-parser': 3.0.3
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-base64@3.0.0':
     dependencies:
       '@smithy/util-buffer-from': 3.0.0
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-body-length-browser@3.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-body-length-node@3.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-buffer-from@2.0.0':
     dependencies:
       '@smithy/is-array-buffer': 2.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-buffer-from@3.0.0':
     dependencies:
       '@smithy/is-array-buffer': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-config-provider@3.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/util-defaults-mode-browser@3.0.7':
+  '@smithy/util-defaults-mode-browser@3.0.13':
     dependencies:
       '@smithy/property-provider': 3.1.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
       bowser: 2.11.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/util-defaults-mode-node@3.0.7':
+  '@smithy/util-defaults-mode-node@3.0.13':
     dependencies:
-      '@smithy/config-resolver': 3.0.4
-      '@smithy/credential-provider-imds': 3.1.3
-      '@smithy/node-config-provider': 3.1.3
+      '@smithy/config-resolver': 3.0.5
+      '@smithy/credential-provider-imds': 3.2.0
+      '@smithy/node-config-provider': 3.1.4
       '@smithy/property-provider': 3.1.3
-      '@smithy/smithy-client': 3.1.5
+      '@smithy/smithy-client': 3.1.11
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/util-endpoints@2.0.4':
+  '@smithy/util-endpoints@2.0.5':
     dependencies:
-      '@smithy/node-config-provider': 3.1.3
+      '@smithy/node-config-provider': 3.1.4
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-hex-encoding@3.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-middleware@3.0.3':
     dependencies:
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-retry@3.0.3':
     dependencies:
       '@smithy/service-error-classification': 3.0.3
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
-  '@smithy/util-stream@3.0.5':
+  '@smithy/util-stream@3.1.3':
     dependencies:
-      '@smithy/fetch-http-handler': 3.2.0
-      '@smithy/node-http-handler': 3.1.1
+      '@smithy/fetch-http-handler': 3.2.4
+      '@smithy/node-http-handler': 3.1.4
       '@smithy/types': 3.3.0
       '@smithy/util-base64': 3.0.0
       '@smithy/util-buffer-from': 3.0.0
       '@smithy/util-hex-encoding': 3.0.0
       '@smithy/util-utf8': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-uri-escape@2.2.0':
     dependencies:
@@ -15934,120 +15611,96 @@ snapshots:
 
   '@smithy/util-uri-escape@3.0.0':
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-utf8@2.0.0':
     dependencies:
       '@smithy/util-buffer-from': 2.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-utf8@3.0.0':
     dependencies:
       '@smithy/util-buffer-from': 3.0.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@smithy/util-waiter@3.1.2':
     dependencies:
       '@smithy/abort-controller': 3.1.1
       '@smithy/types': 3.3.0
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@sqltools/formatter@1.2.5': {}
 
-  '@storybook/addon-actions@8.1.11':
+  '@storybook/addon-actions@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/core-events': 8.1.11
       '@storybook/global': 5.0.0
       '@types/uuid': 9.0.8
       dequal: 2.0.3
       polished: 4.2.2
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       uuid: 9.0.1
 
-  '@storybook/addon-backgrounds@8.1.11':
+  '@storybook/addon-backgrounds@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
 
-  '@storybook/addon-controls@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/addon-controls@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/blocks': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       dequal: 2.0.3
       lodash: 4.17.21
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
-      - encoding
-      - prettier
-      - react
-      - react-dom
-      - supports-color
 
-  '@storybook/addon-docs@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
+  '@storybook/addon-docs@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
       '@babel/core': 7.24.7
       '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@18.3.1)
-      '@storybook/blocks': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/client-logger': 8.1.11
-      '@storybook/components': 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/csf-plugin': 8.1.11
-      '@storybook/csf-tools': 8.1.11
+      '@storybook/blocks': 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/csf-plugin': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/global': 5.0.0
-      '@storybook/node-logger': 8.1.11
-      '@storybook/preview-api': 8.1.11
-      '@storybook/react-dom-shim': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.1.11
+      '@storybook/react-dom-shim': 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@types/react': 18.0.28
       fs-extra: 11.1.1
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
       rehype-external-links: 3.0.0
       rehype-slug: 6.0.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
     transitivePeerDependencies:
-      - '@types/react-dom'
-      - encoding
-      - prettier
       - supports-color
 
-  '@storybook/addon-essentials@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/addon-essentials@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/addon-actions': 8.1.11
-      '@storybook/addon-backgrounds': 8.1.11
-      '@storybook/addon-controls': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/addon-docs': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
-      '@storybook/addon-highlight': 8.1.11
-      '@storybook/addon-measure': 8.1.11
-      '@storybook/addon-outline': 8.1.11
-      '@storybook/addon-toolbars': 8.1.11
-      '@storybook/addon-viewport': 8.1.11
-      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
-      '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/node-logger': 8.1.11
-      '@storybook/preview-api': 8.1.11
+      '@storybook/addon-actions': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-backgrounds': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-controls': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-docs': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-highlight': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-measure': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-outline': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-toolbars': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/addon-viewport': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
     transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
-      - encoding
-      - prettier
-      - react
-      - react-dom
       - supports-color
 
-  '@storybook/addon-highlight@8.1.11':
+  '@storybook/addon-highlight@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
       '@storybook/global': 5.0.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
 
-  '@storybook/addon-interactions@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
+  '@storybook/addon-interactions@8.2.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.12))(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))':
     dependencies:
       '@storybook/global': 5.0.0
-      '@storybook/instrumenter': 8.1.11
-      '@storybook/test': 8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
-      '@storybook/types': 8.1.11
+      '@storybook/instrumenter': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/test': 8.2.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.12))(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))
       polished: 4.2.2
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@jest/globals'
@@ -16056,84 +15709,76 @@ snapshots:
       - jest
       - vitest
 
-  '@storybook/addon-links@8.1.11(react@18.3.1)':
+  '@storybook/addon-links@8.2.6(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/csf': 0.1.9
+      '@storybook/csf': 0.1.11
       '@storybook/global': 5.0.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
     optionalDependencies:
       react: 18.3.1
 
-  '@storybook/addon-mdx-gfm@8.1.11':
+  '@storybook/addon-mdx-gfm@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/node-logger': 8.1.11
       remark-gfm: 4.0.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
 
-  '@storybook/addon-measure@8.1.11':
+  '@storybook/addon-measure@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
       '@storybook/global': 5.0.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       tiny-invariant: 1.3.3
 
-  '@storybook/addon-outline@8.1.11':
+  '@storybook/addon-outline@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
       '@storybook/global': 5.0.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
 
-  '@storybook/addon-storysource@8.1.11':
+  '@storybook/addon-storysource@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/source-loader': 8.1.11
+      '@storybook/source-loader': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       estraverse: 5.3.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       tiny-invariant: 1.3.3
 
-  '@storybook/addon-toolbars@8.1.11': {}
+  '@storybook/addon-toolbars@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
+    dependencies:
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
 
-  '@storybook/addon-viewport@8.1.11':
+  '@storybook/addon-viewport@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
       memoizerific: 1.11.3
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
 
-  '@storybook/blocks@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/blocks@8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/channels': 8.1.11
-      '@storybook/client-logger': 8.1.11
-      '@storybook/components': 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/core-events': 8.1.11
-      '@storybook/csf': 0.1.9
-      '@storybook/docs-tools': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/csf': 0.1.11
       '@storybook/global': 5.0.0
       '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/preview-api': 8.1.11
-      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.1.11
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
       lodash: 4.17.21
-      markdown-to-jsx: 7.3.2(react@18.3.1)
+      markdown-to-jsx: 7.4.7(react@18.3.1)
       memoizerific: 1.11.3
       polished: 4.2.2
       react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       telejson: 7.2.0
-      tocbot: 4.21.1
       ts-dedent: 2.2.0
       util-deprecate: 1.0.2
     optionalDependencies:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
-      - encoding
-      - prettier
-      - supports-color
 
-  '@storybook/builder-manager@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
+  '@storybook/builder-manager@8.1.11(encoding@0.1.13)(prettier@3.3.3)':
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/manager': 8.1.11
       '@storybook/node-logger': 8.1.11
       '@types/ejs': 3.1.2
@@ -16151,11 +15796,11 @@ snapshots:
       - prettier
       - supports-color
 
-  '@storybook/builder-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))':
+  '@storybook/builder-vite@8.1.11(encoding@0.1.13)(prettier@3.3.3)(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))':
     dependencies:
       '@storybook/channels': 8.1.11
       '@storybook/client-logger': 8.1.11
-      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/core-events': 8.1.11
       '@storybook/csf-plugin': 8.1.11
       '@storybook/node-logger': 8.1.11
@@ -16170,14 +15815,32 @@ snapshots:
       fs-extra: 11.1.1
       magic-string: 0.30.10
       ts-dedent: 2.2.0
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
     transitivePeerDependencies:
       - encoding
       - prettier
       - supports-color
 
+  '@storybook/builder-vite@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))':
+    dependencies:
+      '@storybook/csf-plugin': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@types/find-cache-dir': 3.2.1
+      browser-assert: 1.2.1
+      es-module-lexer: 1.5.4
+      express: 4.19.2
+      find-cache-dir: 3.3.2
+      fs-extra: 11.1.1
+      magic-string: 0.30.10
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      ts-dedent: 2.2.0
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
+    optionalDependencies:
+      typescript: 5.5.4
+    transitivePeerDependencies:
+      - supports-color
+
   '@storybook/channels@8.1.11':
     dependencies:
       '@storybook/client-logger': 8.1.11
@@ -16186,96 +15849,35 @@ snapshots:
       telejson: 7.2.0
       tiny-invariant: 1.3.3
 
-  '@storybook/cli@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)':
-    dependencies:
-      '@babel/core': 7.24.7
-      '@babel/types': 7.24.7
-      '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 8.1.11
-      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
-      '@storybook/core-events': 8.1.11
-      '@storybook/core-server': 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
-      '@storybook/csf-tools': 8.1.11
-      '@storybook/node-logger': 8.1.11
-      '@storybook/telemetry': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
-      '@storybook/types': 8.1.11
-      '@types/semver': 7.5.8
-      '@yarnpkg/fslib': 2.10.3
-      '@yarnpkg/libzip': 2.3.0
-      chalk: 4.1.2
-      commander: 6.2.1
-      cross-spawn: 7.0.3
-      detect-indent: 6.1.0
-      envinfo: 7.8.1
-      execa: 5.1.1
-      find-up: 5.0.0
-      fs-extra: 11.1.1
-      get-npm-tarball-url: 2.0.3
-      giget: 1.1.2
-      globby: 14.0.1
-      jscodeshift: 0.15.1(@babel/preset-env@7.24.7(@babel/core@7.24.7))
-      leven: 3.1.0
-      ora: 5.4.1
-      prettier: 3.3.2
-      prompts: 2.4.2
-      read-pkg-up: 7.0.1
-      semver: 7.6.0
-      strip-json-comments: 3.1.1
-      tempy: 3.1.0
-      tiny-invariant: 1.3.3
-      ts-dedent: 2.2.0
-    transitivePeerDependencies:
-      - '@babel/preset-env'
-      - bufferutil
-      - encoding
-      - react
-      - react-dom
-      - supports-color
-      - utf-8-validate
-
   '@storybook/client-logger@8.1.11':
     dependencies:
       '@storybook/global': 5.0.0
 
-  '@storybook/codemod@8.1.11':
+  '@storybook/codemod@8.2.6(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
     dependencies:
       '@babel/core': 7.24.7
       '@babel/preset-env': 7.24.7(@babel/core@7.24.7)
       '@babel/types': 7.24.7
-      '@storybook/csf': 0.1.9
-      '@storybook/csf-tools': 8.1.11
-      '@storybook/node-logger': 8.1.11
-      '@storybook/types': 8.1.11
+      '@storybook/core': 8.2.6(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      '@storybook/csf': 0.1.11
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 14.0.1
       jscodeshift: 0.15.1(@babel/preset-env@7.24.7(@babel/core@7.24.7))
       lodash: 4.17.21
-      prettier: 3.3.2
+      prettier: 3.3.3
       recast: 0.23.6
       tiny-invariant: 1.3.3
     transitivePeerDependencies:
+      - bufferutil
       - supports-color
+      - utf-8-validate
 
-  '@storybook/components@8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/components@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@radix-ui/react-dialog': 1.1.1(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@radix-ui/react-slot': 1.1.0(@types/react@18.0.28)(react@18.3.1)
-      '@storybook/client-logger': 8.1.11
-      '@storybook/csf': 0.1.9
-      '@storybook/global': 5.0.0
-      '@storybook/icons': 1.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.1.11
-      memoizerific: 1.11.3
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-      util-deprecate: 1.0.2
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
 
-  '@storybook/core-common@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
+  '@storybook/core-common@8.1.11(encoding@0.1.13)(prettier@3.3.3)':
     dependencies:
       '@storybook/core-events': 8.1.11
       '@storybook/csf-tools': 8.1.11
@@ -16292,13 +15894,13 @@ snapshots:
       find-cache-dir: 3.3.2
       find-up: 5.0.0
       fs-extra: 11.1.1
-      glob: 10.4.2
+      glob: 10.3.10
       handlebars: 4.7.7
       lazy-universal-dotenv: 4.0.0
       node-fetch: 2.7.0(encoding@0.1.13)
       picomatch: 2.3.1
       pkg-dir: 5.0.0
-      prettier-fallback: prettier@3.3.2
+      prettier-fallback: prettier@3.3.3
       pretty-hrtime: 1.0.3
       resolve-from: 5.0.0
       semver: 7.6.0
@@ -16307,7 +15909,7 @@ snapshots:
       ts-dedent: 2.2.0
       util: 0.12.5
     optionalDependencies:
-      prettier: 3.3.2
+      prettier: 3.3.3
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -16317,15 +15919,19 @@ snapshots:
       '@storybook/csf': 0.1.9
       ts-dedent: 2.2.0
 
-  '@storybook/core-server@8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)':
+  '@storybook/core-events@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
+    dependencies:
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+
+  '@storybook/core-server@8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)':
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@babel/core': 7.24.7
       '@babel/parser': 7.24.7
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/builder-manager': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/channels': 8.1.11
-      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/core-events': 8.1.11
       '@storybook/csf': 0.1.9
       '@storybook/csf-tools': 8.1.11
@@ -16335,7 +15941,7 @@ snapshots:
       '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       '@storybook/node-logger': 8.1.11
       '@storybook/preview-api': 8.1.11
-      '@storybook/telemetry': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/telemetry': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/types': 8.1.11
       '@types/detect-port': 1.3.2
       '@types/diff': 5.2.1
@@ -16363,7 +15969,7 @@ snapshots:
       util: 0.12.5
       util-deprecate: 1.0.2
       watchpack: 2.4.0
-      ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -16373,6 +15979,24 @@ snapshots:
       - supports-color
       - utf-8-validate
 
+  '@storybook/core@8.2.6(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
+    dependencies:
+      '@storybook/csf': 0.1.11
+      '@types/express': 4.17.21
+      '@types/node': 18.17.15
+      browser-assert: 1.2.1
+      esbuild: 0.19.11
+      esbuild-register: 3.5.0(esbuild@0.19.11)
+      express: 4.19.2
+      process: 0.11.10
+      recast: 0.23.6
+      util: 0.12.5
+      ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
   '@storybook/csf-plugin@8.1.11':
     dependencies:
       '@storybook/csf-tools': 8.1.11
@@ -16380,6 +16004,11 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@storybook/csf-plugin@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
+    dependencies:
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      unplugin: 1.4.0
+
   '@storybook/csf-tools@8.1.11':
     dependencies:
       '@babel/generator': 7.24.7
@@ -16394,15 +16023,19 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@storybook/csf@0.1.11':
+    dependencies:
+      type-fest: 2.19.0
+
   '@storybook/csf@0.1.9':
     dependencies:
       type-fest: 2.19.0
 
   '@storybook/docs-mdx@3.1.0-next.0': {}
 
-  '@storybook/docs-tools@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
+  '@storybook/docs-tools@8.1.11(encoding@0.1.13)(prettier@3.3.3)':
     dependencies:
-      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/core-events': 8.1.11
       '@storybook/preview-api': 8.1.11
       '@storybook/types': 8.1.11
@@ -16422,14 +16055,11 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  '@storybook/instrumenter@8.1.11':
+  '@storybook/instrumenter@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/channels': 8.1.11
-      '@storybook/client-logger': 8.1.11
-      '@storybook/core-events': 8.1.11
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.1.11
       '@vitest/utils': 1.6.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       util: 0.12.5
 
   '@storybook/manager-api@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
@@ -16453,6 +16083,10 @@ snapshots:
       - react
       - react-dom
 
+  '@storybook/manager-api@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
+    dependencies:
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+
   '@storybook/manager@8.1.11': {}
 
   '@storybook/node-logger@8.1.11': {}
@@ -16474,46 +16108,48 @@ snapshots:
       ts-dedent: 2.2.0
       util-deprecate: 1.0.2
 
+  '@storybook/preview-api@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
+    dependencies:
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+
   '@storybook/preview@8.1.11': {}
 
-  '@storybook/react-dom-shim@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+  '@storybook/react-dom-shim@8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
 
-  '@storybook/react-vite@8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))':
+  '@storybook/react-vite@8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.19.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))':
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
-      '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
-      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
-      '@storybook/node-logger': 8.1.11
-      '@storybook/react': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)
-      '@storybook/types': 8.1.11
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))
+      '@rollup/pluginutils': 5.1.0(rollup@4.19.1)
+      '@storybook/builder-vite': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))
+      '@storybook/react': 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.4)
       find-up: 5.0.0
       magic-string: 0.30.10
       react: 18.3.1
       react-docgen: 7.0.1
       react-dom: 18.3.1(react@18.3.1)
       resolve: 1.22.8
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       tsconfig-paths: 4.2.0
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
     transitivePeerDependencies:
       - '@preact/preset-vite'
-      - encoding
-      - prettier
       - rollup
       - supports-color
       - typescript
       - vite-plugin-glimmerx
 
-  '@storybook/react@8.1.11(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.3)':
+  '@storybook/react@8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.4)':
     dependencies:
-      '@storybook/client-logger': 8.1.11
-      '@storybook/docs-tools': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/components': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 8.1.11
-      '@storybook/react-dom-shim': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.1.11
+      '@storybook/manager-api': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/preview-api': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/react-dom-shim': 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/theming': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 18.17.15
@@ -16528,15 +16164,12 @@ snapshots:
       react-dom: 18.3.1(react@18.3.1)
       react-element-to-jsx-string: 15.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       semver: 7.6.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       util-deprecate: 1.0.2
     optionalDependencies:
-      typescript: 5.5.3
-    transitivePeerDependencies:
-      - encoding
-      - prettier
-      - supports-color
+      typescript: 5.5.4
 
   '@storybook/router@8.1.11':
     dependencies:
@@ -16544,18 +16177,18 @@ snapshots:
       memoizerific: 1.11.3
       qs: 6.11.1
 
-  '@storybook/source-loader@8.1.11':
+  '@storybook/source-loader@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/csf': 0.1.9
-      '@storybook/types': 8.1.11
+      '@storybook/csf': 0.1.11
       estraverse: 5.3.0
       lodash: 4.17.21
-      prettier: 3.3.2
+      prettier: 3.3.3
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
 
-  '@storybook/telemetry@8.1.11(encoding@0.1.13)(prettier@3.3.2)':
+  '@storybook/telemetry@8.1.11(encoding@0.1.13)(prettier@3.3.3)':
     dependencies:
       '@storybook/client-logger': 8.1.11
-      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/core-common': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/csf-tools': 8.1.11
       chalk: 4.1.2
       detect-package-manager: 2.0.1
@@ -16567,17 +16200,16 @@ snapshots:
       - prettier
       - supports-color
 
-  '@storybook/test@8.1.11(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
+  '@storybook/test@8.2.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.12))(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))':
     dependencies:
-      '@storybook/client-logger': 8.1.11
-      '@storybook/core-events': 8.1.11
-      '@storybook/instrumenter': 8.1.11
-      '@storybook/preview-api': 8.1.11
+      '@storybook/csf': 0.1.11
+      '@storybook/instrumenter': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
       '@testing-library/dom': 10.1.0
-      '@testing-library/jest-dom': 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))
+      '@testing-library/jest-dom': 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.12))(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))
       '@testing-library/user-event': 14.5.2(@testing-library/dom@10.1.0)
       '@vitest/expect': 1.6.0
       '@vitest/spy': 1.6.0
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
       util: 0.12.5
     transitivePeerDependencies:
       - '@jest/globals'
@@ -16596,24 +16228,32 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
+  '@storybook/theming@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
+    dependencies:
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+
   '@storybook/types@8.1.11':
     dependencies:
       '@storybook/channels': 8.1.11
       '@types/express': 4.17.17
       file-system-cache: 2.3.0
 
-  '@storybook/vue3-vite@8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))':
+  '@storybook/types@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
     dependencies:
-      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))
-      '@storybook/core-server': 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+
+  '@storybook/vue3-vite@8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))':
+    dependencies:
+      '@storybook/builder-vite': 8.1.11(encoding@0.1.13)(prettier@3.3.3)(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))
+      '@storybook/core-server': 8.1.11(bufferutil@4.0.8)(encoding@0.1.13)(prettier@3.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
       '@storybook/types': 8.1.11
-      '@storybook/vue3': 8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))
+      '@storybook/vue3': 8.1.11(encoding@0.1.13)(prettier@3.3.3)(vue@3.4.34(typescript@5.5.4))
       find-package-json: 1.2.0
       magic-string: 0.30.10
-      typescript: 5.5.3
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
-      vue-component-meta: 2.0.16(typescript@5.5.3)
-      vue-docgen-api: 4.75.1(vue@3.4.31(typescript@5.5.3))
+      typescript: 5.5.4
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
+      vue-component-meta: 2.0.16(typescript@5.5.4)
+      vue-docgen-api: 4.75.1(vue@3.4.34(typescript@5.5.4))
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - bufferutil
@@ -16626,9 +16266,9 @@ snapshots:
       - vite-plugin-glimmerx
       - vue
 
-  '@storybook/vue3@8.1.11(encoding@0.1.13)(prettier@3.3.2)(vue@3.4.31(typescript@5.5.3))':
+  '@storybook/vue3@8.1.11(encoding@0.1.13)(prettier@3.3.3)(vue@3.4.34(typescript@5.5.4))':
     dependencies:
-      '@storybook/docs-tools': 8.1.11(encoding@0.1.13)(prettier@3.3.2)
+      '@storybook/docs-tools': 8.1.11(encoding@0.1.13)(prettier@3.3.3)
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 8.1.11
       '@storybook/types': 8.1.11
@@ -16636,13 +16276,28 @@ snapshots:
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.4.31(typescript@5.5.3)
-      vue-component-type-helpers: 2.0.26
+      vue: 3.4.34(typescript@5.5.4)
+      vue-component-type-helpers: 2.0.29
     transitivePeerDependencies:
       - encoding
       - prettier
       - supports-color
 
+  '@storybook/vue3@8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(vue@3.4.34(typescript@5.5.4))':
+    dependencies:
+      '@storybook/components': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/global': 5.0.0
+      '@storybook/manager-api': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/preview-api': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/theming': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@vue/compiler-core': 3.4.31
+      lodash: 4.17.21
+      storybook: 8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      ts-dedent: 2.2.0
+      type-fest: 2.19.0
+      vue: 3.4.34(typescript@5.5.4)
+      vue-component-type-helpers: 2.0.29
+
   '@swc/cli@0.3.12(@swc/core@1.6.6)(chokidar@3.5.3)':
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
@@ -16663,13 +16318,16 @@ snapshots:
       '@swc/wasm': 1.2.130
     optional: true
 
+  '@swc/core-darwin-arm64@1.3.56':
+    optional: true
+
   '@swc/core-darwin-arm64@1.6.13':
     optional: true
 
   '@swc/core-darwin-arm64@1.6.6':
     optional: true
 
-  '@swc/core-darwin-arm64@1.7.0-nightly-20240715.2':
+  '@swc/core-darwin-x64@1.3.56':
     optional: true
 
   '@swc/core-darwin-x64@1.6.13':
@@ -16678,21 +16336,21 @@ snapshots:
   '@swc/core-darwin-x64@1.6.6':
     optional: true
 
-  '@swc/core-darwin-x64@1.7.0-nightly-20240715.2':
-    optional: true
-
   '@swc/core-freebsd-x64@1.3.11':
     dependencies:
       '@swc/wasm': 1.2.130
     optional: true
 
+  '@swc/core-linux-arm-gnueabihf@1.3.56':
+    optional: true
+
   '@swc/core-linux-arm-gnueabihf@1.6.13':
     optional: true
 
   '@swc/core-linux-arm-gnueabihf@1.6.6':
     optional: true
 
-  '@swc/core-linux-arm-gnueabihf@1.7.0-nightly-20240715.2':
+  '@swc/core-linux-arm64-gnu@1.3.56':
     optional: true
 
   '@swc/core-linux-arm64-gnu@1.6.13':
@@ -16701,7 +16359,7 @@ snapshots:
   '@swc/core-linux-arm64-gnu@1.6.6':
     optional: true
 
-  '@swc/core-linux-arm64-gnu@1.7.0-nightly-20240715.2':
+  '@swc/core-linux-arm64-musl@1.3.56':
     optional: true
 
   '@swc/core-linux-arm64-musl@1.6.13':
@@ -16710,7 +16368,7 @@ snapshots:
   '@swc/core-linux-arm64-musl@1.6.6':
     optional: true
 
-  '@swc/core-linux-arm64-musl@1.7.0-nightly-20240715.2':
+  '@swc/core-linux-x64-gnu@1.3.56':
     optional: true
 
   '@swc/core-linux-x64-gnu@1.6.13':
@@ -16719,7 +16377,7 @@ snapshots:
   '@swc/core-linux-x64-gnu@1.6.6':
     optional: true
 
-  '@swc/core-linux-x64-gnu@1.7.0-nightly-20240715.2':
+  '@swc/core-linux-x64-musl@1.3.56':
     optional: true
 
   '@swc/core-linux-x64-musl@1.6.13':
@@ -16728,7 +16386,7 @@ snapshots:
   '@swc/core-linux-x64-musl@1.6.6':
     optional: true
 
-  '@swc/core-linux-x64-musl@1.7.0-nightly-20240715.2':
+  '@swc/core-win32-arm64-msvc@1.3.56':
     optional: true
 
   '@swc/core-win32-arm64-msvc@1.6.13':
@@ -16737,7 +16395,7 @@ snapshots:
   '@swc/core-win32-arm64-msvc@1.6.6':
     optional: true
 
-  '@swc/core-win32-arm64-msvc@1.7.0-nightly-20240715.2':
+  '@swc/core-win32-ia32-msvc@1.3.56':
     optional: true
 
   '@swc/core-win32-ia32-msvc@1.6.13':
@@ -16746,7 +16404,7 @@ snapshots:
   '@swc/core-win32-ia32-msvc@1.6.6':
     optional: true
 
-  '@swc/core-win32-ia32-msvc@1.7.0-nightly-20240715.2':
+  '@swc/core-win32-x64-msvc@1.3.56':
     optional: true
 
   '@swc/core-win32-x64-msvc@1.6.13':
@@ -16755,9 +16413,6 @@ snapshots:
   '@swc/core-win32-x64-msvc@1.6.6':
     optional: true
 
-  '@swc/core-win32-x64-msvc@1.7.0-nightly-20240715.2':
-    optional: true
-
   '@swc/core@1.6.13':
     dependencies:
       '@swc/counter': 0.1.3
@@ -16833,39 +16488,41 @@ snapshots:
 
   '@tabler/icons@3.3.0': {}
 
-  '@tensorflow/tfjs-backend-cpu@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
       '@types/seedrandom': 2.4.34
       seedrandom: 3.0.5
 
-  '@tensorflow/tfjs-backend-webgl@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-backend-webgl@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-backend-cpu': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
-      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
       '@types/offscreencanvas': 2019.3.0
       '@types/seedrandom': 2.4.34
+      '@types/webgl-ext': 0.0.30
       seedrandom: 3.0.5
 
-  '@tensorflow/tfjs-converter@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-converter@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
 
-  '@tensorflow/tfjs-core@4.20.0(encoding@0.1.13)':
+  '@tensorflow/tfjs-core@4.4.0(encoding@0.1.13)':
     dependencies:
       '@types/long': 4.0.2
       '@types/offscreencanvas': 2019.7.0
-      '@types/seedrandom': 2.4.30
-      '@webgpu/types': 0.1.38
+      '@types/seedrandom': 2.4.34
+      '@types/webgl-ext': 0.0.30
+      '@webgpu/types': 0.1.30
       long: 4.0.0
-      node-fetch: 2.6.11(encoding@0.1.13)
+      node-fetch: 2.6.13(encoding@0.1.13)
       seedrandom: 3.0.5
     transitivePeerDependencies:
       - encoding
 
-  '@tensorflow/tfjs-data@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)':
+  '@tensorflow/tfjs-data@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
-      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
       '@types/node-fetch': 2.6.11
       node-fetch: 2.6.13(encoding@0.1.13)
       seedrandom: 3.0.5
@@ -16873,34 +16530,34 @@ snapshots:
     transitivePeerDependencies:
       - encoding
 
-  '@tensorflow/tfjs-layers@4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))':
+  '@tensorflow/tfjs-layers@4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))':
     dependencies:
-      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
 
-  '@tensorflow/tfjs-node@4.20.0(encoding@0.1.13)(seedrandom@3.0.5)':
+  '@tensorflow/tfjs-node@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
       '@mapbox/node-pre-gyp': 1.0.9(encoding@0.1.13)
-      '@tensorflow/tfjs': 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
+      '@tensorflow/tfjs': 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
       adm-zip: 0.5.10
       google-protobuf: 3.21.2
       https-proxy-agent: 2.2.4
       progress: 2.0.3
       rimraf: 2.7.1
-      tar: 6.2.1
+      tar: 4.4.19
     transitivePeerDependencies:
       - encoding
       - seedrandom
       - supports-color
     optional: true
 
-  '@tensorflow/tfjs@4.20.0(encoding@0.1.13)(seedrandom@3.0.5)':
+  '@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)':
     dependencies:
-      '@tensorflow/tfjs-backend-cpu': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
-      '@tensorflow/tfjs-backend-webgl': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
-      '@tensorflow/tfjs-converter': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
-      '@tensorflow/tfjs-core': 4.20.0(encoding@0.1.13)
-      '@tensorflow/tfjs-data': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)
-      '@tensorflow/tfjs-layers': 4.20.0(@tensorflow/tfjs-core@4.20.0(encoding@0.1.13))
+      '@tensorflow/tfjs-backend-cpu': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-backend-webgl': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-converter': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
+      '@tensorflow/tfjs-core': 4.4.0(encoding@0.1.13)
+      '@tensorflow/tfjs-data': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))(encoding@0.1.13)(seedrandom@3.0.5)
+      '@tensorflow/tfjs-layers': 4.4.0(@tensorflow/tfjs-core@4.4.0(encoding@0.1.13))
       argparse: 1.0.10
       chalk: 4.1.2
       core-js: 3.29.1
@@ -16932,11 +16589,11 @@ snapshots:
       lz-string: 1.5.0
       pretty-format: 27.5.1
 
-  '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.9))(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
+  '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.12))(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))':
     dependencies:
       '@adobe/css-tools': 4.3.3
       '@babel/runtime': 7.23.4
-      aria-query: 5.1.3
+      aria-query: 5.3.0
       chalk: 3.0.0
       css.escape: 1.5.1
       dom-accessibility-api: 0.6.3
@@ -16945,21 +16602,21 @@ snapshots:
     optionalDependencies:
       '@jest/globals': 29.7.0
       '@types/jest': 29.5.12
-      jest: 29.7.0(@types/node@20.14.9)
-      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
+      jest: 29.7.0(@types/node@20.14.12)
+      vitest: 1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3)
 
   '@testing-library/user-event@14.5.2(@testing-library/dom@10.1.0)':
     dependencies:
       '@testing-library/dom': 10.1.0
 
-  '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
+  '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.4.34)(@vue/server-renderer@3.4.34(vue@3.4.34(typescript@5.5.4)))(vue@3.4.34(typescript@5.5.4))':
     dependencies:
       '@babel/runtime': 7.23.4
       '@testing-library/dom': 9.3.4
-      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
-      vue: 3.4.31(typescript@5.5.3)
+      '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.34(vue@3.4.34(typescript@5.5.4)))(vue@3.4.34(typescript@5.5.4))
+      vue: 3.4.34(typescript@5.5.4)
     optionalDependencies:
-      '@vue/compiler-sfc': 3.4.31
+      '@vue/compiler-sfc': 3.4.34
     transitivePeerDependencies:
       - '@vue/server-renderer'
 
@@ -16975,7 +16632,7 @@ snapshots:
 
   '@types/accepts@1.3.7':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/archiver@6.0.2':
     dependencies:
@@ -17011,7 +16668,7 @@ snapshots:
   '@types/body-parser@1.19.5':
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/braces@3.0.1': {}
 
@@ -17019,7 +16676,7 @@ snapshots:
     dependencies:
       '@types/http-cache-semantics': 4.0.4
       '@types/keyv': 3.1.4
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       '@types/responselike': 1.0.0
 
   '@types/color-convert@2.0.3':
@@ -17030,26 +16687,19 @@ snapshots:
 
   '@types/connect@3.4.35':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/connect@3.4.36':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/content-disposition@0.5.8': {}
 
   '@types/cookie@0.6.0': {}
 
-  '@types/cookies@0.9.0':
-    dependencies:
-      '@types/connect': 3.4.36
-      '@types/express': 4.17.17
-      '@types/keygrip': 1.0.6
-      '@types/node': 20.14.9
-
   '@types/cross-spawn@6.0.2':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/debug@4.1.12':
     dependencies:
@@ -17084,7 +16734,7 @@ snapshots:
 
   '@types/express-serve-static-core@4.17.33':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
 
@@ -17095,20 +16745,27 @@ snapshots:
       '@types/qs': 6.9.7
       '@types/serve-static': 1.15.1
 
+  '@types/express@4.17.21':
+    dependencies:
+      '@types/body-parser': 1.19.5
+      '@types/express-serve-static-core': 4.17.33
+      '@types/qs': 6.9.7
+      '@types/serve-static': 1.15.1
+
   '@types/find-cache-dir@3.2.1': {}
 
   '@types/fluent-ffmpeg@2.1.24':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/glob@7.2.0':
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/graceful-fs@4.1.6':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/hast@3.0.4':
     dependencies:
@@ -17116,15 +16773,11 @@ snapshots:
 
   '@types/htmlescape@1.1.3': {}
 
-  '@types/http-assert@1.5.5': {}
-
   '@types/http-cache-semantics@4.0.4': {}
 
-  '@types/http-errors@2.0.4': {}
-
   '@types/http-link-header@1.0.7':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/istanbul-lib-coverage@2.0.4': {}
 
@@ -17145,7 +16798,7 @@ snapshots:
 
   '@types/jsdom@21.1.7':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
 
@@ -17155,34 +16808,13 @@ snapshots:
 
   '@types/json5@0.0.29': {}
 
-  '@types/jsonld@1.5.14': {}
+  '@types/jsonld@1.5.15': {}
 
   '@types/jsrsasign@10.5.14': {}
 
-  '@types/keygrip@1.0.6': {}
-
   '@types/keyv@3.1.4':
     dependencies:
-      '@types/node': 20.14.9
-
-  '@types/koa-compose@3.2.8':
-    dependencies:
-      '@types/koa': 2.14.0
-
-  '@types/koa@2.14.0':
-    dependencies:
-      '@types/accepts': 1.3.7
-      '@types/content-disposition': 0.5.8
-      '@types/cookies': 0.9.0
-      '@types/http-assert': 1.5.5
-      '@types/http-errors': 2.0.4
-      '@types/keygrip': 1.0.6
-      '@types/koa-compose': 3.2.8
-      '@types/node': 20.14.9
-
-  '@types/koa__router@12.0.3':
-    dependencies:
-      '@types/koa': 2.14.0
+      '@types/node': 20.14.12
 
   '@types/lodash@4.14.191': {}
 
@@ -17190,6 +16822,8 @@ snapshots:
 
   '@types/matter-js@0.19.6': {}
 
+  '@types/matter-js@0.19.7': {}
+
   '@types/mdast@4.0.3':
     dependencies:
       '@types/unist': 3.0.2
@@ -17212,15 +16846,15 @@ snapshots:
 
   '@types/mute-stream@0.0.4':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/mysql@2.15.22':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/node-fetch@2.6.11':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       form-data: 4.0.0
 
   '@types/node@18.17.15': {}
@@ -17229,7 +16863,7 @@ snapshots:
     dependencies:
       undici-types: 5.26.5
 
-  '@types/node@20.14.9':
+  '@types/node@20.14.12':
     dependencies:
       undici-types: 5.26.5
 
@@ -17239,7 +16873,7 @@ snapshots:
 
   '@types/nodemailer@6.4.15':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/normalize-package-data@2.4.1': {}
 
@@ -17250,11 +16884,11 @@ snapshots:
   '@types/oauth2orize@1.11.5':
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/oauth@0.9.5':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/offscreencanvas@2019.3.0': {}
 
@@ -17266,13 +16900,13 @@ snapshots:
 
   '@types/pg@8.11.6':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       pg-protocol: 1.6.1
       pg-types: 4.0.1
 
   '@types/pg@8.6.1':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       pg-protocol: 1.6.1
       pg-types: 2.2.0
 
@@ -17286,7 +16920,7 @@ snapshots:
 
   '@types/qrcode@1.5.5':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/qs@6.9.7': {}
 
@@ -17304,7 +16938,7 @@ snapshots:
 
   '@types/readdir-glob@1.1.1':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/rename@1.0.7': {}
 
@@ -17312,7 +16946,7 @@ snapshots:
 
   '@types/responselike@1.0.0':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/sanitize-html@2.11.0':
     dependencies:
@@ -17320,8 +16954,6 @@ snapshots:
 
   '@types/scheduler@0.16.2': {}
 
-  '@types/seedrandom@2.4.30': {}
-
   '@types/seedrandom@2.4.34': {}
 
   '@types/seedrandom@3.0.8': {}
@@ -17331,7 +16963,7 @@ snapshots:
   '@types/serve-static@1.15.1':
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/serviceworker@0.0.67': {}
 
@@ -17361,6 +16993,8 @@ snapshots:
 
   '@types/tough-cookie@4.0.2': {}
 
+  '@types/tough-cookie@4.0.5': {}
+
   '@types/unist@3.0.2': {}
 
   '@types/uuid@10.0.0': {}
@@ -17369,17 +17003,19 @@ snapshots:
 
   '@types/vary@1.1.3':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/web-push@3.6.3':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
+
+  '@types/webgl-ext@0.0.30': {}
 
   '@types/wrap-ansi@3.0.0': {}
 
-  '@types/ws@8.5.10':
+  '@types/ws@8.5.11':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
 
   '@types/yargs-parser@21.0.0': {}
 
@@ -17389,19 +17025,19 @@ snapshots:
 
   '@types/yauzl@2.10.0':
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
     optional: true
 
-  '@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0(eslint@9.8.0)(typescript@5.3.3))(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.11.0(eslint@9.8.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/type-utils': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 6.11.0(eslint@9.8.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.11.0(eslint@9.8.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.7.0
+      eslint: 9.8.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -17412,16 +17048,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3))(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@9.8.0)(typescript@5.3.3))(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 7.1.0(eslint@9.8.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 7.1.0
-      '@typescript-eslint/type-utils': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 7.1.0(eslint@9.8.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.1.0(eslint@9.8.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.7.0
+      eslint: 9.8.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -17432,91 +17068,60 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0)(typescript@5.5.3)':
+  '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0)(typescript@5.5.4)':
     dependencies:
-      '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
-      '@typescript-eslint/scope-manager': 7.15.0
-      '@typescript-eslint/type-utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
-      '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
-      '@typescript-eslint/visitor-keys': 7.15.0
-      eslint: 9.6.0
+      '@eslint-community/regexpp': 4.11.0
+      '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4)
+      '@typescript-eslint/scope-manager': 7.17.0
+      '@typescript-eslint/type-utils': 7.17.0(eslint@9.8.0)(typescript@5.5.4)
+      '@typescript-eslint/utils': 7.17.0(eslint@9.8.0)(typescript@5.5.4)
+      '@typescript-eslint/visitor-keys': 7.17.0
+      eslint: 9.8.0
       graphemer: 1.4.0
       ignore: 5.3.1
       natural-compare: 1.4.0
-      ts-api-utils: 1.3.0(typescript@5.5.3)
+      ts-api-utils: 1.3.0(typescript@5.5.4)
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0)(typescript@5.5.3)':
-    dependencies:
-      '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
-      '@typescript-eslint/scope-manager': 7.15.0
-      '@typescript-eslint/type-utils': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
-      '@typescript-eslint/utils': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
-      '@typescript-eslint/visitor-keys': 7.15.0
-      eslint: 9.7.0
-      graphemer: 1.4.0
-      ignore: 5.3.1
-      natural-compare: 1.4.0
-      ts-api-utils: 1.3.0(typescript@5.5.3)
-    optionalDependencies:
-      typescript: 5.5.3
-    transitivePeerDependencies:
-      - supports-color
-
-  '@typescript-eslint/parser@6.11.0(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/parser@6.11.0(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.7.0
+      eslint: 9.8.0
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@7.1.0(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/parser@7.1.0(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/scope-manager': 7.1.0
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 7.1.0
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.7.0
+      eslint: 9.8.0
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3)':
+  '@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4)':
     dependencies:
-      '@typescript-eslint/scope-manager': 7.15.0
-      '@typescript-eslint/types': 7.15.0
-      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
-      '@typescript-eslint/visitor-keys': 7.15.0
+      '@typescript-eslint/scope-manager': 7.17.0
+      '@typescript-eslint/types': 7.17.0
+      '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4)
+      '@typescript-eslint/visitor-keys': 7.17.0
       debug: 4.3.5(supports-color@8.1.1)
-      eslint: 9.6.0
+      eslint: 9.8.0
     optionalDependencies:
-      typescript: 5.5.3
-    transitivePeerDependencies:
-      - supports-color
-
-  '@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3)':
-    dependencies:
-      '@typescript-eslint/scope-manager': 7.15.0
-      '@typescript-eslint/types': 7.15.0
-      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
-      '@typescript-eslint/visitor-keys': 7.15.0
-      debug: 4.3.5(supports-color@8.1.1)
-      eslint: 9.7.0
-    optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
     transitivePeerDependencies:
       - supports-color
 
@@ -17530,56 +17135,44 @@ snapshots:
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/visitor-keys': 7.1.0
 
-  '@typescript-eslint/scope-manager@7.15.0':
+  '@typescript-eslint/scope-manager@7.17.0':
     dependencies:
-      '@typescript-eslint/types': 7.15.0
-      '@typescript-eslint/visitor-keys': 7.15.0
+      '@typescript-eslint/types': 7.17.0
+      '@typescript-eslint/visitor-keys': 7.17.0
 
-  '@typescript-eslint/type-utils@6.11.0(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/type-utils@6.11.0(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.11.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.11.0(eslint@9.8.0)(typescript@5.3.3)
       debug: 4.3.5(supports-color@8.1.1)
-      eslint: 9.7.0
+      eslint: 9.8.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/type-utils@7.1.0(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/type-utils@7.1.0(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 7.1.0(eslint@9.7.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 7.1.0(eslint@9.8.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.7.0
+      eslint: 9.8.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
     optionalDependencies:
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/type-utils@7.15.0(eslint@9.6.0)(typescript@5.5.3)':
+  '@typescript-eslint/type-utils@7.17.0(eslint@9.8.0)(typescript@5.5.4)':
     dependencies:
-      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
-      '@typescript-eslint/utils': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4)
+      '@typescript-eslint/utils': 7.17.0(eslint@9.8.0)(typescript@5.5.4)
       debug: 4.3.5(supports-color@8.1.1)
-      eslint: 9.6.0
-      ts-api-utils: 1.3.0(typescript@5.5.3)
+      eslint: 9.8.0
+      ts-api-utils: 1.3.0(typescript@5.5.4)
     optionalDependencies:
-      typescript: 5.5.3
-    transitivePeerDependencies:
-      - supports-color
-
-  '@typescript-eslint/type-utils@7.15.0(eslint@9.7.0)(typescript@5.5.3)':
-    dependencies:
-      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
-      '@typescript-eslint/utils': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
-      debug: 4.3.5(supports-color@8.1.1)
-      eslint: 9.7.0
-      ts-api-utils: 1.3.0(typescript@5.5.3)
-    optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
     transitivePeerDependencies:
       - supports-color
 
@@ -17587,7 +17180,7 @@ snapshots:
 
   '@typescript-eslint/types@7.1.0': {}
 
-  '@typescript-eslint/types@7.15.0': {}
+  '@typescript-eslint/types@7.17.0': {}
 
   '@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.3)':
     dependencies:
@@ -17618,67 +17211,56 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/typescript-estree@7.15.0(typescript@5.5.3)':
+  '@typescript-eslint/typescript-estree@7.17.0(typescript@5.5.4)':
     dependencies:
-      '@typescript-eslint/types': 7.15.0
-      '@typescript-eslint/visitor-keys': 7.15.0
+      '@typescript-eslint/types': 7.17.0
+      '@typescript-eslint/visitor-keys': 7.17.0
       debug: 4.3.5(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.4
       semver: 7.6.0
-      ts-api-utils: 1.3.0(typescript@5.5.3)
+      ts-api-utils: 1.3.0(typescript@5.5.4)
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/utils@6.11.0(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/utils@6.11.0(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
-      eslint: 9.7.0
+      eslint: 9.8.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@7.1.0(eslint@9.7.0)(typescript@5.3.3)':
+  '@typescript-eslint/utils@7.1.0(eslint@9.8.0)(typescript@5.3.3)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.8
       '@typescript-eslint/scope-manager': 7.1.0
       '@typescript-eslint/types': 7.1.0
       '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
-      eslint: 9.7.0
+      eslint: 9.8.0
       semver: 7.6.0
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@7.15.0(eslint@9.6.0)(typescript@5.5.3)':
+  '@typescript-eslint/utils@7.17.0(eslint@9.8.0)(typescript@5.5.4)':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
-      '@typescript-eslint/scope-manager': 7.15.0
-      '@typescript-eslint/types': 7.15.0
-      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
-      eslint: 9.6.0
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-
-  '@typescript-eslint/utils@7.15.0(eslint@9.7.0)(typescript@5.5.3)':
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
-      '@typescript-eslint/scope-manager': 7.15.0
-      '@typescript-eslint/types': 7.15.0
-      '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3)
-      eslint: 9.7.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0)
+      '@typescript-eslint/scope-manager': 7.17.0
+      '@typescript-eslint/types': 7.17.0
+      '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4)
+      eslint: 9.8.0
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -17693,19 +17275,19 @@ snapshots:
       '@typescript-eslint/types': 7.1.0
       eslint-visitor-keys: 3.4.3
 
-  '@typescript-eslint/visitor-keys@7.15.0':
+  '@typescript-eslint/visitor-keys@7.17.0':
     dependencies:
-      '@typescript-eslint/types': 7.15.0
+      '@typescript-eslint/types': 7.17.0
       eslint-visitor-keys: 3.4.3
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-vue@5.0.5(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2))(vue@3.4.31(typescript@5.5.3))':
+  '@vitejs/plugin-vue@5.1.0(vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34(typescript@5.5.4))':
     dependencies:
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
-      vue: 3.4.31(typescript@5.5.3)
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
+      vue: 3.4.34(typescript@5.5.4)
 
-  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2))':
+  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3))':
     dependencies:
       '@ampproject/remapping': 2.2.1
       '@bcoe/v8-coverage': 0.2.3
@@ -17720,7 +17302,7 @@ snapshots:
       std-env: 3.7.0
       strip-literal: 2.1.0
       test-exclude: 6.0.0
-      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
+      vitest: 1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3)
     transitivePeerDependencies:
       - supports-color
 
@@ -17757,24 +17339,24 @@ snapshots:
     dependencies:
       '@volar/source-map': 2.2.0
 
-  '@volar/language-core@2.4.0-alpha.11':
+  '@volar/language-core@2.4.0-alpha.18':
     dependencies:
-      '@volar/source-map': 2.4.0-alpha.11
+      '@volar/source-map': 2.4.0-alpha.18
 
   '@volar/source-map@2.2.0':
     dependencies:
       muggle-string: 0.4.1
 
-  '@volar/source-map@2.4.0-alpha.11': {}
+  '@volar/source-map@2.4.0-alpha.18': {}
 
   '@volar/typescript@2.2.0':
     dependencies:
       '@volar/language-core': 2.2.0
       path-browserify: 1.0.1
 
-  '@volar/typescript@2.4.0-alpha.11':
+  '@volar/typescript@2.4.0-alpha.18':
     dependencies:
-      '@volar/language-core': 2.4.0-alpha.11
+      '@volar/language-core': 2.4.0-alpha.18
       path-browserify: 1.0.1
       vscode-uri: 3.0.8
 
@@ -17794,6 +17376,14 @@ snapshots:
       estree-walker: 2.0.2
       source-map-js: 1.2.0
 
+  '@vue/compiler-core@3.4.34':
+    dependencies:
+      '@babel/parser': 7.24.7
+      '@vue/shared': 3.4.34
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.0
+
   '@vue/compiler-dom@3.4.29':
     dependencies:
       '@vue/compiler-core': 3.4.29
@@ -17804,90 +17394,102 @@ snapshots:
       '@vue/compiler-core': 3.4.31
       '@vue/shared': 3.4.31
 
-  '@vue/compiler-sfc@3.4.31':
+  '@vue/compiler-dom@3.4.34':
+    dependencies:
+      '@vue/compiler-core': 3.4.34
+      '@vue/shared': 3.4.34
+
+  '@vue/compiler-sfc@3.4.34':
     dependencies:
       '@babel/parser': 7.24.7
-      '@vue/compiler-core': 3.4.31
-      '@vue/compiler-dom': 3.4.31
-      '@vue/compiler-ssr': 3.4.31
-      '@vue/shared': 3.4.31
+      '@vue/compiler-core': 3.4.34
+      '@vue/compiler-dom': 3.4.34
+      '@vue/compiler-ssr': 3.4.34
+      '@vue/shared': 3.4.34
       estree-walker: 2.0.2
       magic-string: 0.30.10
-      postcss: 8.4.38
+      postcss: 8.4.40
       source-map-js: 1.2.0
 
-  '@vue/compiler-ssr@3.4.31':
+  '@vue/compiler-ssr@3.4.34':
     dependencies:
-      '@vue/compiler-dom': 3.4.31
-      '@vue/shared': 3.4.31
+      '@vue/compiler-dom': 3.4.34
+      '@vue/shared': 3.4.34
+
+  '@vue/compiler-vue2@2.7.16':
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
 
   '@vue/devtools-api@6.6.1': {}
 
-  '@vue/language-core@2.0.16(typescript@5.5.3)':
+  '@vue/language-core@2.0.16(typescript@5.5.4)':
     dependencies:
       '@volar/language-core': 2.2.0
-      '@vue/compiler-dom': 3.4.29
-      '@vue/shared': 3.4.29
+      '@vue/compiler-dom': 3.4.31
+      '@vue/shared': 3.4.31
       computeds: 0.0.1
       minimatch: 9.0.4
       path-browserify: 1.0.1
       vue-template-compiler: 2.7.14
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
-  '@vue/language-core@2.0.24(typescript@5.5.3)':
+  '@vue/language-core@2.0.29(typescript@5.5.4)':
     dependencies:
-      '@volar/language-core': 2.4.0-alpha.11
-      '@vue/compiler-dom': 3.4.29
-      '@vue/shared': 3.4.29
+      '@volar/language-core': 2.4.0-alpha.18
+      '@vue/compiler-dom': 3.4.31
+      '@vue/compiler-vue2': 2.7.16
+      '@vue/shared': 3.4.31
       computeds: 0.0.1
       minimatch: 9.0.4
       muggle-string: 0.4.1
       path-browserify: 1.0.1
-      vue-template-compiler: 2.7.14
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
-  '@vue/reactivity@3.4.31':
+  '@vue/reactivity@3.4.34':
     dependencies:
-      '@vue/shared': 3.4.31
+      '@vue/shared': 3.4.34
 
-  '@vue/runtime-core@3.4.31':
+  '@vue/runtime-core@3.4.34':
     dependencies:
-      '@vue/reactivity': 3.4.31
-      '@vue/shared': 3.4.31
+      '@vue/reactivity': 3.4.34
+      '@vue/shared': 3.4.34
 
-  '@vue/runtime-dom@3.4.31':
+  '@vue/runtime-dom@3.4.34':
     dependencies:
-      '@vue/reactivity': 3.4.31
-      '@vue/runtime-core': 3.4.31
-      '@vue/shared': 3.4.31
+      '@vue/reactivity': 3.4.34
+      '@vue/runtime-core': 3.4.34
+      '@vue/shared': 3.4.34
       csstype: 3.1.3
 
-  '@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3))':
+  '@vue/server-renderer@3.4.34(vue@3.4.34(typescript@5.5.4))':
     dependencies:
-      '@vue/compiler-ssr': 3.4.31
-      '@vue/shared': 3.4.31
-      vue: 3.4.31(typescript@5.5.3)
+      '@vue/compiler-ssr': 3.4.34
+      '@vue/shared': 3.4.34
+      vue: 3.4.34(typescript@5.5.4)
 
   '@vue/shared@3.4.29': {}
 
   '@vue/shared@3.4.31': {}
 
-  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
+  '@vue/shared@3.4.34': {}
+
+  '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.34(vue@3.4.34(typescript@5.5.4)))(vue@3.4.34(typescript@5.5.4))':
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.4.31(typescript@5.5.3)
+      vue: 3.4.34(typescript@5.5.4)
       vue-component-type-helpers: 1.8.4
     optionalDependencies:
-      '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3))
+      '@vue/server-renderer': 3.4.34(vue@3.4.34(typescript@5.5.4))
 
-  '@webgpu/types@0.1.38': {}
+  '@webgpu/types@0.1.30': {}
 
   '@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.19.11)':
     dependencies:
       esbuild: 0.19.11
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   '@yarnpkg/fslib@2.10.3':
     dependencies:
@@ -17914,22 +17516,22 @@ snapshots:
       mime-types: 2.1.35
       negotiator: 0.6.3
 
-  acorn-import-assertions@1.9.0(acorn@8.12.0):
+  acorn-import-assertions@1.9.0(acorn@8.12.1):
     dependencies:
-      acorn: 8.12.0
+      acorn: 8.12.1
     optional: true
 
-  acorn-import-attributes@1.9.5(acorn@8.12.0):
+  acorn-import-attributes@1.9.5(acorn@8.12.1):
     dependencies:
-      acorn: 8.12.0
+      acorn: 8.12.1
 
   acorn-jsx@5.3.2(acorn@7.4.1):
     dependencies:
       acorn: 7.4.1
 
-  acorn-jsx@5.3.2(acorn@8.12.0):
+  acorn-jsx@5.3.2(acorn@8.12.1):
     dependencies:
-      acorn: 8.12.0
+      acorn: 8.12.1
 
   acorn-walk@7.2.0: {}
 
@@ -17937,7 +17539,7 @@ snapshots:
 
   acorn@7.4.1: {}
 
-  acorn@8.12.0: {}
+  acorn@8.12.1: {}
 
   address@1.2.2: {}
 
@@ -17980,13 +17582,13 @@ snapshots:
     optionalDependencies:
       ajv: 8.13.0
 
-  ajv-draft-04@1.0.0(ajv@8.16.0):
+  ajv-draft-04@1.0.0(ajv@8.17.1):
     optionalDependencies:
-      ajv: 8.16.0
+      ajv: 8.17.1
 
-  ajv-formats@2.1.1(ajv@8.16.0):
+  ajv-formats@2.1.1(ajv@8.17.1):
     optionalDependencies:
-      ajv: 8.16.0
+      ajv: 8.17.1
 
   ajv-formats@3.0.1(ajv@8.13.0):
     optionalDependencies:
@@ -18013,12 +17615,12 @@ snapshots:
       require-from-string: 2.0.2
       uri-js: 4.4.1
 
-  ajv@8.16.0:
+  ajv@8.17.1:
     dependencies:
       fast-deep-equal: 3.1.3
+      fast-uri: 3.0.1
       json-schema-traverse: 1.0.0
       require-from-string: 2.0.2
-      uri-js: 4.4.1
 
   ansi-colors@4.1.3: {}
 
@@ -18062,7 +17664,7 @@ snapshots:
 
   archiver-utils@5.0.2:
     dependencies:
-      glob: 10.4.2
+      glob: 10.3.10
       graceful-fs: 4.2.11
       is-stream: 2.0.1
       lazystream: 1.0.1
@@ -18096,10 +17698,6 @@ snapshots:
 
   argparse@2.0.1: {}
 
-  aria-hidden@1.2.4:
-    dependencies:
-      tslib: 2.6.2
-
   aria-query@5.1.3:
     dependencies:
       deep-equal: 2.2.0
@@ -18175,7 +17773,7 @@ snapshots:
     dependencies:
       pvtsutils: 1.3.5
       pvutils: 1.1.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   assert-never@1.2.1: {}
 
@@ -18193,7 +17791,7 @@ snapshots:
 
   ast-types@0.16.1:
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   astral-regex@2.0.0: {}
 
@@ -18435,10 +18033,6 @@ snapshots:
 
   browser-assert@1.2.1: {}
 
-  browserify-zlib@0.1.4:
-    dependencies:
-      pako: 0.2.9
-
   browserslist@4.22.2:
     dependencies:
       caniuse-lite: 1.0.30001566
@@ -18480,19 +18074,24 @@ snapshots:
       base64-js: 1.5.1
       ieee754: 1.2.1
 
+  bufferutil@4.0.7:
+    dependencies:
+      node-gyp-build: 4.6.0
+    optional: true
+
   bufferutil@4.0.8:
     dependencies:
       node-gyp-build: 4.6.0
     optional: true
 
-  bullmq@5.8.3:
+  bullmq@5.10.4:
     dependencies:
       cron-parser: 4.8.1
       ioredis: 5.4.1
       msgpackr: 1.10.1
       node-abort-controller: 3.1.1
       semver: 7.6.0
-      tslib: 2.6.2
+      tslib: 2.6.3
       uuid: 9.0.1
     transitivePeerDependencies:
       - supports-color
@@ -18513,7 +18112,7 @@ snapshots:
     dependencies:
       '@npmcli/fs': 3.1.0
       fs-minipass: 3.0.3
-      glob: 10.4.2
+      glob: 10.3.10
       lru-cache: 10.2.2
       minipass: 7.1.2
       minipass-collect: 1.0.2
@@ -18701,11 +18300,12 @@ snapshots:
     optionalDependencies:
       fsevents: 2.3.3
 
-  chownr@1.1.4: {}
+  chownr@1.1.4:
+    optional: true
 
   chownr@2.0.0: {}
 
-  chromatic@11.5.4: {}
+  chromatic@11.5.6: {}
 
   ci-info@3.7.1: {}
 
@@ -18832,7 +18432,7 @@ snapshots:
 
   commondir@1.0.1: {}
 
-  compare-versions@6.1.0: {}
+  compare-versions@6.1.1: {}
 
   compress-commons@6.0.2:
     dependencies:
@@ -18922,13 +18522,13 @@ snapshots:
       crc-32: 1.2.2
       readable-stream: 4.3.0
 
-  create-jest@29.7.0(@types/node@20.14.9):
+  create-jest@29.7.0(@types/node@20.14.12):
     dependencies:
       '@jest/types': 29.6.3
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.14.9)
+      jest-config: 29.7.0(@types/node@20.14.12)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -18941,10 +18541,10 @@ snapshots:
     dependencies:
       luxon: 3.3.0
 
-  cropperjs@2.0.0-beta.5:
+  cropperjs@2.0.0-rc.1:
     dependencies:
-      '@cropper/elements': 2.0.0-beta.5
-      '@cropper/utils': 2.0.0-beta.5
+      '@cropper/elements': 2.0.0-rc.1
+      '@cropper/utils': 2.0.0-rc.1
 
   cross-env@7.0.3:
     dependencies:
@@ -18978,9 +18578,9 @@ snapshots:
     dependencies:
       type-fest: 1.4.0
 
-  css-declaration-sorter@7.2.0(postcss@8.4.38):
+  css-declaration-sorter@7.2.0(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
 
   css-select@5.1.0:
     dependencies:
@@ -19006,49 +18606,49 @@ snapshots:
 
   cssesc@3.0.0: {}
 
-  cssnano-preset-default@6.1.2(postcss@8.4.38):
+  cssnano-preset-default@6.1.2(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
-      css-declaration-sorter: 7.2.0(postcss@8.4.38)
-      cssnano-utils: 4.0.2(postcss@8.4.38)
-      postcss: 8.4.38
-      postcss-calc: 9.0.1(postcss@8.4.38)
-      postcss-colormin: 6.1.0(postcss@8.4.38)
-      postcss-convert-values: 6.1.0(postcss@8.4.38)
-      postcss-discard-comments: 6.0.2(postcss@8.4.38)
-      postcss-discard-duplicates: 6.0.3(postcss@8.4.38)
-      postcss-discard-empty: 6.0.3(postcss@8.4.38)
-      postcss-discard-overridden: 6.0.2(postcss@8.4.38)
-      postcss-merge-longhand: 6.0.5(postcss@8.4.38)
-      postcss-merge-rules: 6.1.1(postcss@8.4.38)
-      postcss-minify-font-values: 6.1.0(postcss@8.4.38)
-      postcss-minify-gradients: 6.0.3(postcss@8.4.38)
-      postcss-minify-params: 6.1.0(postcss@8.4.38)
-      postcss-minify-selectors: 6.0.4(postcss@8.4.38)
-      postcss-normalize-charset: 6.0.2(postcss@8.4.38)
-      postcss-normalize-display-values: 6.0.2(postcss@8.4.38)
-      postcss-normalize-positions: 6.0.2(postcss@8.4.38)
-      postcss-normalize-repeat-style: 6.0.2(postcss@8.4.38)
-      postcss-normalize-string: 6.0.2(postcss@8.4.38)
-      postcss-normalize-timing-functions: 6.0.2(postcss@8.4.38)
-      postcss-normalize-unicode: 6.1.0(postcss@8.4.38)
-      postcss-normalize-url: 6.0.2(postcss@8.4.38)
-      postcss-normalize-whitespace: 6.0.2(postcss@8.4.38)
-      postcss-ordered-values: 6.0.2(postcss@8.4.38)
-      postcss-reduce-initial: 6.1.0(postcss@8.4.38)
-      postcss-reduce-transforms: 6.0.2(postcss@8.4.38)
-      postcss-svgo: 6.0.3(postcss@8.4.38)
-      postcss-unique-selectors: 6.0.4(postcss@8.4.38)
+      css-declaration-sorter: 7.2.0(postcss@8.4.40)
+      cssnano-utils: 4.0.2(postcss@8.4.40)
+      postcss: 8.4.40
+      postcss-calc: 9.0.1(postcss@8.4.40)
+      postcss-colormin: 6.1.0(postcss@8.4.40)
+      postcss-convert-values: 6.1.0(postcss@8.4.40)
+      postcss-discard-comments: 6.0.2(postcss@8.4.40)
+      postcss-discard-duplicates: 6.0.3(postcss@8.4.40)
+      postcss-discard-empty: 6.0.3(postcss@8.4.40)
+      postcss-discard-overridden: 6.0.2(postcss@8.4.40)
+      postcss-merge-longhand: 6.0.5(postcss@8.4.40)
+      postcss-merge-rules: 6.1.1(postcss@8.4.40)
+      postcss-minify-font-values: 6.1.0(postcss@8.4.40)
+      postcss-minify-gradients: 6.0.3(postcss@8.4.40)
+      postcss-minify-params: 6.1.0(postcss@8.4.40)
+      postcss-minify-selectors: 6.0.4(postcss@8.4.40)
+      postcss-normalize-charset: 6.0.2(postcss@8.4.40)
+      postcss-normalize-display-values: 6.0.2(postcss@8.4.40)
+      postcss-normalize-positions: 6.0.2(postcss@8.4.40)
+      postcss-normalize-repeat-style: 6.0.2(postcss@8.4.40)
+      postcss-normalize-string: 6.0.2(postcss@8.4.40)
+      postcss-normalize-timing-functions: 6.0.2(postcss@8.4.40)
+      postcss-normalize-unicode: 6.1.0(postcss@8.4.40)
+      postcss-normalize-url: 6.0.2(postcss@8.4.40)
+      postcss-normalize-whitespace: 6.0.2(postcss@8.4.40)
+      postcss-ordered-values: 6.0.2(postcss@8.4.40)
+      postcss-reduce-initial: 6.1.0(postcss@8.4.40)
+      postcss-reduce-transforms: 6.0.2(postcss@8.4.40)
+      postcss-svgo: 6.0.3(postcss@8.4.40)
+      postcss-unique-selectors: 6.0.4(postcss@8.4.40)
 
-  cssnano-utils@4.0.2(postcss@8.4.38):
+  cssnano-utils@4.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
 
-  cssnano@6.1.2(postcss@8.4.38):
+  cssnano@6.1.2(postcss@8.4.40):
     dependencies:
-      cssnano-preset-default: 6.1.2(postcss@8.4.38)
+      cssnano-preset-default: 6.1.2(postcss@8.4.40)
       lilconfig: 3.1.1
-      postcss: 8.4.38
+      postcss: 8.4.40
 
   csso@5.0.5:
     dependencies:
@@ -19064,7 +18664,7 @@ snapshots:
     dependencies:
       uniq: 1.0.1
 
-  cypress@13.13.0:
+  cypress@13.13.1:
     dependencies:
       '@cypress/request': 3.0.0
       '@cypress/xvfb': 1.2.4(supports-color@8.1.1)
@@ -19254,15 +18854,10 @@ snapshots:
 
   detect-indent@6.1.0: {}
 
-  detect-libc@2.0.2:
-    optional: true
-
   detect-libc@2.0.3: {}
 
   detect-newline@3.1.0: {}
 
-  detect-node-es@1.1.0: {}
-
   detect-package-manager@2.0.1:
     dependencies:
       execa: 5.1.1
@@ -19332,13 +18927,6 @@ snapshots:
 
   duplexer@0.1.2: {}
 
-  duplexify@3.7.1:
-    dependencies:
-      end-of-stream: 1.4.4
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-      stream-shift: 1.0.1
-
   eastasianwidth@0.2.0: {}
 
   ecc-jsbn@0.1.2:
@@ -19570,32 +19158,32 @@ snapshots:
       '@esbuild/win32-ia32': 0.21.5
       '@esbuild/win32-x64': 0.21.5
 
-  esbuild@0.22.0:
+  esbuild@0.23.0:
     optionalDependencies:
-      '@esbuild/aix-ppc64': 0.22.0
-      '@esbuild/android-arm': 0.22.0
-      '@esbuild/android-arm64': 0.22.0
-      '@esbuild/android-x64': 0.22.0
-      '@esbuild/darwin-arm64': 0.22.0
-      '@esbuild/darwin-x64': 0.22.0
-      '@esbuild/freebsd-arm64': 0.22.0
-      '@esbuild/freebsd-x64': 0.22.0
-      '@esbuild/linux-arm': 0.22.0
-      '@esbuild/linux-arm64': 0.22.0
-      '@esbuild/linux-ia32': 0.22.0
-      '@esbuild/linux-loong64': 0.22.0
-      '@esbuild/linux-mips64el': 0.22.0
-      '@esbuild/linux-ppc64': 0.22.0
-      '@esbuild/linux-riscv64': 0.22.0
-      '@esbuild/linux-s390x': 0.22.0
-      '@esbuild/linux-x64': 0.22.0
-      '@esbuild/netbsd-x64': 0.22.0
-      '@esbuild/openbsd-arm64': 0.22.0
-      '@esbuild/openbsd-x64': 0.22.0
-      '@esbuild/sunos-x64': 0.22.0
-      '@esbuild/win32-arm64': 0.22.0
-      '@esbuild/win32-ia32': 0.22.0
-      '@esbuild/win32-x64': 0.22.0
+      '@esbuild/aix-ppc64': 0.23.0
+      '@esbuild/android-arm': 0.23.0
+      '@esbuild/android-arm64': 0.23.0
+      '@esbuild/android-x64': 0.23.0
+      '@esbuild/darwin-arm64': 0.23.0
+      '@esbuild/darwin-x64': 0.23.0
+      '@esbuild/freebsd-arm64': 0.23.0
+      '@esbuild/freebsd-x64': 0.23.0
+      '@esbuild/linux-arm': 0.23.0
+      '@esbuild/linux-arm64': 0.23.0
+      '@esbuild/linux-ia32': 0.23.0
+      '@esbuild/linux-loong64': 0.23.0
+      '@esbuild/linux-mips64el': 0.23.0
+      '@esbuild/linux-ppc64': 0.23.0
+      '@esbuild/linux-riscv64': 0.23.0
+      '@esbuild/linux-s390x': 0.23.0
+      '@esbuild/linux-x64': 0.23.0
+      '@esbuild/netbsd-x64': 0.23.0
+      '@esbuild/openbsd-arm64': 0.23.0
+      '@esbuild/openbsd-x64': 0.23.0
+      '@esbuild/sunos-x64': 0.23.0
+      '@esbuild/win32-arm64': 0.23.0
+      '@esbuild/win32-ia32': 0.23.0
+      '@esbuild/win32-x64': 0.23.0
 
   escalade@3.1.1: {}
 
@@ -19638,27 +19226,17 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0):
+  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.8.0):
     dependencies:
       debug: 3.2.7(supports-color@8.1.1)
     optionalDependencies:
-      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
-      eslint: 9.6.0
+      '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4)
+      eslint: 9.8.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.7.0):
-    dependencies:
-      debug: 3.2.7(supports-color@8.1.1)
-    optionalDependencies:
-      '@typescript-eslint/parser': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
-      eslint: 9.7.0
-      eslint-import-resolver-node: 0.3.9
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint@9.6.0):
+  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint@9.8.0):
     dependencies:
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
@@ -19666,9 +19244,9 @@ snapshots:
       array.prototype.flatmap: 1.3.2
       debug: 3.2.7(supports-color@8.1.1)
       doctrine: 2.1.0
-      eslint: 9.6.0
+      eslint: 9.8.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.17.0(eslint@9.8.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.8.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -19679,49 +19257,22 @@ snapshots:
       semver: 6.3.1
       tsconfig-paths: 3.15.0
     optionalDependencies:
-      '@typescript-eslint/parser': 7.15.0(eslint@9.6.0)(typescript@5.5.3)
+      '@typescript-eslint/parser': 7.17.0(eslint@9.8.0)(typescript@5.5.4)
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint@9.7.0):
+  eslint-plugin-vue@9.27.0(eslint@9.8.0):
     dependencies:
-      array-includes: 3.1.7
-      array.prototype.findlastindex: 1.2.3
-      array.prototype.flat: 1.3.2
-      array.prototype.flatmap: 1.3.2
-      debug: 3.2.7(supports-color@8.1.1)
-      doctrine: 2.1.0
-      eslint: 9.7.0
-      eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.15.0(eslint@9.7.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.7.0)
-      hasown: 2.0.0
-      is-core-module: 2.13.1
-      is-glob: 4.0.3
-      minimatch: 3.1.2
-      object.fromentries: 2.0.7
-      object.groupby: 1.0.1
-      object.values: 1.1.7
-      semver: 6.3.1
-      tsconfig-paths: 3.15.0
-    optionalDependencies:
-      '@typescript-eslint/parser': 7.15.0(eslint@9.7.0)(typescript@5.5.3)
-    transitivePeerDependencies:
-      - eslint-import-resolver-typescript
-      - eslint-import-resolver-webpack
-      - supports-color
-
-  eslint-plugin-vue@9.26.0(eslint@9.7.0):
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
-      eslint: 9.7.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0)
+      eslint: 9.8.0
       globals: 13.24.0
       natural-compare: 1.4.0
       nth-check: 2.1.1
       postcss-selector-parser: 6.0.16
       semver: 7.6.0
-      vue-eslint-parser: 9.4.3(eslint@9.7.0)
+      vue-eslint-parser: 9.4.3(eslint@9.8.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -19733,11 +19284,6 @@ snapshots:
       esrecurse: 4.3.0
       estraverse: 5.3.0
 
-  eslint-scope@8.0.1:
-    dependencies:
-      esrecurse: 4.3.0
-      estraverse: 5.3.0
-
   eslint-scope@8.0.2:
     dependencies:
       esrecurse: 4.3.0
@@ -19747,52 +19293,13 @@ snapshots:
 
   eslint-visitor-keys@4.0.0: {}
 
-  eslint@9.6.0:
+  eslint@9.8.0:
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
-      '@eslint-community/regexpp': 4.10.0
-      '@eslint/config-array': 0.17.0
-      '@eslint/eslintrc': 3.1.0
-      '@eslint/js': 9.6.0
-      '@humanwhocodes/module-importer': 1.0.1
-      '@humanwhocodes/retry': 0.3.0
-      '@nodelib/fs.walk': 1.2.8
-      ajv: 6.12.6
-      chalk: 4.1.2
-      cross-spawn: 7.0.3
-      debug: 4.3.5(supports-color@8.1.1)
-      escape-string-regexp: 4.0.0
-      eslint-scope: 8.0.1
-      eslint-visitor-keys: 4.0.0
-      espree: 10.1.0
-      esquery: 1.5.0
-      esutils: 2.0.3
-      fast-deep-equal: 3.1.3
-      file-entry-cache: 8.0.0
-      find-up: 5.0.0
-      glob-parent: 6.0.2
-      ignore: 5.3.1
-      imurmurhash: 0.1.4
-      is-glob: 4.0.3
-      is-path-inside: 3.0.3
-      json-stable-stringify-without-jsonify: 1.0.1
-      levn: 0.4.1
-      lodash.merge: 4.6.2
-      minimatch: 3.1.2
-      natural-compare: 1.4.0
-      optionator: 0.9.3
-      strip-ansi: 6.0.1
-      text-table: 0.2.0
-    transitivePeerDependencies:
-      - supports-color
-
-  eslint@9.7.0:
-    dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@9.7.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0)
       '@eslint-community/regexpp': 4.11.0
-      '@eslint/config-array': 0.17.0
+      '@eslint/config-array': 0.17.1
       '@eslint/eslintrc': 3.1.0
-      '@eslint/js': 9.7.0
+      '@eslint/js': 9.8.0
       '@humanwhocodes/module-importer': 1.0.1
       '@humanwhocodes/retry': 0.3.0
       '@nodelib/fs.walk': 1.2.8
@@ -19827,14 +19334,14 @@ snapshots:
 
   espree@10.1.0:
     dependencies:
-      acorn: 8.12.0
-      acorn-jsx: 5.3.2(acorn@8.12.0)
+      acorn: 8.12.1
+      acorn-jsx: 5.3.2(acorn@8.12.1)
       eslint-visitor-keys: 4.0.0
 
   espree@9.6.1:
     dependencies:
-      acorn: 8.12.0
-      acorn-jsx: 5.3.2(acorn@8.12.0)
+      acorn: 8.12.1
+      acorn-jsx: 5.3.2(acorn@8.12.1)
       eslint-visitor-keys: 3.4.3
 
   esprima@4.0.1: {}
@@ -19843,10 +19350,6 @@ snapshots:
     dependencies:
       estraverse: 5.3.0
 
-  esquery@1.5.0:
-    dependencies:
-      estraverse: 5.3.0
-
   esquery@1.6.0:
     dependencies:
       estraverse: 5.3.0
@@ -19928,7 +19431,7 @@ snapshots:
       human-signals: 3.0.1
       is-stream: 3.0.0
       merge-stream: 2.0.0
-      npm-run-path: 5.1.0
+      npm-run-path: 5.3.0
       onetime: 6.0.0
       signal-exit: 3.0.7
       strip-final-newline: 3.0.0
@@ -19945,7 +19448,7 @@ snapshots:
       signal-exit: 4.1.0
       strip-final-newline: 3.0.0
 
-  execa@9.2.0:
+  execa@9.3.0:
     dependencies:
       '@sindresorhus/merge-streams': 4.0.0
       cross-spawn: 7.0.3
@@ -20056,8 +19559,8 @@ snapshots:
   fast-json-stringify@5.8.0:
     dependencies:
       '@fastify/deepmerge': 1.3.0
-      ajv: 8.16.0
-      ajv-formats: 2.1.1(ajv@8.16.0)
+      ajv: 8.17.1
+      ajv-formats: 2.1.1(ajv@8.17.1)
       fast-deep-equal: 3.1.3
       fast-uri: 2.2.0
       rfdc: 1.3.0
@@ -20074,6 +19577,8 @@ snapshots:
 
   fast-uri@2.2.0: {}
 
+  fast-uri@3.0.1: {}
+
   fast-xml-parser@4.2.5:
     dependencies:
       strnum: 1.0.5
@@ -20115,6 +19620,10 @@ snapshots:
     dependencies:
       bser: 2.1.1
 
+  fd-package-json@1.2.0:
+    dependencies:
+      walk-up-path: 3.0.1
+
   fd-slicer@1.1.0:
     dependencies:
       pend: 1.2.0
@@ -20153,11 +19662,11 @@ snapshots:
       strtok3: 7.0.0
       token-types: 5.0.1
 
-  file-type@19.0.0:
+  file-type@19.3.0:
     dependencies:
-      readable-web-to-node-stream: 3.0.2
-      strtok3: 7.0.0
-      token-types: 5.0.1
+      strtok3: 8.0.1
+      token-types: 6.0.0
+      uint8array-extras: 1.4.0
 
   filelist@1.0.4:
     dependencies:
@@ -20293,8 +19802,6 @@ snapshots:
 
   from@0.1.7: {}
 
-  fs-constants@1.0.0: {}
-
   fs-extra@11.1.1:
     dependencies:
       graceful-fs: 4.2.11
@@ -20320,6 +19827,11 @@ snapshots:
       jsonfile: 6.1.0
       universalify: 2.0.0
 
+  fs-minipass@1.2.7:
+    dependencies:
+      minipass: 2.9.0
+    optional: true
+
   fs-minipass@2.1.0:
     dependencies:
       minipass: 3.3.6
@@ -20370,10 +19882,6 @@ snapshots:
       has-proto: 1.0.1
       has-symbols: 1.0.3
 
-  get-nonce@1.0.1: {}
-
-  get-npm-tarball-url@2.0.3: {}
-
   get-package-type@0.1.0: {}
 
   get-pixels-frame-info-update@3.3.2:
@@ -20463,14 +19971,6 @@ snapshots:
       minipass: 7.0.4
       path-scurry: 1.10.1
 
-  glob@10.3.12:
-    dependencies:
-      foreground-child: 3.1.1
-      jackspeak: 2.3.6
-      minimatch: 9.0.3
-      minipass: 7.0.4
-      path-scurry: 1.10.2
-
   glob@10.4.2:
     dependencies:
       foreground-child: 3.1.1
@@ -20480,6 +19980,15 @@ snapshots:
       package-json-from-dist: 1.0.0
       path-scurry: 1.11.1
 
+  glob@11.0.0:
+    dependencies:
+      foreground-child: 3.1.1
+      jackspeak: 4.0.1
+      minimatch: 10.0.1
+      minipass: 7.1.2
+      package-json-from-dist: 1.0.0
+      path-scurry: 2.0.0
+
   glob@7.2.3:
     dependencies:
       fs.realpath: 1.0.0
@@ -20509,7 +20018,7 @@ snapshots:
 
   globals@14.0.0: {}
 
-  globals@15.7.0: {}
+  globals@15.8.0: {}
 
   globalthis@1.0.3:
     dependencies:
@@ -20568,15 +20077,14 @@ snapshots:
       p-cancelable: 3.0.0
       responselike: 3.0.0
 
-  got@14.4.1:
+  got@14.4.2:
     dependencies:
-      '@sindresorhus/is': 6.3.1
+      '@sindresorhus/is': 7.0.0
       '@szmarczak/http-timer': 5.0.1
       cacheable-lookup: 7.0.0
       cacheable-request: 12.0.1
       decompress-response: 6.0.0
       form-data-encoder: 4.0.2
-      get-stream: 8.0.1
       http2-wrapper: 2.2.1
       lowercase-keys: 3.0.0
       p-cancelable: 4.0.1
@@ -20591,15 +20099,6 @@ snapshots:
 
   graphql@16.8.1: {}
 
-  gunzip-maybe@1.4.2:
-    dependencies:
-      browserify-zlib: 0.1.4
-      is-deflate: 1.0.0
-      is-gzip: 1.0.0
-      peek-stream: 1.1.3
-      pumpify: 1.5.1
-      through2: 2.0.5
-
   hammerjs@2.0.8: {}
 
   handlebars@4.7.7:
@@ -20721,17 +20220,10 @@ snapshots:
 
   http-link-header@1.1.3: {}
 
-  http-proxy-agent@7.0.0:
-    dependencies:
-      agent-base: 7.1.0
-      debug: 4.3.5(supports-color@8.1.1)
-    transitivePeerDependencies:
-      - supports-color
-
   http-proxy-agent@7.0.2:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.5(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -20784,7 +20276,14 @@ snapshots:
   https-proxy-agent@7.0.4:
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.5(supports-color@8.1.1)
+    transitivePeerDependencies:
+      - supports-color
+
+  https-proxy-agent@7.0.5:
+    dependencies:
+      agent-base: 7.1.0
+      debug: 4.3.5(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -20812,9 +20311,9 @@ snapshots:
 
   ignore-by-default@1.0.1: {}
 
-  ignore-walk@6.0.4:
+  ignore-walk@6.0.5:
     dependencies:
-      minimatch: 9.0.3
+      minimatch: 9.0.4
 
   ignore@5.2.4: {}
 
@@ -20827,21 +20326,21 @@ snapshots:
       parent-module: 1.0.1
       resolve-from: 4.0.0
 
-  import-in-the-middle@1.4.2:
+  import-in-the-middle@1.10.0:
     dependencies:
-      acorn: 8.12.0
-      acorn-import-assertions: 1.9.0(acorn@8.12.0)
+      acorn: 8.12.1
+      acorn-import-attributes: 1.9.5(acorn@8.12.1)
+      cjs-module-lexer: 1.2.2
+      module-details-from-path: 1.0.3
+
+  import-in-the-middle@1.7.1:
+    dependencies:
+      acorn: 8.12.1
+      acorn-import-assertions: 1.9.0(acorn@8.12.1)
       cjs-module-lexer: 1.2.2
       module-details-from-path: 1.0.3
     optional: true
 
-  import-in-the-middle@1.8.1:
-    dependencies:
-      acorn: 8.12.0
-      acorn-import-attributes: 1.9.5(acorn@8.12.0)
-      cjs-module-lexer: 1.2.2
-      module-details-from-path: 1.0.3
-
   import-lazy@4.0.0: {}
 
   import-local@3.1.0:
@@ -20878,10 +20377,6 @@ snapshots:
 
   intersection-observer@0.12.2: {}
 
-  invariant@2.2.4:
-    dependencies:
-      loose-envify: 1.4.0
-
   ioredis@5.4.1:
     dependencies:
       '@ioredis/commands': 1.2.0
@@ -20963,8 +20458,6 @@ snapshots:
     dependencies:
       has-tostringtag: 1.0.0
 
-  is-deflate@1.0.0: {}
-
   is-docker@2.2.1: {}
 
   is-expression@4.0.0:
@@ -20988,8 +20481,6 @@ snapshots:
     dependencies:
       is-extglob: 2.1.1
 
-  is-gzip@1.0.0: {}
-
   is-installed-globally@0.4.0:
     dependencies:
       global-dirs: 3.0.1
@@ -21173,6 +20664,12 @@ snapshots:
     optionalDependencies:
       '@pkgjs/parseargs': 0.11.0
 
+  jackspeak@4.0.1:
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+
   jake@10.8.5:
     dependencies:
       async: 3.2.4
@@ -21192,7 +20689,7 @@ snapshots:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -21212,16 +20709,16 @@ snapshots:
       - babel-plugin-macros
       - supports-color
 
-  jest-cli@29.7.0(@types/node@20.14.9):
+  jest-cli@29.7.0(@types/node@20.14.12):
     dependencies:
       '@jest/core': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
       chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.14.9)
+      create-jest: 29.7.0(@types/node@20.14.12)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.14.9)
+      jest-config: 29.7.0(@types/node@20.14.12)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -21231,7 +20728,7 @@ snapshots:
       - supports-color
       - ts-node
 
-  jest-config@29.7.0(@types/node@20.14.9):
+  jest-config@29.7.0(@types/node@20.14.12):
     dependencies:
       '@babel/core': 7.23.5
       '@jest/test-sequencer': 29.7.0
@@ -21256,7 +20753,7 @@ snapshots:
       slash: 3.0.0
       strip-json-comments: 3.1.1
     optionalDependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
     transitivePeerDependencies:
       - babel-plugin-macros
       - supports-color
@@ -21285,7 +20782,7 @@ snapshots:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       jest-mock: 29.7.0
       jest-util: 29.7.0
 
@@ -21302,7 +20799,7 @@ snapshots:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -21341,7 +20838,7 @@ snapshots:
   jest-mock@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       jest-util: 29.7.0
 
   jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -21376,7 +20873,7 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -21404,7 +20901,7 @@ snapshots:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -21450,7 +20947,7 @@ snapshots:
   jest-util@29.7.0:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -21469,7 +20966,7 @@ snapshots:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -21483,17 +20980,17 @@ snapshots:
 
   jest-worker@29.7.0:
     dependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
-  jest@29.7.0(@types/node@20.14.9):
+  jest@29.7.0(@types/node@20.14.12):
     dependencies:
       '@jest/core': 29.7.0
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.14.9)
+      jest-cli: 29.7.0(@types/node@20.14.12)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -21567,7 +21064,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4):
+  jsdom@24.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3):
     dependencies:
       cssstyle: 4.0.1
       data-urls: 5.0.0
@@ -21575,9 +21072,9 @@ snapshots:
       form-data: 4.0.0
       html-encoding-sniffer: 4.0.0
       http-proxy-agent: 7.0.2
-      https-proxy-agent: 7.0.4
+      https-proxy-agent: 7.0.5
       is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.10
+      nwsapi: 2.2.12
       parse5: 7.1.2
       rrweb-cssom: 0.7.1
       saxes: 6.0.0
@@ -21588,13 +21085,42 @@ snapshots:
       whatwg-encoding: 3.1.1
       whatwg-mimetype: 4.0.0
       whatwg-url: 14.0.0
-      ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      ws: 8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xml-name-validator: 5.0.0
     transitivePeerDependencies:
       - bufferutil
       - supports-color
       - utf-8-validate
 
+  jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4):
+    dependencies:
+      cssstyle: 4.0.1
+      data-urls: 5.0.0
+      decimal.js: 10.4.3
+      form-data: 4.0.0
+      html-encoding-sniffer: 4.0.0
+      http-proxy-agent: 7.0.2
+      https-proxy-agent: 7.0.5
+      is-potential-custom-element-name: 1.0.1
+      nwsapi: 2.2.12
+      parse5: 7.1.2
+      rrweb-cssom: 0.7.1
+      saxes: 6.0.0
+      symbol-tree: 3.2.4
+      tough-cookie: 4.1.4
+      w3c-xmlserializer: 5.0.0
+      webidl-conversions: 7.0.0
+      whatwg-encoding: 3.1.1
+      whatwg-mimetype: 4.0.0
+      whatwg-url: 14.0.0
+      ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      xml-name-validator: 5.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    optional: true
+
   jsesc@0.5.0: {}
 
   jsesc@2.5.2: {}
@@ -21817,6 +21343,8 @@ snapshots:
 
   lru-cache@10.2.2: {}
 
+  lru-cache@11.0.0: {}
+
   lru-cache@4.1.5:
     dependencies:
       pseudomap: 1.0.2
@@ -21895,7 +21423,7 @@ snapshots:
 
   markdown-table@3.0.3: {}
 
-  markdown-to-jsx@7.3.2(react@18.3.1):
+  markdown-to-jsx@7.4.7(react@18.3.1):
     dependencies:
       react: 18.3.1
 
@@ -22269,6 +21797,10 @@ snapshots:
 
   minimalistic-assert@1.0.1: {}
 
+  minimatch@10.0.1:
+    dependencies:
+      brace-expansion: 2.0.1
+
   minimatch@3.0.8:
     dependencies:
       brace-expansion: 1.1.11
@@ -22325,6 +21857,12 @@ snapshots:
     dependencies:
       minipass: 3.3.6
 
+  minipass@2.9.0:
+    dependencies:
+      safe-buffer: 5.2.1
+      yallist: 3.1.1
+    optional: true
+
   minipass@3.3.6:
     dependencies:
       yallist: 4.0.0
@@ -22335,13 +21873,16 @@ snapshots:
 
   minipass@7.1.2: {}
 
+  minizlib@1.3.3:
+    dependencies:
+      minipass: 2.9.0
+    optional: true
+
   minizlib@2.1.2:
     dependencies:
       minipass: 3.3.6
       yallist: 4.0.0
 
-  mkdirp-classic@0.5.3: {}
-
   mkdirp@0.5.6:
     dependencies:
       minimist: 1.2.8
@@ -22352,7 +21893,7 @@ snapshots:
 
   mlly@1.5.0:
     dependencies:
-      acorn: 8.12.0
+      acorn: 8.12.1
       pathe: 1.1.2
       pkg-types: 1.0.3
       ufo: 1.3.2
@@ -22391,17 +21932,17 @@ snapshots:
     optionalDependencies:
       msgpackr-extract: 3.0.2
 
-  msw-storybook-addon@2.0.2(msw@2.3.1(typescript@5.5.3)):
+  msw-storybook-addon@2.0.3(msw@2.3.4(typescript@5.5.4)):
     dependencies:
       is-node-process: 1.2.0
-      msw: 2.3.1(typescript@5.5.3)
+      msw: 2.3.4(typescript@5.5.4)
 
-  msw@2.3.1(typescript@5.5.3):
+  msw@2.3.4(typescript@5.5.4):
     dependencies:
       '@bundled-es-modules/cookie': 2.0.0
       '@bundled-es-modules/statuses': 1.0.1
+      '@bundled-es-modules/tough-cookie': 0.1.6
       '@inquirer/confirm': 3.1.6
-      '@mswjs/cookies': 1.1.0
       '@mswjs/interceptors': 0.29.1
       '@open-draft/until': 2.1.0
       '@types/cookie': 0.6.0
@@ -22413,10 +21954,10 @@ snapshots:
       outvariant: 1.4.2
       path-to-regexp: 6.2.1
       strict-event-emitter: 0.5.1
-      type-fest: 4.9.0
+      type-fest: 4.20.1
       yargs: 17.7.2
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
   muggle-string@0.4.1: {}
 
@@ -22520,12 +22061,6 @@ snapshots:
 
   node-fetch-native@1.0.2: {}
 
-  node-fetch@2.6.11(encoding@0.1.13):
-    dependencies:
-      whatwg-url: 5.0.0
-    optionalDependencies:
-      encoding: 0.1.13
-
   node-fetch@2.6.13(encoding@0.1.13):
     dependencies:
       whatwg-url: 5.0.0
@@ -22554,7 +22089,7 @@ snapshots:
     dependencies:
       env-paths: 2.2.1
       exponential-backoff: 3.1.1
-      glob: 10.4.2
+      glob: 10.3.10
       graceful-fs: 4.2.11
       make-fetch-happen: 13.0.0
       nopt: 7.2.0
@@ -22662,16 +22197,16 @@ snapshots:
       set-blocking: 2.0.0
     optional: true
 
-  nsfwjs@2.4.2(@tensorflow/tfjs@4.20.0(encoding@0.1.13)(seedrandom@3.0.5)):
+  nsfwjs@2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)):
     dependencies:
       '@nsfw-filter/gif-frames': 1.0.2
-      '@tensorflow/tfjs': 4.20.0(encoding@0.1.13)(seedrandom@3.0.5)
+      '@tensorflow/tfjs': 4.4.0(encoding@0.1.13)(seedrandom@3.0.5)
 
   nth-check@2.1.1:
     dependencies:
       boolbase: 1.0.0
 
-  nwsapi@2.2.10: {}
+  nwsapi@2.2.12: {}
 
   oauth-sign@0.9.0: {}
 
@@ -22769,24 +22304,15 @@ snapshots:
       undici: 5.28.2
       yargs-parser: 21.1.1
 
-  opentelemetry-instrumentation-fetch-node@1.2.0:
+  opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0):
     dependencies:
       '@opentelemetry/api': 1.9.0
-      '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.9.0)
+      '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0)
       '@opentelemetry/semantic-conventions': 1.25.1
     transitivePeerDependencies:
       - supports-color
     optional: true
 
-  optionator@0.9.3:
-    dependencies:
-      '@aashutoshrathi/word-wrap': 1.2.6
-      deep-is: 0.1.4
-      fast-levenshtein: 2.0.6
-      levn: 0.4.1
-      prelude-ls: 1.2.1
-      type-check: 0.4.0
-
   optionator@0.9.4:
     dependencies:
       deep-is: 0.1.4
@@ -22871,8 +22397,6 @@ snapshots:
 
   package-json-from-dist@1.0.0: {}
 
-  pako@0.2.9: {}
-
   parent-module@1.0.1:
     dependencies:
       callsites: 3.1.0
@@ -22883,7 +22407,7 @@ snapshots:
 
   parse-json@5.2.0:
     dependencies:
-      '@babel/code-frame': 7.23.5
+      '@babel/code-frame': 7.24.7
       error-ex: 1.3.2
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
@@ -22932,16 +22456,16 @@ snapshots:
       lru-cache: 10.0.2
       minipass: 7.0.4
 
-  path-scurry@1.10.2:
-    dependencies:
-      lru-cache: 10.2.2
-      minipass: 7.0.4
-
   path-scurry@1.11.1:
     dependencies:
       lru-cache: 10.2.2
       minipass: 7.1.2
 
+  path-scurry@2.0.0:
+    dependencies:
+      lru-cache: 11.0.0
+      minipass: 7.1.2
+
   path-to-regexp@0.1.7: {}
 
   path-to-regexp@1.8.0:
@@ -22966,11 +22490,7 @@ snapshots:
 
   peek-readable@5.0.0: {}
 
-  peek-stream@1.1.3:
-    dependencies:
-      buffer-from: 1.1.2
-      duplexify: 3.7.1
-      through2: 2.0.5
+  peek-readable@5.1.3: {}
 
   pend@1.2.0: {}
 
@@ -23027,6 +22547,8 @@ snapshots:
 
   picocolors@1.0.0: {}
 
+  picocolors@1.0.1: {}
+
   picomatch@2.3.1: {}
 
   pid-port@1.0.0:
@@ -23102,140 +22624,140 @@ snapshots:
     dependencies:
       '@babel/runtime': 7.23.4
 
-  postcss-calc@9.0.1(postcss@8.4.38):
+  postcss-calc@9.0.1(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-selector-parser: 6.0.15
       postcss-value-parser: 4.2.0
 
-  postcss-colormin@6.1.0(postcss@8.4.38):
+  postcss-colormin@6.1.0(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
       caniuse-api: 3.0.0
       colord: 2.9.3
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-convert-values@6.1.0(postcss@8.4.38):
+  postcss-convert-values@6.1.0(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-discard-comments@6.0.2(postcss@8.4.38):
+  postcss-discard-comments@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
 
-  postcss-discard-duplicates@6.0.3(postcss@8.4.38):
+  postcss-discard-duplicates@6.0.3(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
 
-  postcss-discard-empty@6.0.3(postcss@8.4.38):
+  postcss-discard-empty@6.0.3(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
 
-  postcss-discard-overridden@6.0.2(postcss@8.4.38):
+  postcss-discard-overridden@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
 
-  postcss-merge-longhand@6.0.5(postcss@8.4.38):
+  postcss-merge-longhand@6.0.5(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
-      stylehacks: 6.1.1(postcss@8.4.38)
+      stylehacks: 6.1.1(postcss@8.4.40)
 
-  postcss-merge-rules@6.1.1(postcss@8.4.38):
+  postcss-merge-rules@6.1.1(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
       caniuse-api: 3.0.0
-      cssnano-utils: 4.0.2(postcss@8.4.38)
-      postcss: 8.4.38
+      cssnano-utils: 4.0.2(postcss@8.4.40)
+      postcss: 8.4.40
       postcss-selector-parser: 6.0.16
 
-  postcss-minify-font-values@6.1.0(postcss@8.4.38):
+  postcss-minify-font-values@6.1.0(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-minify-gradients@6.0.3(postcss@8.4.38):
+  postcss-minify-gradients@6.0.3(postcss@8.4.40):
     dependencies:
       colord: 2.9.3
-      cssnano-utils: 4.0.2(postcss@8.4.38)
-      postcss: 8.4.38
+      cssnano-utils: 4.0.2(postcss@8.4.40)
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-minify-params@6.1.0(postcss@8.4.38):
+  postcss-minify-params@6.1.0(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
-      cssnano-utils: 4.0.2(postcss@8.4.38)
-      postcss: 8.4.38
+      cssnano-utils: 4.0.2(postcss@8.4.40)
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-minify-selectors@6.0.4(postcss@8.4.38):
+  postcss-minify-selectors@6.0.4(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-selector-parser: 6.0.16
 
-  postcss-normalize-charset@6.0.2(postcss@8.4.38):
+  postcss-normalize-charset@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
 
-  postcss-normalize-display-values@6.0.2(postcss@8.4.38):
+  postcss-normalize-display-values@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-normalize-positions@6.0.2(postcss@8.4.38):
+  postcss-normalize-positions@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-normalize-repeat-style@6.0.2(postcss@8.4.38):
+  postcss-normalize-repeat-style@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-normalize-string@6.0.2(postcss@8.4.38):
+  postcss-normalize-string@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-normalize-timing-functions@6.0.2(postcss@8.4.38):
+  postcss-normalize-timing-functions@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-normalize-unicode@6.1.0(postcss@8.4.38):
+  postcss-normalize-unicode@6.1.0(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-normalize-url@6.0.2(postcss@8.4.38):
+  postcss-normalize-url@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-normalize-whitespace@6.0.2(postcss@8.4.38):
+  postcss-normalize-whitespace@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-ordered-values@6.0.2(postcss@8.4.38):
+  postcss-ordered-values@6.0.2(postcss@8.4.40):
     dependencies:
-      cssnano-utils: 4.0.2(postcss@8.4.38)
-      postcss: 8.4.38
+      cssnano-utils: 4.0.2(postcss@8.4.40)
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
-  postcss-reduce-initial@6.1.0(postcss@8.4.38):
+  postcss-reduce-initial@6.1.0(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
       caniuse-api: 3.0.0
-      postcss: 8.4.38
+      postcss: 8.4.40
 
-  postcss-reduce-transforms@6.0.2(postcss@8.4.38):
+  postcss-reduce-transforms@6.0.2(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
 
   postcss-selector-parser@6.0.15:
@@ -23248,15 +22770,15 @@ snapshots:
       cssesc: 3.0.0
       util-deprecate: 1.0.2
 
-  postcss-svgo@6.0.3(postcss@8.4.38):
+  postcss-svgo@6.0.3(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-value-parser: 4.2.0
       svgo: 3.2.0
 
-  postcss-unique-selectors@6.0.4(postcss@8.4.38):
+  postcss-unique-selectors@6.0.4(postcss@8.4.40):
     dependencies:
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-selector-parser: 6.0.16
 
   postcss-value-parser@4.2.0: {}
@@ -23267,6 +22789,12 @@ snapshots:
       picocolors: 1.0.0
       source-map-js: 1.2.0
 
+  postcss@8.4.40:
+    dependencies:
+      nanoid: 3.3.7
+      picocolors: 1.0.1
+      source-map-js: 1.2.0
+
   postgres-array@2.0.0: {}
 
   postgres-array@3.0.2: {}
@@ -23291,7 +22819,7 @@ snapshots:
 
   prelude-ls@1.2.1: {}
 
-  prettier@3.3.2: {}
+  prettier@3.3.3: {}
 
   pretty-bytes@5.6.0: {}
 
@@ -23461,29 +22989,18 @@ snapshots:
       pug-runtime: 3.0.1
       pug-strip-comments: 2.0.0
 
-  pump@2.0.1:
-    dependencies:
-      end-of-stream: 1.4.4
-      once: 1.4.0
-
   pump@3.0.0:
     dependencies:
       end-of-stream: 1.4.4
       once: 1.4.0
 
-  pumpify@1.5.1:
-    dependencies:
-      duplexify: 3.7.1
-      inherits: 2.0.4
-      pump: 2.0.1
-
   punycode@2.3.1: {}
 
   pure-rand@6.0.0: {}
 
   pvtsutils@1.3.5:
     dependencies:
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   pvutils@1.1.3: {}
 
@@ -23556,9 +23073,9 @@ snapshots:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  react-docgen-typescript@2.2.2(typescript@5.5.3):
+  react-docgen-typescript@2.2.2(typescript@5.5.4):
     dependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
   react-docgen@7.0.1:
     dependencies:
@@ -23597,34 +23114,6 @@ snapshots:
 
   react-is@18.2.0: {}
 
-  react-remove-scroll-bar@2.3.6(@types/react@18.0.28)(react@18.3.1):
-    dependencies:
-      react: 18.3.1
-      react-style-singleton: 2.2.1(@types/react@18.0.28)(react@18.3.1)
-      tslib: 2.6.2
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  react-remove-scroll@2.5.7(@types/react@18.0.28)(react@18.3.1):
-    dependencies:
-      react: 18.3.1
-      react-remove-scroll-bar: 2.3.6(@types/react@18.0.28)(react@18.3.1)
-      react-style-singleton: 2.2.1(@types/react@18.0.28)(react@18.3.1)
-      tslib: 2.6.2
-      use-callback-ref: 1.3.2(@types/react@18.0.28)(react@18.3.1)
-      use-sidecar: 1.1.2(@types/react@18.0.28)(react@18.3.1)
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  react-style-singleton@2.2.1(@types/react@18.0.28)(react@18.3.1):
-    dependencies:
-      get-nonce: 1.0.1
-      invariant: 2.2.4
-      react: 18.3.1
-      tslib: 2.6.2
-    optionalDependencies:
-      '@types/react': 18.0.28
-
   react@18.3.1:
     dependencies:
       loose-envify: 1.4.0
@@ -23692,7 +23181,7 @@ snapshots:
       esprima: 4.0.1
       source-map: 0.6.1
       tiny-invariant: 1.3.3
-      tslib: 2.6.2
+      tslib: 2.6.3
 
   reconnecting-websocket@4.4.0: {}
 
@@ -23895,26 +23384,26 @@ snapshots:
       glob: 7.2.3
     optional: true
 
-  rollup@4.18.0:
+  rollup@4.19.1:
     dependencies:
       '@types/estree': 1.0.5
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.18.0
-      '@rollup/rollup-android-arm64': 4.18.0
-      '@rollup/rollup-darwin-arm64': 4.18.0
-      '@rollup/rollup-darwin-x64': 4.18.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.18.0
-      '@rollup/rollup-linux-arm-musleabihf': 4.18.0
-      '@rollup/rollup-linux-arm64-gnu': 4.18.0
-      '@rollup/rollup-linux-arm64-musl': 4.18.0
-      '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0
-      '@rollup/rollup-linux-riscv64-gnu': 4.18.0
-      '@rollup/rollup-linux-s390x-gnu': 4.18.0
-      '@rollup/rollup-linux-x64-gnu': 4.18.0
-      '@rollup/rollup-linux-x64-musl': 4.18.0
-      '@rollup/rollup-win32-arm64-msvc': 4.18.0
-      '@rollup/rollup-win32-ia32-msvc': 4.18.0
-      '@rollup/rollup-win32-x64-msvc': 4.18.0
+      '@rollup/rollup-android-arm-eabi': 4.19.1
+      '@rollup/rollup-android-arm64': 4.19.1
+      '@rollup/rollup-darwin-arm64': 4.19.1
+      '@rollup/rollup-darwin-x64': 4.19.1
+      '@rollup/rollup-linux-arm-gnueabihf': 4.19.1
+      '@rollup/rollup-linux-arm-musleabihf': 4.19.1
+      '@rollup/rollup-linux-arm64-gnu': 4.19.1
+      '@rollup/rollup-linux-arm64-musl': 4.19.1
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.19.1
+      '@rollup/rollup-linux-riscv64-gnu': 4.19.1
+      '@rollup/rollup-linux-s390x-gnu': 4.19.1
+      '@rollup/rollup-linux-x64-gnu': 4.19.1
+      '@rollup/rollup-linux-x64-musl': 4.19.1
+      '@rollup/rollup-win32-arm64-msvc': 4.19.1
+      '@rollup/rollup-win32-ia32-msvc': 4.19.1
+      '@rollup/rollup-win32-x64-msvc': 4.19.1
       fsevents: 2.3.3
 
   rrweb-cssom@0.6.0: {}
@@ -23968,7 +23457,7 @@ snapshots:
       parse-srcset: 1.0.2
       postcss: 8.4.38
 
-  sass@1.77.6:
+  sass@1.77.8:
     dependencies:
       chokidar: 3.5.3
       immutable: 4.2.2
@@ -24088,9 +23577,10 @@ snapshots:
 
   shebang-regex@3.0.0: {}
 
-  shiki@1.10.0:
+  shiki@1.12.0:
     dependencies:
-      '@shikijs/core': 1.10.0
+      '@shikijs/core': 1.12.0
+      '@types/hast': 3.0.4
 
   shimmer@1.2.1: {}
 
@@ -24106,11 +23596,11 @@ snapshots:
 
   signal-exit@4.1.0: {}
 
-  simple-oauth2@5.0.1:
+  simple-oauth2@5.1.0:
     dependencies:
       '@hapi/hoek': 11.0.4
       '@hapi/wreck': 18.0.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.5(supports-color@8.1.1)
       joi: 17.11.0
     transitivePeerDependencies:
       - supports-color
@@ -24325,28 +23815,52 @@ snapshots:
 
   store2@2.14.2: {}
 
-  storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@8.1.11)(@storybook/manager-api@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/preview-api@8.1.11)(@storybook/theming@8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/types@8.1.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+  storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(3rvqj7p7l43ansgshs3zbslm7u):
     dependencies:
-      '@storybook/blocks': 8.1.11(@types/react@18.0.28)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/components': 8.1.11(@types/react@18.0.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/core-events': 8.1.11
-      '@storybook/manager-api': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/preview-api': 8.1.11
-      '@storybook/theming': 8.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
-      '@storybook/types': 8.1.11
+      '@storybook/blocks': 8.2.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/components': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/core-events': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/manager-api': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/preview-api': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/theming': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+      '@storybook/types': 8.2.6(storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4))
     optionalDependencies:
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
 
-  storybook@8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4):
+  storybook@8.2.6(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(utf-8-validate@6.0.4):
     dependencies:
-      '@storybook/cli': 8.1.11(@babel/preset-env@7.24.7(@babel/core@7.24.7))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.4)
+      '@babel/core': 7.24.7
+      '@babel/types': 7.24.7
+      '@storybook/codemod': 8.2.6(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      '@storybook/core': 8.2.6(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      '@types/semver': 7.5.8
+      '@yarnpkg/fslib': 2.10.3
+      '@yarnpkg/libzip': 2.3.0
+      chalk: 4.1.2
+      commander: 6.2.1
+      cross-spawn: 7.0.3
+      detect-indent: 6.1.0
+      envinfo: 7.8.1
+      execa: 5.1.1
+      fd-package-json: 1.2.0
+      find-up: 5.0.0
+      fs-extra: 11.1.1
+      giget: 1.1.2
+      globby: 14.0.1
+      jscodeshift: 0.15.1(@babel/preset-env@7.24.7(@babel/core@7.24.7))
+      leven: 3.1.0
+      ora: 5.4.1
+      prettier: 3.3.3
+      prompts: 2.4.2
+      semver: 7.6.0
+      strip-json-comments: 3.1.1
+      tempy: 3.1.0
+      tiny-invariant: 1.3.3
+      ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@babel/preset-env'
       - bufferutil
-      - encoding
-      - react
-      - react-dom
       - supports-color
       - utf-8-validate
 
@@ -24365,8 +23879,6 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  stream-shift@1.0.1: {}
-
   stream-wormhole@1.1.0: {}
 
   streamsearch@1.1.0: {}
@@ -24474,10 +23986,15 @@ snapshots:
       '@tokenizer/token': 0.3.0
       peek-readable: 5.0.0
 
-  stylehacks@6.1.1(postcss@8.4.38):
+  strtok3@8.0.1:
+    dependencies:
+      '@tokenizer/token': 0.3.0
+      peek-readable: 5.1.3
+
+  stylehacks@6.1.1(postcss@8.4.40):
     dependencies:
       browserslist: 4.23.0
-      postcss: 8.4.38
+      postcss: 8.4.40
       postcss-selector-parser: 6.0.16
 
   supports-color@5.5.0:
@@ -24515,27 +24032,23 @@ snapshots:
 
   systeminformation@5.22.11: {}
 
-  tar-fs@2.1.1:
-    dependencies:
-      chownr: 1.1.4
-      mkdirp-classic: 0.5.3
-      pump: 3.0.0
-      tar-stream: 2.2.0
-
-  tar-stream@2.2.0:
-    dependencies:
-      bl: 4.1.0
-      end-of-stream: 1.4.4
-      fs-constants: 1.0.0
-      inherits: 2.0.4
-      readable-stream: 3.6.0
-
   tar-stream@3.1.6:
     dependencies:
       b4a: 1.6.4
       fast-fifo: 1.3.0
       streamx: 2.15.0
 
+  tar@4.4.19:
+    dependencies:
+      chownr: 1.1.4
+      fs-minipass: 1.2.7
+      minipass: 2.9.0
+      minizlib: 1.3.3
+      mkdirp: 0.5.6
+      safe-buffer: 5.2.1
+      yallist: 3.1.1
+    optional: true
+
   tar@6.2.1:
     dependencies:
       chownr: 2.0.0
@@ -24566,20 +24079,12 @@ snapshots:
       type-fest: 2.19.0
       unique-string: 3.0.0
 
-  terser@5.31.1:
-    dependencies:
-      '@jridgewell/source-map': 0.3.5
-      acorn: 8.12.0
-      commander: 2.20.3
-      source-map-support: 0.5.21
-
-  terser@5.31.2:
+  terser@5.31.3:
     dependencies:
       '@jridgewell/source-map': 0.3.6
-      acorn: 8.12.0
+      acorn: 8.12.1
       commander: 2.20.3
       source-map-support: 0.5.21
-    optional: true
 
   test-exclude@6.0.0:
     dependencies:
@@ -24603,17 +24108,12 @@ snapshots:
     dependencies:
       real-require: 0.2.0
 
-  three@0.165.0: {}
+  three@0.167.0: {}
 
   throttle-debounce@5.0.2: {}
 
   throttleit@1.0.0: {}
 
-  through2@2.0.5:
-    dependencies:
-      readable-stream: 2.3.7
-      xtend: 4.0.2
-
   through@2.3.4: {}
 
   through@2.3.8: {}
@@ -24644,8 +24144,6 @@ snapshots:
 
   toad-cache@3.7.0: {}
 
-  tocbot@4.21.1: {}
-
   toidentifier@1.0.1: {}
 
   token-stream@1.0.0: {}
@@ -24655,6 +24153,11 @@ snapshots:
       '@tokenizer/token': 0.3.0
       ieee754: 1.2.1
 
+  token-types@6.0.0:
+    dependencies:
+      '@tokenizer/token': 0.3.0
+      ieee754: 1.2.1
+
   touch@3.1.0:
     dependencies:
       nopt: 1.0.10
@@ -24691,9 +24194,9 @@ snapshots:
     dependencies:
       typescript: 5.3.3
 
-  ts-api-utils@1.3.0(typescript@5.5.3):
+  ts-api-utils@1.3.0(typescript@5.5.4):
     dependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
   ts-case-convert@2.0.2: {}
 
@@ -24774,8 +24277,6 @@ snapshots:
 
   type-fest@4.20.1: {}
 
-  type-fest@4.9.0: {}
-
   type-is@1.6.18:
     dependencies:
       media-typer: 0.3.0
@@ -24837,7 +24338,7 @@ snapshots:
 
   typescript@5.4.2: {}
 
-  typescript@5.5.3: {}
+  typescript@5.5.4: {}
 
   ufo@1.3.2: {}
 
@@ -24850,6 +24351,8 @@ snapshots:
     dependencies:
       '@lukeed/csprng': 1.0.1
 
+  uint8array-extras@1.4.0: {}
+
   ulid@2.3.0: {}
 
   unbox-primitive@1.0.2:
@@ -24935,7 +24438,7 @@ snapshots:
 
   unplugin@1.4.0:
     dependencies:
-      acorn: 8.12.0
+      acorn: 8.12.1
       chokidar: 3.5.3
       webpack-sources: 3.2.3
       webpack-virtual-modules: 0.5.0
@@ -24963,20 +24466,10 @@ snapshots:
       querystringify: 2.2.0
       requires-port: 1.0.0
 
-  use-callback-ref@1.3.2(@types/react@18.0.28)(react@18.3.1):
+  utf-8-validate@6.0.3:
     dependencies:
-      react: 18.3.1
-      tslib: 2.6.2
-    optionalDependencies:
-      '@types/react': 18.0.28
-
-  use-sidecar@1.1.2(@types/react@18.0.28)(react@18.3.1):
-    dependencies:
-      detect-node-es: 1.1.0
-      react: 18.3.1
-      tslib: 2.6.2
-    optionalDependencies:
-      '@types/react': 18.0.28
+      node-gyp-build: 4.6.0
+    optional: true
 
   utf-8-validate@6.0.4:
     dependencies:
@@ -25003,14 +24496,14 @@ snapshots:
 
   uuid@9.0.1: {}
 
-  v-code-diff@1.12.0(vue@3.4.31(typescript@5.5.3)):
+  v-code-diff@1.12.0(vue@3.4.34(typescript@5.5.4)):
     dependencies:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.9.0
-      vue: 3.4.31(typescript@5.5.3)
-      vue-demi: 0.14.7(vue@3.4.31(typescript@5.5.3))
-      vue-i18n: 9.13.1(vue@3.4.31(typescript@5.5.3))
+      vue: 3.4.34(typescript@5.5.4)
+      vue-demi: 0.14.7(vue@3.4.34(typescript@5.5.4))
+      vue-i18n: 9.13.1(vue@3.4.34(typescript@5.5.4))
 
   v8-to-istanbul@9.2.0:
     dependencies:
@@ -25042,13 +24535,13 @@ snapshots:
       unist-util-stringify-position: 4.0.0
       vfile-message: 4.0.2
 
-  vite-node@1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2):
+  vite-node@1.6.0(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3):
     dependencies:
       cac: 6.7.14
       debug: 4.3.5(supports-color@8.1.1)
       pathe: 1.1.2
       picocolors: 1.0.0
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -25061,25 +24554,25 @@ snapshots:
 
   vite-plugin-turbosnap@1.0.3: {}
 
-  vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2):
+  vite@5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3):
     dependencies:
       esbuild: 0.21.5
-      postcss: 8.4.38
-      rollup: 4.18.0
+      postcss: 8.4.40
+      rollup: 4.19.1
     optionalDependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       fsevents: 2.3.3
-      sass: 1.77.6
-      terser: 5.31.2
+      sass: 1.77.8
+      terser: 5.31.3
 
-  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)):
+  vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3)):
     dependencies:
       cross-fetch: 3.1.6(encoding@0.1.13)
-      vitest: 1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2)
+      vitest: 1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3)
     transitivePeerDependencies:
       - encoding
 
-  vitest@1.6.0(@types/node@20.14.9)(happy-dom@10.0.3)(jsdom@24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.6)(terser@5.31.2):
+  vitest@1.6.0(@types/node@20.14.12)(happy-dom@10.0.3)(jsdom@24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4))(sass@1.77.8)(terser@5.31.3):
     dependencies:
       '@vitest/expect': 1.6.0
       '@vitest/runner': 1.6.0
@@ -25098,13 +24591,13 @@ snapshots:
       strip-literal: 2.1.0
       tinybench: 2.6.0
       tinypool: 0.8.4
-      vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
-      vite-node: 1.6.0(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.2)
+      vite: 5.3.5(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
+      vite-node: 1.6.0(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
       why-is-node-running: 2.2.2
     optionalDependencies:
-      '@types/node': 20.14.9
+      '@types/node': 20.14.12
       happy-dom: 10.0.3
-      jsdom: 24.1.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+      jsdom: 24.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -25139,46 +24632,44 @@ snapshots:
 
   vscode-uri@3.0.8: {}
 
-  vue-component-meta@2.0.16(typescript@5.5.3):
+  vue-component-meta@2.0.16(typescript@5.5.4):
     dependencies:
       '@volar/typescript': 2.2.0
-      '@vue/language-core': 2.0.16(typescript@5.5.3)
+      '@vue/language-core': 2.0.16(typescript@5.5.4)
       path-browserify: 1.0.1
       vue-component-type-helpers: 2.0.16
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
   vue-component-type-helpers@1.8.4: {}
 
   vue-component-type-helpers@2.0.16: {}
 
-  vue-component-type-helpers@2.0.24: {}
+  vue-component-type-helpers@2.0.29: {}
 
-  vue-component-type-helpers@2.0.26: {}
-
-  vue-demi@0.14.7(vue@3.4.31(typescript@5.5.3)):
+  vue-demi@0.14.7(vue@3.4.34(typescript@5.5.4)):
     dependencies:
-      vue: 3.4.31(typescript@5.5.3)
+      vue: 3.4.34(typescript@5.5.4)
 
-  vue-docgen-api@4.75.1(vue@3.4.31(typescript@5.5.3)):
+  vue-docgen-api@4.75.1(vue@3.4.34(typescript@5.5.4)):
     dependencies:
       '@babel/parser': 7.24.7
       '@babel/types': 7.24.7
       '@vue/compiler-dom': 3.4.29
-      '@vue/compiler-sfc': 3.4.31
+      '@vue/compiler-sfc': 3.4.34
       ast-types: 0.16.1
       hash-sum: 2.0.0
       lru-cache: 8.0.4
       pug: 3.0.3
       recast: 0.23.6
       ts-map: 1.0.3
-      vue: 3.4.31(typescript@5.5.3)
-      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.31(typescript@5.5.3))
+      vue: 3.4.34(typescript@5.5.4)
+      vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.4.34(typescript@5.5.4))
 
-  vue-eslint-parser@9.4.3(eslint@9.7.0):
+  vue-eslint-parser@9.4.3(eslint@9.8.0):
     dependencies:
       debug: 4.3.4(supports-color@5.5.0)
-      eslint: 9.7.0
+      eslint: 9.8.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1
@@ -25188,43 +24679,43 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  vue-i18n@9.13.1(vue@3.4.31(typescript@5.5.3)):
+  vue-i18n@9.13.1(vue@3.4.34(typescript@5.5.4)):
     dependencies:
       '@intlify/core-base': 9.13.1
       '@intlify/shared': 9.13.1
       '@vue/devtools-api': 6.6.1
-      vue: 3.4.31(typescript@5.5.3)
+      vue: 3.4.34(typescript@5.5.4)
 
-  vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.31(typescript@5.5.3)):
+  vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.4.34(typescript@5.5.4)):
     dependencies:
-      vue: 3.4.31(typescript@5.5.3)
+      vue: 3.4.34(typescript@5.5.4)
 
   vue-template-compiler@2.7.14:
     dependencies:
       de-indent: 1.0.2
       he: 1.2.0
 
-  vue-tsc@2.0.24(typescript@5.5.3):
+  vue-tsc@2.0.29(typescript@5.5.4):
     dependencies:
-      '@volar/typescript': 2.4.0-alpha.11
-      '@vue/language-core': 2.0.24(typescript@5.5.3)
+      '@volar/typescript': 2.4.0-alpha.18
+      '@vue/language-core': 2.0.29(typescript@5.5.4)
       semver: 7.6.0
-      typescript: 5.5.3
+      typescript: 5.5.4
 
-  vue@3.4.31(typescript@5.5.3):
+  vue@3.4.34(typescript@5.5.4):
     dependencies:
-      '@vue/compiler-dom': 3.4.31
-      '@vue/compiler-sfc': 3.4.31
-      '@vue/runtime-dom': 3.4.31
-      '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3))
-      '@vue/shared': 3.4.31
+      '@vue/compiler-dom': 3.4.34
+      '@vue/compiler-sfc': 3.4.34
+      '@vue/runtime-dom': 3.4.34
+      '@vue/server-renderer': 3.4.34(vue@3.4.34(typescript@5.5.4))
+      '@vue/shared': 3.4.34
     optionalDependencies:
-      typescript: 5.5.3
+      typescript: 5.5.4
 
-  vuedraggable@4.1.0(vue@3.4.31(typescript@5.5.3)):
+  vuedraggable@4.1.0(vue@3.4.34(typescript@5.5.4)):
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.4.31(typescript@5.5.3)
+      vue: 3.4.34(typescript@5.5.4)
 
   w3c-xmlserializer@5.0.0:
     dependencies:
@@ -25240,6 +24731,8 @@ snapshots:
     transitivePeerDependencies:
       - debug
 
+  walk-up-path@3.0.1: {}
+
   walker@1.0.8:
     dependencies:
       makeerror: 1.0.12
@@ -25387,7 +24880,12 @@ snapshots:
       imurmurhash: 0.1.4
       signal-exit: 3.0.7
 
-  ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4):
+  ws@8.18.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+    optionalDependencies:
+      bufferutil: 4.0.7
+      utf-8-validate: 6.0.3
+
+  ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4):
     optionalDependencies:
       bufferutil: 4.0.8
       utf-8-validate: 6.0.4

From 61f4a03e6cd65af4a825f666acd31f4853c5bbad Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Sun, 28 Jul 2024 11:19:32 +0900
Subject: [PATCH 159/206] =?UTF-8?q?Fix(frontend):=20=E4=B8=8B=E6=9B=B8?=
 =?UTF-8?q?=E3=81=8D/=E5=89=8A=E9=99=A4=E3=81=97=E3=81=A6=E7=B7=A8?=
 =?UTF-8?q?=E9=9B=86=E3=81=A7=E4=BF=9D=E6=8C=81=E3=81=95=E3=82=8C=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E9=A0=85=E7=9B=AE=E3=81=8C=E3=81=82=E3=81=A3=E3=81=9F?=
 =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#14285)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore(frontend): reorder assignments

* fix(frontend): visibleUserIds is not kept when deleteAndEdit

* fix(frontend): quoteId is not kept on draft

* fix(frontend): reactionAcceptance is not kept for draft/deleteAndEdit

* docs(changelog): update changelog
---
 CHANGELOG.md                                  |  3 +++
 .../backend/src/models/json-schema/note.ts    |  1 +
 .../frontend/src/components/MkPostForm.vue    | 20 +++++++++++++++----
 packages/misskey-js/src/autogen/types.ts      |  3 ++-
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d48d14c71..4b35034cc8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,9 @@
 - Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/672)
 - Fix: `/share`ページにおいて絵文字ピッカーを開くことができない問題を修正
+- Fix: ダイレクト投稿の"削除して編集"において、宛先が保持されていなかった問題を修正
+- Fix: 投稿フォームへのURL貼り付けによる引用が下書きに保存されていなかった問題を修正
+- Fix: "削除して編集"や下書きにおいて、リアクションの受け入れ設定が保持/保存されていなかった問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index 2641161c8b..61f62dce60 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -204,6 +204,7 @@ export const packedNoteSchema = {
 		reactionAcceptance: {
 			type: 'string',
 			optional: false, nullable: true,
+			enum: ['likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'],
 		},
 		reactionEmojis: {
 			type: 'object',
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index d057d197ec..64ad60ae85 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -367,6 +367,8 @@ function watchForDraft() {
 	watch(files, () => saveDraft(), { deep: true });
 	watch(visibility, () => saveDraft());
 	watch(localOnly, () => saveDraft());
+	watch(quoteId, () => saveDraft());
+	watch(reactionAcceptance, () => saveDraft());
 }
 
 function checkMissingMention() {
@@ -703,6 +705,8 @@ function saveDraft() {
 			files: files.value,
 			poll: poll.value,
 			visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(x => x.id) : undefined,
+			quoteId: quoteId.value,
+			reactionAcceptance: reactionAcceptance.value,
 		},
 	};
 
@@ -991,6 +995,8 @@ onMounted(() => {
 						users.forEach(u => pushVisibleUser(u));
 					});
 				}
+				quoteId.value = draft.data.quoteId;
+				reactionAcceptance.value = draft.data.reactionAcceptance;
 			}
 		}
 
@@ -998,9 +1004,11 @@ onMounted(() => {
 		if (props.initialNote) {
 			const init = props.initialNote;
 			text.value = init.text ? init.text : '';
-			files.value = init.files ?? [];
-			cw.value = init.cw ?? null;
 			useCw.value = init.cw != null;
+			cw.value = init.cw ?? null;
+			visibility.value = init.visibility;
+			localOnly.value = init.localOnly ?? false;
+			files.value = init.files ?? [];
 			if (init.poll) {
 				poll.value = {
 					choices: init.poll.choices.map(x => x.text),
@@ -1009,9 +1017,13 @@ onMounted(() => {
 					expiredAfter: null,
 				};
 			}
-			visibility.value = init.visibility;
-			localOnly.value = init.localOnly ?? false;
+			if (init.visibleUserIds) {
+				misskeyApi('users/show', { userIds: init.visibleUserIds }).then(users => {
+					users.forEach(u => pushVisibleUser(u));
+				});
+			}
 			quoteId.value = init.renote ? init.renote.id : null;
+			reactionAcceptance.value = init.reactionAcceptance;
 		}
 
 		nextTick(() => watchForDraft());
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index d3c857219b..b2b8938baa 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4089,7 +4089,8 @@ export type components = {
         userId: string | null;
       }) | null;
       localOnly?: boolean;
-      reactionAcceptance: string | null;
+      /** @enum {string|null} */
+      reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote';
       reactionEmojis: {
         [key: string]: string;
       };

From efa80f9ad4ae8f50c8f6b3ccb554506f10418b59 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Sun, 28 Jul 2024 02:23:46 +0000
Subject: [PATCH 160/206] Bump version to 2024.7.0-beta.3

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

diff --git a/package.json b/package.json
index 8c04c6a806..ab7835c1f6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-beta.2",
+	"version": "2024.7.0-beta.3",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index a047e50063..1e1a239bf3 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-beta.2",
+	"version": "2024.7.0-beta.3",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 865b3039ccf6eec4f277dbb33296ab767d2ed7ce Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sun, 28 Jul 2024 14:44:15 +0900
Subject: [PATCH 161/206] =?UTF-8?q?fix:=20deck=20ui=E3=81=AE=E9=80=9A?=
 =?UTF-8?q?=E7=9F=A5=E9=9F=B3=E3=81=8C=E9=87=8D=E3=81=AA=E3=82=8B=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=20(#14029)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: deck uiの通知音が重なる

* docs: Fix: deck uiの通知音が重なる問題

* unexport internal function

* fix

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>

* chore: improve condition

* docs: move js dco comment

---------

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                           |  1 +
 packages/frontend/src/scripts/sound.ts | 23 ++++++++++++++++-------
 2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b35034cc8..c2e9c8e258 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@
 - Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正  
   (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/672)
 - Fix: `/share`ページにおいて絵文字ピッカーを開くことができない問題を修正
+- Fix: deck uiの通知音が重なる問題 (#14029)
 - Fix: ダイレクト投稿の"削除して編集"において、宛先が保持されていなかった問題を修正
 - Fix: 投稿フォームへのURL貼り付けによる引用が下書きに保存されていなかった問題を修正
 - Fix: "削除して編集"や下書きにおいて、リアクションの受け入れ設定が保持/保存されていなかった問題を修正
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index bba855cd64..814e080811 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -124,10 +124,23 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
  */
 export function playMisskeySfx(operationType: OperationType) {
 	const sound = defaultStore.state[`sound_${operationType}`];
-	if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return;
+	playMisskeySfxFile(sound);
+}
+
+/**
+ * サウンド設定形式で指定された音声を再生する
+ * @param soundStore サウンド設定
+ */
+export function playMisskeySfxFile(soundStore: SoundStore) {
+	// 連続して再生しない
+	if (!canPlay) return;
+	// ユーザーアクティベーションが必要な場合はそれがない場合は再生しない
+	if ('userActivation' in navigator && !navigator.userActivation.hasBeenActive) return;
+	// サウンドがない場合は再生しない
+	if (soundStore.type === null || soundStore.type === '_driveFile_' && !soundStore.fileUrl) return;
 
 	canPlay = false;
-	playMisskeySfxFile(sound).finally(() => {
+	playMisskeySfxFileInternal(soundStore).finally(() => {
 		// ごく短時間に音が重複しないように
 		setTimeout(() => {
 			canPlay = true;
@@ -135,11 +148,7 @@ export function playMisskeySfx(operationType: OperationType) {
 	});
 }
 
-/**
- * サウンド設定形式で指定された音声を再生する
- * @param soundStore サウンド設定
- */
-export async function playMisskeySfxFile(soundStore: SoundStore) {
+async function playMisskeySfxFileInternal(soundStore: SoundStore) {
 	if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
 		return;
 	}

From 0f0660d49eee3fabc4f98fc2a4ee687cb691d6a6 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 28 Jul 2024 14:55:28 +0900
Subject: [PATCH 162/206] New Crowdin updates (#13916)

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

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

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

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Indonesian)

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

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Italian)

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

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Romanian)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Norwegian)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Ukrainian)

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Uzbek)

* New translations ja-jp.yml (Lao)

* New translations ja-jp.yml (Kabyle)

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

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

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

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

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

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

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (Korean)

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

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

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

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

* New translations ja-jp.yml (Vietnamese)

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

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

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Greek)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

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

* New translations ja-jp.yml (Spanish)

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

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

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

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

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Indonesian)

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

* New translations ja-jp.yml (Italian)

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

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

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Lao)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)
---
 locales/ar-SA.yml   |   6 +-
 locales/bn-BD.yml   |   6 +-
 locales/ca-ES.yml   |   6 +-
 locales/cs-CZ.yml   |   6 +-
 locales/de-DE.yml   |   6 +-
 locales/el-GR.yml   |   2 -
 locales/en-US.yml   |  51 +++++-
 locales/es-ES.yml   |   8 +-
 locales/fr-FR.yml   |   6 +-
 locales/id-ID.yml   |  16 +-
 locales/it-IT.yml   |  63 ++++++-
 locales/ja-KS.yml   |  12 +-
 locales/kab-KAB.yml |   4 +
 locales/ko-GS.yml   |   4 +
 locales/ko-KR.yml   | 116 ++++++++----
 locales/lo-LA.yml   | 196 +++++++++++----------
 locales/no-NO.yml   |   4 +
 locales/pl-PL.yml   |   6 +-
 locales/pt-PT.yml   |   4 +
 locales/ro-RO.yml   |   4 +
 locales/ru-RU.yml   |   6 +-
 locales/sk-SK.yml   |   6 +-
 locales/sv-SE.yml   |   5 +-
 locales/th-TH.yml   | 420 ++++++++++++++++++++++++--------------------
 locales/uk-UA.yml   |   6 +-
 locales/uz-UZ.yml   |   4 +
 locales/vi-VN.yml   |   9 +-
 locales/zh-CN.yml   |  70 ++++++--
 locales/zh-TW.yml   |  54 ++++--
 29 files changed, 722 insertions(+), 384 deletions(-)

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 955d672c1d..b6bfbfa682 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -1262,8 +1262,6 @@ _sfx:
   note: "الملاحظات"
   noteMy: "ملاحظتي"
   notification: "الإشعارات"
-  antenna: "الهوائيات"
-  channel: "إشعارات القنات"
 _ago:
   future: "المستقبَل"
   justNow: "اللحظة"
@@ -1566,6 +1564,10 @@ _webhookSettings:
   active: "مُفعّل"
   _events:
     reaction: "عند التفاعل"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "البريد الإلكتروني "
 _moderationLogTypes:
   suspend: "علِق"
   deleteDriveFile: "حُذف الملف"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index abcf07da83..6fb51ea5d8 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -1033,8 +1033,6 @@ _sfx:
   note: "নোটগুলি"
   noteMy: "নোট (আপনার)"
   notification: "বিজ্ঞপ্তি"
-  antenna: "অ্যান্টেনাগুলি"
-  channel: "চ্যানেলের বিজ্ঞপ্তি"
 _ago:
   future: "ভবিষ্যৎ"
   justNow: "এইমাত্র"
@@ -1346,6 +1344,10 @@ _deck:
 _webhookSettings:
   name: "নাম"
   active: "চালু"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "ইমেইল"
 _moderationLogTypes:
   suspend: "স্থগিত করা"
   resetPassword: "পাসওয়ার্ড রিসেট করুন"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 0345ee0326..7bd9a1bb32 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -1893,8 +1893,6 @@ _sfx:
   note: "Notes"
   noteMy: "Nota (per mi)"
   notification: "Notificacions"
-  antenna: "Antenes"
-  channel: "Notificacions dels canals"
   reaction: "Quan se selecciona una reacció "
 _soundSettings:
   driveFile: "Fer servir un fitxer d'àudio del disc"
@@ -2225,6 +2223,10 @@ _deck:
 _webhookSettings:
   name: "Nom"
   active: "Activat"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Correu electrònic"
 _moderationLogTypes:
   suspend: "Suspèn"
   resetPassword: "Restableix la contrasenya"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index c8a0b0cb28..aa970f823a 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1645,8 +1645,6 @@ _sfx:
   note: "Poznámky"
   noteMy: "Moje poznámka"
   notification: "Oznámení"
-  antenna: "Antény"
-  channel: "Oznámení kanálu"
 _ago:
   future: "Budoucí"
   justNow: "Teď"
@@ -2022,6 +2020,10 @@ _webhookSettings:
     renote: "Při renotaci poznámky"
     reaction: "Při obdržení reakce"
     mention: "Při zmínce"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Email"
 _moderationLogTypes:
   suspend: "Zmrazit"
   resetPassword: "Resetovat heslo"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 9e42e01252..a319af56cb 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1800,8 +1800,6 @@ _sfx:
   note: "Notizen"
   noteMy: "Meine Notizen"
   notification: "Benachrichtigungen"
-  antenna: "Antennen"
-  channel: "Kanalbenachrichtigung"
 _ago:
   future: "Zukunft"
   justNow: "Gerade eben"
@@ -2203,6 +2201,10 @@ _webhookSettings:
     renote: "Wenn du ein Renote erhältst"
     reaction: "Wenn du eine Reaktion erhältst"
     mention: "Wenn du erwähnt wirst"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Email"
 _moderationLogTypes:
   createRole: "Rolle erstellt"
   deleteRole: "Rolle gelöscht"
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index 2098c7ef50..5eca348e18 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -301,8 +301,6 @@ _theme:
 _sfx:
   note: "Σημειώματα"
   notification: "Ειδοποιήσεις"
-  antenna: "Αντένες"
-  channel: "Ειδοποιήσεις καναλιών"
 _ago:
   future: "Μελλοντικό"
   justNow: "Μόλις τώρα"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index c20a1ac7d8..b751b039e6 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -108,7 +108,7 @@ enterEmoji: "Enter an emoji"
 renote: "Renote"
 unrenote: "Remove renote"
 renoted: "Renoted."
-renotedToX: "Renote from {name} users。"
+renotedToX: "Renote to {name}."
 cantRenote: "This post can't be renoted."
 cantReRenote: "A renote can't be renoted."
 quote: "Quote"
@@ -125,8 +125,8 @@ add: "Add"
 reaction: "Reactions"
 reactions: "Reactions"
 emojiPicker: "Emoji picker"
-pinnedEmojisForReactionSettingDescription: "Set the emojis which should be pinned and displayed immediately when reacting."
-pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker"
+pinnedEmojisForReactionSettingDescription: "Set the emojis to be pinned and displayed when reacting."
+pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when viewing emoji picker."
 emojiPickerDisplay: "Emoji picker display"
 overwriteFromPinnedEmojisForReaction: "Override from reaction settings"
 overwriteFromPinnedEmojis: "Override from general settings"
@@ -180,6 +180,10 @@ addAccount: "Add account"
 reloadAccountsList: "Reload account list"
 loginFailed: "Failed to sign in"
 showOnRemote: "View on remote instance"
+continueOnRemote: "リモートで続行"
+chooseServerOnMisskeyHub: "Choose a server from the Misskey Hub"
+specifyServerHost: "Specify a server host directly"
+inputHostName: "Enter the domain"
 general: "General"
 wallpaper: "Wallpaper"
 setWallpaper: "Set wallpaper"
@@ -316,6 +320,7 @@ selectFile: "Select a file"
 selectFiles: "Select files"
 selectFolder: "Select a folder"
 selectFolders: "Select folders"
+fileNotSelected: ""
 renameFile: "Rename file"
 folderName: "Folder name"
 createFolder: "Create a folder"
@@ -476,6 +481,7 @@ noMessagesYet: "No messages yet"
 newMessageExists: "There are new messages"
 onlyOneFileCanBeAttached: "You can only attach one file to a message"
 signinRequired: "Please register or sign in before continuing"
+signinOrContinueOnRemote: "To continue, you  need to move your server or sign up / log in to this server."
 invitations: "Invites"
 invitationCode: "Invitation code"
 checking: "Checking..."
@@ -836,6 +842,7 @@ administration: "Management"
 accounts: "Accounts"
 switch: "Switch"
 noMaintainerInformationWarning: "Maintainer information is not configured."
+noInquiryUrlWarning: "Inquiry URL isn’t set"
 noBotProtectionWarning: "Bot protection is not configured."
 configure: "Configure"
 postToGallery: "Create new gallery post"
@@ -1025,6 +1032,7 @@ thisPostMayBeAnnoyingHome: "Post to home timeline"
 thisPostMayBeAnnoyingCancel: "Cancel"
 thisPostMayBeAnnoyingIgnore: "Post anyway"
 collapseRenotes: "Collapse renotes you've already seen"
+collapseRenotesDescription: "Collapse notes that you've reacted to or renoted before."
 internalServerError: "Internal Server Error"
 internalServerErrorDescription: "The server has run into an unexpected error."
 copyErrorInfo: "Copy error details"
@@ -1239,6 +1247,9 @@ keepOriginalFilenameDescription: "If you turn off this setting, files names will
 noDescription: "There is not the explanation"
 alwaysConfirmFollow: "Always confirm when following"
 inquiry: "Contact"
+tryAgain: "Please try again later"
+confirmWhenRevealingSensitiveMedia: "Confirm when revealing sensitive media"
+sensitiveMediaRevealConfirm: "This might be a sensitive media. Are you sure to reveal?"
 _delivery:
   status: "Delivery status"
   stop: "Suspended"
@@ -1373,6 +1384,8 @@ _serverSettings:
   fanoutTimelineDescription: "Greatly increases performance of timeline retrieval and reduces load on the database when enabled. In exchange, memory usage of Redis will increase. Consider disabling this in case of low server memory or server instability."
   fanoutTimelineDbFallback: "Fallback to database"
   fanoutTimelineDbFallbackDescription: "When enabled, the timeline will fall back to the database for additional queries if the timeline is not cached. Disabling it further reduces the server load by eliminating the fallback process, but limits the range of timelines that can be retrieved."
+  inquiryUrl: "Query URL"
+  inquiryUrlDescription: "Specify a URL for the inquiry form to the server maintainer or a web page for the contact information."
 _accountMigration:
   moveFrom: "Migrate another account to this one"
   moveFromSub: "Create alias to another account"
@@ -1689,6 +1702,7 @@ _role:
     canManageAvatarDecorations: "Manage avatar decorations"
     driveCapacity: "Drive capacity"
     alwaysMarkNsfw: "Always mark files as NSFW"
+    canUpdateBioMedia: "Allow to edit an icon or a banner image"
     pinMax: "Maximum number of pinned notes"
     antennaMax: "Maximum number of antennas"
     wordMuteMax: "Maximum number of characters allowed in word mutes"
@@ -1932,8 +1946,6 @@ _sfx:
   note: "New note"
   noteMy: "Own note"
   notification: "Notifications"
-  antenna: "Antennas"
-  channel: "Channel notifications"
   reaction: "On choosing a reaction"
 _soundSettings:
   driveFile: "Use an audio file in Drive."
@@ -2059,7 +2071,7 @@ _permissions:
   "read:admin:invite-codes": "View invite codes"
   "write:admin:announcements": "Manage announcements"
   "read:admin:announcements": "View announcements"
-  "write:admin:avatar-decorations": "Manage avatar decorations"
+  "write:admin:avatar-decorations": "Can manage avatar decorations"
   "read:admin:avatar-decorations": "View avatar decorations"
   "write:admin:federation": "Manage federation data"
   "write:admin:account": "Manage user account"
@@ -2358,6 +2370,7 @@ _deck:
   alwaysShowMainColumn: "Always show main column"
   columnAlign: "Align columns"
   addColumn: "Add column"
+  newNoteNotificationSettings: "Notification setting for new notes"
   configureColumn: "Column settings"
   swapLeft: "Swap with the left column"
   swapRight: "Swap with the right column"
@@ -2396,6 +2409,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "Ascending Dates"
 _webhookSettings:
   createWebhook: "Create Webhook"
+  modifyWebhook: "Modify Webhook"
   name: "Name"
   secret: "Secret"
   events: "Webhook Events"
@@ -2408,6 +2422,25 @@ _webhookSettings:
     renote: "When renoted"
     reaction: "When receiving a reaction"
     mention: "When being mentioned"
+  _systemEvents:
+    abuseReport: "When received a new abuse report"
+    abuseReportResolved: "When resolved abuse reports"
+  deleteConfirm: "Are you sure you want to delete the Webhook?"
+_abuseReport:
+  _notificationRecipient:
+    createRecipient: "Add a recipient for abuse reports"
+    modifyRecipient: "Edit a recipient for abuse reports"
+    recipientType: "Notification type"
+    _recipientType:
+      mail: "Email"
+      webhook: "Webhook"
+      _captions:
+        mail: "Send the email to moderators' email addresses when you receive abuse."
+        webhook: "Send a notification to SystemWebhook when you receive or resolve abuse."
+    keywords: "Keywords"
+    notifiedUser: "Users to notify"
+    notifiedWebhook: "Webhook to use"
+    deleteConfirm: "Are you sure that you want to delete the notification recipient?"
 _moderationLogTypes:
   createRole: "Role created"
   deleteRole: "Role deleted"
@@ -2445,6 +2478,12 @@ _moderationLogTypes:
   deleteAvatarDecoration: "Avatar decoration deleted"
   unsetUserAvatar: "Unset this user's avatar"
   unsetUserBanner: "Unset this user's banner"
+  createSystemWebhook: "Create SystemWebhook"
+  updateSystemWebhook: "Update SystemWebHook"
+  deleteSystemWebhook: "Delete SystemWebhook"
+  createAbuseReportNotificationRecipient: "Create a recipient for abuse reports"
+  updateAbuseReportNotificationRecipient: "Update recipients for abuse reports"
+  deleteAbuseReportNotificationRecipient: "Delete a recipient for abuse reports"
 _fileViewer:
   title: "File details"
   type: "File type"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 5c8249ded5..3ae3ba3b8a 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -302,7 +302,7 @@ location: "Lugar"
 theme: "Tema"
 themeForLightMode: "Tema para usar en Modo Linterna"
 themeForDarkMode: "Tema para usar en Modo Oscuro"
-light: "Linterna"
+light: "Claro"
 dark: "Oscuro"
 lightThemes: "Tema claro"
 darkThemes: "Tema oscuro"
@@ -1920,8 +1920,6 @@ _sfx:
   note: "Notas"
   noteMy: "Nota (a mí mismo)"
   notification: "Notificaciones"
-  antenna: "Antena receptora"
-  channel: "Notificaciones del canal"
   reaction: "Al seleccionar una reacción"
 _soundSettings:
   driveFile: "Usar un archivo de audio en Drive"
@@ -2394,6 +2392,10 @@ _webhookSettings:
     renote: "Cuando reciba un \"re-note\""
     reaction: "Cuando se recibe una reacción"
     mention: "Cuando hay una mención"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Correo"
 _moderationLogTypes:
   createRole: "Rol creado"
   deleteRole: "Rol eliminado"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 8d66c3d375..ee08dfddf1 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1712,8 +1712,6 @@ _sfx:
   note: "Nouvelle note"
   noteMy: "Ma note"
   notification: "Notifications"
-  antenna: "Réception de l’antenne"
-  channel: "Notifications de canal"
   reaction: "Lors de la sélection de la réaction"
 _soundSettings:
   driveFile: "Utiliser un effet sonore sur le Disque"
@@ -2073,6 +2071,10 @@ _drivecleaner:
 _webhookSettings:
   name: "Nom"
   active: "Activé"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "E-mail "
 _moderationLogTypes:
   createRole: "Rôle créé"
   deleteRole: "Rôle supprimé"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 7f509afa50..de50569e89 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -180,6 +180,10 @@ addAccount: "Tambahkan akun"
 reloadAccountsList: "Muat ulang daftar akun"
 loginFailed: "Gagal untuk masuk"
 showOnRemote: "Lihat profil asli"
+continueOnRemote: "Lihat di peladen asal"
+chooseServerOnMisskeyHub: "Pilih peladen dari Misskey Hub"
+specifyServerHost: "Tentukan domain peladen"
+inputHostName: "Masukkan nama domain"
 general: "Umum"
 wallpaper: "Wallpaper"
 setWallpaper: "Atur wallpaper"
@@ -316,6 +320,7 @@ selectFile: "Pilih berkas"
 selectFiles: "Pilih berkas"
 selectFolder: "Pilih folder"
 selectFolders: "Pilih folder"
+fileNotSelected: "Tidak ada file yang dipilih"
 renameFile: "Ubah nama berkas"
 folderName: "Nama folder"
 createFolder: "Buat folder"
@@ -1239,6 +1244,7 @@ keepOriginalFilenameDescription: "Apabila pengaturan ini dimatikan, nama berkas
 noDescription: "Tidak ada deskripsi"
 alwaysConfirmFollow: "Selalu konfirmasi ketika mengikuti"
 inquiry: "Hubungi kami"
+tryAgain: "Silahkan coba lagi."
 _delivery:
   status: "Status pengiriman"
   stop: "Ditangguhkan"
@@ -1739,7 +1745,7 @@ _emailUnavailable:
   smtp: "Peladen alamat surel ini tidak merespon"
   banned: "Kamu tidak dapat mendaftar dengan alamat surel ini"
 _ffVisibility:
-  public: "Terbitkan"
+  public: "Publik"
   followers: "Tampil untuk pengikut saja"
   private: "Tersembunyi"
 _signup:
@@ -1932,8 +1938,6 @@ _sfx:
   note: "Catatan"
   noteMy: "Catatan (Saya)"
   notification: "Notifikasi"
-  antenna: "Penerimaan Antenna"
-  channel: "Notifikasi Kanal"
   reaction: "Ketika memilih reaksi"
 _soundSettings:
   driveFile: "Menggunakan berkas audio dalam Drive"
@@ -2396,6 +2400,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "Tanggal (Naik)"
 _webhookSettings:
   createWebhook: "Buat Webhook"
+  modifyWebhook: "Sunting Webhook"
   name: "Nama"
   secret: "Secret"
   events: "Webhook Events"
@@ -2408,6 +2413,11 @@ _webhookSettings:
     renote: "Ketika direnote"
     reaction: "Ketika menerima reaksi"
     mention: "Ketika sedang disebut"
+  deleteConfirm: "Apakah kamu yakin ingin menghapus Webhook?"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Surel"
 _moderationLogTypes:
   createRole: "Peran telah dibuat"
   deleteRole: "Peran telah dihapus"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 1d12a62cca..64cd26878e 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -108,11 +108,14 @@ enterEmoji: "Inserisci emoji"
 renote: "Rinota"
 unrenote: "Elimina la Rinota"
 renoted: "Rinotata!"
+renotedToX: "Rinota da {name}."
 cantRenote: "È impossibile rinotare questa nota."
 cantReRenote: "È impossibile rinotare una Rinota."
 quote: "Citazione"
 inChannelRenote: "Rinota nel canale"
 inChannelQuote: "Cita nel canale"
+renoteToChannel: "Rinota al canale"
+renoteToOtherChannel: "Rinota a un altro canale"
 pinnedNote: "Nota in primo piano"
 pinned: "Fissa sul profilo"
 you: "Tu"
@@ -177,6 +180,10 @@ addAccount: "Aggiungi profilo"
 reloadAccountsList: "Ricarica l'elenco dei profili"
 loginFailed: "Accesso non riuscito"
 showOnRemote: "Leggi sull'istanza remota"
+continueOnRemote: "Continua da remoto"
+chooseServerOnMisskeyHub: "Scegli l'istanza sul sito Misskey Hub"
+specifyServerHost: "Indica l'indirizzo dell'istanza"
+inputHostName: "Digita il nome del dominio "
 general: "Generali"
 wallpaper: "Sfondo"
 setWallpaper: "Imposta sfondo"
@@ -313,6 +320,7 @@ selectFile: "Scelta allegato"
 selectFiles: "Scelta allegato"
 selectFolder: "Seleziona cartella"
 selectFolders: "Seleziona cartella"
+fileNotSelected: "Nessun file selezionato"
 renameFile: "Rinomina file"
 folderName: "Nome della cartella"
 createFolder: "Nuova cartella"
@@ -468,10 +476,12 @@ retype: "Conferma"
 noteOf: "Note di {user}"
 quoteAttached: "Citazione allegata"
 quoteQuestion: "Vuoi aggiungere una citazione?"
+attachAsFileQuestion: "Il testo copiato eccede le dimensioni, vuoi allegarlo?"
 noMessagesYet: "Ancora nessuna chat"
 newMessageExists: "Hai ricevuto un nuovo messaggio"
 onlyOneFileCanBeAttached: "È possibile allegare al messaggio soltanto uno file"
 signinRequired: "Occorre avere un profilo registrato su questa istanza"
+signinOrContinueOnRemote: "Per continuare, devi accedere alla tua istanza o registrarti su questa e poi accedere"
 invitations: "Invita"
 invitationCode: "Codice di invito"
 checking: "Confermando"
@@ -695,7 +705,7 @@ reporterOrigin: "Segnalazione da"
 forwardReport: "Inoltro di un report a un'istanza remota."
 forwardReportIsAnonymous: "L'istanza remota non vedrà le tue informazioni, apparirai come profilo di sistema, anonimo."
 send: "Inviare"
-abuseMarkAsResolved: "Contrassegna la segnalazione come risolta"
+abuseMarkAsResolved: "Risolvi segnalazione"
 openInNewTab: "Apri in una nuova scheda"
 openInSideView: "Apri in vista laterale"
 defaultNavigationBehaviour: "Navigazione preimpostata"
@@ -832,6 +842,7 @@ administration: "Gestione"
 accounts: "Profilo"
 switch: "Cambia"
 noMaintainerInformationWarning: "Mancano le informazioni sull'amministratore."
+noInquiryUrlWarning: "Non è stata impostata la URL di contatto"
 noBotProtectionWarning: "Non è stata impostata alcuna protezione dai Bot"
 configure: "Imposta"
 postToGallery: "Pubblicare nella galleria"
@@ -1021,6 +1032,7 @@ thisPostMayBeAnnoyingHome: "Pubblica sulla timeline principale"
 thisPostMayBeAnnoyingCancel: "Annulla"
 thisPostMayBeAnnoyingIgnore: "Pubblica lo stesso"
 collapseRenotes: "Comprimi le Rinota già viste"
+collapseRenotesDescription: "Comprimi le Note con cui hai già interagito."
 internalServerError: "Errore interno del server"
 internalServerErrorDescription: "Si è verificato un errore imprevisto all'interno del server"
 copyErrorInfo: "Copia le informazioni sull'errore"
@@ -1233,10 +1245,20 @@ useNativeUIForVideoAudioPlayer: "Riprodurre audio/video usando le funzionalità
 keepOriginalFilename: "Mantieni il nome file originale"
 keepOriginalFilenameDescription: "Disattivandola, i file verranno caricati usando nomi casuali."
 noDescription: "Manca la descrizione"
+alwaysConfirmFollow: "Richiedi conferma per i Follow"
+inquiry: "Contattaci"
+tryAgain: "Per favore riprova"
+confirmWhenRevealingSensitiveMedia: "Richiedi conferma prima di mostrare gli allegati espliciti"
+sensitiveMediaRevealConfirm: "Questo allegato è esplicito, vuoi vederlo?"
 _delivery:
+  status: "Stato della consegna"
   stop: "Sospensione"
+  resume: "Riprendi la consegna"
   _type:
     none: "Pubblicazione"
+    manuallySuspended: "Sospesa manualmente"
+    goneSuspended: "Sospensione server a causa dell'eliminazione"
+    autoSuspendedForNotResponding: "Sospensione del server a causa di mancata risposta"
 _bubbleGame:
   howToPlay: "Come giocare"
   hold: "Tieni"
@@ -1362,6 +1384,8 @@ _serverSettings:
   fanoutTimelineDescription: "Attivando questa funzionalità migliori notevolmente la capacità delle Timeline di collezionare Note, riducendo il carico sul database. Tuttavia, aumenterà l'impiego di memoria RAM per Redis. Disattiva se il tuo server ha poca RAM o la funzionalità è irregolare."
   fanoutTimelineDbFallback: "Elaborazione dati alternativa"
   fanoutTimelineDbFallbackDescription: "Attivando l'elaborazione alternativa, verrà interrogato ulteriormente il database se la timeline non è nella cache. \nDisattivando, si può ridurre ulteriormente il carico del server, evitando l'elaborazione alternativa, ma limitando l'intervallo recuperabile delle timeline."
+  inquiryUrl: "URL di contatto"
+  inquiryUrlDescription: "Specificare l'URL al modulo di contatto, oppure le informazioni con i dati di contatto dell'amministrazione."
 _accountMigration:
   moveFrom: "Migra un altro profilo dentro a questo"
   moveFromSub: "Crea un alias verso un altro profilo remoto"
@@ -1678,6 +1702,7 @@ _role:
     canManageAvatarDecorations: "Gestisce le decorazioni di immagini del profilo"
     driveCapacity: "Capienza del Drive"
     alwaysMarkNsfw: "Impostare sempre come esplicito (NSFW)"
+    canUpdateBioMedia: "Può aggiornare foto profilo e di testata"
     pinMax: "Quantità massima di Note in primo piano"
     antennaMax: "Quantità massima di Antenne"
     wordMuteMax: "Lunghezza massima del filtro parole"
@@ -1696,6 +1721,11 @@ _role:
     roleAssignedTo: "Assegnato a ruoli manualmente"
     isLocal: "Profilo locale"
     isRemote: "Profilo remoto"
+    isCat: "È un gattino"
+    isBot: "È un bot"
+    isSuspended: "È sospeso"
+    isLocked: "È in stato privato"
+    isExplorable: "Autorizza la pubblicazione nei cataloghi"
     createdLessThan: "Profilo creato da meno di N"
     createdMoreThan: "Profilo creato da più di N"
     followersLessThanOrEq: "Profilo con N follower o meno"
@@ -1916,8 +1946,6 @@ _sfx:
   note: "Nota"
   noteMy: "Mia nota"
   notification: "Notifiche"
-  antenna: "Ricezione dell'antenna"
-  channel: "Notifiche di canale"
   reaction: "Quando seleziono una reazione"
 _soundSettings:
   driveFile: "Suoni del Drive"
@@ -2342,6 +2370,7 @@ _deck:
   alwaysShowMainColumn: "Mostra sempre la colonna principale"
   columnAlign: "Allineare colonne"
   addColumn: "Aggiungi colonna"
+  newNoteNotificationSettings: "Preferenze per le notifiche di nuove Note"
   configureColumn: "Impostazioni colonna"
   swapLeft: "Sposta a sinistra"
   swapRight: "Sposta a destra"
@@ -2380,6 +2409,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "Dal più vecchio al più recente"
 _webhookSettings:
   createWebhook: "Creazione Webhook"
+  modifyWebhook: "Modifica Webhook"
   name: "Nome"
   secret: "Segreto"
   events: "Quando eseguire il Webhook"
@@ -2392,6 +2422,25 @@ _webhookSettings:
     renote: "Quando la Nota è Rinotata"
     reaction: "Quando ricevo una reazione"
     mention: "Quando mi menzionano"
+  _systemEvents:
+    abuseReport: "Quando arriva una segnalazione"
+    abuseReportResolved: "Quando una segnalazione è risolta"
+  deleteConfirm: "Vuoi davvero eliminare il Webhook?"
+_abuseReport:
+  _notificationRecipient:
+    createRecipient: "Aggiungi destinatario della segnalazione"
+    modifyRecipient: "Modifica destinatario della segnalazione"
+    recipientType: "Tipo di notifica"
+    _recipientType:
+      mail: "Email"
+      webhook: "Webhook"
+      _captions:
+        mail: "Quando ricevi un abuso, notifica l'amministrazione via email"
+        webhook: "Spedire una notifica al SystemWebhook specificato (sia quando si riceve una segnalazione, che quando viene risolta)"
+    keywords: "Parole chiave"
+    notifiedUser: "Profili da notificare"
+    notifiedWebhook: "Webhook da usare"
+    deleteConfirm: "Vuoi davvero rimuovere il destinatario della notifica?"
 _moderationLogTypes:
   createRole: "Ruolo creato"
   deleteRole: "Ruolo eliminato"
@@ -2429,6 +2478,12 @@ _moderationLogTypes:
   deleteAvatarDecoration: "Eliminazione decorazione della foto profilo"
   unsetUserAvatar: "Rimossa foto profilo"
   unsetUserBanner: "Rimossa intestazione profilo"
+  createSystemWebhook: "Crea un SystemWebhook"
+  updateSystemWebhook: "Modifica SystemWebhook"
+  deleteSystemWebhook: "Elimina SystemWebhook"
+  createAbuseReportNotificationRecipient: "Crea destinatario per le notifiche di segnalazioni"
+  updateAbuseReportNotificationRecipient: "Aggiorna destinatario notifiche di segnalazioni"
+  deleteAbuseReportNotificationRecipient: "Elimina destinatario notifiche di segnalazioni"
 _fileViewer:
   title: "Dettagli del file"
   type: "Tipo di file"
@@ -2554,6 +2609,8 @@ _urlPreviewSetting:
   userAgent: "User-Agent"
   userAgentDescription: "Definire con quale User-Agent si intende identificarsi durante l'acquisizione di un'anteprima. Se è vuoto, useremo il valore predefinito."
   summaryProxy: "Endpoint proxy che genera l'anteprima"
+  summaryProxyDescription: "Genera anteprime utilizzando un proxy Summaly anziché Misskey."
+  summaryProxyDescription2: "I parametri sono collegano al proxy come stringa query. Se il proxy non li supporta, verranno ignorati."
 _mediaControls:
   pip: "Sovraimpressione"
   playbackRate: "Velocità di riproduzione"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 7a33968e9e..774031f6f5 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -108,6 +108,7 @@ enterEmoji: "絵文字を入れてや"
 renote: "リノート"
 unrenote: "リノートやめる"
 renoted: "リノートしたで。"
+renotedToX: "{name}にリノートしたで"
 cantRenote: "この投稿はリノートできへんっぽい。"
 cantReRenote: "リノート自体はリノートできへんで。"
 quote: "引用"
@@ -313,6 +314,7 @@ selectFile: "ファイル選んでや"
 selectFiles: "ファイル選んでや"
 selectFolder: "フォルダ選んでや"
 selectFolders: "フォルダ選んでや"
+fileNotSelected: "ファイルが選択されてへんで"
 renameFile: "ファイル名をいらう"
 folderName: "フォルダー名"
 createFolder: "フォルダー作る"
@@ -468,6 +470,7 @@ retype: "もっかい入力"
 noteOf: "{user}はんのノート"
 quoteAttached: "引用付いとるで"
 quoteQuestion: "引用として添付してもええか?"
+attachAsFileQuestion: "クリップボードのテキストが長すぎるからテキストファイルとして添付してもええか?"
 noMessagesYet: "まだチャットはあらへんで"
 newMessageExists: "新しいメッセージがきたで"
 onlyOneFileCanBeAttached: "ごめんな、メッセージに添付できるファイルはひとつだけなんよ。"
@@ -832,6 +835,7 @@ administration: "管理"
 accounts: "アカウント"
 switch: "切り替え"
 noMaintainerInformationWarning: "管理者情報が設定されてへんで"
+noInquiryUrlWarning: "問い合わせ先URLが設定されてへんで。"
 noBotProtectionWarning: "Botプロテクションが設定されてへんで。"
 configure: "設定する"
 postToGallery: "ギャラリーへ投稿"
@@ -1923,8 +1927,6 @@ _sfx:
   note: "ノート"
   noteMy: "ノート(自分)"
   notification: "通知"
-  antenna: "アンテナ受信"
-  channel: "チャンネル通知"
   reaction: "ツッコミ選んどるとき"
 _soundSettings:
   driveFile: "ドライブん中の音使う"
@@ -2399,6 +2401,12 @@ _webhookSettings:
     renote: "リノートされるとき~!"
     reaction: "ツッコまれたとき~!"
     mention: "メンションがあるとき~!"
+  deleteConfirm: "ほんまにWebhookをほかしてもええんか?"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "メール"
+    deleteConfirm: "通知先を削除してもええか?"
 _moderationLogTypes:
   createRole: "ロールを追加すんで"
   deleteRole: "ロールほかす"
diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml
index 22e24d3baa..d4aa36fa70 100644
--- a/locales/kab-KAB.yml
+++ b/locales/kab-KAB.yml
@@ -104,3 +104,7 @@ _deck:
   _columns:
     notifications: "Ilɣuyen"
     list: "Tibdarin"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Imayl"
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index 9466aff01f..9323ed2a26 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -805,6 +805,10 @@ _deck:
     mentions: "받언 멘션"
 _webhookSettings:
   name: "이럼"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "전자우펜"
 _moderationLogTypes:
   suspend: "얼우기"
   deleteNote: "노트 뭉캐기"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 294a5a1520..e3b6d31910 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -52,14 +52,14 @@ deleteAndEditConfirm: "이 노트를 삭제한 뒤 다시 편집하시겠습니
 addToList: "리스트에 추가"
 addToAntenna: "안테나에 추가"
 sendMessage: "메시지 보내기"
-copyRSS: "RSS 복사"
-copyUsername: "사용자 이름 복사"
-copyUserId: "사용자 ID 복사"
+copyRSS: "RSS 주소 복사"
+copyUsername: "유저명 복사"
+copyUserId: "유저 ID 복사"
 copyNoteId: "노트 ID 복사"
 copyFileId: "파일 ID 복사"
 copyFolderId: "폴더 ID 복사"
 copyProfileUrl: "프로필 URL 복사"
-searchUser: "사용자 검색"
+searchUser: "유저 검색"
 reply: "답글"
 loadMore: "더 보기"
 showMore: "더 보기"
@@ -108,22 +108,25 @@ enterEmoji: "이모지 입력"
 renote: "리노트"
 unrenote: "리노트 취소"
 renoted: "리노트했습니다"
+renotedToX: "{name}명이 리노트했습니다."
 cantRenote: "이 게시물은 리노트 할 수 없습니다."
-cantReRenote: "리노트를 리노트할 수 없습니다."
+cantReRenote: "리노트를 리노트 할 수 없습니다."
 quote: "인용"
 inChannelRenote: "채널 내 리노트"
 inChannelQuote: "채널 내 인용"
+renoteToChannel: "채널에 리노트"
+renoteToOtherChannel: "다른 채널에 리노트"
 pinnedNote: "고정된 노트"
 pinned: "고정하기"
 you: "나"
 clickToShow: "클릭하여 보기"
 sensitive: "열람 주의"
 add: "추가"
-reaction: "반응"
-reactions: "반응"
+reaction: "리액션"
+reactions: "리액션"
 emojiPicker: "이모지 선택기"
-pinnedEmojisForReactionSettingDescription: "리액션을 할 때 프로필에 고정하여 표시할 이모지를 설정할 수 있습니다"
-pinnedEmojisSettingDescription: "이모지를 입력할 때 프로필에 고정하여 표시할 이모지를 설정할 수 있습니다"
+pinnedEmojisForReactionSettingDescription: "리액션을 할 때 이모지 선택기 상단에 표시할 이모지를 설정할 수 있습니다."
+pinnedEmojisSettingDescription: "이모지를 입력할 때 이모지 선택기 상단에 표시할 이모지를 설정할 수 있습니다."
 emojiPickerDisplay: "선택기 표시"
 overwriteFromPinnedEmojisForReaction: "리액션 설정을 덮어쓰기"
 overwriteFromPinnedEmojis: "일반 설정을 덮어쓰기"
@@ -136,7 +139,7 @@ unmarkAsSensitive: "열람주의 해제"
 enterFileName: "파일명을 입력"
 mute: "뮤트"
 unmute: "뮤트 해제"
-renoteMute: "리노트 뮤트하기"
+renoteMute: "리노트 뮤트"
 renoteUnmute: "리노트 뮤트 해제"
 block: "차단"
 unblock: "차단 해제"
@@ -174,12 +177,12 @@ flagShowTimelineReplies: "타임라인에 노트의 답글을 표시하기"
 flagShowTimelineRepliesDescription: "이 설정을 활성화하면 타임라인에 다른 유저 간의 답글을 표시합니다."
 autoAcceptFollowed: "팔로우 중인 유저로부터의 팔로우 요청을 자동 수락"
 addAccount: "계정 추가"
-reloadAccountsList: "계정 리스트 정보 갱신"
+reloadAccountsList: "계정 목록 새로고침"
 loginFailed: "로그인에 실패했습니다"
 showOnRemote: "리모트에서 보기"
 general: "일반"
 wallpaper: "배경"
-setWallpaper: "배경화면 설정"
+setWallpaper: "배경 설정"
 removeWallpaper: "배경 제거"
 searchWith: "검색: {q}"
 youHaveNoLists: "리스트가 없습니다"
@@ -187,7 +190,7 @@ followConfirm: "{name}님을 팔로우 하시겠습니까?"
 proxyAccount: "프록시 계정"
 proxyAccountDescription: "프록시 계정은 특정 조건 하에서 유저의 리모트 팔로우를 대행하는 계정입니다. 예를 들면, 유저가 리모트 유저를 리스트에 넣었을 때, 리스트에 들어간 유저를 아무도 팔로우한 적이 없다면 액티비티가 서버로 배달되지 않기 때문에, 대신 프록시 계정이 해당 유저를 팔로우하도록 합니다."
 host: "호스트"
-selectUser: "사용자 선택"
+selectUser: "유저 선택"
 recipient: "수신인"
 annotation: "내용에 대한 주석"
 federation: "연합"
@@ -230,7 +233,7 @@ noUsers: "아무도 없습니다"
 editProfile: "프로필 수정"
 noteDeleteConfirm: "이 노트를 삭제하시겠습니까?"
 pinLimitExceeded: "더 이상 고정할 수 없습니다."
-intro: "Misskey의 설치를 완료했습니다! 관리자 계정을 만들어 주세요."
+intro: "Misskey의 설치가 완료되었습니다! 관리자 계정을 생성해주세요."
 done: "완료"
 processing: "처리중"
 preview: "미리보기"
@@ -247,7 +250,7 @@ publishing: "배포 중"
 notResponding: "응답 없음"
 instanceFollowing: "서버의 팔로잉"
 instanceFollowers: "서버의 팔로워"
-instanceUsers: "서버의 유저"
+instanceUsers: "서버의 사용자"
 changePassword: "비밀번호 변경"
 security: "보안"
 retypedNotMatch: "입력이 일치하지 않습니다."
@@ -263,12 +266,12 @@ lookup: "찾아보기"
 announcements: "공지사항"
 imageUrl: "이미지 URL"
 remove: "삭제"
-removed: "삭제하였습니다"
+removed: "삭제했습니다"
 removeAreYouSure: "\"{x}\" 을(를) 삭제하시겠습니까?"
 deleteAreYouSure: "\"{x}\" 을(를) 삭제하시겠습니까?"
 resetAreYouSure: "초기화 하시겠습니까?"
 areYouSure: "계속 진행하시겠습니까?"
-saved: "저장하였습니다"
+saved: "저장했습니다"
 messaging: "대화"
 upload: "업로드"
 keepOriginalUploading: "원본 이미지를 유지"
@@ -296,7 +299,7 @@ activity: "활동"
 images: "이미지"
 image: "이미지"
 birthday: "생일"
-yearsOld: "만 {age} 세"
+yearsOld: "{age}세"
 registeredDate: "등록일"
 location: "장소"
 theme: "테마"
@@ -313,6 +316,7 @@ selectFile: "파일 선택"
 selectFiles: "파일 선택"
 selectFolder: "폴더 선택"
 selectFolders: "폴더 선택"
+fileNotSelected: "파일을 선택하지 않았습니다"
 renameFile: "파일 이름 변경"
 folderName: "폴더 이름"
 createFolder: "폴더 만들기"
@@ -370,7 +374,7 @@ inMb: "메가바이트 단위"
 bannerUrl: "배너 이미지 URL"
 backgroundImageUrl: "배경 이미지 URL"
 basicInfo: "기본 정보"
-pinnedUsers: "고정된 유저"
+pinnedUsers: "고정한 사용자"
 pinnedUsersDescription: "\"발견하기\" 페이지 등에 고정하고 싶은 유저를 한 줄에 한 명씩 적습니다."
 pinnedPages: "고정한 페이지"
 pinnedPagesDescription: "서버의 대문에 고정하고 싶은 페이지의 경로를 한 줄에 하나씩 적습니다."
@@ -437,13 +441,13 @@ moderationNote: "조정 기록"
 addModerationNote: "조정 기록 추가하기"
 moderationLogs: "모더레이션 로그"
 nUsersMentioned: "{n}명이 언급함"
-securityKeyAndPasskey: "보안 키 또는 패스 키"
+securityKeyAndPasskey: "보안 키 또는 패스키"
 securityKey: "보안 키"
 lastUsed: "마지막 사용"
 lastUsedAt: "마지막 사용: {t}"
 unregister: "등록 해제"
 passwordLessLogin: "비밀번호 없이 로그인"
-passwordLessLoginDescription: "비밀번호를 사용하지 않고 보안 키 또는 패스 키 등으로만 로그인합니다."
+passwordLessLoginDescription: "비밀번호 없이 보안 키 또는 패스키만 사용해서 로그인합니다."
 resetPassword: "비밀번호 재설정"
 newPasswordIs: "새로운 비밀번호는 \"{password}\" 입니다"
 reduceUiAnimation: "UI의 애니메이션을 줄이기"
@@ -468,6 +472,7 @@ retype: "다시 입력"
 noteOf: "{user}의 노트"
 quoteAttached: "인용함"
 quoteQuestion: "인용해서 작성하시겠습니까?"
+attachAsFileQuestion: "붙여넣으려는 글이 너무 깁니다. 텍스트 파일로 첨부하시겠습니까?"
 noMessagesYet: "아직 대화가 없습니다"
 newMessageExists: "새 메시지가 있습니다"
 onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
@@ -486,7 +491,7 @@ strongPassword: "강한 비밀번호"
 passwordMatched: "일치합니다"
 passwordNotMatched: "일치하지 않습니다"
 signinWith: "{x}로 로그인"
-signinFailed: "로그인할 수 없습니다. 사용자명과 비밀번호를 확인하여 주십시오."
+signinFailed: "로그인할 수 없습니다. 사용자 이름과 비밀번호를 확인해 주십시오."
 or: "혹은"
 language: "언어"
 uiLanguage: "UI 표시 언어"
@@ -494,7 +499,7 @@ aboutX: "{x}에 대하여"
 emojiStyle: "이모지 스타일"
 native: "기본"
 disableDrawer: "드로어 메뉴를 사용하지 않기"
-showNoteActionsOnlyHover: "노트 액션 버튼을 마우스를 올렸을 때에만 표시"
+showNoteActionsOnlyHover: "마우스가 올라간 때에만 노트 동작 버튼을 표시하기"
 showReactionsCount: "노트의 반응 수를 표시하기"
 noHistory: "기록이 없습니다"
 signinHistory: "로그인 기록"
@@ -559,7 +564,7 @@ popout: "새 창으로 열기"
 volume: "음량"
 masterVolume: "마스터 볼륨"
 notUseSound: "음소거 하기"
-useSoundOnlyWhenActive: "Misskey가 활성화 되어져 있을 때만 소리 출력하기"
+useSoundOnlyWhenActive: "Misskey를 활성화한 때에만 소리를 출력하기"
 details: "자세히"
 chooseEmoji: "이모지 선택"
 unableToProcess: "작업을 완료할 수 없습니다"
@@ -588,7 +593,7 @@ deleteAllFiles: "모든 파일 삭제"
 deleteAllFilesConfirm: "모든 파일을 삭제하시겠습니까?"
 removeAllFollowing: "모든 팔로잉 해제"
 removeAllFollowingDescription: "{host} 서버의 모든 팔로잉을 해제합니다. 해당 서버가 더 이상 존재하지 않는 경우 등에 실행해 주세요."
-userSuspended: "이 계정은 정지된 상태입니다."
+userSuspended: "이 사용자는 정지되었습니다."
 userSilenced: "이 계정은 사일런스된 상태입니다."
 yourAccountSuspendedTitle: "계정이 정지되었습니다"
 yourAccountSuspendedDescription: "이 계정은 서버의 이용 약관을 위반하거나, 기타 다른 이유로 인해 정지되었습니다. 자세한 사항은 관리자에게 문의해 주십시오. 계정을 새로 생성하지 마십시오."
@@ -752,7 +757,7 @@ experimentalFeatures: "실험실"
 experimental: "실험실"
 thisIsExperimentalFeature: "이 기능은 실험적인 기능입니다. 사양이 변경되거나 정상적으로 동작하지 않을 가능성이 있습니다."
 developer: "개발자"
-makeExplorable: "\"발견하기\"에 내 계정 보이기"
+makeExplorable: "계정을 쉽게 발견하도록 하기"
 makeExplorableDescription: "비활성화하면 \"발견하기\"에 나의 계정을 표시하지 않습니다."
 showGapBetweenNotesInTimeline: "타임라인의 노트 사이를 띄워서 표시"
 duplicate: "복제"
@@ -798,7 +803,7 @@ emailNotification: "메일 알림"
 publish: "게시"
 inChannelSearch: "채널에서 검색"
 useReactionPickerForContextMenu: "우클릭하여 리액션 선택기 열기"
-typingUsers: "{users} 님이 입력하고 있어요.."
+typingUsers: "{users}님이 입력 중"
 jumpToSpecifiedDate: "특정 날짜로 이동"
 showingPastTimeline: "과거의 타임라인을 표시하고 있어요"
 clear: "지우기"
@@ -832,6 +837,7 @@ administration: "관리"
 accounts: "계정"
 switch: "전환"
 noMaintainerInformationWarning: "관리자 정보가 설정되어 있지 않습니다."
+noInquiryUrlWarning: "문의처 주소를 설정하지 않았습니다."
 noBotProtectionWarning: "Bot 방어가 설정되어 있지 않습니다."
 configure: "설정하기"
 postToGallery: "갤러리에 업로드"
@@ -1021,6 +1027,7 @@ thisPostMayBeAnnoyingHome: "홈에 게시"
 thisPostMayBeAnnoyingCancel: "그만두기"
 thisPostMayBeAnnoyingIgnore: "이대로 게시"
 collapseRenotes: "이미 본 리노트를 간략화하기"
+collapseRenotesDescription: "반응이나 리노트를 한 노트를 접어서 표시합니다."
 internalServerError: "내부 서버 오류"
 internalServerErrorDescription: "내부 서버에서 예기치 않은 오류가 발생했습니다."
 copyErrorInfo: "오류 정보 복사"
@@ -1090,7 +1097,7 @@ serverRules: "서버 규칙"
 pleaseConfirmBelowBeforeSignup: "이 서버에 가입하기 전에 아래 사항을 확인하여 주십시오."
 pleaseAgreeAllToContinue: "계속하시려면 모든 항목에 동의하십시오."
 continue: "계속"
-preservedUsernames: "예약된 사용자명"
+preservedUsernames: "예약한 사용자 이름"
 preservedUsernamesDescription: "예약할 사용자명을 한 줄에 하나씩 입력합니다. 여기에서 지정한 사용자명으로는 계정을 생성할 수 없게 됩니다. 단, 관리자 권한으로 계정을 생성할 때에는 해당되지 않으며, 이미 존재하는 계정도 영향을 받지 않습니다."
 createNoteFromTheFile: "이 파일로 노트를 작성"
 archive: "아카이브"
@@ -1230,10 +1237,20 @@ useTotp: "일회용 비밀번호 사용"
 useBackupCode: "백업 코드 사용"
 launchApp: "앱 실행"
 useNativeUIForVideoAudioPlayer: "브라우저 UI에서 미디어 재생"
+keepOriginalFilename: "원본 파일 이름을 유지"
+keepOriginalFilenameDescription: "이 설정을 끄면 업로드를 할 때 파일 이름이 자동으로 무작위 문자열로 바뀝니다."
+noDescription: "설명문이 없습니다"
+alwaysConfirmFollow: "팔로우일 때 항상 확인하기"
+inquiry: "문의하기"
 _delivery:
+  status: "전송 상태"
   stop: "정지됨"
+  resume: "전송 다시 시작"
   _type:
     none: "배포 중"
+    manuallySuspended: "수동 정지 중"
+    goneSuspended: "서버 삭제를 이유로 정지 중"
+    autoSuspendedForNotResponding: "서버 응답 없음을 이유로 정지 중"
 _bubbleGame:
   howToPlay: "설명"
   hold: "홀드"
@@ -1359,6 +1376,8 @@ _serverSettings:
   fanoutTimelineDescription: "활성화하면 각종 타임라인을 가져올 때의 성능을 대폭 향상하며, 데이터베이스의 부하를 줄일 수 있습니다. 단, Redis의 메모리 사용량이 증가합니다. 서버의 메모리 용량이 작거나, 서비스가 불안정해지는 경우 비활성화할 수 있습니다."
   fanoutTimelineDbFallback: "데이터베이스를 예비로 사용하기"
   fanoutTimelineDbFallbackDescription: "활성화하면 타임라인의 캐시되어 있지 않은 부분에 대해 DB에 질의하여 정보를 가져옵니다. 비활성화하면 이를 실행하지 않음으로써 서버의 부하를 줄일 수 있지만, 타임라인에서 가져올 수 있는 게시물 범위가 한정됩니다."
+  inquiryUrl: "문의처 URL"
+  inquiryUrlDescription: "서버 운영자에게 보내는 문의 양식의 URL이나 운영자의 연락처 등이 적힌 웹 페이지의 URL을 설정합니다."
 _accountMigration:
   moveFrom: "다른 계정에서 이 계정으로 이사"
   moveFromSub: "다른 계정에 대한 별칭을 생성"
@@ -1678,7 +1697,7 @@ _role:
     pinMax: "고정할 수 있는 노트 수"
     antennaMax: "만들 수 있는 안테나 수"
     wordMuteMax: "단어 뮤트할 수 있는 문자 수"
-    webhookMax: "만들 수 있는 웹후크 수"
+    webhookMax: "만들 수 있는 Webhook 수"
     clipMax: "만들 수 있는 클립 수"
     noteEachClipsMax: "클립에 넣을 수 있는 노트 수"
     userListMax: "만들 수 있는 사용자 리스트 수"
@@ -1693,6 +1712,11 @@ _role:
     roleAssignedTo: "수동 역할에 이미 할당됨"
     isLocal: "로컬 사용자"
     isRemote: "리모트 사용자"
+    isCat: "고양이 사용자"
+    isBot: "봇 사용자"
+    isSuspended: "정지된 사용자"
+    isLocked: "잠금 계정 사용자"
+    isExplorable: "‘계정을 쉽게 발견하도록 하기’를 활성화한 사용자"
     createdLessThan: "가입한 지 다음 일수 이내인 유저"
     createdMoreThan: "가입한 지 다음 일수 이상인 유저"
     followersLessThanOrEq: "팔로워 수가 다음 이하인 유저"
@@ -1913,8 +1937,6 @@ _sfx:
   note: "새 노트"
   noteMy: "내 노트"
   notification: "알림"
-  antenna: "안테나 수신"
-  channel: "채널 알림"
   reaction: "리액션 선택"
 _soundSettings:
   driveFile: "드라이브에 있는 오디오를 사용"
@@ -1975,6 +1997,7 @@ _2fa:
   backupCodesDescription: "인증 앱을 사용할 수 없게 된 경우 아래 백업 코드를 사용하여 계정에 액세스 할 수 있습니다.이 코드들은 반드시 안전한 장소에 보관하십시오.각 코드는 한 번만 사용할 수 있습니다."
   backupCodeUsedWarning: "백업 코드가 사용되었습니다.인증 앱을 사용할 수 없게 된 경우, 조속히 인증 앱을 다시 설정해 주십시오."
   backupCodesExhaustedWarning: "백업 코드가 모두 사용되었습니다.인증 앱을 사용할 수 없는 경우 더 이상 계정에 액세스하는 것이 불가능합니다.인증 앱을 다시 등록해 주세요."
+  moreDetailedGuideHere: "여기에 자세한 설명이 있습니다"
 _permissions:
   "read:account": "계정의 정보를 봅니다"
   "write:account": "계정의 정보를 변경합니다"
@@ -2163,7 +2186,7 @@ _postForm:
     c: "무엇을 생각하고 있나요?"
     d: "말하고 싶은 게 있나요?"
     e: "여기에 적어 주세요"
-    f: "글 쓰기를 기다려요…"
+    f: "작성해주시길 기다리고 있어요..."
 _profile:
   name: "이름"
   username: "사용자 이름"
@@ -2338,6 +2361,7 @@ _deck:
   alwaysShowMainColumn: "메인 칼럼 항상 표시"
   columnAlign: "칼럼 정렬"
   addColumn: "칼럼 추가"
+  newNoteNotificationSettings: "새 노트 알림 설정"
   configureColumn: "칼럼 설정"
   swapLeft: "왼쪽으로 이동"
   swapRight: "오른쪽으로 이동"
@@ -2376,6 +2400,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "등록일이 오래된 순"
 _webhookSettings:
   createWebhook: "Webhook 생성"
+  modifyWebhook: "Webhook 수정"
   name: "이름"
   secret: "시크릿"
   events: "Webhook을 실행할 타이밍"
@@ -2388,6 +2413,25 @@ _webhookSettings:
     renote: "누군가 내 글을 리노트했을 때"
     reaction: "누군가 내 노트에 리액션했을 때"
     mention: "누군가 나를 멘션했을 때"
+  _systemEvents:
+    abuseReport: "유저로부터 신고를 받았을 때"
+    abuseReportResolved: "받은 신고를 처리했을 때"
+  deleteConfirm: "Webhook을 삭제할까요?"
+_abuseReport:
+  _notificationRecipient:
+    createRecipient: "신고 수신자 추가"
+    modifyRecipient: "신고 수신자 편집"
+    recipientType: "알림 수신 유형"
+    _recipientType:
+      mail: "이메일"
+      webhook: "Webhook"
+      _captions:
+        mail: "모더레이터 권한을 가진 사용자의 이메일 주소에 알림을 보냅니다 (신고를 받은 때에만)"
+        webhook: "지정한 SystemWebhook에 알림을 보냅니다 (신고를 받은 때와 해결했을 때에 송신)"
+    keywords: "키워드"
+    notifiedUser: "신고 알림을 보낼 유저"
+    notifiedWebhook: "사용할 Webhook"
+    deleteConfirm: "수신자를 삭제하시겠습니까?"
 _moderationLogTypes:
   createRole: "역할 생성"
   deleteRole: "역할 삭제"
@@ -2403,7 +2447,7 @@ _moderationLogTypes:
   updateUserNote: "조정 기록 갱신"
   deleteDriveFile: "파일 삭제"
   deleteNote: "노트 삭제"
-  createGlobalAnnouncement: "모든 공지사항 만들기"
+  createGlobalAnnouncement: "전역 공지사항 생성"
   createUserAnnouncement: "사용자 공지사항 만들기"
   updateGlobalAnnouncement: "모든 공지사항 수정"
   updateUserAnnouncement: "사용자 공지사항 수정"
@@ -2425,6 +2469,12 @@ _moderationLogTypes:
   deleteAvatarDecoration: "아바타 장식 삭제"
   unsetUserAvatar: "유저 아바타 제거"
   unsetUserBanner: "유저 배너 제거"
+  createSystemWebhook: "SystemWebhook을 생성"
+  updateSystemWebhook: "SystemWebhook을 수정"
+  deleteSystemWebhook: "SystemWebhook을 삭제"
+  createAbuseReportNotificationRecipient: "신고 알림 수신자 생성"
+  updateAbuseReportNotificationRecipient: "신고 알림 수신자 편집"
+  deleteAbuseReportNotificationRecipient: "신고 알림 수신자 삭제"
 _fileViewer:
   title: "파일 상세"
   type: "파일 유형"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index 087bac3745..1bead5635d 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -18,15 +18,15 @@ enterUsername: "ປ້ອນຊື່ຜູ້ໃຊ້"
 renotedBy: "Renoted ໂດຍ {user}"
 noNotes: "ບໍ່ມີ note"
 noNotifications: "ບໍ່ມີການແຈ້ງເຕືອນ"
-instance: "ອີນສະແຕນ"
-settings: "ກຳນົດຄ່າ"
+instance: "ເຊີຟເວີຣ໌"
+settings: "ຕັ້ງຄ່າ"
 notificationSettings: "ຕັ້ງຄ່າການແຈ້ງເຕືອນ"
 basicSettings: "ການຕັ້ງຄ່າພື້ນຖານ"
 otherSettings: "ການຕັ້ງຄ່າອື່ນໆ"
-openInWindow: "ເປີດໃນປ່ອງຢ້ຽມ"
-profile: "ໂພຼຟາຍ"
+openInWindow: "ເປີດໃນ window"
+profile: "ໂປຣໄຟລ໌"
 timeline: "ໄທມ໌ໄລນ໌"
-noAccountDescription: "ຜູ້ໃຊ້ນີ້ຍັງບໍ່ໄດ້ຂຽນໃນຊີວະປະຫວັດຂອງເຂົາເຈົ້າເທື່ອ"
+noAccountDescription: "ຜູ້ໃຊ້ຄົນນີ້ຍັງບໍ່ໄດ້ຂຽນຄຳແນະນຳໂຕ"
 login: "ເຂົ້າ​ສູ່​ລະ​ບົບ"
 loggingIn: "ກຳລັງເຂົ້າສູ່ລະບົບ..."
 logout: "ອອກ​ຈາກ​ລະ​ບົບ"
@@ -37,7 +37,7 @@ users: "ຜູ້ໃຊ້"
 addUser: "ເພີ່ມຜູ້ໃຊ້"
 favorite: "ເພີ່ມໃສ່ລາຍການທີ່ມັກ"
 favorites: "ລາຍການທີ່ມັກ"
-unfavorite: "ລຶບອອກຈາກລາຍການທີ່ມັກ"
+unfavorite: "ເອົາອອກຈາກລາຍການທີ່ມັກ"
 favorited: "ເພີ່ມໃສ່ລາຍການທີ່ມັກແລ້ວ"
 alreadyFavorited: "ເພີ່ມເຂົ້າໃນລາຍການທີ່ມັກແລ້ວ."
 cantFavorite: "ບໍ່ສາມາດເພີ່ມໃສ່ລາຍການທີ່ມັກໄດ້."
@@ -48,41 +48,41 @@ copyLink: "ຄັດລອກລິ້ງ"
 copyLinkRenote: "ຄັດລອກລິ້ງຂອງ renote"
 delete: "ລຶບ"
 deleteAndEdit: "ລຶບ​ແລະ​ແກ້​ໄຂ​"
-deleteAndEditConfirm: "ເຈົ້າ​ແນ່​ໃຈ​ບໍ່? ທີ່ທ່ານຕ້ອງການທີ່ຈະລຶບ note ນີ້ ແລະແກ້ໄຂມັນ ທ່ານອາດຈະສູນເສຍ reaction, renote, ແລະການຕອບກັບທັງໝົດ"
+deleteAndEditConfirm: "ຕ້ອງການລຶບ note ນີ້ແລະແກ້ໄຂໃໝ່ແມ່ນບໍ່? reaction, renote ແລະການຕອບກັບຕໍ່ note ນີ້ ທັງເບິດຈະຖືກລຶບອອກ"
 addToList: "ເພີ່ມໃສ່ລາຍຊື່"
 addToAntenna: "ເພີ່ມໃສ່ເສົາອາກາດ"
 sendMessage: "ສົ່ງຂໍ້ຄວາມ"
-copyRSS: "ສຳເນົາ RSS"
-copyUsername: "ສຳເນົາຊື່ຜູ້ໃຊ້"
-copyUserId: "ສຳເນົາ ID ຜູ້ໃຊ້"
-copyNoteId: "ສຳເນົາ ID ບັນທຶກ"
-copyFileId: "ສຳເນົາ ID ໄຟລ໌"
-copyFolderId: "ສຳເນົາ ID ໂຟນເດີ"
-copyProfileUrl: "ສຳເນົາ URL ໂປຣໄຟລ໌"
+copyRSS: "ຄັດລອກ RSS"
+copyUsername: "ຄັດລອກຊື່ຜູ້ໃຊ້"
+copyUserId: "ຄັດລອກ ID ຜູ້ໃຊ້"
+copyNoteId: "ຄັດລອກ ID ຂອງ note"
+copyFileId: "ຄັດລອກ ID ໄຟລ໌"
+copyFolderId: "ຄັດລອກ ID ໂຟລ໌ເດີຣ໌"
+copyProfileUrl: "ຄັດລອກ URL ໂປຣໄຟລ໌"
 searchUser: "ຄົ້ນຫາຜູ້ໃຊ້"
-reply: "ຕອບ​ໄປ​ທີ"
+reply: "ຕອບ​ກັບ"
 loadMore: "ໂຫຼດເພີ່ມເຕີມ"
 showMore: "ໂຫຼດເພີ່ມເຕີມ"
 showLess: "ປິດ"
-youGotNewFollower: "ໄດ້ຕິດຕາມທ່ານ"
-receiveFollowRequest: "ປະຕິບັດຕາມຄໍາຮ້ອງຂໍທີ່ໄດ້ຮັບ"
-followRequestAccepted: "ຜູ້ຕິດຕາມໄດ້ຍອມຮັບຄໍາຮ້ອງຂໍຂອງທ່ານ"
-mention: "ກ່າວຖືງ"
-mentions: "ກ່າວເຖິງ"
+youGotNewFollower: "ໄດ້ຕິດຕາມເຈົ້າ"
+receiveFollowRequest: "ມີຄຳຂໍຕິດຕາມສົ່ງມາ"
+followRequestAccepted: "ການຕິດຕາມໄດ້ຮັບອນຸຍາດແລ້ວ"
+mention: "ເວົ້າເຖີງ"
+mentions: "ເວົ້າເຖີງເຈົ້າ"
 directNotes: "ໂພສ Direct note"
 importAndExport: "ນໍາເຂົ້າ / ສົ່ງອອກ"
 import: "ນຳເຂົ້າ"
 export: "ສົ່ງອອກ"
 files: "ໄຟລ໌"
 download: "ດາວໂຫລດ"
-driveFileDeleteConfirm: "ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບໄຟລ໌ \"{name}\"? note ທີ່ມີໄຟລ໌ແນບນີ້ຈະຖືກລຶບຖິ້ມ"
-unfollowConfirm: "ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການເຊົາຕິດຕາມ {name}?"
-exportRequested: "ໃນເວລາທີ່ທ່ານໄດ້ຮ້ອງຂໍການສົ່ງອອກ ມັນອາດຈະໃຊ້ເວລາບາງເວລາ ແລະມັນຈະຖືກເພີ່ມໃສ່ drive ຂອງທ່ານເມື່ອມັນສຳເລັດແລ້ວ"
-importRequested: "ໃນເວລາທີ່ທ່ານໄດ້ຮ້ອງຂໍການນໍາເຂົ້າ ມັນອາດຈະໃຊ້ເວລາບາງເວລາ"
+driveFileDeleteConfirm: "ຕ້ອງການລຶບໄຟລ໌ “{name}” ແມ່ນບໍ່? Note ທີ່ແນບມາກັບໄຟລ໌ນີ້ຈະຖືກລຶບອອກ"
+unfollowConfirm: "ຕ້ອງການເລີກຕິດຕາມ {name} ແມ່ນບໍ່?"
+exportRequested: "ເຈົ້າໄດ້ຮ້ອງຂໍການສົ່ງອອກ ອາດໃຊ້ເວລາຈັກໜ່ອຍ ເມື່ອແລ້ວຈະຖືກເພີ່ມໃສ່ drive"
+importRequested: "ເຈົ້າໄດ້ຮ້ອງຂໍການນຳເຂົ້າ ການດຳເນິນການນີ້ອາດໃຊ້ເວລາຈັກໜ່ອຍ"
 lists: "ລາຍການ"
-noLists: "ທ່ານ​ບໍ່​ມີ​ລາຍ​ການ​ໃດໆ​"
-note: "ບັນທຶກ"
-notes: "ບັນທຶກ"
+noLists: "ບໍ່​ມີ​ລາຍ​ການ​ໃດໆ​"
+note: "Note"
+notes: "Note"
 following: "ກຳລັງຕິດຕາມ"
 followers: "ຜູ້ຕິດຕາມ"
 followsYou: "ຕິດ​ຕາມ​ເຈົ້າ"
@@ -124,11 +124,11 @@ reactions: "reaction"
 attachCancel: "ເອົາໄຟລ໌ແນບ"
 mute: "ປີດສຽງ"
 unmute: "ເປີດສຽງ"
-block: "ບ໋ອກ"
-unblock: "ຍົກເລີກກາຮົບລັອກ"
+block: "ບລັອກ"
+unblock: "ເລີກບລັອກ"
 suspend: "ລະງັບ"
 unsuspend: "ເຊົາ​ລະ​ງັບ"
-selectList: "ເລືອກບັນຊີລາຍການ"
+selectList: "ເລືອກລາຍຊື່"
 editList: "ແກ້ໄຂລາຍຊື່"
 selectChannel: "ເລືອກຊ່ອງ"
 selectAntenna: "ເລືອກເສົາອາກາດ"
@@ -151,30 +151,30 @@ flagShowTimelineRepliesDescription: "ສະແດງການຕອບກັບ
 autoAcceptFollowed: "ອະນຸມັດອັດຕະໂນມັດຕາມຄຳຮ້ອງຂໍຈາກຜູ້ໃຊ້ທີ່ທ່ານກຳລັງຕິດຕາມຢູ່"
 addAccount: "ເພີ່ມບັນຊີ"
 loginFailed: "ການເຂົ້າສູ່ລະບົບບໍ່ສຳເລັດ"
-showOnRemote: "ເບິ່ງຢູ່ໃນຕົວຢ່າງໄລຍະໄກ"
+showOnRemote: "ເບິ່ງໃນເຊີຟເວີຣ໌ໄລຍະໄກ"
 general: "ທົ່ວໄປ"
 wallpaper: "ພາບພື້ນຫລັງ"
 setWallpaper: "ຕັ້ງເປັນພາບພື້ນຫຼັງ"
 removeWallpaper: "ລຶບຮູບວໍເປເປີອອກ"
 searchWith: "ຊອກຫາ: {q}"
-youHaveNoLists: "ທ່ານ​ບໍ່​ມີ​ລາຍ​ການ​ໃດໆ​"
+youHaveNoLists: "ເຈົ້າບໍ່ມີລາຍຊື່ໃດໆ"
 proxyAccount: "ບັນຊີພຣັອກຊີ"
-host: "ໂຮດສ"
+host: "ໂຮສຕ໌"
 selectUser: "ເລືອກຜູ້ໃຊ້"
 recipient: "ເຖິງ"
 annotation: "ຄຳເຫັນ"
 federation: "ສະຫະພັນ"
-instances: "ອີນສະແຕນ"
+instances: "ເຊີຟເວີຣ໌"
 registeredAt: "ລົງທະບຽນຢູ່"
 storageUsage: "ບ່ອນ​ຈັດ​ເກັບ​ຂໍ້​ມູນທີ່ໃຊ້"
-charts: "ອັນດັບເພງ"
+charts: "ແຜນພູມ"
 perHour: "ຕໍ່ຊົ່ວໂມງ"
 perDay: "ຕໍ່​ມື້"
 stopActivityDelivery: "ຢຸດເຊົາການສົ່ງກິດຈະກໍາ"
 blockThisInstance: "ຂັດຂວາງຕົວຢ່າງນີ້"
 operations: "ການດຳເນີນງານ"
 software: "ຊອບແວ"
-version: "ສະບັບ"
+version: "ເວີຣ໌ຊັນ"
 metadata: "Metadata"
 withNFiles: "{n} ໄຟລ໌(s)"
 monitor: "ຈໍພາບ"
@@ -199,15 +199,15 @@ federating: "ສະຫະພັນ"
 blocked: "ບລັອກແລ້ວ "
 suspended: "ໂຈະ"
 all: "ທັງໝົດ"
-subscribing: "ສະໝັກສະມາຊິກແລັວ"
-publishing: "ການ​ພິມ​ເຜີຍ​ແຜ່"
+subscribing: "ກຳລັງສະມັກສະມາຊິກ"
+publishing: "ກຳລັງ​ເຜີຍ​ແພ່"
 notResponding: "ບໍ່ຕອບສະໜອງ"
-instanceFollowing: "ກຳລັງຕິດຕາມສຸດຕົວຢ່າງ"
-instanceFollowers: "ຜູ້ຕິດຕາມຕົວຢ່າງ"
-instanceUsers: "ຜູ້​ຊົມ​ໃຊ້​ຂອງ​ຕົວ​ຢ່າງ​ນີ້​"
+instanceFollowing: "ກຳລັງຕິດຕາມບົນເຊີຟເວີຣ໌"
+instanceFollowers: "ຜູ້ຕິດຕາມຂອງເຊີຟເວີຣ໌"
+instanceUsers: "ຜູ້​ໃຊ້​ຂອງ​ເຊີຟເວີຣ໌ນີ້"
 changePassword: "ປ່ຽນ​ລະ​ຫັດ​ຜ່ານ"
 security: "ຄວາມປອດໄພ"
-retypedNotMatch: "ວັດສະດຸປ້ອນບໍ່ກົງກັນ"
+retypedNotMatch: "ປ້ອນຂໍ້ມູນບໍ່ກົງກັນ"
 currentPassword: "ລະຫັດຜ່ານປະຈຸບັນ"
 newPassword: "ລະຫັດຜ່ານໃໝ່"
 newPasswordRetype: "ໃສ່ລະຫັດຜ່ານໃໝ່ອີກເທື່ອໜຶ່ງ"
@@ -223,14 +223,14 @@ remove: "ລຶບ"
 removed: "ລຶບແລ້ວ"
 resetAreYouSure: "ຣີ​ເຊັດບໍ?"
 saved: "ບັນທຶກແລ້ວ"
-messaging: "ແຊ໋ດ"
+messaging: "ແຊັຕ"
 upload: "ອັບໂຫຼດ"
 keepOriginalUploading: "ຮັກສາຮູບພາບຕົ້ນສະບັບ"
 fromDrive: "ຈາກ Drive"
 fromUrl: "ຈາກ URL"
 uploadFromUrl: "ອັບໂຫຼດຈາກ URL"
 uploadFromUrlDescription: "URL ຂອງໄຟລ໌ທີ່ທ່ານຕ້ອງການອັບໂຫລດ"
-uploadFromUrlRequested: "ຮ້ອງຂໍການອັບໂຫລດ"
+uploadFromUrlRequested: "ຮ້ອງຂໍການອັບໂຫລດແລ້ວ"
 explore: "ສຳຫຼວດ"
 messageRead: "ອ່ານແລ້ວ"
 startMessaging: "ເລີ່ມການສົນທະນາໃໝ່"
@@ -244,47 +244,47 @@ images: "ຮູບພາບ"
 image: "ຮູບພາບ"
 birthday: "ວັນເກີດ"
 yearsOld: "{age} ປີ"
-registeredDate: "ວັນທີ່ເປັນສະມາຊິກ"
+registeredDate: "ວັນທີ່ລົງທະບຽນ"
 location: "ທີ່ຕັ້ງ"
-theme: "ແທ໋ມ"
-themeForLightMode: "ຮູບແບບສີສັນເພື່ອໃຊ້ໃນໂໝດແສງ"
-themeForDarkMode: "ຮູບແບບສີສັນທີ່ຈະໃຊ້ຢູ່ໃນໂໝດມືດ"
+theme: "Theme"
+themeForLightMode: "Theme ໃຊ້ໃນໂໝດສະຫວ່າງ"
+themeForDarkMode: "Theme ໃຊ້ໃນໂໝດມືດ"
 light: "ສະຫວ່າງ"
 dark: "ມືດ"
 lightThemes: "ຊຸດຮູບແບບສະຫວ່າງ"
 darkThemes: "ຮູບແບບສີສັນມືດ"
 syncDeviceDarkMode: "ຊິງຄ໌ໂໝດມືດກັບການຕັ້ງຄ່າທົ່ວອຸປະກອນ"
-drive: "ຂັບ"
+drive: "Drive"
 fileName: "ຊື່ໄຟລ໌"
 selectFile: "ເລືອກໄຟລ໌"
 selectFiles: "ເລືອກໄຟລ໌"
 selectFolder: "ເລືອກໂຟລເດີ"
 selectFolders: "ເລືອກໂຟລເດີ"
 renameFile: "ປ່ຽນຊື່ໄຟລ໌"
-folderName: "ຊື່ໂຟນເດີ"
+folderName: "ຊື່ໂຟລເດີຣ໌"
 createFolder: "​ສ້າງ​ໂຟ​ລ​ເດີ"
 renameFolder: "ປ່ຽນຊື່ໂຟນເດີນີ້"
 deleteFolder: "ລົບໂຟ​ລ​ເດີ​"
 addFile: "ເພີ່ມໄຟລ໌"
 emptyDrive: "Drive ຂອງທ່ານຫວ່າງເປົ່າ"
-emptyFolder: "ໂຟນເດີນີ້ເປົ່າຫວ່າງ"
+emptyFolder: "ໂຟລເດີຣ໌ນີ້ວ່າງເປົ່າ"
 unableToDelete: "ບໍ່​ສາ​ມາດລົບໄດ້"
 inputNewFileName: "ໃສ່ຊື່ໄຟລ໌ໃໝ່"
 inputNewDescription: "ໃສ່ຄຳບັນຍາຍໃໝ່"
 inputNewFolderName: "ໃສ່ຊື່ໂຟນເດີໃໝ່"
 circularReferenceFolder: "ໂຟນເດີປາຍທາງແມ່ນໂຟນເດີຍ່ອຍຂອງໂຟນເດີທີ່ທ່ານຕ້ອງການຍ້າຍ"
 rename: "ປ່ຽນຊື່"
-doNothing: "ບໍ່ສົນໃຈ"
-watch: "ເບິ່ງ"
-unwatch: "ຢຸດເບິ່ງ"
+doNothing: "ຢ່າມັນ"
+watch: "ເພັ່ງເລັງ"
+unwatch: "ຢຸດເພັ່ງເລັງ"
 accept: "ອະນຸຍາດ"
 reject: "ປະຕິເສດ"
 normal: "ປົກກະຕິ"
 instanceName: "ຊື່ເຊີເວີ້"
-instanceDescription: "ຄໍາອະທິບາຍຕົວຢ່າງ"
+instanceDescription: "ຄຳອະທິບາຍແນະນຳເຊີຟເວີຣ໌"
 maintainerName: "ຜູ້ດູແລ"
-maintainerEmail: "ອີເມວ admin"
-tosUrl: "ເງື່ອນໄຂການໃຫ້ບໍລິການ URL"
+maintainerEmail: "ອີເມລຜູ້ດູແລ"
+tosUrl: " URL ເງື່ອນໄຂການໃຫ້ບໍລິການ"
 thisYear: "ປີນີ້"
 thisMonth: "ເດືອນນີ້"
 today: "ມື້ນີ້"
@@ -292,34 +292,34 @@ dayX: "ວັນ {day}"
 monthX: "ເດືອນ {month}"
 yearX: "ປີ {year}"
 pages: "ໜ້າ"
-integration: "ຄວາມສຳພັນຂອງ"
+integration: "ເຊື່ອມໂຍງ"
 connectService: "ເຊື່ອມຕໍ່"
 disconnectService: "ຕັດການເຊື່ອມຕໍ່"
 enableLocalTimeline: "ເປີດໃຊ້ທາມລາຍທ້ອງຖິ່ນ"
 enableGlobalTimeline: "ເປີດໃຊ້ທາມລາຍທົ່ວໂລກ"
-disablingTimelinesInfo: "ຜູ້ເບິ່ງແຍງລະບົບ ແລະຜູ້ຄວບຄຸມຈະມີການເຂົ້າເຖິງທຸກກຳນົດເວລາ, ເຖິງແມ່ນວ່າຈະບໍ່ໄດ້ເປີດໃຊ້ງານກໍຕາມ"
+disablingTimelinesInfo: "ຜູ້ດູແລລະບບແລະຜູ້ຄວບຄຸມຈະສາມາດເຂົ້າເຖີງໄທມ໌ໄລນ໌ທັ້ງເບີດ ເຖີງວ່າຈະບໍ່ໄດ້ເປີດໃຊ້ງານກໍ່ຕາມ"
 registration: "ລົງທະບຽນ"
 enableRegistration: "ເປີດໃຊ້ການລົງທະບຽນຜູ້ໃຊ້ໃໝ່"
 invite: "ເຊີນ"
-driveCapacityPerLocalAccount: "ຄວາມອາດສາມາດຂັບຕໍ່ຜູ້ໃຊ້ທ້ອງຖິ່ນ"
-driveCapacityPerRemoteAccount: "ໄດຣຟ໌ຄວາມອາດສາມາດຕໍ່ຜູ້ໃຊ້ທາງໄກ"
+driveCapacityPerLocalAccount: "ຄວາມຈຸຂອງ drive ຕໍ່ຜູ້ໃຊ້ທ້ອງຖິ່ນ"
+driveCapacityPerRemoteAccount: "ຄວາມຈຸຂອງ drive ຕໍ່ຜູ້ໃຊ້ໄລຍະໄກ"
 basicInfo: "ຂໍ້ມຸນເບື້ອງຕົ້ນ"
-pinnedNotes: "ບັນທຶກທີ່ປັກໝຸດໄວ້"
-hcaptchaSiteKey: "ກະແຈໄຊທ໌"
-hcaptchaSecretKey: "ກະແຈລັບ"
-mcaptchaSiteKey: "ກະແຈໄຊທ໌"
-mcaptchaSecretKey: "ກະແຈລັບ"
+pinnedNotes: "Note ທີ່ປັກໝຸດໄວ້"
+hcaptchaSiteKey: "Site key"
+hcaptchaSecretKey: "Secret key"
+mcaptchaSiteKey: "Site key"
+mcaptchaSecretKey: "Secret Key"
 recaptcha: "reCAPTCHA"
-enableRecaptcha: "ເປີດໃຊ້ງານລີແຄ໋ບຈາ"
-recaptchaSiteKey: "ກະແຈໄຊທ໌"
-recaptchaSecretKey: "ກະແຈລັບ"
-turnstileSiteKey: "ກະແຈໄຊທ໌"
-turnstileSecretKey: "ກະແຈລັບ"
+enableRecaptcha: "ເປີດໃຊ້ງານ reCAPTCHA"
+recaptchaSiteKey: "Site key"
+recaptchaSecretKey: "Secret key"
+turnstileSiteKey: "Site key"
+turnstileSecretKey: "Secret key"
 name: "ຊື່"
 userList: "ລາຍການ"
 about: "ກ່ຽວກັບ"
 aboutMisskey: "ກ່ຽວກັບ Misskey"
-administrator: "ຜູ້ບໍລິຫານ"
+administrator: "ຜູ້ດູແລ"
 token: "ໂທເຄັນ"
 share: "ແບ່ງປັນ"
 notFound: "ບໍ່ພົບ"
@@ -332,27 +332,27 @@ title: "ຫົວຂໍ້"
 text: "ຂໍ້ຄວາມ"
 enable: "ເປີດໃຊ້"
 next: "ຕໍ່ໄປ"
-retype: "ເຂົ້າໄປອີກຄັ້ງ"
-quoteAttached: "ວົງຢືມ"
+retype: "ລອງພິມລະຫັດອີກເທື່ອໜຶ່ງ"
+quoteAttached: "ອ້າງອິງ"
 invitations: "ເຊີນ"
 unavailable: "ບໍ່​ສາ​ມາດ​ໃຊ້​ໄດ້"
 language: "ພາສາ"
 aboutX: "ກ່ຽວກັບ {x}"
 emojiStyle: "ຮູບແບບອີໂມຈິ"
 native: "ພາ​ສາ​ແມ່"
-noHistory: "​ບໍ່​ມີ​ລາຍ​ການ​ຢູ່​ບ່ອນ​ນີ້"
+noHistory: "​ບໍ່​ມີປະຫວັດ"
 doing: "ກຳລັງປະມວນຜົນ..."
 category: "ຫມວດຫມູ່"
-tags: "ແທ໋ກ"
+tags: "Aliases"
 createAccount: "ສ້າງບັນຊີ"
-existingAccount: "ທີ່ມີຢູ່"
-dashboard: "ໜ້າປັດ"
+existingAccount: "ບັນຊີທີ່ມີຢູ່ແລ້ວ"
+dashboard: "Dashboard"
 local: "ທ້ອງຖິ່ນ"
 numberOfDays: "ຈຳນວນມື້"
 objectStorageBucket: "Bucket"
 objectStoragePrefix: "Prefix"
 objectStorageEndpoint: "Endpoint"
-objectStorageRegion: "ພາກ​ພື້ນ"
+objectStorageRegion: "ພູມິພາກ"
 deleteAll: "ລຶບທັງໝົດ"
 sounds: "ສຽງ"
 sound: "ສຽງ"
@@ -365,11 +365,11 @@ state: "ສະຖານະ"
 sort: "ຈັດຮຽງໂດຍ"
 ascendingOrder: "ນ້ອຍໄປຫາໃຫຍ່"
 descendingOrder: "ໃຫຍ່ຫານ້ອຍ"
-output: "ຜົນຜະລິດ"
-script: "ບົດ​ຄວາມ"
+output: "Output"
+script: "Script"
 menu: "ເມນູ"
-rearrange: "ຈັດລຽງຄືນ"
-poll: "ການພູນ"
+rearrange: "ຈັດລຽງໃໝ່"
+poll: "Poll"
 description: "ລາຍລະອຽດ"
 author: "ຜູ້ຂຽນ"
 manage: "ການຈັດການ"
@@ -383,7 +383,7 @@ permission: "ການອະນຸຍາດ"
 notificationType: "​ປະເພດການ​ແຈ້ງ​ເຕືອນ"
 edit: "ແກ້ໄຂ"
 email: "ອີເມວ"
-smtpHost: "ໂຮດສ"
+smtpHost: "ໂຮສຕ໌"
 smtpUser: "ຊື່ຜູ້ໃຊ້"
 smtpPass: "ລະຫັດຜ່ານ"
 clearCache: "ລຶບລ້າງແຄສ"
@@ -393,12 +393,12 @@ administration: "ການຈັດການ"
 middle: "ປານກາງ"
 searchByGoogle: "ຄົ້ນຫາ"
 file: "ໄຟລ໌"
-replies: "ຕອບ​ໄປ​ທີ"
+replies: "ຕອບ​ກັບ"
 renotes: "Renote"
 _delivery:
   stop: "ໂຈະ"
   _type:
-    none: "ການ​ພິມ​ເຜີຍ​ແຜ່"
+    none: "ກຳລັງ​ເຜີຍ​ແພ່"
 _role:
   _priority:
     middle: "ປານກາງ"
@@ -416,8 +416,8 @@ _sfx:
 _2fa:
   renewTOTPCancel: "ບໍ່​ແມ່ນ​ຕອນ​ນີ້"
 _widgets:
-  profile: "ໂພຼຟາຍ"
-  instanceInfo: "ອີນສະແຕນ"
+  profile: "ໂປຣໄຟລ໌"
+  instanceInfo: "ຂໍ້ມູລເຊີຟເວີຣ໌"
   notifications: "ການແຈ້ງເຕືອນ"
   timeline: "​ເສັ້ນກຳ​ນົດ​ເວ​ລາ​"
   activity: "ກິດຈະກຳ"
@@ -436,28 +436,28 @@ _profile:
 _exportOrImport:
   followingList: "ກຳລັງຕິດຕາມ"
   muteList: "ປີດສຽງ"
-  blockingList: "ບ໋ອກ"
+  blockingList: "ບລັອກ"
   userLists: "ລາຍການ"
 _charts:
   federation: "ສະຫະພັນ"
 _timelines:
   home: "ໜ້າຫຼັກ"
 _play:
-  script: "ບົດ​ຄວາມ"
+  script: "Script"
   summary: "ລາຍລະອຽດ"
 _pages:
   blocks:
     image: "ຮູບພາບ"
 _notification:
-  youWereFollowed: "ໄດ້ຕິດຕາມທ່ານ"
+  youWereFollowed: "ໄດ້ຕິດຕາມເຈົ້າ"
   _types:
     follow: "ກຳລັງຕິດຕາມ"
-    mention: "ໄດ້ກ່າວມາ"
+    mention: "ໄດ້ກ່າວເຖິງ"
     renote: "Renote"
-    quote: "ລວມຂໍ້ຄວາມອ້າງອີງ"
-    reaction: "ປະຕິກິລິຍາ"
+    quote: "ອ້າງອີງ"
+    reaction: "Reaction"
   _actions:
-    reply: "ຕອບ​ໄປ​ທີ"
+    reply: "ຕອບ​ກັບ"
     renote: "Renote"
 _deck:
   _columns:
@@ -465,8 +465,12 @@ _deck:
     tl: "​ເສັ້ນກຳ​ນົດ​ເວ​ລາ​"
     list: "ລາຍການ"
     channel: "ຊ່ອງ"
-    mentions: "ກ່າວເຖິງ"
+    mentions: "ກ່າວເຖິງເຈົ້າ"
 _webhookSettings:
   name: "ຊື່"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "ອີເມວ"
 _moderationLogTypes:
   suspend: "ລະງັບ"
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 2b4c9b7776..cd00ecf9ab 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -721,5 +721,9 @@ _deck:
     direct: "Direkte"
 _webhookSettings:
   name: "Navn"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "E-post"
 _moderationLogTypes:
   suspend: "Suspender"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 9d75f7a9d7..7513d056b4 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -1221,8 +1221,6 @@ _sfx:
   note: "Wpisy"
   noteMy: "Mój wpis"
   notification: "Powiadomienia"
-  antenna: "Anteny"
-  channel: "Powiadomienia kanału"
 _ago:
   future: "W przyszłości"
   justNow: "Przed chwilą"
@@ -1556,6 +1554,10 @@ _webhookSettings:
     renote: "Po udostępnieniu wpisu"
     reaction: "Po otrzymaniu reakcji"
     mention: "Po zostaniu wspomnianym"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Adres e-mail"
 _moderationLogTypes:
   suspend: "Zawieś"
   resetPassword: "Zresetuj hasło"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index cfc576b6e1..2e26f6d12a 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -1500,6 +1500,10 @@ _webhookSettings:
     follow: "Quando seguindo um usuário"
     followed: "Quando sendo seguido"
     renote: "Quando repostado"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "E-mail"
 _moderationLogTypes:
   suspend: "Suspender"
   resetPassword: "Redefinir senha"
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index 328d34405e..b4c9b90de9 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -728,6 +728,10 @@ _deck:
     mentions: "Mențiuni"
 _webhookSettings:
   name: "Nume"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Email"
 _moderationLogTypes:
   suspend: "Suspendă"
   resetPassword: "Resetează parola"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 71f5cad601..88f59155d6 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -1612,8 +1612,6 @@ _sfx:
   note: "Заметки"
   noteMy: "Собственные заметки"
   notification: "Уведомления"
-  antenna: "Антенна"
-  channel: "Канал"
 _ago:
   future: "Из будущего"
   justNow: "Только что"
@@ -1983,6 +1981,10 @@ _webhookSettings:
   createWebhook: "Создать вебхук"
   name: "Название"
   active: "Вкл."
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Электронная почта"
 _moderationLogTypes:
   suspend: "Заморозить"
   addCustomEmoji: "Добавлено эмодзи"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 52f6bf142c..41f8949196 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -1124,8 +1124,6 @@ _sfx:
   note: "Poznámky"
   noteMy: "Vlastná poznámka"
   notification: "Oznámenia"
-  antenna: "Antény"
-  channel: "Upozornenia kanála"
 _ago:
   future: "Budúcnosť"
   justNow: "Teraz"
@@ -1447,6 +1445,10 @@ _deck:
 _webhookSettings:
   name: "Názov"
   active: "Zapnuté"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Email"
 _moderationLogTypes:
   suspend: "Zmraziť"
   resetPassword: "Resetovať heslo"
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 089dc3949f..c1a998b8fb 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -512,7 +512,6 @@ _theme:
 _sfx:
   note: "Noter"
   notification: "Notifikationer"
-  antenna: "Antenner"
 _2fa:
   renewTOTPCancel: "Nej tack"
 _antennaSources:
@@ -577,6 +576,10 @@ _deck:
 _webhookSettings:
   name: "Namn"
   active: "Aktiverad"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "E-post"
 _moderationLogTypes:
   suspend: "Suspendera"
   resetPassword: "Återställ Lösenord"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index ab09ac4d5a..117920e88d 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -2,10 +2,10 @@
 _lang_: "ภาษาไทย"
 headlineMisskey: "เชื่อมต่อเครือข่ายโดยโน้ต"
 introMisskey: "ยินดีต้อนรับทุกคนจ้า! Misskey คือ ซอฟต์แวร์โอเพนซอร์สสำหรับบริการไมโครบล็อกกิ้ง (MicroBlogging) แบบกระจายศูนย์อำนาจ (Decentralized) \n\nเขียน “โน้ต (Note)” เพื่อส่งต่อเรื่องราวของคุณให้ทั้งโลกได้รับรู้📡\nและอย่าลืมที่จะ “รีแอคชั่น” กับเรื่องราวของคนอื่น ๆ ด้วยนะ! 👍\n\nท่องสำรวจโลกใบใหม่กันเถอะ🚀"
-poweredByMisskeyDescription: "{name} เป็นส่วนหนึ่งในบริการที่ถูกขับเคลื่อนโดยแพลตฟอร์มโอเพ่นซอร์ส <b>Misskey</b> (เรียกว่า \"อินสแตนซ์ Misskey\")"
+poweredByMisskeyDescription: "{name} เป็นหนึ่งในเซิร์ฟเวอร์ของแพลตฟอร์มโอเพ่นซอร์ส <b>Misskey</b>"
 monthAndDay: "{month}/{day}"
 search: "ค้นหา"
-notifications: "การเเจ้งเตือน"
+notifications: "เเจ้งเตือน"
 username: "ชื่อผู้ใช้"
 password: "รหัสผ่าน"
 forgotPassword: "ลืมรหัสผ่าน"
@@ -18,7 +18,7 @@ enterUsername: "กรอกชื่อผู้ใช้"
 renotedBy: "รีโน้ตโดย {user}"
 noNotes: "ไม่มีโน้ต"
 noNotifications: "ไม่มีการแจ้งเตือน"
-instance: "อินสแตนซ์"
+instance: "เซิร์ฟเวอร์"
 settings: "การตั้งค่า"
 notificationSettings: "ตั้งค่าการแจ้งเตือน"
 basicSettings: "การตั้งค่าพื้นฐาน"
@@ -48,7 +48,7 @@ copyLink: "คัดลอกลิงก์"
 copyLinkRenote: "คัดลอกลิงก์รีโน้ต"
 delete: "ลบ"
 deleteAndEdit: "ลบและแก้ไข"
-deleteAndEditConfirm: "คุณต้องการลบโน้ตนี้และแก้ไขใหม่ใช่ไหม? รีแอคชั่น รีโน้ต และการตอบกลับต่อโน้ตนี้ทั้งหมดจะถูกลบออกด้วย"
+deleteAndEditConfirm: "ต้องการลบโน้ตนี้และแก้ไขใหม่ใช่ไหม? รีแอคชั่น รีโน้ต และการตอบกลับต่อโน้ตนี้ทั้งหมดจะถูกลบออกด้วย"
 addToList: "เพิ่มลงรายชื่อ"
 addToAntenna: "เพิ่มไปยังเสาอากาศ"
 sendMessage: "ส่งข้อความ"
@@ -68,7 +68,7 @@ youGotNewFollower: "ได้ติดตามคุณ"
 receiveFollowRequest: "มีคำขอติดตามส่งมาหา"
 followRequestAccepted: "การติดตามได้รับการอนุมัติแล้ว"
 mention: "กล่าวถึง"
-mentions: "พูดถึง"
+mentions: "กล่าวถึงคุณ"
 directNotes: "โพสต์แบบไดเร็กต์"
 importAndExport: "นำเข้า / ส่งออก"
 import: "นำเข้า"
@@ -92,7 +92,7 @@ error: "ผิดพลาด!"
 somethingHappened: "อุ๊ย ! มีอะไรบางอย่างผิดพลาด"
 retry: "ลองใหม่อีกครั้ง"
 pageLoadError: "เกิดข้อผิดพลาดในการโหลดหน้านี้"
-pageLoadErrorDescription: "โดยปกติแล้วมักจะเกิดจากข้อผิดพลาดของเครือข่ายหรือแคชของเบราว์เซอร์ ลองล้างแคชแล้วลองใหม่อีกครั้งหลังจากรอสักครู่ "
+pageLoadErrorDescription: "ปัญหานี้มักเกิดจากแคชของเครือข่ายหรือเบราว์เซอร์ ควรล้างแคช, รอสักครู่ แล้วลองใหม่อีกครั้ง"
 serverIsDead: "เซิร์ฟเวอร์นี้ไม่มีการตอบสนอง โปรดกรุณารอสักครู่แล้วลองใหม่อีกครั้ง"
 youShouldUpgradeClient: "หากต้องการดูหน้านี้ กรุณาโหลดหน้าใหม่เพื่ออัปเดตไคลเอ็นต์ของคุณ"
 enterListName: "ป้อนนามเรียกของรายชื่อชุดนี้"
@@ -108,11 +108,14 @@ enterEmoji: "พิมพ์เอโมจิ"
 renote: "รีโน้ต"
 unrenote: "เลิกรีโน้ต"
 renoted: "รีโน้ตแล้ว"
+renotedToX: "รีโน้ตให้ {name} แล้ว"
 cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตใหม่ได้"
 cantReRenote: "รีโน้ตไม่สามารถรีโน้ตซ้ำได้"
 quote: "อ้างอิง"
 inChannelRenote: "รีโน้ตในช่องเท่านั้น"
 inChannelQuote: "อ้างอิงในช่องเท่านั้น"
+renoteToChannel: "รีโน้ตไปที่ช่อง"
+renoteToOtherChannel: "รีโน้ตไปยังช่องอื่น"
 pinnedNote: "โน้ตที่ปักหมุดไว้"
 pinned: "ปักหมุด"
 you: "คุณ"
@@ -163,20 +166,24 @@ addEmoji: "แทรกเอโมจิ"
 settingGuide: "การตั้งค่าที่แนะนำ"
 cacheRemoteFiles: "แคชไฟล์ระยะไกล"
 cacheRemoteFilesDescription: "หากเปิดใช้งาน ไฟล์ระยะไกลจะถูกแคชไว้ ทำให้แสดงภาพเร็วขึ้น แต่ก็ใช้พื้นที่เก็บข้อมูลของเซิร์ฟเวอร์มากขึ้นเช่นกัน สำหรับขีดจำกัดที่ผู้ใช้ระยะไกลถูกแคชไว้จะขึ้นอยู่กับความจุไดรฟ์ตามบทบาทของเขา เมื่อเกินแล้วไฟล์เก่าจะถูกลบออกและเก็บเป็นลิงก์แทน หากปิดใช้งาน ไฟล์ระยะไกลจะถูกเก็บเป็นลิงก์ตั้งแต่ต้น เราแนะนำให้ตั้งค่า proxyRemoteFiles ใน default.yml เป็น true เพื่อสร้างธัมบ์เนลและปกป้องความเป็นส่วนตัวของผู้ใช้"
-youCanCleanRemoteFilesCache: "คุณสามารถล้างแคชได้โดยคลิกที่ปุ่ม 🗑️ ในมุมมองการจัดการไฟล์"
+youCanCleanRemoteFilesCache: "สามารถลบแคชทั้งหมดได้โดยใช้ปุ่ม 🗑️ ในหน้าการจัดการไฟล์"
 cacheRemoteSensitiveFiles: "แคชไฟล์ระยะไกลที่มีเนื้อหาละเอียดอ่อน"
-cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานการตั้งค่านี้ ไฟล์ระยะไกลที่มีเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช"
+cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานการตั้งค่านี้ ไฟล์ระยะไกลที่มีเนื้อหาละเอียดอ่อนจะถูกโหลดโดยตรงจากเซิร์ฟเวอร์ระยะไกลโดยไม่มีการแคช"
 flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอต"
-flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ และยังสามารถปรับเปลี่ยนระบบภายในของ Misskey เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท"
+flagAsBotDescription: "เปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยโปรแกรม เมื่อเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นในการป้องกันการสร้างห่วงโซ่การโต้ตอบแบบอนันต์กับบอตตัวอื่น และปรับระบบภายในของ Misskey เพื่อจัดการบัญชีนี้ในฐานะบอต"
 flagAsCat: "เมี้ยววววววววววววววว!!!!!!!!!!!"
 flagAsCatDescription: "เหมียวเหมียวเมี้ยว??"
-flagShowTimelineReplies: "แสดงตอบกลับ ในไทม์ไลน์"
-flagShowTimelineRepliesDescription: "แสดงการตอบกลับของผู้ใช้งานไปยังโน้ตของผู้ใช้งานรายอื่นๆในไทม์ไลน์หากได้เปิดเอาไว้"
-autoAcceptFollowed: "อนุมัติคำขอติดตามโดยอัตโนมัติทันที จากผู้ใช้งานที่คุณกำลังติดตาม"
+flagShowTimelineReplies: "แสดงตอบกลับโน้ตลงไทม์ไลน์"
+flagShowTimelineRepliesDescription: "เมื่อเปิดใช้งาน จะแสดงการตอบกลับของผู้ใช้คนนั้นต่อโน้ตอื่นๆ ในไทม์ไลน์ด้วย"
+autoAcceptFollowed: "อนุมัติคำขอติดตามจากผู้ใช้ที่คุณติดตามอยู่โดยอัตโนมัติ"
 addAccount: "เพิ่มบัญชี"
 reloadAccountsList: "รีโหลดรายการบัญชีใหม่"
 loginFailed: "การเข้าสู่ระบบไม่สำเร็จ"
-showOnRemote: "ดูบนอินสแตนซ์ระยะไกล"
+showOnRemote: "ดูบนเซิร์ฟเวอร์ฝั่งระยะไกล"
+continueOnRemote: "ดำเนินการต่อบนเซิร์ฟเวอร์ฝั่งระยะไกล"
+chooseServerOnMisskeyHub: "เลือกเซิร์ฟเวอร์จาก Misskey Hub"
+specifyServerHost: "ระบุโดเมนของเซิร์ฟเวอร์โดยตรง"
+inputHostName: "โปรดป้อนโดเมน"
 general: "ทั่วไป"
 wallpaper: "ภาพพื้นหลัง"
 setWallpaper: "ตั้งค่าภาพพื้นหลัง"
@@ -185,23 +192,23 @@ searchWith: "ค้นหา: {q}"
 youHaveNoLists: "คุณไม่มีรายชื่อใดๆ "
 followConfirm: "ต้องการติดตาม {name} ใช่ไหม?"
 proxyAccount: "บัญชีพร็อกซี่"
-proxyAccountDescription: "บัญชีพร็อกซี่ คือ บัญชีที่จะทำหน้าที่เป็นผู้ติดตามระยะไกลสำหรับผู้ใช้งานที่อยู่ภายใต้ด้วยเงื่อนไขบางอย่าง ยกตัวอย่าง เช่น เมื่อมีผู้ใช้งานนั้นได้เพิ่มผู้ใช้งานจากระยะไกลลงในรายการ แต่กิจกรรมของผู้ใช้ในระยะไกลนั้นจะไม่ถูกส่งไปยังอินสแตนซ์หากไม่มีผู้ใช้งานในพื้นที่ติดตามผู้ใช้รายนั้น ดังนั้นบัญชีพร็อกซีนี้จะติดตามแทน"
+proxyAccountDescription: "บัญชีพร็อกซี คือ บัญชีที่ทำหน้าที่ติดตาม(ผู้ใช้)ระยะไกลภายใต้เงื่อนไขบางประการ ตัวอย่างเช่น เมื่อผู้ใช้ท้องถิ่นเพิ่มผู้ใช้ระยะไกลลงรายชื่อ หากไม่มีใครติดตามผู้ใช้ระยะไกลในรายชื่อนั้น กิจกรรมก็จะไม่ถูกส่งมายังเซิร์ฟเวอร์ ดังนั้นจึงมีบัญชีพร็อกซีไว้ติดตามผู้ใช้ระยะไกลเหล่านั้น"
 host: "โฮสต์"
 selectUser: "เลือกผู้ใช้งาน"
 recipient: "ผู้รับ"
 annotation: "หมายเหตุประกอบ"
 federation: "สหพันธ์"
-instances: "อินสแตนซ์"
+instances: "เซิร์ฟเวอร์"
 registeredAt: "วันที่ลงทะเบียน"
 latestRequestReceivedAt: "คำขอล่าสุดที่ได้รับ"
 latestStatus: "สถานะล่าสุด"
 storageUsage: "พื้นที่จัดเก็บข้อมูลที่ใช้ไป"
-charts: "โดดเด่น"
-perHour: "ทุกชั่วโมง"
+charts: "แผนภูมิ"
+perHour: "ต่อชั่วโมง"
 perDay: "ต่อวัน"
 stopActivityDelivery: "หยุดส่งกิจกรรม"
-blockThisInstance: "บล็อกอินสแตนซ์นี้"
-silenceThisInstance: "ปิดปากอินสแตนซ์นี้"
+blockThisInstance: "บล็อกเซิร์ฟเวอร์นี้"
+silenceThisInstance: "ปิดปากเซิร์ฟเวอร์นี้"
 operations: "ดำเนินการ"
 software: "ซอฟต์แวร์"
 version: "เวอร์ชั่น"
@@ -212,17 +219,17 @@ jobQueue: "คิวงาน"
 cpuAndMemory: "ซีพียู และ หน่วยความจำ"
 network: "เครือข่าย"
 disk: "ดิสก์"
-instanceInfo: "ข้อมูลอินสแตนซ์"
+instanceInfo: "ข้อมูลเซิร์ฟเวอร์"
 statistics: "สถิติการใช้งาน"
 clearQueue: "ล้างคิว"
 clearQueueConfirmTitle: "ต้องการล้างคิวใช่ไหม?"
 clearQueueConfirmText: "โพสต์ที่ยังค้างในคิวจะไม่ถูกจัดส่งอีกต่อไป โดยปกติแล้วการดำเนินการนี้ไม่จำเป็น"
 clearCachedFiles: "ล้างแคช"
 clearCachedFilesConfirm: "ต้องการลบไฟล์ระยะไกลที่แคชไว้ทั้งหมดใช่ไหม?"
-blockedInstances: "อินสแตนซ์ที่ถูกบล็อก"
-blockedInstancesDescription: "ระบุชื่อโฮสต์ของอินสแตนซ์ที่คุณต้องการบล็อก อินสแตนซ์ที่อยู่ในรายการนั้นจะไม่สามารถพูดคุยกับอินสแตนซ์นี้ได้อีกต่อไป"
-silencedInstances: "ปิดปากอินสแตนซ์นี้แล้ว"
-silencedInstancesDescription: "ตั้งค่ารายชื่อโฮสต์ของอินสแตนซ์ที่คุณต้องการปิดปาก บัญชีทั้งหมดของอินสแตนซ์ที่อยู่ในรายชื่อนั้นๆ จะถือว่าถูกปิดปากเช่นกัน ทำได้เฉพาะคำขอติดตามเท่านั้น และไม่สามารถกล่าวถึงบัญชีในพื้นที่ได้หากไม่ได้ติดตาม | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก"
+blockedInstances: "เซิร์ฟเวอร์ที่ถูกบล็อก"
+blockedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการบล็อก คั่นด้วยการขึ้นบรรทัดใหม่ เซิร์ฟเวอร์ที่ถูกบล็อกจะไม่สามารถติดต่อกับอินสแตนซ์นี้ได้"
+silencedInstances: "ปิดปากเซิร์ฟเวอร์นี้แล้ว"
+silencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปาก คั่นด้วยการขึ้นบรรทัดใหม่, บัญชีทั้งหมดของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปากเช่นกัน ทำได้เฉพาะคำขอติดตามเท่านั้น และไม่สามารถกล่าวถึงบัญชีในเซิร์ฟเวอร์นี้ได้หากไม่ได้ถูกติดตามกลับ | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก"
 muteAndBlock: "ปิดเสียงและบล็อก"
 mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง"
 blockedUsers: "ผู้ใช้ที่ถูกบล็อก"
@@ -240,14 +247,14 @@ noCustomEmojis: "ไม่มีเอโมจิ"
 noJobs: "ไม่มีงาน"
 federating: "สหพันธ์"
 blocked: "ถูกบล็อก"
-suspended: "ถูกระงับ"
+suspended: "ระงับการส่ง"
 all: "ทั้งหมด"
-subscribing: "สมัครแล้ว"
+subscribing: "กำลังสมัครสมาชิก"
 publishing: "กำลังเผยแพร่"
 notResponding: "ไม่มีการตอบสนอง"
-instanceFollowing: "กำลังติดตามบนอินสแตนซ์"
-instanceFollowers: "ผู้ติดตามของอินสแตนซ์"
-instanceUsers: "ผู้ใช้งานของอินสแตนซ์นี้"
+instanceFollowing: "กำลังติดตามบนเซิร์ฟเวอร์"
+instanceFollowers: "ผู้ติดตามของเซิร์ฟเวอร์"
+instanceUsers: "ผู้ใช้ของเซิร์ฟเวอร์นี้"
 changePassword: "เปลี่ยนรหัสผ่าน"
 security: "ความปลอดภัย"
 retypedNotMatch: "ทั้งสองป้อนข้อมูลไม่สอดคล้องกัน"
@@ -284,20 +291,20 @@ messageRead: "อ่านแล้ว"
 noMoreHistory: "ไม่มีประวัติเพิ่มเติม"
 startMessaging: "เริ่มการสนทนา"
 nUsersRead: "อ่านโดย {n}"
-agreeTo: "ฉันยอมรับที่จะ {0}"
+agreeTo: "ฉันยอมรับ {0}"
 agree: "ยอมรับ"
-agreeBelow: "ฉันยอมรับถึงด้านล่าง"
+agreeBelow: "ยอมรับตามที่ระบุด้านล่าง"
 basicNotesBeforeCreateAccount: "หมายเหตุสำคัญ"
 termsOfService: "เงื่อนไขการให้บริการ"
 start: "เริ่ม"
-home: "หน้าแรก"
-remoteUserCaution: "ข้อมูลอาจไม่สมบูรณ์เนื่องจากผู้ใช้รายนี้มาจากอินสแตนซ์ระยะไกล"
+home: "หน้าหลัก"
+remoteUserCaution: "ข้อมูลอาจไม่สมบูรณ์เนื่องจากผู้ใช้รายนี้มาจากเซิร์ฟเวอร์ระยะไกล"
 activity: "กิจกรรม"
 images: "รูปภาพ"
 image: "รูปภาพ"
 birthday: "วันเกิด"
 yearsOld: "{age} ปี"
-registeredDate: "วันที่สมัครสมาชิก"
+registeredDate: "วันที่ลงทะเบียน"
 location: "ตำแหน่งที่ตั้ง"
 theme: "ธีม"
 themeForLightMode: "ธีมที่จะใช้ในโหมดสว่าง"
@@ -313,6 +320,7 @@ selectFile: "เลือกไฟล์"
 selectFiles: "เลือกไฟล์"
 selectFolder: "เลือกโฟลเดอร์"
 selectFolders: "เลือกโฟลเดอร์"
+fileNotSelected: "ยังไม่ได้เลือกไฟล์"
 renameFile: "เปลี่ยนชื่อไฟล์"
 folderName: "ชื่อโฟลเดอร์"
 createFolder: "สร้างโฟลเดอร์"
@@ -336,15 +344,15 @@ displayOfSensitiveMedia: "แสดงสื่อที่มีเนื้อ
 whenServerDisconnected: "เมื่อสูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์"
 disconnectedFromServer: "การเชื่อมต่อเซิร์ฟเวอร์ถูกตัด"
 reload: "รีโหลด"
-doNothing: "เมิน"
+doNothing: "ช่างมัน"
 reloadConfirm: "รีโหลดเลยไหม?"
-watch: "ดู"
-unwatch: "หยุดดู"
+watch: "เพ่งเล็ง"
+unwatch: "เลิกเพ่งเล็ง"
 accept: "ยอมรับ"
 reject: "ปฏิเสธ"
 normal: "ปกติ"
-instanceName: "ชื่ออินสแตนซ์"
-instanceDescription: "คำอธิบายอินสแตนซ์"
+instanceName: "ชื่อเซิร์ฟเวอร์"
+instanceDescription: "คำอธิบายแนะนำเซิร์ฟเวอร์"
 maintainerName: "ผู้ดูแล"
 maintainerEmail: "อีเมลผู้ดูแลระบบ"
 tosUrl: "URL เงื่อนไขการให้บริการ"
@@ -355,16 +363,16 @@ dayX: "{day}"
 monthX: "เดือน {month}"
 yearX: "{year}"
 pages: "หน้าเพจ"
-integration: "รวบรวม"
+integration: "เชื่อมโยง"
 connectService: "เชื่อมต่อ"
 disconnectService: "ตัดการเชื่อมต่อ"
-enableLocalTimeline: "เปิดใช้งานไทม์ไลน์ในพื้นที่"
+enableLocalTimeline: "เปิดใช้งานไทม์ไลน์ท้องถิ่น"
 enableGlobalTimeline: "เปิดใช้งานไทม์ไลน์ทั่วโลก"
 disablingTimelinesInfo: "ผู้ดูแลระบบและผู้ควบคุมจะสามารถเข้าถึงไทม์ไลน์ทั้งหมด ถึงแม้ว่าจะไม่ได้เปิดใช้งานก็ตาม"
 registration: "ลงทะเบียน"
 enableRegistration: "เปิดใช้งานการลงทะเบียนผู้ใช้ใหม่"
 invite: "คำเชิญ"
-driveCapacityPerLocalAccount: "ความจุของไดรฟ์ต่อผู้ใช้ภายในเครื่อง"
+driveCapacityPerLocalAccount: "ความจุของไดรฟ์ต่อผู้ใช้ท้องถิ่น"
 driveCapacityPerRemoteAccount: "ความจุของไดรฟ์ต่อผู้ใช้ระยะไกล"
 inMb: "เป็นเมกะไบต์"
 bannerUrl: "URL รูปภาพแบนเนอร์"
@@ -373,7 +381,7 @@ basicInfo: "ข้อมูลเบื้องต้น"
 pinnedUsers: "ผู้ใช้ที่ถูกปักหมุด"
 pinnedUsersDescription: "ป้อนชื่อผู้ใช้ที่คุณต้องการปักหมุดในหน้า “ค้นพบ” ฯลฯ คั่นด้วยการขึ้นบรรทัดใหม่"
 pinnedPages: "หน้าเพจที่ปักหมุด"
-pinnedPagesDescription: "ป้อนเส้นทางของหน้าเพจที่คุณต้องการปักหมุดไว้ที่หน้าแรกของอินสแตนซ์นี้ คั่นด้วยขึ้นบรรทัดใหม่"
+pinnedPagesDescription: "ป้อนเส้นทางของหน้าเพจที่คุณต้องการปักหมุดไว้ที่หน้าแรกของเซิร์ฟเวอร์นี้ คั่นด้วยการขึ้นบรรทัดใหม่"
 pinnedClipId: "ID ของคลิปที่จะปักหมุด"
 pinnedNotes: "โน้ตที่ปักหมุดไว้"
 hcaptcha: "hCaptcha"
@@ -389,11 +397,11 @@ recaptcha: "reCAPTCHA"
 enableRecaptcha: "เปิดใช้ reCAPTCHA"
 recaptchaSiteKey: "คีย์ไซต์"
 recaptchaSecretKey: "คีย์ลับ"
-turnstile: "เทิร์น'สไทล"
-enableTurnstile: "เปิดใช้งาน เทิร์น'สไทล"
+turnstile: "Turnstile"
+enableTurnstile: "เปิดใช้งาน Turnstile"
 turnstileSiteKey: "คีย์ไซต์"
 turnstileSecretKey: "คีย์ลับ"
-avoidMultiCaptchaConfirm: "การใช้ระบบ Captcha หลายระบบอาจทำให้เกิดการรบกวนหรืออาจจะเกิดข้อผิดพลาดได้ หากต้องการที่จะปิดการใช้งานระบบ Captcha อื่น ๆ แนะนำให้ปิดตัวอื่นๆก่อน ถ้าหากคุณต้องการให้เปิดใช้งานต่อไป ให้ กด ยกเลิก"
+avoidMultiCaptchaConfirm: "การใช้ Captcha หลายตัวอาจทำให้เกิดการรบกวนหรือข้อผิดพลาดได้ ต้องการที่จะปิดการใช้งาน Captcha ตัวอื่นเลยไหม? หากต้องการให้เปิดใช้งานต่อไป ให้กดยกเลิก"
 antennas: "เสาอากาศ"
 manageAntennas: "จัดการเสาอากาศ"
 name: "ชื่อ"
@@ -401,7 +409,7 @@ antennaSource: "แหล่งเสาอากาศ"
 antennaKeywords: "คีย์เวิร์ดที่ควรฟัง"
 antennaExcludeKeywords: "คีย์เวิร์ดที่จะยกเว้น"
 antennaExcludeBots: "ยกเว้นบัญชีบอต"
-antennaKeywordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR"
+antennaKeywordsDescription: "คั่นด้วยเว้นวรรคสำหรับเงื่อนไข AND, หรือขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR"
 notifyAntenna: "แจ้งเตือนเกี่ยวกับโน้ตใหม่"
 withFileAntenna: "เฉพาะโน้ตที่มีไฟล์"
 enableServiceworker: "เปิดใช้งานการแจ้งเตือนแบบพุชไปยังเบราว์เซอร์ของคุณ"
@@ -418,7 +426,7 @@ unsilenceConfirm: "ต้องการเลิกปิดปากผู้
 popularUsers: "ผู้ใช้ที่เป็นที่นิยม"
 recentlyUpdatedUsers: "ผู้ใช้ที่เพิ่งใช้งานล่าสุด"
 recentlyRegisteredUsers: "ผู้ใช้ที่เข้าร่วมใหม่"
-recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพบใหม่"
+recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพบล่าสุด"
 exploreUsersCount: "มีผู้ใช้ {count} ราย"
 exploreFediverse: "สำรวจสหพันธ์"
 popularTags: "แท็กยอดนิยม"
@@ -468,33 +476,35 @@ retype: "พิมพ์รหัสอีกครั้ง"
 noteOf: "โน้ตของ {user}"
 quoteAttached: "อ้างอิง"
 quoteQuestion: "ต้องการที่จะแนบมันเพื่ออ้างอิงใช่ไหม?"
+attachAsFileQuestion: "ข้อความในคลิปบอร์ดยาวเกินไป คุณต้องการแนบเป็นไฟล์ข้อความหรือไม่?"
 noMessagesYet: "ยังไม่มีข้อความ"
 newMessageExists: "คุณมีข้อความใหม่"
 onlyOneFileCanBeAttached: "สามารถแนบไฟล์ได้เพียงไฟล์เดียวต่อ 1 ข้อความ"
-signinRequired: "กรุณาลงทะเบียนหรือลงชื่อเข้าใช้ก่อนดำเนินการต่อ"
+signinRequired: "ก่อนดำเนินการต่อ กรุณาลงทะเบียนหรือเข้าสู่ระบบ"
+signinOrContinueOnRemote: "เพื่อดำเนินการต่อได้ คุณต้องไปที่เซิร์ฟเวอร์ที่คุณใช้งานอยู่ หรือลงทะเบียน/เข้าสู่ระบบเซิร์ฟเวอร์นี้"
 invitations: "คำเชิญ"
 invitationCode: "รหัสเชิญ"
 checking: "Checking"
 available: "พร้อมใช้งาน"
 unavailable: "ไม่พร้อมใช้"
-usernameInvalidFormat: "คุณสามารถใช้อักษรตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก ตัวเลข และขีดล่างได้นะ ( a-z , A-Z , 0-9 , รวมไปถึงอักษรพิเศษเช่น + * / , . - อื่นๆเป็นต้น )"
+usernameInvalidFormat: "สามารถใช้ a~z A~Z 0~9 และ _ ได้"
 tooShort: "สั้นเกินไปนะ"
 tooLong: "ยาวเกินไปนะ"
-weakPassword: "รหัสผ่าน แย่มาก"
+weakPassword: "รหัสผ่านแย่มาก"
 normalPassword: "รหัสผ่านปกติ"
 strongPassword: "รหัสผ่านรัดกุมมาก"
 passwordMatched: "ถูกต้อง!"
 passwordNotMatched: "ไม่ถูกต้อง"
-signinWith: "ลงชื่อเข้าใช้ด้วย {x}"
-signinFailed: "ไม่สามารถลงชื่อผู้เข้าใช้ได้ เนื่องจาก ชื่อผู้ใช้หรือรหัสผ่านที่คุณป้อนนั้นไม่ถูกต้องนะ"
+signinWith: "เข้าสู่ระบบด้วย {x}"
+signinFailed: "ไม่สามารถเข้าสู่ระบบได้ กรุณาตรวจสอบชื่อผู้ใช้และรหัสผ่าน"
 or: "หรือ"
 language: "ภาษา"
 uiLanguage: "ภาษาอินเทอร์เฟซผู้ใช้งาน"
 aboutX: "เกี่ยวกับ {x}"
-emojiStyle: "สไตล์เอโมจิ"
+emojiStyle: "สไตล์ของเอโมจิ"
 native: "ภาษาแม่"
-disableDrawer: "อย่าใช้ลิ้นชักสไตล์เมนู"
-showNoteActionsOnlyHover: "แสดงการดำเนินการเฉพาะโน้ตเมื่อโฮเวอร์"
+disableDrawer: "ไม่แสดงเมนูในรูปแบบลิ้นชัก"
+showNoteActionsOnlyHover: "แสดงการดำเนินการโน้ตเมื่อโฮเวอร์(วางเมาส์เหนือ)เท่านั้น"
 showReactionsCount: "แสดงจำนวนรีแอกชั่นในโน้ต"
 noHistory: "ไม่มีประวัติ"
 signinHistory: "ประวัติการเข้าสู่ระบบ"
@@ -505,7 +515,7 @@ category: "หมวดหมู่"
 tags: "นามแฝง"
 docSource: "ที่มาของเอกสารนี้"
 createAccount: "สร้างบัญชี"
-existingAccount: "บัญชีที่มีอยู่"
+existingAccount: "บัญชีที่มีอยู่แล้ว"
 regenerate: "สร้างอีกครั้ง"
 fontSize: "ขนาดตัวอักษร"
 mediaListWithOneImageAppearance: "ความสูงของรายการสื่อที่มีเพียงรูปเดียว"
@@ -513,11 +523,11 @@ limitTo: "จำกัดไว้ที่ {x}"
 noFollowRequests: "คุณไม่มีคำขอติดตามที่รอดำเนินการ"
 openImageInNewTab: "เปิดรูปภาพในแท็บใหม่"
 dashboard: "หน้ากระดานหลัก"
-local: "ในพื้นที่"
+local: "ท้องถิ่น"
 remote: "ระยะไกล"
 total: "รวมทั้งหมด"
-weekOverWeekChanges: "เปลี่ยนแปลงไปเมื่อสัปดาห์ที่แล้ว"
-dayOverDayChanges: "เปลี่ยนแปลงไปเมื่อวานนี้"
+weekOverWeekChanges: "เทียบกับสัปดาห์ก่อน"
+dayOverDayChanges: "เทียบกับเมื่อวาน"
 appearance: "ภาพลักษณ์"
 clientSettings: "การตั้งค่าไคลเอนต์"
 accountSettings: "ตั้งค่าบัญชี"
@@ -531,19 +541,19 @@ useObjectStorage: "ใช้การจัดเก็บในรูปแบ
 objectStorageBaseUrl: "Base URL"
 objectStorageBaseUrlDesc: "URL ที่ใช้เป็นข้อมูลอ้างอิง ระบุ URL ของ CDN หรือ Proxy ถ้าหากคุณใช้อย่างใดอย่างหนึ่ง\n สำหรับการใช้งาน S3 'https://<bucket>.s3.amazonaws.com' และสำหรับ GCS หรือบริการที่เทียบเท่าใช้ 'https://storage.googleapis.com/<bucket>', เป็นต้น"
 objectStorageBucket: "Bucket"
-objectStorageBucketDesc: "โปรดระบุชื่อที่เก็บข้อมูลที่ใช้กับผู้ให้บริการของคุณ"
+objectStorageBucketDesc: "โปรดระบุชื่อบัคเก็ตของบริการที่ใช้อยู่"
 objectStoragePrefix: "คำนำหน้า"
 objectStoragePrefixDesc: "ไฟล์ทั้งหมดจะถูกเก็บไว้ภายใต้ไดเร็กทอรีที่มีคำนำหน้านี้"
 objectStorageEndpoint: "ปลายทาง"
 objectStorageEndpointDesc: "เว้นว่างไว้หากคุณใช้ AWS S3 หรือระบุปลายทางเป็น '<host>' หรือ '<host>:<port>' ทั้งนี้ขึ้นอยู่กับผู้ให้บริการที่คุณใช้อยู่ด้วย"
 objectStorageRegion: "ภูมิภาค"
-objectStorageRegionDesc: "ระบุภูมิภาค เช่น 'xx-east-1' ถ้าหากบริการของคุณไม่ได้แยกความแตกต่างระหว่างภูมิภาคก็ให้ เว้นว่างไว้หรือป้อน 'us-east-1'"
+objectStorageRegionDesc: "ระบุภูมิภาค เช่น ‘xx-east-1’ หากบริการของคุณไม่แยกภูมิภาค ให้ระบุเป็น ‘us-east-1’ หรือเว้นวางไว้หากใช้ AWS configuration files / environment variables"
 objectStorageUseSSL: "ใช้ SSL"
 objectStorageUseSSLDesc: "ปิดการทำงานนี้ไว้ ถ้าหากคุณจะไม่ใช้ HTTPS สำหรับการเชื่อมต่อ API"
 objectStorageUseProxy: "เชื่อมต่อผ่านพร็อกซี"
 objectStorageUseProxyDesc: "ปิดสิ่งนี้ไว้ถ้าหากคุณจะไม่ใช้ Proxy สำหรับการเชื่อมต่อ API"
 objectStorageSetPublicRead: "ตั้งค่าเป็น “public-read” เมื่ออัปโหลด"
-s3ForcePathStyleDesc: "ถ้าหากเปิดใช้งาน s3ForcePathStyle ชื่อบัคเก็ตนั้นอาจจะต้องรวมอยู่ในเส้นทางของ URL ซึ่งตรงข้ามกับชื่อโฮสต์ของ URL คุณอาจจะต้องเปิดใช้งานการตั้งค่านี้เมื่อใช้บริการต่างๆ เช่น อินสแตนซ์ Minio ที่โฮสต์เองนะ"
+s3ForcePathStyleDesc: "เมื่อเปิดใช้งาน s3ForcePathStyle จะบังคับให้ ระบุชื่อบัคเก็ตเป็นส่วนหนึ่งของพาธ แทนที่จะเป็นชื่อโฮสต์ใน URL, อาจจำเป็นต้องเปิดใช้งานตัวเลือกนี้เมื่อใช้กับ Minio ที่โฮสต์เองหรือบริการที่คล้ายกัน"
 serverLogs: "ปูมของเซิร์ฟเวอร์"
 deleteAll: "ลบทั้งหมด"
 showFixedPostForm: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนสุดของไทม์ไลน์"
@@ -575,7 +585,7 @@ sort: "เรียงลำดับ"
 ascendingOrder: "เรียงลำดับขึ้น"
 descendingOrder: "เรียงลำดับลง"
 scratchpad: "Scratchpad"
-scratchpadDescription: "Scratchpad เป็นการจัดเตรียมสภาพแวดล้อมสำหรับการทดลอง AiScript แต่คุณสามารถเขียน ดำเนินการ และตรวจสอบผลลัพธ์ของการโต้ตอบกับ Misskey มันได้ด้วยนะ"
+scratchpadDescription: "Scratchpad ให้สภาพแวดล้อมสำหรับการทดลอง AiScript คุณสามารถเขียนโค้ด/สั่งดำเนินการ/ตรวจสอบผลลัพธ์ ของการโต้ตอบกับ Misskey ได้"
 output: "เอาท์พุต"
 script: "สคริปต์"
 disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ"
@@ -587,15 +597,15 @@ unsetUserBannerConfirm: "ต้องการเลิกตั้งแบน
 deleteAllFiles: "ลบไฟล์ทั้งหมด"
 deleteAllFilesConfirm: "ต้องการลบไฟล์ทั้งหมดใช่ไหม?"
 removeAllFollowing: "เลิกติดตามผู้ใช้ที่ติดตามทั้งหมด"
-removeAllFollowingDescription: "เลิกติดตามทั้งหมดจาก {host} โปรดเรียกใช้สิ่งนี้เมื่ออินสแตนซ์ดังกล่าวได้สูญหายตายจากไปแล้ว"
+removeAllFollowingDescription: "จะเลิกติดตามทั้งหมดจาก {host} โปรดดำเนินการสิ่งนี้เมื่อเซิร์ฟเวอร์ดังกล่าวได้สูญหายตายจากไปแล้ว"
 userSuspended: "ผู้ใช้รายนี้ถูกระงับการใช้งาน"
 userSilenced: "ผู้ใช้รายนี้ถูกปิดปากอยู่"
 yourAccountSuspendedTitle: "บัญชีนี้นั้นถูกระงับ"
 yourAccountSuspendedDescription: "บัญชีนี้ถูกระงับ เนื่องจากละเมิดข้อกำหนดในการให้บริการของเซิร์ฟเวอร์หรืออาจจะละเมิดหลักเกณฑ์ชุมชน หรือ อาจจะโดนร้องเรียนเรื่องการละเมิดลิขสิทธิ์และอื่นๆอย่างต่อเนื่องซ้ำๆ หากคุณคิดว่าไม่ได้ทำผิดจริงๆหรือตัดสินผิดพลาด ได้โปรดกรุณาติดต่อผู้ดูแลระบบหากคุณต้องการทราบเหตุผลโดยละเอียดเพิ่มเติม และขอความกรุณาอย่าสร้างบัญชีใหม่"
 tokenRevoked: "โทเค็นไม่ถูกต้อง"
-tokenRevokedDescription: "โทเค็นนี้หมดอายุแล้วนะค่ะกรุณาเข้าสู่ระบบอีกครั้งนะ"
+tokenRevokedDescription: "โทเค็นการเข้าสู่ระบบหมดอายุ กรุณาเข้าสู่ระบบใหม่อีกครั้ง"
 accountDeleted: "ลบบัญชีแล้ว"
-accountDeletedDescription: "บัญชีนี้ถูกลบไปแล้วนะ"
+accountDeletedDescription: "บัญชีนี้ถูกลบแล้ว"
 menu: "เมนู"
 divider: "ตัวแบ่ง"
 addItem: "เพิ่มรายการ"
@@ -622,7 +632,7 @@ author: "ผู้เขียน"
 leaveConfirm: "มีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก ต้องการละทิ้งมันใช่ไหม?"
 manage: "การจัดการ"
 plugins: "ปลั๊กอิน"
-preferencesBackups: "ตั้งค่าการสำรองข้อมูล"
+preferencesBackups: "สำรองการตั้งค่า"
 deck: "เด็ค"
 undeck: "ออกจากเด็ค"
 useBlurEffectForModal: "ใช้เอฟเฟกต์เบลอสำหรับโมดอล"
@@ -632,21 +642,21 @@ height: "ความสูง"
 large: "ใหญ่"
 medium: "ปานกลาง"
 small: "เล็ก"
-generateAccessToken: "สร้างการเข้าถึงโทเค็น"
-permission: "การอนุญาต"
+generateAccessToken: "สร้างโทเค็นการเข้าถึง"
+permission: "สิทธิ์"
 adminPermission: "สิทธิ์ของผู้ดูแลระบบ"
 enableAll: "เปิดใช้งานทั้งหมด"
 disableAll: "ปิดการใช้งานทั้งหมด"
 tokenRequested: "ให้สิทธิ์การเข้าถึงบัญชี"
-pluginTokenRequestedDescription: "ปลั๊กอินนี้จะสามารถใช้การอนุญาตที่ตั้งค่าไว้ที่นี่นะ"
+pluginTokenRequestedDescription: "ปลั๊กอินนี้จะใช้สิทธิ์ตามที่ตั้งค่าไว้ที่นี่"
 notificationType: "ประเภทการแจ้งเตือน"
 edit: "แก้ไข"
-emailServer: "อีเมลเซิร์ฟเวอร์"
+emailServer: "เซิร์ฟเวอร์ของอีเมล"
 enableEmail: "เปิดใช้งานการกระจายอีเมล"
-emailConfigInfo: "ใช้เพื่อยืนยันอีเมลของคุณระหว่างการสมัครหรือถ้าหากคุณลืมรหัสผ่าน"
+emailConfigInfo: "ใช้สำหรับการยืนยันอีเมลหรือการรีเซ็ตรหัสผ่าน"
 email: "อีเมล"
 emailAddress: "ที่อยู่อีเมล"
-smtpConfig: "กำหนดค่าเซิร์ฟเวอร์ SMTP"
+smtpConfig: "ตั้งค่าเซิร์ฟเวอร์ SMTP"
 smtpHost: "โฮสต์"
 smtpPort: "พอร์ต"
 smtpUser: "ชื่อผู้ใช้"
@@ -657,9 +667,9 @@ smtpSecureInfo: "ปิดสิ่งนี้เมื่อใช้ STARTTLS
 testEmail: "ทดสอบการส่งอีเมล"
 wordMute: "ปิดเสียงคำ"
 hardWordMute: "ปิดเสียงคำยาก"
-regexpError: "ข้อผิดพลาดของนิพจน์ทั่วไป"
-regexpErrorDescription: "เกิดข้อผิดพลาดในนิพจน์ทั่วไปในบรรทัดที่ {line} ของการปิดเสียงคำ {tab} ของคุณ:"
-instanceMute: "ปิดเสียง อินสแตนซ์"
+regexpError: "เกิดข้อผิดพลาดใน regular expression"
+regexpErrorDescription: "เกิดข้อผิดพลาดใน regular expression บรรทัดที่ {line} ของการปิดเสียงคำ {tab} :"
+instanceMute: "ปิดเสียงเซิร์ฟเวอร์"
 userSaysSomething: "{name} พูดอะไรบางอย่าง"
 makeActive: "เปิดใช้งาน"
 display: "แสดงผล"
@@ -690,17 +700,17 @@ reportAbuseOf: "รายงาน {name}"
 fillAbuseReportDescription: "กรุณากรอกรายละเอียดเกี่ยวกับรายงานนี้ หากเป็นเรื่องเกี่ยวกับโน้ตโดยเฉพาะ ได้โปรดระบุ URL"
 abuseReported: "เราได้ส่งรายงานของคุณไปแล้ว ขอบคุณมากๆนะ"
 reporter: "ผู้รายงาน"
-reporteeOrigin: "รายงานต้นทาง"
+reporteeOrigin: "ปลายทางรายงาน"
 reporterOrigin: "แหล่งผู้รายงาน"
-forwardReport: "ส่งต่อรายงานไปยังอินสแตนซ์ระยะไกล"
-forwardReportIsAnonymous: "ข้อมูลของคุณจะไม่ปรากฏบนอินสแตนซ์ระยะไกลและปรากฏเป็นบัญชีระบบที่ไม่ระบุชื่อ"
+forwardReport: "ส่งต่อรายงานไปยังเซิร์ฟเวอร์ระยะไกล"
+forwardReportIsAnonymous: "ข้อมูลของคุณจะไม่ปรากฏบนเซิร์ฟเวอร์ระยะไกลและปรากฏเป็นบัญชีระบบที่ไม่ระบุชื่อ"
 send: "ส่ง"
 abuseMarkAsResolved: "ทำเครื่องหมายรายงานว่าแก้ไขแล้ว"
 openInNewTab: "เปิดในแท็บใหม่"
 openInSideView: "เปิดในมุมมองด้านข้าง"
 defaultNavigationBehaviour: "พฤติกรรมการนำทางที่เป็นค่าเริ่มต้น"
 editTheseSettingsMayBreakAccount: "การแก้ไขการตั้งค่าเหล่านี้อาจทำให้บัญชีของคุณเสียหายนะ"
-instanceTicker: "ข้อมูลอินสแตนซ์ของโน้ต"
+instanceTicker: "ข้อมูลเซิร์ฟเวอร์ของโน้ต"
 waitingFor: "กำลังรอ {x}"
 random: "สุ่มค่า"
 system: "ระบบ"
@@ -739,7 +749,7 @@ alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเ
 loadRawImages: "โหลดภาพต้นฉบับแทนการแสดงภาพขนาดย่อ"
 disableShowingAnimatedImages: "ไม่ต้องเล่นภาพเคลื่อนไหว"
 highlightSensitiveMedia: "ไฮไลท์สื่อที่มีเนื้อหาละเอียดอ่อน"
-verificationEmailSent: "ส่งอีเมลยืนยันแล้วนะ ได้โปรดกรุณาไปที่ลิงก์ที่รวมไว้เพื่อทำการตรวจสอบให้เสร็จสิ้น"
+verificationEmailSent: "ได้ส่งอีเมลยืนยันแล้ว กรุณาเข้าลิงก์ที่ระบุในอีเมลเพื่อทำการตั้งค่าให้เสร็จสิ้น"
 notSet: "ไม่ได้ตั้งค่า"
 emailVerified: "อีเมลได้รับการยืนยันแล้ว"
 noteFavoritesCount: "จำนวนโน้ตที่ชื่นชอบ"
@@ -750,7 +760,7 @@ useSystemFont: "ใช้ฟอนต์เริ่มต้นของระ
 clips: "คลิป"
 experimentalFeatures: "ฟังก์ชั่นทดสอบ"
 experimental: "ทดลอง"
-thisIsExperimentalFeature: "นี่คือฟีเจอร์ทดลองนะค่ะ ฟังก์ชันการทำงานบางอย่างอาจเปลี่ยนแปลงได้ และอาจไม่ทำงานหรือไม่เสถียรตามที่ตั้งใจไว้นะ"
+thisIsExperimentalFeature: "นี่เป็นฟีเจอร์ทดลอง ซึ่งอาจมีการเปลี่ยนแปลงการทำงาน และอาจไม่ทำงานตามที่ตั้งใจไว้"
 developer: "สำหรับนักพัฒนา"
 makeExplorable: "ทำให้บัญชีมองเห็นใน “สำรวจ”"
 makeExplorableDescription: "ถ้าหากคุณปิดการทำงานนี้ บัญชีของคุณนั้นจะไม่แสดงในส่วน “สำรวจ”"
@@ -761,14 +771,14 @@ center: "กึ่งกลาง"
 wide: "กว้าง"
 narrow: "ชิด"
 reloadToApplySetting: "การตั้งค่านี้จะมีผลหลังจากโหลดหน้าซ้ำเท่านั้น ต้องการที่จะโหลดใหม่เลยไหม?"
-needReloadToApply: "จำเป็นต้องโหลดซ้ำถึงจะมีผลนะ"
+needReloadToApply: "ต้องรีโหลดเพื่อให้การเปลี่ยนแปลงมีผล"
 showTitlebar: "แสดงแถบชื่อ"
 clearCache: "ล้างแคช"
-onlineUsersCount: "{n} ผู้ใช้คนนี้กำลังออนไลน์"
+onlineUsersCount: "{n} รายกำลังออนไลน์"
 nUsers: "{n} ผู้ใช้งาน"
 nNotes: "{n} โน้ต"
-sendErrorReports: "ส่งรายงานว่าข้อผิดพลาด"
-sendErrorReportsDescription: "เมื่อเปิดใช้งาน ข้อมูลข้อผิดพลาดโดยรายละเอียดนั้นจะถูกแชร์ให้กับ Misskey เมื่อเกิดปัญหา ซึ่งช่วยปรับปรุงคุณภาพของ Misskey\nซึ่งจะรวมถึงข้อมูล เช่น เวอร์ชั่นของระบบปฏิบัติการ เบราว์เซอร์ที่คุณใช้ กิจกรรมของคุณใน Misskey เป็นต้น"
+sendErrorReports: "ส่งรายงานข้อผิดพลาด"
+sendErrorReportsDescription: "เมื่อเปิดใช้งาน การแจ้งข้อผิดพลาดจะถูกแชร์กับ Misskey เมื่อเกิดปัญหา ซึ่งช่วยในการปรับปรุงคุณภาพของซอฟต์แวร์ ข้อมูลข้อผิดพลาดอาจรวมถึงเวอร์ชันของระบบปฏิบัติการ ประเภทของเบราว์เซอร์ และประวัติการใช้งาน ฯลฯ"
 myTheme: "ธีมของฉัน"
 backgroundColor: "สีพื้นหลัง"
 accentColor: "สีหลัก"
@@ -780,7 +790,7 @@ value: "ค่า"
 createdAt: "สร้างเมื่อ"
 updatedAt: "อัปเดตล่าสุด"
 saveConfirm: "บันทึกเปลี่ยนแปลงมั้ย?"
-deleteConfirm: "ลบจริงๆเหรอ?"
+deleteConfirm: "ต้องการลบใช่ไหม?"
 invalidValue: "ค่านี้ไม่ถูกต้อง"
 registry: "ทะเบียน"
 closeAccount: "ปิด บัญชี"
@@ -793,7 +803,7 @@ capacity: "ความจุ"
 inUse: "ใช้แล้ว"
 editCode: "แก้ไขโค้ด"
 apply: "นำไปใช้"
-receiveAnnouncementFromInstance: "รับการแจ้งเตือนจากอินสแตนซ์นี้"
+receiveAnnouncementFromInstance: "รับการแจ้งเตือนจากเซิร์ฟเวอร์นี้"
 emailNotification: "การแจ้งเตือนทางอีเมล"
 publish: "เผยแพร่"
 inChannelSearch: "ค้นหาในช่อง"
@@ -821,7 +831,7 @@ active: "ใช้งานอยู่"
 offline: "ออฟไลน์"
 notRecommended: "ไม่แนะนำ"
 botProtection: "การป้องกัน Bot"
-instanceBlocking: "อินสแตนซ์ที่ถูกบล็อก"
+instanceBlocking: "เซิร์ฟเวอร์ที่ถูกบล็อก/ปิดปาก"
 selectAccount: "เลือกบัญชี"
 switchAccount: "สลับบัญชีผู้ใช้"
 enabled: "เปิดใช้งาน"
@@ -831,9 +841,10 @@ user: "ผู้ใช้"
 administration: "การจัดการ"
 accounts: "บัญชีผู้ใช้"
 switch: "สลับ"
-noMaintainerInformationWarning: "ข้อมูลผู้ดูแลไม่ได้รับการกำหนดค่านะ"
-noBotProtectionWarning: "ไม่ได้กำหนดค่าการป้องกันบอทนะ"
-configure: "กำหนดค่า"
+noMaintainerInformationWarning: "ยังไม่ได้ตั้งค่าข้อมูลของผู้ดูแลระบบ"
+noInquiryUrlWarning: "ยังไม่ได้ตั้งค่า URL สำหรับการติดต่อสอบถาม"
+noBotProtectionWarning: "ยังไม่ได้ตั้งค่าการป้องกันบอต"
+configure: "ตั้งค่า"
 postToGallery: "สร้างโพสต์แกลเลอรี่ใหม่"
 postToHashtag: "โพสต์ไปที่แฮชแท็กนี้"
 gallery: "แกลเลอรี่"
@@ -909,8 +920,8 @@ themeColor: "สีธีม"
 size: "ขนาด"
 numberOfColumn: "จำนวนคอลัมน์"
 searchByGoogle: "ค้นหา"
-instanceDefaultLightTheme: "ธีมสว่างตามค่าเริ่มต้นของอินสแตนซ์"
-instanceDefaultDarkTheme: "ธีมมืดตามค่าเริ่มต้นของอินสแตนซ์"
+instanceDefaultLightTheme: "ธีมสว่างตามค่าเริ่มต้นของเซิร์ฟเวอร์"
+instanceDefaultDarkTheme: "ธีมมืดตามค่าเริ่มต้นของเซิร์ฟเวอร์"
 instanceDefaultThemeDescription: "ป้อนรหัสธีมในรูปแบบออบเจ็กต์"
 mutePeriod: "ระยะเวลาปิดเสียง"
 period: "ระยะเวลา"
@@ -930,7 +941,7 @@ cropNo: "ใช้ตามที่เป็นอยู่"
 file: "ไฟล์"
 recentNHours: "ล่าสุด {n} ชั่วโมงที่แล้ว"
 recentNDays: "ล่าสุด {n} วันที่แล้ว"
-noEmailServerWarning: "ไม่ได้กำหนดค่าเซิร์ฟเวอร์อีเมลนี้"
+noEmailServerWarning: "ยังไม่ได้ตั้งค่าเซิร์ฟเวอร์ของอีเมล"
 thereIsUnresolvedAbuseReportWarning: "มีรายงานที่ยังไม่ได้แก้ไข"
 recommended: "แนะนำ"
 check: "ตรวจสอบ"
@@ -965,7 +976,7 @@ cannotUploadBecauseExceedsFileSizeLimit: "ไม่สามารถอัป
 beta: "เบต้า"
 enableAutoSensitive: "ทำเครื่องหมายว่ามีเนื้อหาที่ละเอียดอ่อนโดยอัตโนมัติ"
 enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อว่ามีเนื้อหาโดยละเอียดอ่อนโดยอัตโนมัติ ผ่าน Machine Learning หากเป็นไปได้ แม้ว่าคุณจะปิดคุณสมบัตินี้ ก็อาจถูกตั้งค่าโดยอัตโนมัติ ทั้งนี้ขึ้นอยู่กับเซิร์ฟเวอร์"
-activeEmailValidationDescription: "เปิดใช้งานการตรวจสอบที่อยู่อีเมลให้มีความเข้มงวดยิ่งขึ้น ซึ่งอาจจะรวมไปถึงการตรวจสอบที่อยู่อีเมล์ที่ใช้แล้วทิ้งและโดยให้พิจารณาว่าสามารถสื่อสารด้วยได้หรือไม่ เมื่อไม่เลือกระบบจะตรวจสอบเฉพาะรูปแบบของอีเมลเท่านั้น"
+activeEmailValidationDescription: "การตรวจสอบอีเมลของผู้ใช้จะเข้มงวดมากขึ้น โดยพิจารณาว่าเป็นอีเมลชั่วคราวหรือไม่ และสามารถติดต่อได้จริงหรือไม่ หากปิดการตรวจสอบนี้ จะตรวจสอบเพียงว่ารูปแบบอีเมลที่ถูกต้องหรือไม่เท่านั้น"
 navbar: "แถบนำทาง"
 shuffle: "สลับ"
 account: "บัญชีผู้ใช้"
@@ -974,7 +985,7 @@ pushNotification: "การแจ้งเตือนแบบพุช"
 subscribePushNotification: "เปิดการแจ้งเตือนแบบพุช"
 unsubscribePushNotification: "ปิดการแจ้งเตือนแบบพุช"
 pushNotificationAlreadySubscribed: "การแจ้งเตือนแบบพุชได้เปิดใช้งานแล้ว"
-pushNotificationNotSupported: "เบราว์เซอร์หรืออินสแตนซ์ของคุณนั้นไม่รองรับการแจ้งเตือนแบบพุช"
+pushNotificationNotSupported: "เบราว์เซอร์หรือเซิร์ฟเวอร์ไม่รองรับการแจ้งเตือนแบบพุช"
 sendPushNotificationReadMessage: "ลบการแจ้งเตือนแบบพุชเมื่ออ่านการแจ้งเตือนหรือข้อความที่เกี่ยวข้องแล้ว"
 sendPushNotificationReadMessageCaption: "อาจทำให้อุปกรณ์ของคุณใช้พลังงานมากขึ้น"
 windowMaximize: "ขยายใหญ่สุด"
@@ -1017,36 +1028,37 @@ achievements: "ความสำเร็จ"
 gotInvalidResponseError: "การตอบสนองเซิร์ฟเวอร์ไม่ถูกต้อง"
 gotInvalidResponseErrorDescription: "เซิร์ฟเวอร์อาจไม่สามารถเข้าถึงได้หรืออาจจะกำลังอยู่ในระหว่างปรับปรุง กรุณาลองใหม่อีกครั้งในภายหลังนะคะ"
 thisPostMayBeAnnoying: "โน้ตนี้อาจจะเป็นการรบกวนผู้อื่นนะคะ"
-thisPostMayBeAnnoyingHome: "โพสต์ไปยังไทม์ไลน์หน้าแรก"
+thisPostMayBeAnnoyingHome: "โพสต์ไปยังไทม์ไลน์หลัก"
 thisPostMayBeAnnoyingCancel: "เลิก"
 thisPostMayBeAnnoyingIgnore: "โพสต์ยังไงก็แล้วแต่"
 collapseRenotes: "ยุบรีโน้ตที่คุณเคยเห็นแล้ว"
+collapseRenotesDescription: "พับย่อโน้ตที่เคยตอบสนองหรือรีโน้ตแล้ว"
 internalServerError: "เซิร์ฟเวอร์ภายในเกิดข้อผิดพลาด"
 internalServerErrorDescription: "เซิร์ฟเวอร์รันค้นพบข้อผิดพลาดที่ไม่คาดคิด"
 copyErrorInfo: "คัดลอกรายละเอียดข้อผิดพลาด"
-joinThisServer: "ลงชื่อสมัครใช้ในอินสแตนซ์นี้"
-exploreOtherServers: "มองหาอินสแตนซ์อื่น"
+joinThisServer: "ลงทะเบียนบนเซิร์ฟเวอร์นี้"
+exploreOtherServers: "มองหาเซิร์ฟเวอร์อื่น"
 letsLookAtTimeline: "มาดูไทม์ไลน์กัน"
-disableFederationConfirm: "ปิดใช้งานสหพันธ์จริงๆหรอแน่ใจแล้วนะ?"
+disableFederationConfirm: "ปิดใช้งานสหพันธ์เลยใช่ไหม?"
 disableFederationConfirmWarn: "โพสต์จะยังคงเป็นสาธารณะต่อไป เว้นแต่จะตั้งค่าเป็นอย่างอื่น"
 disableFederationOk: "ปิดการใช้งาน"
-invitationRequiredToRegister: "อินสแตนซ์นี้เป็นแบบรับเชิญ เฉพาะผู้ที่มีรหัสเชิญเท่านั้นที่สามารถลงทะเบียนได้"
-emailNotSupported: "อินสแตนซ์นี้ไม่รองรับการส่งอีเมล"
+invitationRequiredToRegister: "ขณะนี้เซิร์ฟเวอร์นี้เปิดรับสมาชิกใหม่ผ่านระบบเชิญเท่านั้น ผู้ที่มีรหัสเชิญสามารถลงทะเบียนได้"
+emailNotSupported: "เซิร์ฟเวอร์นี้ไม่รองรับการส่งอีเมล"
 postToTheChannel: "โพสต์ลงช่อง"
 cannotBeChangedLater: "สิ่งนี้ไม่สามารถเปลี่ยนแปลงได้ในภายหลังนะ"
 reactionAcceptance: "การยอมรับรีแอคชั่น"
 likeOnly: "ที่ถูกใจเท่านั้น"
-likeOnlyForRemote: "ทั้งหมด (เฉพาะการถูกใจจากอินสแตนซ์ระยะไกล)"
+likeOnlyForRemote: "ทั้งหมด (เฉพาะการถูกใจจากเซิร์ฟเวอร์ระยะไกล)"
 nonSensitiveOnly: "เฉพาะไม่มีเนื้อหาละเอียดอ่อน"
 nonSensitiveOnlyForLocalLikeOnlyForRemote: "เฉพาะไม่มีเนื้อหาละเอียดอ่อน (เฉพาะการถูกใจจากระยะไกลเท่านั้น)"
 rolesAssignedToMe: "บทบาทที่ได้รับมอบหมายให้ฉัน"
 resetPasswordConfirm: "รีเซ็ตรหัสผ่านของคุณจริงๆหรอ?"
 sensitiveWords: "คำที่มีเนื้อหาละเอียดอ่อน"
-sensitiveWordsDescription: "การเปิดเผยโน้ตทั้งหมดที่มีคำที่กำหนดค่าไว้จะถูกตั้งค่าเป็น \"หน้าแรก\" โดยอัตโนมัติ คุณยังสามารถแสดงหลายรายการได้โดยแยกรายการโดยใช้ตัวแบ่งบรรทัดได้นะ"
-sensitiveWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
+sensitiveWordsDescription: "โน้ตที่มีคำที่ระบุไว้จะถูกตั้งค่าการมองเห็นของให้แสดงเฉพาะในหน้าหลักเท่านั้น คั่นคำด้วยการขึ้นบรรทัดใหม่"
+sensitiveWordsDescription2: "ถ้าแยกด้วยเว้นวรรคจะเป็นการระบุ AND และถ้าล้อมคำด้วยสแลช (/) จะเป็นการใช้ regular expression"
 prohibitedWords: "คำต้องห้าม"
 prohibitedWordsDescription: "จะแจ้งเตือนว่าเกิดข้อผิดพลาดเมื่อพยายามโพสต์โน้ตที่มีคำที่กำหนดไว้ สามารถตั้งได้หลายคำด้วยการขึ้นบรรทัดใหม่"
-prohibitedWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ"
+prohibitedWordsDescription2: "ถ้าแยกด้วยเว้นวรรคจะเป็นการระบุ AND และถ้าล้อมคำด้วยสแลช (/) จะเป็นการใช้ regular expression"
 hiddenTags: "แฮชแท็กที่ซ่อนอยู่"
 hiddenTagsDescription: "เลือกแท็กที่จะไม่แสดงในรายการเทรนด์ สามารถลงทะเบียนหลายแท็กได้โดยขึ้นบรรทัดใหม่"
 notesSearchNotAvailable: "การค้นหาโน้ตไม่พร้อมใช้งาน"
@@ -1058,7 +1070,7 @@ retryAllQueuesNow: "ลองเรียกใช้คิวทั้งหม
 retryAllQueuesConfirmTitle: "ลองใหม่ทั้งหมดจริงๆหรอแน่ใจนะ?"
 retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มการโหลดเซิร์ฟเวอร์ชั่วคราวนะ"
 enableChartsForRemoteUser: "สร้างแผนภูมิข้อมูลผู้ใช้ระยะไกล"
-enableChartsForFederatedInstances: "สร้างแผนภูมิข้อมูลอินสแตนซ์ระยะไกล"
+enableChartsForFederatedInstances: "สร้างแผนภูมิของเซิร์ฟเวอร์ระยะไกล"
 showClipButtonInNoteFooter: "เพิ่ม “คลิป” ไปยังเมนูสั่งการของโน้ต"
 reactionsDisplaySize: "ขนาดของรีแอคชั่น"
 limitWidthOfReaction: "จำกัดความกว้างสูงสุดของรีแอคชั่นและแสดงให้เล็กลง"
@@ -1077,7 +1089,7 @@ addMemo: "เพิ่มเมโม"
 editMemo: "แก้ไขเมโม"
 reactionsList: "รายการรีแอคชั่น"
 renotesList: "รายการรีโน้ต"
-notificationDisplay: "การแจ้งเตือน"
+notificationDisplay: "การแสดงการแจ้งเตือน"
 leftTop: "บนซ้าย"
 rightTop: "บนขวา"
 leftBottom: "ล่างซ้าย"
@@ -1087,7 +1099,7 @@ vertical: "แนวตั้ง"
 horizontal: "แนวนอน"
 position: "ตำแหน่ง"
 serverRules: "กฎของเซิร์ฟเวอร์"
-pleaseConfirmBelowBeforeSignup: "โปรดยืนยันที่ด้านล่างก่อนสมัครใช้งาน"
+pleaseConfirmBelowBeforeSignup: "หากต้องการลงทะเบียนบนเซิร์ฟเวอร์นี้ คุณต้องตรวจสอบและยอมรับสิ่งต่อไปนี้"
 pleaseAgreeAllToContinue: "คุณต้องยอมรับทุกช่องตรงด้านบนเพื่อดำเนินการต่อค่ะ"
 continue: "ดำเนินการต่อ"
 preservedUsernames: "ชื่อผู้ใช้ที่สงวนไว้"
@@ -1168,8 +1180,8 @@ showRepliesToOthersInTimeline: "แสดงการตอบกลับผู
 hideRepliesToOthersInTimeline: "ไม่แสดงการตอบกลับผู้อื่นลงในไทม์ไลน์"
 showRepliesToOthersInTimelineAll: "รวมตอบกลับจากทุกคนที่คุณติดตามไว้ในไทม์ไลน์ของคุณ"
 hideRepliesToOthersInTimelineAll: "ซ่อนตอบกลับจากทุกคนที่คุณติดตามไปจากไทม์ไลน์ของคุณ"
-confirmShowRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการแสดงการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
-confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
+confirmShowRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการแสดงการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ ใส่ลงไทม์ไลน์ใช่ไหม?"
+confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ ไปจากไทม์ไลน์ใช่ไหม?"
 externalServices: "บริการภายนอก"
 sourceCode: "ซอร์สโค้ด"
 sourceCodeIsNotYetProvided: "ซอร์สโค้ดยังไม่พร้อมใช้งาน โปรดติดต่อผู้ดูแลระบบของคุณเพื่อแก้ไขปัญหานี้"
@@ -1227,7 +1239,7 @@ surrender: "ยอมแพ้"
 gameRetry: "เริ่มเกมใหม่"
 notUsePleaseLeaveBlank: "หากไม่ได้ใช้กรุณาเว้นว่างไว้"
 useTotp: "ใช้รหัสผ่านแบบใช้ครั้งเดียว (TOTP)"
-useBackupCode: "ใช้รหัสสำรอง"
+useBackupCode: "ใช้รหัสแบ๊กอัป"
 launchApp: "เริ่มแอป"
 useNativeUIForVideoAudioPlayer: "ใช้ UI ของเบราว์เซอร์เพื่อเล่นวิดีโอ/เสียง"
 keepOriginalFilename: "คงชื่อไฟล์เดิมไว้"
@@ -1235,13 +1247,21 @@ keepOriginalFilenameDescription: "หากปิดการตั้งค่
 noDescription: "ไม่มีข้อความอธิบาย"
 alwaysConfirmFollow: "แสดงข้อความยืนยันเมื่อกดติดตาม"
 inquiry: "ติดต่อเรา"
+tryAgain: "โปรดลองอีกครั้ง"
+confirmWhenRevealingSensitiveMedia: "ตรวจสอบก่อนแสดงสื่อที่มีเนื้อหาละเอียดอ่อน"
+sensitiveMediaRevealConfirm: "สื่อนี้มีเนื้อหาละเอียดอ่อน, ต้องการแสดงใช่ไหม?"
 _delivery:
-  stop: "ถูกระงับ"
+  status: "สถานะการจัดส่ง"
+  stop: "ระงับการส่ง"
+  resume: "จัดส่งต่อ"
   _type:
     none: "กำลังเผยแพร่"
+    manuallySuspended: "หยุดชั่วคราวด้วยตนเอง"
+    goneSuspended: "เซิร์ฟเวอร์ถูกระงับเนื่องจากมีการลบเซิร์ฟเวอร์นี้"
+    autoSuspendedForNotResponding: "เซิร์ฟเวอร์ถูกระงับเนื่องจากไม่ตอบสนอง"
 _bubbleGame:
   howToPlay: "วิธีเล่น"
-  hold: "หยุดชั่วคราว"
+  hold: "ถือไว้"
   _score:
     score: "คะแนน"
     scoreYen: "จำนวนเงินที่ได้รับ"
@@ -1274,8 +1294,8 @@ _initialAccountSetting:
   profileSetting: "ตั้งค่าโปรไฟล์"
   privacySetting: "ตั้งค่าความเป็นส่วนตัว"
   theseSettingsCanEditLater: "คุณสามารถเปลี่ยนการตั้งค่าเหล่านี้ได้ในภายหลังได้ตลอดเวลานะ"
-  youCanEditMoreSettingsInSettingsPageLater: "ยังมีการตั้งค่าอื่นๆ อีกมากมายที่คุณนั้นสามารถกำหนดค่าได้จาก \"การตั้งค่า\" เพื่อให้แน่ใจว่าได้เยี่ยมชมมันได้ภายหลังนะ"
-  followUsers: "ลองติดตามผู้ใช้บางคนที่คุณอาจจะสนใจเพื่อสร้างไทม์ไลน์ของคุณสิ !"
+  youCanEditMoreSettingsInSettingsPageLater: "สามารถตั้งค่าเพิ่มเติมได้ที่หน้า “การตั้งค่า” อย่าลืมไปเยี่ยมชมภายหลังด้วย"
+  followUsers: "ลองติดตามผู้ใช้ที่สนใจเพื่อสร้างไทม์ไลน์ดูสิ"
   pushNotificationDescription: "กำลังเปิดใช้งานการแจ้งเตือนแบบพุชจะช่วยให้คุณได้รับการแจ้งเตือนจาก {name} โดยตรงบนอุปกรณ์ของคุณนะ"
   initialAccountSettingCompleted: "ตั้งค่าโปรไฟล์เสร็จสมบูรณ์แล้ว!"
   haveFun: "ขอให้สนุกกับ {name}!"
@@ -1310,7 +1330,7 @@ _initialTutorial:
     description1: "Misskey มีหลายไทม์ไลน์ขึ้นอยู่กับวิธีการใช้งานของคุณ (บางไทม์ไลน์อาจไม่สามารถใช้ได้ขึ้นอยู่กับนโยบายของเซิร์ฟเวอร์)"
     home: "คุณสามารถดูโพสต์จากบัญชีที่คุณติดตามได้"
     local: "คุณสามารถดูโพสต์จากผู้ใช้ทั้งหมดบนเซิร์ฟเวอร์นี้"
-    social: "โพสต์จากทั้งไทม์ไลน์หน้าแรกและไทม์ไลน์ในพื้นที่ของคุณจะปรากฏขึ้น"
+    social: "จะแสดงโพสต์ทั้งจากไทม์ไลน์หลักและไทม์ไลน์ท้องถิ่น"
     global: "คุณสามารถดูโพสต์จากเซิร์ฟเวอร์ที่เชื่อมต่ออื่นๆ ทั้งหมดได้"
     description2: "คุณสามารถสลับระหว่างแต่ละไทม์ไลน์ได้ตลอดเวลาได้ที่บริเวณด้านบนของหน้าจอ"
     description3: "นอกจากนี้ยังมีรายการไทม์ไลน์ ไทม์ไลน์ของช่อง ฯลฯ โปรดดู {link} สำหรับรายละเอียดเพิ่มเติม"
@@ -1320,7 +1340,7 @@ _initialTutorial:
     _visibility:
       description: "คุณสามารถจำกัดผู้ที่สามารถดูโน้ตของคุณได้นะ"
       public: "โน้ตของคุณนั้นจะปรากฏแก่ผู้ใช้งานทุกคน"
-      home: "เผยแพร่บนไทม์ไลน์หน้าแรกเท่านั้น ผู้คนที่เข้าชมโปรไฟล์ของคุณ ผ่านผู้ติดตาม และผ่านการรีโน้ตสามารถเห็นได้"
+      home: "เผยแพร่บนไทม์ไลน์หลักเท่านั้น แต่ผู้ติดตาม ผู้ที่เข้ามาดูโปรไฟล์ และผู้ที่เห็นจากรีโน้ตยังสามารถดูโพสต์นี้ได้"
       followers: "มองเห็นได้เฉพาะผู้ติดตามเท่านั้น ไม่มีใครอื่นนอกจากตัวคุณเองที่สามารถรีโน้ตได้ และมีเพียงผู้ติดตามของคุณเท่านั้นที่สามารถดูได้"
       direct: "เปิดให้เห็นเฉพาะผู้ใช้ที่ระบุเท่านั้น และพวกเขาจะได้รับแจ้งเตือนด้วย คุณสามารถใช้มันแทนข้อความโดยตรง (dm)"
       doNotSendConfidencialOnDirect1: "โปรดใช้ความระมัดระวังในการส่งข้อมูลที่ละเอียดอ่อน"
@@ -1346,17 +1366,17 @@ _initialTutorial:
     title: "บทเรียนจบลงแล้วจ้า เย่เย่เย่  🎉"
     description: "คุณสมบัติที่แนะนำในที่นี่เป็นเพียงบางส่วนเท่านั้น หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีใช้ Misskey โปรดไปที่ {link}"
 _timelineDescription:
-  home: "บนไทม์ไลน์หน้าแรก คุณสามารถดูโพสต์จากบัญชีที่คุณติดตามได้"
-  local: "ไทม์ไลน์ในพื้นที่ช่วยให้คุณเห็นโพสต์จากผู้ใช้ทั้งหมดบนเซิร์ฟเวอร์นี้"
-  social: "ไทม์ไลน์โซเชียลจะแสดงโพสต์จากทั้งไทม์ไลน์หน้าแรกและไทม์ไลน์ในพื้นที่"
+  home: "บนไทม์ไลน์หลัก คุณสามารถดูโพสต์จากบัญชีที่ติดตามอยู่ได้"
+  local: "ไทม์ไลน์ท้องถิ่นช่วยให้เห็นโพสต์จากผู้ใช้ทั้งหมดบนเซิร์ฟเวอร์นี้"
+  social: "ไทม์ไลน์โซเชียลจะแสดงโพสต์จากทั้งไทม์ไลน์หลักและไทม์ไลน์ท้องถิ่น"
   global: "ในไทม์ไลน์ทั่วโลก คุณสามารถดูโน้ตจากเซิร์ฟเวอร์ที่เชื่อมต่อทั้งหมดได้"
 _serverRules:
   description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ"
 _serverSettings:
   iconUrl: "URL ไอคอน"
   appIconDescription: "ระบุไอคอนที่จะใช้เมื่อ {host} แสดงเป็นแอป"
-  appIconUsageExample: "E.g. เป็น PWA หรือเมื่อแสดงผลเป็นบุ๊กมาร์กหน้าจอหลักบนโทรศัพท์"
-  appIconStyleRecommendation: "เนื่องจากไอคอนอาจถูกครอบตัดเป็นสี่เหลี่ยมจัตุรัสหรือวงกลม จึงแนะนำให้ใช้ไอคอนที่มีขอบสีรอบๆ เนื้อหา"
+  appIconUsageExample: "ตัวอย่างเช่น เมื่อถูกเพิ่มเป็น PWA หรือบุ๊กมาร์กบนหน้าจอหลักในสมาร์ทโฟน"
+  appIconStyleRecommendation: "เนื่องจากอาจถูกครอบตัดเป็นสี่เหลี่ยมหรือวงกลม จึงแนะนำให้ใช้ภาพที่เผื่อพื้นที่รอบๆ ตัวโลโก้ไอคอนไว้"
   appIconResolutionMustBe: "ความละเอียดขั้นต่ำไว้คือ {resolution}."
   manifestJsonOverride: "เขียนทับ manifest.json"
   shortName: "ชื่อย่อ"
@@ -1364,11 +1384,13 @@ _serverSettings:
   fanoutTimelineDescription: "เพิ่มประสิทธิภาพการดึงข้อมูลไทม์ไลน์อย่างมาก และลดภาระในฐานข้อมูลเมื่อเปิดใช้งาน ในทางกลับกัน การใช้หน่วยความจำของ Redis จะเพิ่มขึ้น ลองปิดการใช้งานนี้ในกรณีที่หน่วยความจำเซิร์ฟเวอร์เหลือน้อยหรือเซิร์ฟเวอร์ไม่เสถียร"
   fanoutTimelineDbFallback: "ฟอลแบ๊กกลับฐานข้อมูล"
   fanoutTimelineDbFallbackDescription: "เมื่อเปิดใช้งาน หากไม่ได้แคชไทม์ไลน์ ไทม์ไลน์จะฟอลแบ๊กไปยังฐานข้อมูลสำหรับการ query เพิ่มเติม การปิดใช้งานจะช่วยลดภาระของเซิร์ฟเวอร์ด้วยการกำจัดกระบวนฟอลแบ๊ก แต่มันก็จะจำกัดช่วงเวลาไทม์ไลน์ที่สามารถดึงข้อมูลได้"
+  inquiryUrl: "URL สำหรับการติดต่อสอบถาม"
+  inquiryUrlDescription: "ระบุ URL ของหน้าเว็บที่มีแบบฟอร์มสำหรับติดต่อผู้ดูแลเซิร์ฟเวอร์ หรือข้อมูลการติดต่อของผู้ดูแลเซิร์ฟเวอร์"
 _accountMigration:
   moveFrom: "ย้ายข้อมูลบัญชีอื่นไปยังอีกบัญชีนี้หนึ่ง"
   moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น"
   moveFromLabel: "บัญชีที่จะย้ายจาก #{n}"
-  moveFromDescription: "ถ้าหากคุณต้องการโอนข้อมูล คุณจำเป็นต้องสร้างบัญชีสำรองสำหรับการย้ายบัญชี  หลังจากนั้นป้อนบัญชีที่จะย้ายไปในรูปแบบต่อไปนี้: @person@instance.com"
+  moveFromDescription: "หากต้องการโอนข้อมูลจากบัญชีอื่นมายังบัญชีนี้ จำเป็นต้องสร้างบัญชีนามแฝง (alias) ไว้ที่นี่ด้วย\nกรุณากรอกบัญชีเดิมในรูปแบบ: @username@server.example.com\nหากต้องการลบ alias, ให้เว้นว่างไว้แล้วบันทึก (ไม่แนะนำ)"
   moveTo: "ย้ายข้อมูลบัญชีนี้ไปยังบัญชีอีกหนึ่ง"
   moveToLabel: "บัญชีที่จะย้ายไปที่:"
   moveCannotBeUndone: "ไม่สามารถยกเลิกการโอนย้ายบัญชีได้"
@@ -1569,10 +1591,10 @@ _achievements:
       description: "อ้างโน้ตของคุณเอง"
     _htl20npm:
       title: "ไทม์ไลน์ไหล"
-      description: "มีการทำความเร็วของไทม์ไลน์หน้าแรกเกิน 20 npm (โน้ตต่อนาที)"
+      description: "มีการทำความเร็วของไทม์ไลน์หลักเกิน 20 npm (โน้ตต่อนาที)"
     _viewInstanceChart:
       title: "วิเคราะห์"
-      description: "ดูแผนภูมิอินสแตนซ์ของคุณ"
+      description: "ดูแผนภูมิเซิร์ฟเวอร์ของคุณ"
     _outputHelloWorldOnScratchpad:
       title: "หวัดดีชาวโลก!"
       description: "เอาพุต \"hello world\" ใน Scratchpad"
@@ -1637,7 +1659,7 @@ _role:
   name: "ชื่อบทบาท"
   description: "คำอธิบายบทบาท"
   permission: "สิทธิ์ตามบทบาท"
-  descriptionOfPermission: "<b>ผู้ควบคุม</b> สามารถดำเนินการดูแลขั้นพื้นฐานได้\n<b>ผู้ดูแลระบบ</b> สามารถเปลี่ยนการตั้งค่าทั้งหมดของอินสแตนซ์ได้"
+  descriptionOfPermission: "<b>ผู้ควบคุม</b> สามารถดำเนินการดูแลขั้นพื้นฐานได้\n<b>ผู้ดูแลระบบ</b> สามารถเปลี่ยนการตั้งค่าทั้งหมดของเซิร์ฟเวอร์ได้"
   assignTarget: "มอบหมาย"
   descriptionOfAssignTarget: "แบบ<b>ปรับเอง</b> เพิ่มถอนบทบาทนี้แก่ผู้ใช้ด้วยตัวเอง\nแบบ<b>มีเงื่อนไข</b> เพิ่มถอนบทบาทนี้แก่ผู้ใช้โดยอัตโนมัติหากเข้าเงื่อนไขใดต่อไปนี้"
   manual: "ปรับเอง"
@@ -1668,11 +1690,11 @@ _role:
     middle: "ปานกลาง"
     high: "สูง"
   _options:
-    gtlAvailable: "การดูไทม์ไลน์ทั่วโลก"
-    ltlAvailable: "การดูไทม์ไลน์ในท้องถิ่น"
+    gtlAvailable: "สามารถดูไทม์ไลน์ทั่วโลกได้"
+    ltlAvailable: "สามารถดูไทม์ไลน์ท้องถิ่นได้"
     canPublicNote: "สามารถโพสต์แบบสาธารณะ"
     mentionMax: "จำนวนการกล่าวถึงสูงสุดต่อโน้ต"
-    canInvite: "สร้างรหัสเชิญอินสแตนซ์"
+    canInvite: "สร้างรหัสเชิญเข้าเซิร์ฟเวอร์"
     inviteLimit: "จำกัดการเชิญ"
     inviteLimitCycle: "คูลดาวน์ในการเชิญ"
     inviteExpirationTime: "วันหมดอายุของรหัสการเชิญ"
@@ -1680,6 +1702,7 @@ _role:
     canManageAvatarDecorations: "จัดการตกแต่งอวตาร"
     driveCapacity: "ความจุของไดรฟ์"
     alwaysMarkNsfw: "ทำเครื่องหมายไฟล์ว่าเป็น NSFW เสมอ"
+    canUpdateBioMedia: "อนุญาตให้ปรับปรุงไอคอนและแบนเนอร์"
     pinMax: "จํานวนสูงสุดของโน้ตที่ปักหมุดไว้"
     antennaMax: "จำนวนสูงสุดของเสาอากาศ"
     wordMuteMax: "จำนวนอักขระสูงสุดที่อนุญาตในการปิดเสียงคำ"
@@ -1696,7 +1719,7 @@ _role:
     avatarDecorationLimit: "จำนวนการตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้"
   _condition:
     roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ"
-    isLocal: "ผู้ใช้ในพื้นที่"
+    isLocal: "ผู้ใช้ท้องถิ่น"
     isRemote: "ผู้ใช้ระยะไกล"
     isCat: "ผู้ใช้ที่เป็นแมว"
     isBot: "ผู้ใช้ที่เป็นบอต"
@@ -1755,8 +1778,8 @@ _ad:
   adsTooClose: "เนื่องจากช่วงเวลาการแสดงโฆษณาสั้นมาก ประสบการณ์ผู้ใช้จึงอาจลดลงอย่างมาก"
 _forgotPassword:
   enterEmail: "ป้อนที่อยู่อีเมลที่คุณเคยใช้ในการลงทะเบียนไว้ ลิงก์ที่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูกส่งไปนะ"
-  ifNoEmail: "ถ้าหากคุณไม่ได้ใช้อีเมลระหว่างการลงทะเบียน กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์แทนนะ"
-  contactAdmin: "อินสแตนซ์นี้ไม่รองรับการใช้งานที่อยู่อีเมลนี้ กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์เพื่อรีเซ็ตรหัสผ่านของคุณแทน"
+  ifNoEmail: "หากลงทะเบียนแบบไม่ใช้อีเมล โปรดติดต่อผู้ดูแลระบบ"
+  contactAdmin: "เนื่องจากเซิร์ฟเวอร์นี้ไม่รองรับการส่งอีเมล หากต้องการรีเซ็ตรหัสผ่าน กรุณาติดต่อผู้ดูแลระบบ"
 _gallery:
   my: "แกลลอรี่ของฉัน"
   liked: "โพสต์ที่ถูกใจ"
@@ -1774,23 +1797,23 @@ _plugin:
   viewSource: "ดูต้นฉบับ"
   viewLog: "แสดงปูม"
 _preferencesBackups:
-  list: "สร้างการสำรองข้อมูล"
-  saveNew: "บันทึกข้อมูลสำรองใหม่"
+  list: "การตั้งค่าสำรองที่สร้างไว้"
+  saveNew: "บันทึกการตั้งค่าสำรองใหม่"
   loadFile: "โหลดจากไฟล์"
   apply: "นำไปใช้กับอุปกรณ์นี้"
   save: "บันทึก"
-  inputName: "กรุณาป้อนชื่อสำหรับข้อมูลสำรองนี้"
+  inputName: "กรุณาป้อนชื่อการตั้งค่าสำรองนี้"
   cannotSave: "การบันทึกล้มเหลว"
-  nameAlreadyExists: "มีข้อมูลสำรองชื่อ \"{name}\" นี้อยู่แล้ว กรุณาป้อนชื่ออื่นนะ"
-  applyConfirm: "คุณต้องการใช้ข้อมูลสำรอง \"{name}\" กับอุปกรณ์นี้อย่างงั้นจริงหรอ การตั้งค่าที่มีอยู่ของอุปกรณ์นี้จะถูกเขียนทับนะ"
-  saveConfirm: "บันทึกข้อมูลสำรองเป็น {name} มั้ย?"
-  deleteConfirm: "ลบข้อมูลสำรอง {name} มั้ย?"
-  renameConfirm: "ต้องการเปลี่ยนชื่อข้อมูลสำรองจาก “{old}” เป็น “{new}” ใช่ไหม?"
-  noBackups: "ไม่มีข้อมูลสำรอง สามารถบันทึกการตั้งค่าไคลเอนต์ปัจจุบันไปยังเซิร์ฟเวอร์ด้วย “บันทึกข้อมูลสำรองใหม่”"
+  nameAlreadyExists: "มีการตั้งค่าสำรองชื่อ “{name}” อยู่แล้ว กรุณาป้อนชื่ออื่น"
+  applyConfirm: "ต้องการใช้การตั้งค่าสำรอง “{name}” กับอุปกรณ์นี้ใช่ไหม? การตั้งค่าที่มีอยู่บนอุปกรณ์นี้จะถูกเขียนทับ"
+  saveConfirm: "บันทึกการตั้งค่าสำรองเป็น {name} ใช่ไหม?"
+  deleteConfirm: "ต้องการลบ {name} ใช่ไหม?"
+  renameConfirm: "ต้องการเปลี่ยนชื่อจาก “{old}” เป็น “{new}” ใช่ไหม?"
+  noBackups: "ไม่มีการตั้งค่าสำรอง สามารถบันทึกการตั้งค่าไคลเอนต์ปัจจุบันไปยังเซิร์ฟเวอร์ด้วย “บันทึกการตั้งค่าสำรองใหม่”"
   createdAt: "สร้างเมื่อ: {date} {time}"
   updatedAt: "อัปเดตเมื่อ: {date} {time}"
   cannotLoad: "การโหลดล้มเหลว"
-  invalidFile: "รูปแบบไฟล์ไม่ถูกต้องนะ"
+  invalidFile: "รูปแบบไฟล์ไม่ถูกต้อง"
 _registry:
   scope: "สโคป"
   key: "คีย์"
@@ -1844,10 +1867,10 @@ _wordMute:
   muteWordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR นะ"
   muteWordsDescription2: "ล้อมรอบคีย์เวิร์ดด้วยเครื่องหมายทับเพื่อใช้นิพจน์ทั่วไป"
 _instanceMute:
-  instanceMuteDescription: "การดำเนินการนี้จะปิดเสียง\"โน้ต/รีโน้ต\"จากอินสแตนซ์ที่อยู่ในรายการ รวมถึงบันทึกของผู้ใช้ที่ตอบกลับผู้ใช้จากอินสแตนซ์ที่ปิดเสียง"
+  instanceMuteDescription: "ปิดเสียง “โน้ต/รีโน้ต” ทั้งหมดจากเซิร์ฟเวอร์ที่ระบุไว้ รวมถึงโน้ตของผู้ใช้ที่ตอบกลับผู้ใช้จากเซิร์ฟเวอร์ที่ถูกปิดเสียง"
   instanceMuteDescription2: "คั่นด้วยการขึ้นบรรทัดใหม่"
-  title: "ซ่อนโน้ตจากอินสแตนซ์ที่มีอยู่ในรายชื่อ"
-  heading: "รายชื่ออินสแตนซ์ที่ถูกปิดเสียง"
+  title: "ซ่อนโน้ตจากเซิร์ฟเวอร์ที่มีระบุไว้"
+  heading: "เซิร์ฟเวอร์ที่ถูกปิดเสียง"
 _theme:
   explore: "สำรวจธีม"
   install: "ติดตั้งธีม"
@@ -1923,8 +1946,6 @@ _sfx:
   note: "โน้ต"
   noteMy: "โน้ตของตัวเอง"
   notification: "การเเจ้งเตือน"
-  antenna: "เสาอากาศ"
-  channel: "การแจ้งเตือนช่อง"
   reaction: "เมื่อเลือกรีแอคชั่น"
 _soundSettings:
   driveFile: "ใช้เสียงจากไดรฟ์"
@@ -1932,7 +1953,7 @@ _soundSettings:
   driveFileTypeWarn: "ไม่รองรับไฟล์นี้"
   driveFileTypeWarnDescription: "กรุณาเลือกไฟล์เสียง"
   driveFileDurationWarn: "เสียงยาวเกินไป"
-  driveFileDurationWarnDescription: "การใช้เสียงที่ยาวอาจรบกวนการใช้งาน Misskey, ต้องการดำเนินการต่อหรือไม่?"
+  driveFileDurationWarnDescription: "การใช้เสียงที่ยาว อาจรบกวนการใช้งาน Misskey, ต้องการดำเนินการต่อใช่ไหม?"
 _ago:
   future: "อนาคต"
   justNow: "เมื่อกี๊นี้"
@@ -1976,15 +1997,15 @@ _2fa:
   removeKey: "ลบคีย์ความปลอดภัยออก"
   removeKeyConfirm: "ลบข้อมูลสำรอง {name} มั้ย?"
   whyTOTPOnlyRenew: "ไม่สามารถลบแอปตัวรับรองความถูกต้องได้ตราบใดที่มีการลงทะเบียนคีย์ความปลอดภัยไว้แล้ว"
-  renewTOTP: "กำหนดค่าแอพตัวตรวจสอบสิทธิ์ใหม่"
+  renewTOTP: "ตั้งค่าแอปยืนยันตัวตน"
   renewTOTPConfirm: "วิธีการแบบนี้จะทําให้รหัสยืนยันจากแอพก่อนหน้าของคุณหยุดทํางานเลยนะ"
   renewTOTPOk: "ตั้งค่าคอนฟิกใหม่"
   renewTOTPCancel: "ไม่เป็นไร"
-  checkBackupCodesBeforeCloseThisWizard: "โปรดตรวจสอบรหัสสำรองด้านล่างก่อนที่จะปิดวิซาร์ดนี้"
-  backupCodes: "รหัสสำรองข้อมูล"
+  checkBackupCodesBeforeCloseThisWizard: "โปรดตรวจสอบรหัสแบ๊กอัปด้านล่างก่อนที่จะปิดวิซาร์ดนี้"
+  backupCodes: "รหัสแบ๊กอัป"
   backupCodesDescription: "หากแอปยืนยันตัวตนของคุณไม่พร้อมใช้งาน คุณสามารถใช้รหัสสำรองด้านล่างเพื่อเข้าถึงบัญชีของคุณได้ อย่าลืมเก็บรหัสเหล่านี้ไว้ในที่ปลอดภัย แต่ละรหัสสามารถใช้ได้เพียงครั้งเดียวเท่านั้น"
-  backupCodeUsedWarning: "มีการใช้รหัสสำรองแล้ว โปรดกรุณากำหนดค่าการตรวจสอบสิทธิ์แบบสองปัจจัยโดยเร็วที่สุดถ้าหากคุณยังไม่สามารถใช้งานได้อีก"
-  backupCodesExhaustedWarning: "รหัสสำรองทั้งหมดถูกใช้แล้ว ถ้าหากคุณยังสูญเสียการเข้าถึงแอปการตรวจสอบสิทธิ์แบบสองปัจจัยคุณจะยังไม่สามารถเข้าถึงบัญชีนี้ได้ กรุณากำหนดค่าการรับรองความถูกต้องด้วยการยืนยันสองชั้น"
+  backupCodeUsedWarning: "รหัสแบ๊กอัปถูกใช้งานแล้ว หากแอปพลิเคชันการยืนยันตัวตนไม่สามารถใช้งานได้ ให้รีบทำการตั้งค่าแอปฯใหม่โดยเร็วที่สุด"
+  backupCodesExhaustedWarning: "รหัสแบ๊กอัปทั้งหมดถูกใช้งานแล้ว หากยังไม่สามารถใช้แอปพลิเคชันการยืนยันตัวตนได้ก็จะไม่สามารถเข้าถึงบัญชีนี้ได้อีกต่อไป กรุณาลงทะเบียนแอปพลิเคชันการยืนยันตัวตนใหม่"
   moreDetailedGuideHere: "คลิกที่นี่เพื่อดูคำแนะนำโดยละเอียด"
 _permissions:
   "read:account": "ดูข้อมูลบัญชีของคุณ"
@@ -2074,7 +2095,7 @@ _permissions:
 _auth:
   shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน"
   shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?"
-  shareAccessAsk: "ต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณหรือไม่?"
+  shareAccessAsk: "ต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณใช่ไหม?"
   permission: "{name} ได้ขอสิทธิ์การเข้าถึงดังต่อไปนี้"
   permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้"
   pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน"
@@ -2097,7 +2118,7 @@ _weekday:
   saturday: "วันเสาร์"
 _widgets:
   profile: "โปรไฟล์"
-  instanceInfo: "ข้อมูล อินสแตนซ์"
+  instanceInfo: "ข้อมูลเซิร์ฟเวอร์"
   memo: "โน้ตแปะ"
   notifications: "การเเจ้งเตือน"
   timeline: "ไทม์ไลน์"
@@ -2111,7 +2132,7 @@ _widgets:
   digitalClock: "นาฬิกาดิจิตอล"
   unixClock: "นาฬิกา UNIX"
   federation: "สหพันธ์"
-  instanceCloud: "อินสแตนซ์คลาวด์"
+  instanceCloud: "กลุ่มเมฆเซิร์ฟเวอร์"
   postForm: "แบบฟอร์มการโพสต์"
   slideshow: "แสดงภาพนิ่ง"
   button: "ปุ่ม"
@@ -2156,14 +2177,14 @@ _poll:
 _visibility:
   public: "สาธารณะ"
   publicDescription: "โน้ตของคุณจะปรากฏแก่ผู้ใช้ทุกคน"
-  home: "หน้าแรก"
-  homeDescription: "โพสลงไทม์ไลน์ที่บ้านเท่านั้น"
+  home: "หน้าหลัก"
+  homeDescription: "โพสต์ลงไทม์ไลน์หลักเท่านั้น"
   followers: "ผู้ติดตาม"
   followersDescription: "เฉพาะผู้ติดตามเท่านั้นที่มองเห็นได้"
   specified: "ไดเร็ค"
   specifiedDescription: "ทำให้มองเห็นได้เฉพาะผู้ใช้ที่ระบุเท่านั้น"
   disableFederation: "ไม่มีสหพันธ์"
-  disableFederationDescription: "อย่าส่งไปยังอินสแตนซ์อื่น"
+  disableFederationDescription: "อย่าส่งข้อมูลไปยังเซิร์ฟเวอร์อื่น"
 _postForm:
   replyPlaceholder: "ตอบกลับโน้ตนี้..."
   quotePlaceholder: "อ้างโน้ตนี้..."
@@ -2199,37 +2220,37 @@ _exportOrImport:
   userLists: "รายชื่อ"
   excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง"
   excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน"
-  withReplies: "รวมการตอบกลับจากผู้ใช้ที่นำเข้าไว้ในไทม์ไลน์"
+  withReplies: "รวมการตอบกลับจากผู้ใช้ที่ถูกนำเข้า ลงไทม์ไลน์"
 _charts:
   federation: "สหพันธ์"
   apRequest: "คำขอ"
-  usersIncDec: "ความแตกต่างของจำนวนผู้ใช้งาน"
+  usersIncDec: "การเพิ่มลดของจำนวนผู้ใช้"
   usersTotal: "จำนวนผู้ใช้งานทั้งหมด"
   activeUsers: "จำนวนผู้ใช้งานที่ยังมีความเคลื่อนไหวอยู่"
-  notesIncDec: "ความแตกต่างของจำนวนโน้ต"
-  localNotesIncDec: "ความแตกต่างของจำนวนโน้ตท้องถิ่น"
-  remoteNotesIncDec: "ความแตกต่างของจำนวนโน้ตระยะไกล"
+  notesIncDec: "การเพิ่มลดของจำนวนโน้ต"
+  localNotesIncDec: "การเพิ่มลดของจำนวนโน้ตท้องถิ่น"
+  remoteNotesIncDec: "การเพิ่มลดของจำนวนโน้ตระยะไกล"
   notesTotal: "จำนวนโน้ตทั้งหมด"
-  filesIncDec: "ความแตกต่างของจำนวนไฟล์"
+  filesIncDec: "การเพิ่มลดของจำนวนไฟล์"
   filesTotal: "จำนวนไฟล์ทั้งหมด"
-  storageUsageIncDec: "ความแตกต่างในการใช้พื้นที่เก็บข้อมูล"
+  storageUsageIncDec: "การเพิ่มลดในการใช้พื้นที่เก็บข้อมูล"
   storageUsageTotal: "การใช้พื้นที่เก็บข้อมูลทั้งหมด"
 _instanceCharts:
   requests: "คำขอ"
-  users: "ความแตกต่างของจำนวนผู้ใช้งาน"
+  users: "การเพิ่มลดของจำนวนผู้ใช้งาน"
   usersTotal: "จำนวนผู้ใช้งานสะสม"
-  notes: "ความแตกต่างของจำนวนโน้ต"
+  notes: "การเพิ่มลดของจำนวนโน้ต"
   notesTotal: "จำนวนโน้ตสะสม"
-  ff: "ความแตกต่างของจำนวนผู้ใช้ที่ติดตาม / ผู้ติดตาม"
-  ffTotal: "จำนวนผู้ใช้งานที่ติดตามสะสม / ผู้ติดตาม"
-  cacheSize: "ความแตกต่างในขนาดของแคช"
-  cacheSizeTotal: "ขนาดแคชรวมที่สะสม"
-  files: "ความแตกต่างของจำนวนไฟล์"
+  ff: "การเพิ่มลดของการติดตาม/ผู้ติดตาม"
+  ffTotal: "จำนวนสะสมของการติดตาม/ผู้ติดตาม"
+  cacheSize: "การเพิ่มลดขนาดของแคช"
+  cacheSizeTotal: "ขนาดแคชสะสม"
+  files: "การเพิ่มลดของจำนวนไฟล์"
   filesTotal: "จำนวนไฟล์สะสม"
 _timelines:
-  home: "หน้าแรก"
-  local: "ในพื้นที่"
-  social: "โซเชี่ยล"
+  home: "หน้าหลัก"
+  local: "ท้องถิ่น"
+  social: "โซเชียล"
   global: "ทั่วโลก"
 _play:
   new: "สร้าง Play"
@@ -2333,7 +2354,7 @@ _notification:
     mention: "กล่าวถึง"
     reply: "ตอบกลับ"
     renote: "รีโน้ต"
-    quote: "อ้างคำพูด"
+    quote: "อ้างอิง"
     reaction: "รีแอคชั่น"
     pollEnded: "โพลสิ้นสุดแล้ว"
     receiveFollowRequest: "ได้รับคำร้องขอติดตาม"
@@ -2349,6 +2370,7 @@ _deck:
   alwaysShowMainColumn: "แสดงคอลัมน์หลักเสมอ"
   columnAlign: "จัดแนวคอลัมน์"
   addColumn: "เพิ่มคอลัมน์"
+  newNoteNotificationSettings: "ตั้งค่าการแจ้งเตือนเมื่อมีโน้ตใหม่"
   configureColumn: "ตั้งค่าคอลัมน์"
   swapLeft: "ขยับไปทางซ้าย"
   swapRight: "ขยับไปทางขวา"
@@ -2373,7 +2395,7 @@ _deck:
     antenna: "เสาอากาศ"
     list: "รายการ"
     channel: "ช่อง"
-    mentions: "พูดถึง"
+    mentions: "กล่าวถึงคุณ"
     direct: "ไดเร็กต์"
     roleTimeline: "บทบาทไทม์ไลน์"
 _dialog:
@@ -2387,6 +2409,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "วันที่จากน้อยไปหามาก"
 _webhookSettings:
   createWebhook: "สร้าง Webhook"
+  modifyWebhook: "แก้ไข Webhook"
   name: "ชื่อ"
   secret: "ความลับ"
   events: "อีเว้นท์ Webhook"
@@ -2399,6 +2422,25 @@ _webhookSettings:
     renote: "รีโน้ตแล้วเมื่อ"
     reaction: "เมื่อได้รับรีแอคชั่น"
     mention: "เมื่อกำลังถูกกล่าวถึง"
+  _systemEvents:
+    abuseReport: "เมื่อมีการรายงานจากผู้ใช้"
+    abuseReportResolved: "เมื่อมีการจัดการกับการรายงานจากผู้ใช้"
+  deleteConfirm: "ต้องการลบ Webhook ใช่ไหม?"
+_abuseReport:
+  _notificationRecipient:
+    createRecipient: "เพิ่มปลายทางการแจ้งเตือนการรายงาน"
+    modifyRecipient: "แก้ไขปลายทางการแจ้งเตือนการรายงาน"
+    recipientType: "ประเภทของปลายทางการแจ้งเตือน\n"
+    _recipientType:
+      mail: "อีเมล"
+      webhook: "Webhook"
+      _captions:
+        mail: "ส่งการแจ้งเตือนไปยังที่อยู่อีเมลของผู้ควบคุม (เฉพาะเมื่อได้รับการรายงาน)"
+        webhook: "ส่งการแจ้งเตือนไปยัง SystemWebhook ที่กำหนด (จะส่งเมื่อได้รับการรายงานและเมื่อการรายงานได้รับการแก้ไข)"
+    keywords: "คีย์เวิร์ด"
+    notifiedUser: "ผู้ใช้ที่ได้รับการแจ้งเตือน"
+    notifiedWebhook: "Webhook ที่ใช้"
+    deleteConfirm: "ต้องการลบปลายทางการแจ้งเตือนใช่ไหม?"
 _moderationLogTypes:
   createRole: "สร้างบทบาทแล้ว"
   deleteRole: "ลบบทบาทแล้ว"
@@ -2421,9 +2463,9 @@ _moderationLogTypes:
   deleteGlobalAnnouncement: "ลบประกาศทั่วโลกออกแล้ว"
   deleteUserAnnouncement: "ลบประกาศผู้ใช้ออกแล้ว"
   resetPassword: "รีเซ็ตรหัสผ่าน"
-  suspendRemoteInstance: "ระงับอินสแตนซ์ระยะไกล"
-  unsuspendRemoteInstance: "เลิกระงับอินสแตนซ์ระยะไกล"
-  updateRemoteInstanceNote: "อัปเดตโน้ตการกลั่นกรองของอินสแตนซ์ระยะไกลแล้ว"
+  suspendRemoteInstance: "ระงับเซิร์ฟเวอร์ระยะไกล"
+  unsuspendRemoteInstance: "เลิกระงับเซิร์ฟเวอร์ระยะไกล"
+  updateRemoteInstanceNote: "อัปเดตโน้ตการกลั่นกรองสำหรับเซิร์ฟเวอร์ระยะไกลแล้ว"
   markSensitiveDriveFile: "ทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน"
   unmarkSensitiveDriveFile: "ยกเลิกทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน"
   resolveAbuseReport: "รายงานได้รับการแก้ไขแล้ว"
@@ -2436,6 +2478,12 @@ _moderationLogTypes:
   deleteAvatarDecoration: "ลบการตกแต่งไอคอนแล้ว"
   unsetUserAvatar: "ลบไอคอนผู้ใช้"
   unsetUserBanner: "ลบแบนเนอร์ผู้ใช้"
+  createSystemWebhook: "สร้าง SystemWebhook"
+  updateSystemWebhook: "อัปเดต SystemWebhook"
+  deleteSystemWebhook: "ลบ SystemWebhook"
+  createAbuseReportNotificationRecipient: "สร้างปลายทางการแจ้งเตือนการรายงาน"
+  updateAbuseReportNotificationRecipient: "อัปเดตปลายทางการแจ้งเตือนการรายงาน"
+  deleteAbuseReportNotificationRecipient: "ลบปลายทางการแจ้งเตือนการรายงาน"
 _fileViewer:
   title: "รายละเอียดไฟล์"
   type: "ประเภทไฟล์"
@@ -2448,10 +2496,10 @@ _externalResourceInstaller:
   title: "ติดตั้งจากไซต์ภายนอก"
   checkVendorBeforeInstall: "โปรดตรวจสอบให้แน่ใจว่าแหล่งแจกหน่ายมีความน่าเชื่อถือก่อนทำการติดตั้ง"
   _plugin:
-    title: "ต้องการติดตั้งปลั๊กอินนี้หรือไม่?"
+    title: "ต้องการติดตั้งปลั๊กอินนี้ใช่ไหม?"
     metaTitle: "ข้อมูลส่วนเสริม"
   _theme:
-    title: "ต้องการติดตั้งธีมนี้หรือไม่?"
+    title: "ต้องการติดตั้งธีมนี้ใช่ไหม?"
     metaTitle: "ข้อมูลธีม"
   _meta:
     base: "โทนสีพื้นฐาน"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 661ecf19d7..36d741d30e 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -1318,8 +1318,6 @@ _sfx:
   note: "Нотатки"
   noteMy: "Мої нотатки"
   notification: "Сповіщення"
-  antenna: "Прийом антени"
-  channel: "Повідомлення каналу"
 _ago:
   future: "Майбутнє"
   justNow: "Щойно"
@@ -1622,6 +1620,10 @@ _deck:
 _webhookSettings:
   name: "Ім'я"
   active: "Увімкнено"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "E-mail"
 _moderationLogTypes:
   suspend: "Призупинити"
   resetPassword: "Скинути пароль"
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index 4a930626f4..ee4ab83ce7 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -1089,6 +1089,10 @@ _webhookSettings:
   _events:
     renote: "Qayta qayd qilinganda"
     mention: "Eslanganda"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Email"
 _moderationLogTypes:
   suspend: "To'xtatish"
   resetPassword: "Parolni tiklash"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index acc2e0c6a9..317dea5150 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -376,6 +376,7 @@ mcaptcha: "mCaptcha"
 enableMcaptcha: "Bật mCaptcha"
 mcaptchaSiteKey: "Khóa của trang"
 mcaptchaSecretKey: "Khóa bí mật"
+mcaptchaInstanceUrl: "URL mCaptcha máy chủ"
 recaptcha: "reCAPTCHA"
 enableRecaptcha: "Bật reCAPTCHA"
 recaptchaSiteKey: "Khóa của trang"
@@ -426,6 +427,7 @@ moderator: "Kiểm duyệt viên"
 moderation: "Kiểm duyệt"
 moderationNote: "Ghi chú kiểm duyệt"
 addModerationNote: "Thêm ghi chú kiểm duyệt"
+moderationLogs: "Nhật kí quản trị"
 nUsersMentioned: "Dùng bởi {n} người"
 securityKeyAndPasskey: "Mã bảo mật・Passkey"
 securityKey: "Khóa bảo mật"
@@ -458,6 +460,7 @@ retype: "Nhập lại"
 noteOf: "Tút của {user}"
 quoteAttached: "Trích dẫn"
 quoteQuestion: "Trích dẫn lại?"
+attachAsFileQuestion: "Văn bản ở trong bộ nhớ tạm rất dài. Bạn có muốn đăng nó dưới dạng một tệp văn bản không?"
 noMessagesYet: "Chưa có tin nhắn"
 newMessageExists: "Bạn có tin nhắn mới"
 onlyOneFileCanBeAttached: "Bạn chỉ có thể đính kèm một tập tin"
@@ -1559,8 +1562,6 @@ _sfx:
   note: "Tút"
   noteMy: "Tút của tôi"
   notification: "Thông báo"
-  antenna: "Trạm phát sóng"
-  channel: "Kênh"
 _ago:
   future: "Tương lai"
   justNow: "Vừa xong"
@@ -1922,6 +1923,10 @@ _webhookSettings:
   _events:
     reaction: "Khi nhận được sự kiện"
     mention: "Khi có người nhắc tới bạn"
+_abuseReport:
+  _notificationRecipient:
+    _recipientType:
+      mail: "Email"
 _moderationLogTypes:
   suspend: "Vô hiệu hóa"
   resetPassword: "Đặt lại mật khẩu"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index f92d997b5a..6f10788743 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -180,6 +180,10 @@ addAccount: "添加账户"
 reloadAccountsList: "更新账户列表"
 loginFailed: "登录失败"
 showOnRemote: "转到所在服务器显示"
+continueOnRemote: "转到所在服务器继续"
+chooseServerOnMisskeyHub: "从 Misskey Hub 选择服务器"
+specifyServerHost: "直接输入服务器域名"
+inputHostName: "请输入域名"
 general: "常规设置"
 wallpaper: "壁纸"
 setWallpaper: "设置壁纸"
@@ -351,7 +355,7 @@ instanceName: "服务器名称"
 instanceDescription: "服务器简介"
 maintainerName: "管理员名称"
 maintainerEmail: "管理员电子邮箱"
-tosUrl: "服务条款 URL"
+tosUrl: "服务条款地址"
 thisYear: "今年"
 thisMonth: "本月"
 today: "今天"
@@ -433,8 +437,8 @@ administrator: "管理员"
 token: "Token (令牌)"
 2fa: "双因素认证"
 setupOf2fa: "设置双因素认证"
-totp: "身份验证应用"
-totpDescription: "使用认证应用输入一次性密码。"
+totp: "验证器"
+totpDescription: "使用验证器输入一次性密码"
 moderator: "监察员"
 moderation: "管理"
 moderationNote: "管理笔记"
@@ -477,6 +481,7 @@ noMessagesYet: "现在没有新的聊天"
 newMessageExists: "新信息"
 onlyOneFileCanBeAttached: "只能添加一个附件"
 signinRequired: "请先登录"
+signinOrContinueOnRemote: "若要继续,需要转到您所使用的实例,或者在此服务器上注册或登录。"
 invitations: "邀请"
 invitationCode: "邀请码"
 checking: "正在确认"
@@ -837,6 +842,7 @@ administration: "管理"
 accounts: "账户"
 switch: "切换"
 noMaintainerInformationWarning: "管理人员信息未设置。"
+noInquiryUrlWarning: "尚未设置联络地址。"
 noBotProtectionWarning: "Bot 防御未设置。"
 configure: "设置"
 postToGallery: "发送到图库"
@@ -848,7 +854,7 @@ shareWithNote: "在帖子中分享"
 ads: "广告"
 expiration: "截止时间"
 startingperiod: "开始时间"
-memo: "便笺"
+memo: "备注"
 priority: "优先级"
 high: "高"
 middle: "中"
@@ -1180,7 +1186,7 @@ externalServices: "外部服务"
 sourceCode: "源代码"
 sourceCodeIsNotYetProvided: "还未提供源代码。要解决此问题请联系管理员。"
 repositoryUrl: "仓库地址"
-repositoryUrlDescription: "若源代码所在的仓库是公开的,请填入对应的 URL。若是按原样使用 Misskey(并未追加或者修改代码)的情况请填入 https://github.com/misskey-dev/misskey。"
+repositoryUrlDescription: "若源代码所在的仓库是公开的,请填入对应的 URL。若并未追加或者修改 Misskey 的代码,请填入 https://github.com/misskey-dev/misskey。"
 repositoryUrlOrTarballRequired: "若仓库并未公开,则需要提供 tarball 作为替代。详情请看 .config/example.yml。"
 feedback: "反馈"
 feedbackUrl: "反馈地址"
@@ -1241,6 +1247,9 @@ keepOriginalFilenameDescription: "若关闭此设置,上传文件时文件名
 noDescription: "没有描述"
 alwaysConfirmFollow: "总是确认关注"
 inquiry: "联系我们"
+tryAgain: "请再试一次"
+confirmWhenRevealingSensitiveMedia: "显示敏感内容前需要确认"
+sensitiveMediaRevealConfirm: "这是敏感内容。是否显示?"
 _delivery:
   status: "投递状态"
   stop: "停止投递"
@@ -1375,6 +1384,8 @@ _serverSettings:
   fanoutTimelineDescription: "当启用时,可显著提高获取各种时间线时的性能,并减轻数据库的负荷。但是相对的 Redis 的内存使用量将会增加。如果服务器的内存不是很大,又或者运行不稳定的话可以把它关掉。"
   fanoutTimelineDbFallback: "回退到数据库"
   fanoutTimelineDbFallbackDescription: "当启用时,若时间线未被缓存,则将额外查询数据库。禁用该功能可通过不执行回退处理进一步减少服务器负载,但会限制可检索的时间线范围。"
+  inquiryUrl: "联络地址"
+  inquiryUrlDescription: "用来指定诸如向服务运营商咨询的论坛地址,或记载了运营商联系方式之类的网页地址。"
 _accountMigration:
   moveFrom: "从别的账号迁移到此账户"
   moveFromSub: "为另一个账户建立别名"
@@ -1670,8 +1681,8 @@ _role:
   descriptionOfIsExplorable: "打开后将公开角色时间线。如果角色不是公开的,就无法公开时间线。"
   displayOrder: "显示顺序"
   descriptionOfDisplayOrder: "数字越大,显示位置越靠前。"
-  canEditMembersByModerator: "允许监察者编辑成员"
-  descriptionOfCanEditMembersByModerator: "如果选中,监察者和管理员都能够为用户分配/取消分配角色。如果未选中,则只有管理员可以执行此操作。"
+  canEditMembersByModerator: "允许监察员编辑成员"
+  descriptionOfCanEditMembersByModerator: "如果选中,监察员和管理员都能够为用户分配/取消分配角色。如果未选中,则只有管理员可以执行此操作。"
   priority: "优先级"
   _priority:
     low: "低"
@@ -1690,6 +1701,7 @@ _role:
     canManageAvatarDecorations: "管理头像挂件"
     driveCapacity: "网盘容量"
     alwaysMarkNsfw: "总是将文件标记为 NSFW"
+    canUpdateBioMedia: "可以更新头像和横幅"
     pinMax: "帖子置顶数量限制"
     antennaMax: "可创建的最大天线数量"
     wordMuteMax: "屏蔽词的字数限制"
@@ -1933,8 +1945,6 @@ _sfx:
   note: "帖子"
   noteMy: "我的帖子"
   notification: "通知"
-  antenna: "天线接收"
-  channel: "频道通知"
   reaction: "选择回应时"
 _soundSettings:
   driveFile: "使用网盘内的音频"
@@ -1969,7 +1979,7 @@ _time:
   day: "日"
 _2fa:
   alreadyRegistered: "此设备已被注册"
-  registerTOTP: "开始设置认证应用"
+  registerTOTP: "开始设置验证器"
   step1: "首先,在您的设备上安装验证应用,例如 {a} 或 {b}。"
   step2: "然后,扫描屏幕上显示的二维码。"
   step2Uri: "如果使用桌面应用程序的话,请输入下面的 URI"
@@ -1978,23 +1988,23 @@ _2fa:
   setupCompleted: "设置完成"
   step4: "从现在开始,任何登录操作都将要求您提供动态口令。"
   securityKeyNotSupported: "您的浏览器不支持安全密钥。"
-  registerTOTPBeforeKey: "要注册安全密钥或 Passkey,请先设置验证器应用程序。"
+  registerTOTPBeforeKey: "要注册安全密钥或 Passkey,请先设置验证器。"
   securityKeyInfo: "注册兼容 WebAuthn 的密钥,例如支持 FIDO2 的硬件安全密钥、设备上的生物识别功能、PIN 码以及 Passkey 等。"
   registerSecurityKey: "注册安全密钥或 Passkey"
   securityKeyName: "输入密钥名称"
   tapSecurityKey: "请按照浏览器说明操作来注册安全密钥或 Passkey。"
   removeKey: "删除安全密钥"
   removeKeyConfirm: "您确定要删除 {name} 吗?"
-  whyTOTPOnlyRenew: "如果注册了安全密钥,则无法取消验证器应用程序上的设置。"
-  renewTOTP: "重置验证器应用程序"
-  renewTOTPConfirm: "当前验证器应用程序的验证码将不再有效"
+  whyTOTPOnlyRenew: "当注册了安全密钥时,无法取消使用验证器。"
+  renewTOTP: "重置验证器"
+  renewTOTPConfirm: "当前验证器的验证码及备用代码已失效"
   renewTOTPOk: "重新配置"
   renewTOTPCancel: "不用,谢谢"
   checkBackupCodesBeforeCloseThisWizard: "在关闭此窗口前,请确认下面的备用代码"
   backupCodes: "备用代码"
-  backupCodesDescription: "如果无法使用认证应用,可以使用以下的备用代码来访问账户。请务必将这些代码保存在安全的地方。每个代码仅可使用一次。"
-  backupCodeUsedWarning: "已使用备用代码。如果无法使用认证应用,请尽快重新设定。"
-  backupCodesExhaustedWarning: "已使用完所有的备用代码。如果无法使用认证应用,将无法再访问您的账户。请再次设定认证应用。"
+  backupCodesDescription: "如果无法使用验证器,可以使用以下的备用代码来访问账户。请务必将这些代码保存在安全的地方。每个代码仅可使用一次。"
+  backupCodeUsedWarning: "已使用备用代码。若验证器无法使用,请尽快重置验证器。"
+  backupCodesExhaustedWarning: "已使用完所有的备用代码。若验证器无法使用,则无法再访问您的账户。请重置验证器。"
   moreDetailedGuideHere: "此处为详细指南"
 _permissions:
   "read:account": "查看账户信息"
@@ -2398,6 +2408,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "按添加日期降序排列"
 _webhookSettings:
   createWebhook: "创建 Webhook"
+  modifyWebhook: "编辑 webhook"
   name: "名称"
   secret: "密钥"
   events: "何时运行 Webhook"
@@ -2410,6 +2421,25 @@ _webhookSettings:
     renote: "被转发时"
     reaction: "被回应时"
     mention: "被提及时"
+  _systemEvents:
+    abuseReport: "当收到举报时"
+    abuseReportResolved: "当举报被处理时"
+  deleteConfirm: "要删除 webhook 吗?"
+_abuseReport:
+  _notificationRecipient:
+    createRecipient: "新建举报通知"
+    modifyRecipient: "编辑举报通知"
+    recipientType: "通知类型"
+    _recipientType:
+      mail: "邮箱"
+      webhook: "Webhook"
+      _captions:
+        mail: "当收到新举报时,向持有监察员权限的用户发送通知邮件"
+        webhook: "当收到新举报及举报被处理时,使用指定的 SystemWebhook 发送通知"
+    keywords: "关键字"
+    notifiedUser: "通知的用户"
+    notifiedWebhook: "使用的 webhook"
+    deleteConfirm: "要删除通知吗?"
 _moderationLogTypes:
   createRole: "创建角色"
   deleteRole: "删除角色"
@@ -2447,6 +2477,12 @@ _moderationLogTypes:
   deleteAvatarDecoration: "删除头像挂件"
   unsetUserAvatar: "清除用户头像"
   unsetUserBanner: "清除用户横幅"
+  createSystemWebhook: "新建了 SystemWebhook"
+  updateSystemWebhook: "更新了 SystemWebhook"
+  deleteSystemWebhook: "删除了 SystemWebhook"
+  createAbuseReportNotificationRecipient: "新建了举报通知"
+  updateAbuseReportNotificationRecipient: "更新了举报通知"
+  deleteAbuseReportNotificationRecipient: "删除了举报通知"
 _fileViewer:
   title: "文件信息"
   type: "文件类型"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index aac3f7662c..0929984252 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -180,6 +180,10 @@ addAccount: "新增帳戶"
 reloadAccountsList: "更新帳戶清單的資訊"
 loginFailed: "登入失敗"
 showOnRemote: "轉到所在實例顯示"
+continueOnRemote: "在遠端伺服器繼續"
+chooseServerOnMisskeyHub: "從 Misskey Hub 選擇伺服器"
+specifyServerHost: "直接指定伺服器網域"
+inputHostName: "請輸入域名"
 general: "一般"
 wallpaper: "桌布"
 setWallpaper: "設定桌布"
@@ -243,10 +247,10 @@ noCustomEmojis: "沒有自訂的表情符號"
 noJobs: "沒有任務"
 federating: "聯邦運作中"
 blocked: "已封鎖"
-suspended: "已凍結"
+suspended: "停止發送"
 all: "全部"
 subscribing: "訂閱中"
-publishing: "直播中"
+publishing: "發送中"
 notResponding: "沒有回應"
 instanceFollowing: "追隨的伺服器"
 instanceFollowers: "伺服器的追隨者"
@@ -343,7 +347,7 @@ reload: "重新整理"
 doNothing: "無視"
 reloadConfirm: "確定要重新整理嗎?"
 watch: "關注"
-unwatch: "取消追隨"
+unwatch: "取消關注"
 accept: "接受"
 reject: "拒絕"
 normal: "正常"
@@ -477,6 +481,7 @@ noMessagesYet: "沒有訊息"
 newMessageExists: "有新的訊息"
 onlyOneFileCanBeAttached: "只能加入一個附件"
 signinRequired: "請先登入"
+signinOrContinueOnRemote: "若要繼續,需前往您所在的伺服器,或者註冊並登入此伺服器"
 invitations: "邀請"
 invitationCode: "邀請碼"
 checking: "確認中"
@@ -837,6 +842,7 @@ administration: "管理"
 accounts: "帳戶"
 switch: "切換"
 noMaintainerInformationWarning: "尚未設定管理員訊息。"
+noInquiryUrlWarning: "尚未設定聯絡表單網址。"
 noBotProtectionWarning: "尚未設定 Bot 防護。"
 configure: "設定"
 postToGallery: "發佈到相簿"
@@ -1241,12 +1247,15 @@ keepOriginalFilenameDescription: "如果關閉此設置,上傳時檔案名稱
 noDescription: "沒有說明文字"
 alwaysConfirmFollow: "點擊追隨時總是顯示確認訊息"
 inquiry: "聯絡我們"
+tryAgain: "請再試一次。"
+confirmWhenRevealingSensitiveMedia: "要顯示敏感媒體時需確認"
+sensitiveMediaRevealConfirm: "這是敏感媒體。確定要顯示嗎?"
 _delivery:
   status: "傳送狀態"
-  stop: "停止傳送"
-  resume: "恢復傳送"
+  stop: "停止發送"
+  resume: "恢復發送"
   _type:
-    none: "直播中"
+    none: "發送中"
     manuallySuspended: "手動暫停中"
     goneSuspended: "因為伺服器刪除所以暫停中"
     autoSuspendedForNotResponding: "因為伺服器沒有回應所以暫停中"
@@ -1376,7 +1385,7 @@ _serverSettings:
   fanoutTimelineDbFallback: "資料庫的回退"
   fanoutTimelineDbFallbackDescription: "若啟用,在時間軸沒有快取的情況下將執行回退處理以額外查詢資料庫。若停用,可以透過不執行回退處理來進一步減少伺服器的負荷,但會限制可取得的時間軸範圍。"
   inquiryUrl: "聯絡表單網址"
-  inquiryUrlDescription: "指定伺服器運營者的聯絡表單網址或包含運營者聯絡資訊網頁的網址。"
+  inquiryUrlDescription: "指定伺服器運營者的聯絡表單網址,或包含運營者聯絡資訊網頁的網址。"
 _accountMigration:
   moveFrom: "從其他帳戶遷移到這個帳戶"
   moveFromSub: "為另一個帳戶建立別名"
@@ -1693,6 +1702,7 @@ _role:
     canManageAvatarDecorations: "管理頭像裝飾"
     driveCapacity: "雲端硬碟容量"
     alwaysMarkNsfw: "總是將檔案標記為NSFW"
+    canUpdateBioMedia: "允許更新大頭貼和橫幅"
     pinMax: "置頂貼文的最大數量"
     antennaMax: "可建立的天線數量"
     wordMuteMax: "靜音文字的最大字數"
@@ -1936,8 +1946,6 @@ _sfx:
   note: "貼文"
   noteMy: "我的貼文"
   notification: "通知"
-  antenna: "天線接收"
-  channel: "頻道通知"
   reaction: "選擇反應時"
 _soundSettings:
   driveFile: "使用雲端硬碟的音效檔案"
@@ -2217,7 +2225,7 @@ _charts:
   federation: "聯邦宇宙"
   apRequest: "請求"
   usersIncDec: "使用者增減"
-  usersTotal: "使用者總數"
+  usersTotal: "使用者合計"
   activeUsers: "活躍使用者"
   notesIncDec: "貼文増減"
   localNotesIncDec: "本地貼文増減"
@@ -2401,6 +2409,7 @@ _drivecleaner:
   orderByCreatedAtAsc: "按新增日期降序排列"
 _webhookSettings:
   createWebhook: "建立 Webhook"
+  modifyWebhook: "編輯 Webhook"
   name: "名字"
   secret: "密鑰"
   events: "何時運行 Webhook"
@@ -2413,6 +2422,25 @@ _webhookSettings:
     renote: "當被轉發時"
     reaction: "當獲得反應時"
     mention: "當被提到時"
+  _systemEvents:
+    abuseReport: "當使用者檢舉時"
+    abuseReportResolved: "當處理了使用者的檢舉時"
+  deleteConfirm: "請問是否要刪除 Webhook?"
+_abuseReport:
+  _notificationRecipient:
+    createRecipient: "新增接收檢舉的通知對象"
+    modifyRecipient: "編輯接收檢舉的通知對象"
+    recipientType: "通知對象的種類"
+    _recipientType:
+      mail: "電子郵件"
+      webhook: "Webhook"
+      _captions:
+        mail: "寄送到擁有監察員權限的使用者電子郵件地址(僅在收到檢舉時)"
+        webhook: "向指定的 SystemWebhook 發送通知(在收到檢舉和解決檢舉時發送)"
+    keywords: "關鍵字"
+    notifiedUser: "被通知的使用者"
+    notifiedWebhook: "使用的 Webhook"
+    deleteConfirm: "確定要刪除通知對象嗎?"
 _moderationLogTypes:
   createRole: "新增角色"
   deleteRole: "刪除角色 "
@@ -2450,6 +2478,12 @@ _moderationLogTypes:
   deleteAvatarDecoration: "刪除頭像裝飾"
   unsetUserAvatar: "移除使用者的大頭貼"
   unsetUserBanner: "移除使用者的橫幅圖像"
+  createSystemWebhook: "建立 SystemWebhook"
+  updateSystemWebhook: "更新 SystemWebhook"
+  deleteSystemWebhook: "刪除 SystemWebhook"
+  createAbuseReportNotificationRecipient: "建立接收檢舉的通知對象"
+  updateAbuseReportNotificationRecipient: "更新接收檢舉的通知對象"
+  deleteAbuseReportNotificationRecipient: "刪除接收檢舉的通知對象"
 _fileViewer:
   title: "檔案詳細資訊"
   type: "檔案類型 "

From 72bc78974657b22ab6b1f5a36f6144c294e36de3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Mon, 29 Jul 2024 21:31:32 +0900
Subject: [PATCH 163/206] =?UTF-8?q?feature:=20=E3=83=A6=E3=83=BC=E3=82=B6?=
 =?UTF-8?q?=E4=BD=9C=E6=88=90=E6=99=82=E3=81=ABSystemWebhook=E3=82=92?=
 =?UTF-8?q?=E7=99=BA=E4=BF=A1=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=20(#14321)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feature: ユーザ作成時にSystemWebhookを発信できるようにする

* fix CHANGELOG.md
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |   4 +
 locales/ja-JP.yml                             |   1 +
 .../core/AbuseReportNotificationService.ts    |   2 +-
 packages/backend/src/core/SignupService.ts    |   5 +-
 packages/backend/src/core/UserService.ts      |  24 +++-
 packages/backend/src/models/SystemWebhook.ts  |   2 +
 .../backend/test/e2e/synalio/abuse-report.ts  |  61 ++------
 .../backend/test/e2e/synalio/user-create.ts   | 130 ++++++++++++++++++
 packages/backend/test/utils.ts                |  55 +++++++-
 .../src/components/MkSystemWebhookEditor.vue  |   6 +
 packages/misskey-js/src/autogen/types.ts      |   8 +-
 12 files changed, 237 insertions(+), 62 deletions(-)
 create mode 100644 packages/backend/test/e2e/synalio/user-create.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2e9c8e258..17c233e358 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
 - Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
   - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
+- Feat: ユーザ作成時にSystemWebhookを送信可能に #14281
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 55c65f2aed..2b340ecbb5 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -9392,6 +9392,10 @@ export interface Locale extends ILocale {
              * ユーザーからの通報を処理したとき
              */
             "abuseReportResolved": string;
+            /**
+             * ユーザーが作成されたとき
+             */
+            "userCreated": string;
         };
         /**
          * Webhookを削除しますか?
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3ca4b46682..f0f849fb38 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2491,6 +2491,7 @@ _webhookSettings:
   _systemEvents:
     abuseReport: "ユーザーから通報があったとき"
     abuseReportResolved: "ユーザーからの通報を処理したとき"
+    userCreated: "ユーザーが作成されたとき"
   deleteConfirm: "Webhookを削除しますか?"
 
 _abuseReport:
diff --git a/packages/backend/src/core/AbuseReportNotificationService.ts b/packages/backend/src/core/AbuseReportNotificationService.ts
index 42e5931212..7be5335885 100644
--- a/packages/backend/src/core/AbuseReportNotificationService.ts
+++ b/packages/backend/src/core/AbuseReportNotificationService.ts
@@ -44,7 +44,7 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
 
 	/**
 	 * 管理者用Redisイベントを用いて{@link abuseReports}の内容を管理者各位に通知する.
-	 * 通知先ユーザは{@link RoleService.getModeratorIds}の取得結果に依る.
+	 * 通知先ユーザは{@link getModeratorIds}の取得結果に依る.
 	 *
 	 * @see RoleService.getModeratorIds
 	 * @see GlobalEventService.publishAdminStream
diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts
index 5522ecd6cc..de45898328 100644
--- a/packages/backend/src/core/SignupService.ts
+++ b/packages/backend/src/core/SignupService.ts
@@ -21,6 +21,7 @@ import { bindThis } from '@/decorators.js';
 import UsersChart from '@/core/chart/charts/users.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { MetaService } from '@/core/MetaService.js';
+import { UserService } from '@/core/UserService.js';
 
 @Injectable()
 export class SignupService {
@@ -35,6 +36,7 @@ export class SignupService {
 		private usedUsernamesRepository: UsedUsernamesRepository,
 
 		private utilityService: UtilityService,
+		private userService: UserService,
 		private userEntityService: UserEntityService,
 		private idService: IdService,
 		private metaService: MetaService,
@@ -148,7 +150,8 @@ export class SignupService {
 			}));
 		});
 
-		this.usersChart.update(account, true);
+		this.usersChart.update(account, true).then();
+		this.userService.notifySystemWebhook(account, 'userCreated').then();
 
 		return { account, secret };
 	}
diff --git a/packages/backend/src/core/UserService.ts b/packages/backend/src/core/UserService.ts
index 72fa4d928d..9b1961c631 100644
--- a/packages/backend/src/core/UserService.ts
+++ b/packages/backend/src/core/UserService.ts
@@ -8,15 +8,18 @@ import type { FollowingsRepository, UsersRepository } from '@/models/_.js';
 import type { MiUser } from '@/models/User.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
+import { SystemWebhookService } from '@/core/SystemWebhookService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
 
 @Injectable()
 export class UserService {
 	constructor(
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
-
 		@Inject(DI.followingsRepository)
 		private followingsRepository: FollowingsRepository,
+		private systemWebhookService: SystemWebhookService,
+		private userEntityService: UserEntityService,
 	) {
 	}
 
@@ -50,4 +53,23 @@ export class UserService {
 			});
 		}
 	}
+
+	/**
+	 * SystemWebhookを用いてユーザに関する操作内容を管理者各位に通知する.
+	 * ここではJobQueueへのエンキューのみを行うため、即時実行されない.
+	 *
+	 * @see SystemWebhookService.enqueueSystemWebhook
+	 */
+	@bindThis
+	public async notifySystemWebhook(user: MiUser, type: 'userCreated') {
+		const packedUser = await this.userEntityService.pack(user, null, { schema: 'UserLite' });
+		const recipientWebhookIds = await this.systemWebhookService.fetchSystemWebhooks({ isActive: true, on: [type] });
+		for (const webhookId of recipientWebhookIds) {
+			await this.systemWebhookService.enqueueSystemWebhook(
+				webhookId,
+				type,
+				packedUser,
+			);
+		}
+	}
 }
diff --git a/packages/backend/src/models/SystemWebhook.ts b/packages/backend/src/models/SystemWebhook.ts
index 86fb323d1d..d6c27eae51 100644
--- a/packages/backend/src/models/SystemWebhook.ts
+++ b/packages/backend/src/models/SystemWebhook.ts
@@ -12,6 +12,8 @@ export const systemWebhookEventTypes = [
 	'abuseReport',
 	// 通報を処理したとき
 	'abuseReportResolved',
+	// ユーザが作成された時
+	'userCreated',
 ] as const;
 export type SystemWebhookEventType = typeof systemWebhookEventTypes[number];
 
diff --git a/packages/backend/test/e2e/synalio/abuse-report.ts b/packages/backend/test/e2e/synalio/abuse-report.ts
index b0cc3d13ec..6ce6e47781 100644
--- a/packages/backend/test/e2e/synalio/abuse-report.ts
+++ b/packages/backend/test/e2e/synalio/abuse-report.ts
@@ -5,65 +5,24 @@
 
 import { entities } from 'misskey-js';
 import { beforeEach, describe, test } from '@jest/globals';
-import Fastify from 'fastify';
-import { api, randomString, role, signup, startJobQueue, UserToken } from '../../utils.js';
+import {
+	api,
+	captureWebhook,
+	randomString,
+	role,
+	signup,
+	startJobQueue,
+	UserToken,
+	WEBHOOK_HOST,
+} from '../../utils.js';
 import type { INestApplicationContext } from '@nestjs/common';
 
-const WEBHOOK_HOST = 'http://localhost:15080';
-const WEBHOOK_PORT = 15080;
-process.env.NODE_ENV = 'test';
-
 describe('[シナリオ] ユーザ通報', () => {
 	let queue: INestApplicationContext;
 	let admin: entities.SignupResponse;
 	let alice: entities.SignupResponse;
 	let bob: entities.SignupResponse;
 
-	type SystemWebhookPayload = {
-		server: string;
-		hookId: string;
-		eventId: string;
-		createdAt: string;
-		type: string;
-		body: any;
-	}
-
-	// -------------------------------------------------------------------------------------------
-
-	async function captureWebhook<T = SystemWebhookPayload>(postAction: () => Promise<void>): Promise<T> {
-		const fastify = Fastify();
-
-		let timeoutHandle: NodeJS.Timeout | null = null;
-		const result = await new Promise<string>(async (resolve, reject) => {
-			fastify.all('/', async (req, res) => {
-				timeoutHandle && clearTimeout(timeoutHandle);
-
-				const body = JSON.stringify(req.body);
-				res.status(200).send('ok');
-				await fastify.close();
-				resolve(body);
-			});
-
-			await fastify.listen({ port: WEBHOOK_PORT });
-
-			timeoutHandle = setTimeout(async () => {
-				await fastify.close();
-				reject(new Error('timeout'));
-			}, 3000);
-
-			try {
-				await postAction();
-			} catch (e) {
-				await fastify.close();
-				reject(e);
-			}
-		});
-
-		await fastify.close();
-
-		return JSON.parse(result) as T;
-	}
-
 	async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
 		const res = await api(
 			'admin/system-webhook/create',
diff --git a/packages/backend/test/e2e/synalio/user-create.ts b/packages/backend/test/e2e/synalio/user-create.ts
new file mode 100644
index 0000000000..cb0f68dfea
--- /dev/null
+++ b/packages/backend/test/e2e/synalio/user-create.ts
@@ -0,0 +1,130 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { setTimeout } from 'node:timers/promises';
+import { entities } from 'misskey-js';
+import { beforeEach, describe, test } from '@jest/globals';
+import {
+	api,
+	captureWebhook,
+	randomString,
+	role,
+	signup,
+	startJobQueue,
+	UserToken,
+	WEBHOOK_HOST,
+} from '../../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+
+describe('[シナリオ] ユーザ作成', () => {
+	let queue: INestApplicationContext;
+	let admin: entities.SignupResponse;
+
+	async function createSystemWebhook(args?: Partial<entities.AdminSystemWebhookCreateRequest>, credential?: UserToken): Promise<entities.AdminSystemWebhookCreateResponse> {
+		const res = await api(
+			'admin/system-webhook/create',
+			{
+				isActive: true,
+				name: randomString(),
+				on: ['userCreated'],
+				url: WEBHOOK_HOST,
+				secret: randomString(),
+				...args,
+			},
+			credential ?? admin,
+		);
+		return res.body;
+	}
+
+	// -------------------------------------------------------------------------------------------
+
+	beforeAll(async () => {
+		queue = await startJobQueue();
+		admin = await signup({ username: 'admin' });
+
+		await role(admin, { isAdministrator: true });
+	}, 1000 * 60 * 2);
+
+	afterAll(async () => {
+		await queue.close();
+	});
+
+	// -------------------------------------------------------------------------------------------
+
+	describe('SystemWebhook', () => {
+		beforeEach(async () => {
+			const webhooks = await api('admin/system-webhook/list', {}, admin);
+			for (const webhook of webhooks.body) {
+				await api('admin/system-webhook/delete', { id: webhook.id }, admin);
+			}
+		});
+
+		test('ユーザが作成された -> userCreatedが送出される', async () => {
+			const webhook = await createSystemWebhook({
+				on: ['userCreated'],
+				isActive: true,
+			});
+
+			let alice: any = null;
+			const webhookBody = await captureWebhook(async () => {
+				alice = await signup({ username: 'alice' });
+			});
+
+			// webhookの送出後にいろいろやってるのでちょっと待つ必要がある
+			await setTimeout(2000);
+
+			console.log(alice);
+			console.log(JSON.stringify(webhookBody, null, 2));
+
+			expect(webhookBody.hookId).toBe(webhook.id);
+			expect(webhookBody.type).toBe('userCreated');
+
+			const body = webhookBody.body as entities.UserLite;
+			expect(alice.id).toBe(body.id);
+			expect(alice.name).toBe(body.name);
+			expect(alice.username).toBe(body.username);
+			expect(alice.host).toBe(body.host);
+			expect(alice.avatarUrl).toBe(body.avatarUrl);
+			expect(alice.avatarBlurhash).toBe(body.avatarBlurhash);
+			expect(alice.avatarDecorations).toEqual(body.avatarDecorations);
+			expect(alice.isBot).toBe(body.isBot);
+			expect(alice.isCat).toBe(body.isCat);
+			expect(alice.instance).toEqual(body.instance);
+			expect(alice.emojis).toEqual(body.emojis);
+			expect(alice.onlineStatus).toBe(body.onlineStatus);
+			expect(alice.badgeRoles).toEqual(body.badgeRoles);
+		});
+
+		test('ユーザ作成 -> userCreatedが未許可の場合は送出されない', async () => {
+			await createSystemWebhook({
+				on: [],
+				isActive: true,
+			});
+
+			let alice: any = null;
+			const webhookBody = await captureWebhook(async () => {
+				alice = await signup({ username: 'alice' });
+			}).catch(e => e.message);
+
+			expect(webhookBody).toBe('timeout');
+			expect(alice.id).not.toBeNull();
+		});
+
+		test('ユーザ作成 -> Webhookが無効の場合は送出されない', async () => {
+			await createSystemWebhook({
+				on: ['userCreated'],
+				isActive: false,
+			});
+
+			let alice: any = null;
+			const webhookBody = await captureWebhook(async () => {
+				alice = await signup({ username: 'alice' });
+			}).catch(e => e.message);
+
+			expect(webhookBody).toBe('timeout');
+			expect(alice.id).not.toBeNull();
+		});
+	});
+});
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index e70befeebe..26de19eaf1 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -12,13 +12,14 @@ import WebSocket, { ClientOptions } from 'ws';
 import fetch, { File, RequestInit, type Headers } from 'node-fetch';
 import { DataSource } from 'typeorm';
 import { JSDOM } from 'jsdom';
-import { DEFAULT_POLICIES } from '@/core/RoleService.js';
-import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
+import { type Response } from 'node-fetch';
+import Fastify from 'fastify';
 import { entities } from '../src/postgres.js';
 import { loadConfig } from '../src/config.js';
 import type * as misskey from 'misskey-js';
-import { type Response } from 'node-fetch';
-import { ApiError } from "@/server/api/error.js";
+import { DEFAULT_POLICIES } from '@/core/RoleService.js';
+import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
+import { ApiError } from '@/server/api/error.js';
 
 export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
 
@@ -27,11 +28,23 @@ export interface UserToken {
 	bearer?: boolean;
 }
 
+export type SystemWebhookPayload = {
+	server: string;
+	hookId: string;
+	eventId: string;
+	createdAt: string;
+	type: string;
+	body: any;
+}
+
 const config = loadConfig();
 export const port = config.port;
 export const origin = config.url;
 export const host = new URL(config.url).host;
 
+export const WEBHOOK_HOST = 'http://localhost:15080';
+export const WEBHOOK_PORT = 15080;
+
 export const cookie = (me: UserToken): string => {
 	return `token=${me.token};`;
 };
@@ -645,3 +658,37 @@ export async function sendEnvResetRequest() {
 export function castAsError(obj: Record<string, unknown>): { error: ApiError } {
 	return obj as { error: ApiError };
 }
+
+export async function captureWebhook<T = SystemWebhookPayload>(postAction: () => Promise<void>, port = WEBHOOK_PORT): Promise<T> {
+	const fastify = Fastify();
+
+	let timeoutHandle: NodeJS.Timeout | null = null;
+	const result = await new Promise<string>(async (resolve, reject) => {
+		fastify.all('/', async (req, res) => {
+			timeoutHandle && clearTimeout(timeoutHandle);
+
+			const body = JSON.stringify(req.body);
+			res.status(200).send('ok');
+			await fastify.close();
+			resolve(body);
+		});
+
+		await fastify.listen({ port });
+
+		timeoutHandle = setTimeout(async () => {
+			await fastify.close();
+			reject(new Error('timeout'));
+		}, 3000);
+
+		try {
+			await postAction();
+		} catch (e) {
+			await fastify.close();
+			reject(e);
+		}
+	});
+
+	await fastify.close();
+
+	return JSON.parse(result) as T;
+}
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index 3e6a015018..0beb0a8163 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -40,6 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
 						<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
 					</MkSwitch>
+					<MkSwitch v-model="events.userCreated" :disabled="disabledEvents.userCreated">
+						<template #label>{{ i18n.ts._webhookSettings._systemEvents.userCreated }}</template>
+					</MkSwitch>
 				</div>
 			</MkFolder>
 
@@ -78,6 +81,7 @@ import * as os from '@/os.js';
 type EventType = {
 	abuseReport: boolean;
 	abuseReportResolved: boolean;
+	userCreated: boolean;
 }
 
 const emit = defineEmits<{
@@ -100,12 +104,14 @@ const secret = ref<string>('');
 const events = ref<EventType>({
 	abuseReport: true,
 	abuseReportResolved: true,
+	userCreated: true,
 });
 const isActive = ref<boolean>(true);
 
 const disabledEvents = ref<EventType>({
 	abuseReport: false,
 	abuseReportResolved: false,
+	userCreated: false,
 });
 
 const disableSubmitButton = computed(() => {
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index b2b8938baa..bf35d0f421 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4970,7 +4970,7 @@ export type components = {
       latestSentAt: string | null;
       latestStatus: number | null;
       name: string;
-      on: ('abuseReport' | 'abuseReportResolved')[];
+      on: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
       url: string;
       secret: string;
     };
@@ -10042,7 +10042,7 @@ export type operations = {
         'application/json': {
           isActive: boolean;
           name: string;
-          on: ('abuseReport' | 'abuseReportResolved')[];
+          on: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
           url: string;
           secret: string;
         };
@@ -10152,7 +10152,7 @@ export type operations = {
       content: {
         'application/json': {
           isActive?: boolean;
-          on?: ('abuseReport' | 'abuseReportResolved')[];
+          on?: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
         };
       };
     };
@@ -10265,7 +10265,7 @@ export type operations = {
           id: string;
           isActive: boolean;
           name: string;
-          on: ('abuseReport' | 'abuseReportResolved')[];
+          on: ('abuseReport' | 'abuseReportResolved' | 'userCreated')[];
           url: string;
           secret: string;
         };

From 1991a02aa9ca191639ebd84eeae984ea7ec01cc1 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 09:17:06 +0900
Subject: [PATCH 164/206] update node version

---
 .devcontainer/devcontainer.json          | 2 +-
 .github/workflows/get-api-diff.yml       | 2 +-
 .github/workflows/on-release-created.yml | 2 +-
 .github/workflows/test-backend.yml       | 4 ++--
 .github/workflows/test-frontend.yml      | 4 ++--
 .github/workflows/test-misskey-js.yml    | 2 +-
 .github/workflows/test-production.yml    | 2 +-
 .github/workflows/validate-api-json.yml  | 2 +-
 .node-version                            | 2 +-
 Dockerfile                               | 2 +-
 10 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 7ea23e314e..fbf959d449 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -5,7 +5,7 @@
 	"workspaceFolder": "/workspace",
 	"features": {
 		"ghcr.io/devcontainers/features/node:1": {
-			"version": "20.12.2"
+			"version": "20.16.0"
 		},
 		"ghcr.io/devcontainers-contrib/features/corepack:1": {}
 	},
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 4afafabf2e..af0ab8dde4 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -18,7 +18,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
         api-json-name: [api-base.json, api-head.json]
         include:
           - api-json-name: api-base.json
diff --git a/.github/workflows/on-release-created.yml b/.github/workflows/on-release-created.yml
index 22c04ff297..8dd9ed2513 100644
--- a/.github/workflows/on-release-created.yml
+++ b/.github/workflows/on-release-created.yml
@@ -17,7 +17,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
 
     steps:
       - uses: actions/checkout@v4.1.1
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index bfb79ef090..026550025c 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -22,7 +22,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
 
     services:
       postgres:
@@ -71,7 +71,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
 
     services:
       postgres:
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index c17a9fd387..fcaef52969 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -26,7 +26,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
 
     steps:
     - uses: actions/checkout@v4.1.1
@@ -61,7 +61,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
         browser: [chrome]
 
     services:
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index 6ee67e8735..9ad71919df 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -21,7 +21,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
         # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
 
     steps:
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index 18d02ec030..8ad8a64766 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -16,7 +16,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
 
     steps:
     - uses: actions/checkout@v4.1.1
diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml
index 90f2929a25..06e987f27e 100644
--- a/.github/workflows/validate-api-json.yml
+++ b/.github/workflows/validate-api-json.yml
@@ -18,7 +18,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.12.2]
+        node-version: [20.16.0]
 
     steps:
     - uses: actions/checkout@v4.1.1
diff --git a/.node-version b/.node-version
index 87834047a6..8ce7030825 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-20.12.2
+20.16.0
diff --git a/Dockerfile b/Dockerfile
index d6ca6b8cdf..e247bbcd77 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
 # syntax = docker/dockerfile:1.4
 
-ARG NODE_VERSION=20.12.2-bullseye
+ARG NODE_VERSION=20.16.0-bullseye
 
 # build assets & compile TypeScript
 

From c3437b19083722b7fc506fabe2c9c86107bad23c Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 30 Jul 2024 00:20:09 +0000
Subject: [PATCH 165/206] Bump version to 2024.7.0-rc.4

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

diff --git a/package.json b/package.json
index ab7835c1f6..e33383916c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-beta.3",
+	"version": "2024.7.0-rc.4",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1e1a239bf3..dcddc8087c 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-beta.3",
+	"version": "2024.7.0-rc.4",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 1a79f0dc2adfef82da63136ed4b7ae0182006d29 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 09:47:31 +0900
Subject: [PATCH 166/206] :art:

---
 .../src/components/MkSystemWebhookEditor.vue  | 85 ++++++++++---------
 1 file changed, 45 insertions(+), 40 deletions(-)

diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index 0beb0a8163..f6ce2ccc9f 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -18,47 +18,49 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #header>
 		{{ mode === 'create' ? i18n.ts._webhookSettings.createWebhook : i18n.ts._webhookSettings.modifyWebhook }}
 	</template>
-	<MkSpacer :marginMin="20" :marginMax="28">
-		<MkLoading v-if="loading !== 0"/>
-		<div v-else :class="$style.root" class="_gaps_m">
-			<MkInput v-model="title">
-				<template #label>{{ i18n.ts._webhookSettings.name }}</template>
-			</MkInput>
-			<MkInput v-model="url">
-				<template #label>URL</template>
-			</MkInput>
-			<MkInput v-model="secret">
-				<template #label>{{ i18n.ts._webhookSettings.secret }}</template>
-			</MkInput>
-			<MkFolder :defaultOpen="true">
-				<template #label>{{ i18n.ts._webhookSettings.events }}</template>
 
-				<div class="_gaps_s">
-					<MkSwitch v-model="events.abuseReport" :disabled="disabledEvents.abuseReport">
-						<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReport }}</template>
-					</MkSwitch>
-					<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
-						<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
-					</MkSwitch>
-					<MkSwitch v-model="events.userCreated" :disabled="disabledEvents.userCreated">
-						<template #label>{{ i18n.ts._webhookSettings._systemEvents.userCreated }}</template>
-					</MkSwitch>
-				</div>
-			</MkFolder>
+	<div>
+		<MkSpacer :marginMin="20" :marginMax="28">
+			<MkLoading v-if="loading !== 0"/>
+			<div v-else :class="$style.root" class="_gaps_m">
+				<MkInput v-model="title">
+					<template #label>{{ i18n.ts._webhookSettings.name }}</template>
+				</MkInput>
+				<MkInput v-model="url">
+					<template #label>URL</template>
+				</MkInput>
+				<MkInput v-model="secret">
+					<template #label>{{ i18n.ts._webhookSettings.secret }}</template>
+				</MkInput>
+				<MkFolder :defaultOpen="true">
+					<template #label>{{ i18n.ts._webhookSettings.events }}</template>
 
-			<MkSwitch v-model="isActive">
-				<template #label>{{ i18n.ts.enable }}</template>
-			</MkSwitch>
+					<div class="_gaps_s">
+						<MkSwitch v-model="events.abuseReport" :disabled="disabledEvents.abuseReport">
+							<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReport }}</template>
+						</MkSwitch>
+						<MkSwitch v-model="events.abuseReportResolved" :disabled="disabledEvents.abuseReportResolved">
+							<template #label>{{ i18n.ts._webhookSettings._systemEvents.abuseReportResolved }}</template>
+						</MkSwitch>
+						<MkSwitch v-model="events.userCreated" :disabled="disabledEvents.userCreated">
+							<template #label>{{ i18n.ts._webhookSettings._systemEvents.userCreated }}</template>
+						</MkSwitch>
+					</div>
+				</MkFolder>
 
-			<div :class="$style.footer" class="_buttonsCenter">
-				<MkButton primary :disabled="disableSubmitButton" @click="onSubmitClicked">
-					<i class="ti ti-check"></i>
-					{{ i18n.ts.ok }}
-				</MkButton>
-				<MkButton @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
+				<MkSwitch v-model="isActive">
+					<template #label>{{ i18n.ts.enable }}</template>
+				</MkSwitch>
 			</div>
+		</MkSpacer>
+		<div :class="$style.footer" class="_buttonsCenter">
+			<MkButton primary rounded :disabled="disableSubmitButton" @click="onSubmitClicked">
+				<i class="ti ti-check"></i>
+				{{ i18n.ts.ok }}
+			</MkButton>
+			<MkButton rounded @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
 		</div>
-	</MkSpacer>
+	</div>
 </MkModalWindow>
 </template>
 
@@ -223,9 +225,12 @@ onMounted(async () => {
 }
 
 .footer {
-	display: flex;
-	justify-content: center;
-	align-items: flex-end;
-	margin-top: 20px;
+	position: sticky;
+	bottom: 0;
+	left: 0;
+	padding: 12px;
+	border-top: solid 0.5px var(--divider);
+	-webkit-backdrop-filter: var(--blur, blur(15px));
+	backdrop-filter: var(--blur, blur(15px));
 }
 </style>

From b44313fe3c559badc7b0bf76ba3868f945122f0b Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Tue, 30 Jul 2024 12:32:03 +0900
Subject: [PATCH 167/206] fix(backend): type(schema) of reactionAcceptance was
 wrong (#14317)

---
 packages/backend/src/models/json-schema/note.ts | 2 +-
 packages/misskey-js/src/autogen/types.ts        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index 61f62dce60..432c096e48 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -204,7 +204,7 @@ export const packedNoteSchema = {
 		reactionAcceptance: {
 			type: 'string',
 			optional: false, nullable: true,
-			enum: ['likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'],
+			enum: ['likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote', null],
 		},
 		reactionEmojis: {
 			type: 'object',
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index bf35d0f421..52a571b694 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4090,7 +4090,7 @@ export type components = {
       }) | null;
       localOnly?: boolean;
       /** @enum {string|null} */
-      reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote';
+      reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null;
       reactionEmojis: {
         [key: string]: string;
       };

From de3ddb5b4403d02b8cb7671b9cf26e87ce9a2d17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 13:02:03 +0900
Subject: [PATCH 168/206] =?UTF-8?q?enhance:=20=E7=AE=A1=E7=90=86=E7=94=BB?=
 =?UTF-8?q?=E9=9D=A2=E3=81=A7=E3=82=A2=E3=83=BC=E3=82=AB=E3=82=A4=E3=83=96?=
 =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F=E3=81=8A=E7=9F=A5=E3=82=89=E3=81=9B?=
 =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA=E3=83=BB=E7=B7=A8=E9=9B=86=E3=81=A7?=
 =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#14286)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: 管理画面でアーカイブにしたお知らせを表示できるように

* Update Changelog
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |   8 +
 locales/ja-JP.yml                             |   2 +
 .../api/endpoints/admin/announcements/list.ts |   9 +-
 .../frontend/src/pages/admin/_header_.vue     |   5 +-
 .../src/pages/admin/announcements.vue         | 160 +++++++++++-------
 packages/misskey-js/src/autogen/types.ts      |   5 +
 7 files changed, 127 insertions(+), 63 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 17c233e358..8da94f3749 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 - Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
   - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
 - Feat: ユーザ作成時にSystemWebhookを送信可能に #14281
+- Enhance: 管理画面でアーカイブにしたお知らせを表示・編集できるように
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 2b340ecbb5..d54b752312 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4440,6 +4440,14 @@ export interface Locale extends ILocale {
      * アーカイブ
      */
     "archive": string;
+    /**
+     * アーカイブ済み
+     */
+    "archived": string;
+    /**
+     * アーカイブ解除
+     */
+    "unarchive": string;
     /**
      * {name}をアーカイブしますか?
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index f0f849fb38..d8531070fe 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1106,6 +1106,8 @@ preservedUsernames: "予約ユーザー名"
 preservedUsernamesDescription: "予約するユーザー名を改行で列挙します。ここで指定されたユーザー名はアカウント作成時に使えなくなりますが、管理者によるアカウント作成時はこの制限を受けません。また、既に存在するアカウントも影響を受けません。"
 createNoteFromTheFile: "このファイルからノートを作成"
 archive: "アーカイブ"
+archived: "アーカイブ済み"
+unarchive: "アーカイブ解除"
 channelArchiveConfirmTitle: "{name}をアーカイブしますか?"
 channelArchiveConfirmDescription: "アーカイブすると、チャンネル一覧や検索結果に表示されなくなり、新たな書き込みもできなくなります。"
 thisChannelArchived: "このチャンネルはアーカイブされています。"
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
index 87eaad31a3..7596bf44e3 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
@@ -69,6 +69,7 @@ export const paramDef = {
 		sinceId: { type: 'string', format: 'misskey:id' },
 		untilId: { type: 'string', format: 'misskey:id' },
 		userId: { type: 'string', format: 'misskey:id', nullable: true },
+		status: { type: 'string', enum: ['all', 'active', 'archived'], default: 'active' },
 	},
 	required: [],
 } as const;
@@ -87,7 +88,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId);
-			query.andWhere('announcement.isActive = true');
+
+			if (ps.status === 'archived') {
+				query.andWhere('announcement.isActive = false');
+			} else if (ps.status === 'active') {
+				query.andWhere('announcement.isActive = true');
+			}
+
 			if (ps.userId) {
 				query.andWhere('announcement.userId = :userId', { userId: ps.userId });
 			} else {
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index c5a9609e6e..b88f078598 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -24,8 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div class="buttons right">
 		<template v-if="actions">
 			<template v-for="action in actions">
-				<MkButton v-if="action.asFullButton" class="fullButton" primary @click.stop="action.handler"><i :class="action.icon" style="margin-right: 6px;"></i>{{ action.text }}</MkButton>
-				<button v-else v-tooltip.noDelay="action.text" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
+				<MkButton v-if="action.asFullButton" class="fullButton" primary :disabled="action.disabled" @click.stop="action.handler"><i :class="action.icon" style="margin-right: 6px;"></i>{{ action.text }}</MkButton>
+				<button v-else v-tooltip.noDelay="action.text" class="_button button" :class="{ highlighted: action.highlighted }" :disabled="action.disabled" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
 			</template>
 		</template>
 	</div>
@@ -56,6 +56,7 @@ const props = defineProps<{
 		text: string;
 		icon: string;
 		asFullButton?: boolean;
+		disabled?: boolean;
 		handler: (ev: MouseEvent) => void;
 	}[];
 	thin?: boolean;
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index e7fb62ec1d..b9e09c8d03 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -11,70 +11,83 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkInfo>{{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }}</MkInfo>
 			<MkInfo v-if="announcements.length > 5" warn>{{ i18n.ts._announcement.tooManyActiveAnnouncementDescription }}</MkInfo>
 
-			<MkFolder v-for="announcement in announcements" :key="announcement.id ?? announcement._id" :defaultOpen="announcement.id == null">
-				<template #label>{{ announcement.title }}</template>
-				<template #icon>
-					<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
-					<i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
-					<i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
-					<i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
-				</template>
-				<template #caption>{{ announcement.text }}</template>
+			<MkSelect v-model="announcementsStatus">
+				<template #label>{{ i18n.ts.filter }}</template>
+				<option value="active">{{ i18n.ts.active }}</option>
+				<option value="archived">{{ i18n.ts.archived }}</option>
+			</MkSelect>
 
-				<div class="_gaps_m">
-					<MkInput v-model="announcement.title">
-						<template #label>{{ i18n.ts.title }}</template>
-					</MkInput>
-					<MkTextarea v-model="announcement.text" mfmAutocomplete :mfmPreview="true">
-						<template #label>{{ i18n.ts.text }}</template>
-					</MkTextarea>
-					<MkInput v-model="announcement.imageUrl" type="url">
-						<template #label>{{ i18n.ts.imageUrl }}</template>
-					</MkInput>
-					<MkRadios v-model="announcement.icon">
-						<template #label>{{ i18n.ts.icon }}</template>
-						<option value="info"><i class="ti ti-info-circle"></i></option>
-						<option value="warning"><i class="ti ti-alert-triangle" style="color: var(--warn);"></i></option>
-						<option value="error"><i class="ti ti-circle-x" style="color: var(--error);"></i></option>
-						<option value="success"><i class="ti ti-check" style="color: var(--success);"></i></option>
-					</MkRadios>
-					<MkRadios v-model="announcement.display">
-						<template #label>{{ i18n.ts.display }}</template>
-						<option value="normal">{{ i18n.ts.normal }}</option>
-						<option value="banner">{{ i18n.ts.banner }}</option>
-						<option value="dialog">{{ i18n.ts.dialog }}</option>
-					</MkRadios>
-					<MkInfo v-if="announcement.display === 'dialog'" warn>{{ i18n.ts._announcement.dialogAnnouncementUxWarn }}</MkInfo>
-					<MkSwitch v-model="announcement.forExistingUsers" :helpText="i18n.ts._announcement.forExistingUsersDescription">
-						{{ i18n.ts._announcement.forExistingUsers }}
-					</MkSwitch>
-					<MkSwitch v-model="announcement.silence" :helpText="i18n.ts._announcement.silenceDescription">
-						{{ i18n.ts._announcement.silence }}
-					</MkSwitch>
-					<MkSwitch v-model="announcement.needConfirmationToRead" :helpText="i18n.ts._announcement.needConfirmationToReadDescription">
-						{{ i18n.ts._announcement.needConfirmationToRead }}
-					</MkSwitch>
-					<p v-if="announcement.reads">{{ i18n.tsx.nUsersRead({ n: announcement.reads }) }}</p>
-					<div class="buttons _buttons">
-						<MkButton class="button" inline primary @click="save(announcement)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
-						<MkButton v-if="announcement.id != null" class="button" inline @click="archive(announcement)"><i class="ti ti-check"></i> {{ i18n.ts._announcement.end }} ({{ i18n.ts.archive }})</MkButton>
-						<MkButton v-if="announcement.id != null" class="button" inline danger @click="del(announcement)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+			<MkLoading v-if="loading"/>
+
+			<template v-else>
+				<MkFolder v-for="announcement in announcements" :key="announcement.id ?? announcement._id" :defaultOpen="announcement.id == null">
+					<template #label>{{ announcement.title }}</template>
+					<template #icon>
+						<i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
+						<i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
+						<i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
+						<i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+					</template>
+					<template #caption>{{ announcement.text }}</template>
+
+					<div class="_gaps_m">
+						<MkInput v-model="announcement.title">
+							<template #label>{{ i18n.ts.title }}</template>
+						</MkInput>
+						<MkTextarea v-model="announcement.text" mfmAutocomplete :mfmPreview="true">
+							<template #label>{{ i18n.ts.text }}</template>
+						</MkTextarea>
+						<MkInput v-model="announcement.imageUrl" type="url">
+							<template #label>{{ i18n.ts.imageUrl }}</template>
+						</MkInput>
+						<MkRadios v-model="announcement.icon">
+							<template #label>{{ i18n.ts.icon }}</template>
+							<option value="info"><i class="ti ti-info-circle"></i></option>
+							<option value="warning"><i class="ti ti-alert-triangle" style="color: var(--warn);"></i></option>
+							<option value="error"><i class="ti ti-circle-x" style="color: var(--error);"></i></option>
+							<option value="success"><i class="ti ti-check" style="color: var(--success);"></i></option>
+						</MkRadios>
+						<MkRadios v-model="announcement.display">
+							<template #label>{{ i18n.ts.display }}</template>
+							<option value="normal">{{ i18n.ts.normal }}</option>
+							<option value="banner">{{ i18n.ts.banner }}</option>
+							<option value="dialog">{{ i18n.ts.dialog }}</option>
+						</MkRadios>
+						<MkInfo v-if="announcement.display === 'dialog'" warn>{{ i18n.ts._announcement.dialogAnnouncementUxWarn }}</MkInfo>
+						<MkSwitch v-model="announcement.forExistingUsers" :helpText="i18n.ts._announcement.forExistingUsersDescription">
+							{{ i18n.ts._announcement.forExistingUsers }}
+						</MkSwitch>
+						<MkSwitch v-model="announcement.silence" :helpText="i18n.ts._announcement.silenceDescription">
+							{{ i18n.ts._announcement.silence }}
+						</MkSwitch>
+						<MkSwitch v-model="announcement.needConfirmationToRead" :helpText="i18n.ts._announcement.needConfirmationToReadDescription">
+							{{ i18n.ts._announcement.needConfirmationToRead }}
+						</MkSwitch>
+						<p v-if="announcement.reads">{{ i18n.tsx.nUsersRead({ n: announcement.reads }) }}</p>
+						<div class="buttons _buttons">
+							<MkButton class="button" inline primary @click="save(announcement)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+							<MkButton v-if="announcement.id != null && announcement.isActive" class="button" inline @click="archive(announcement)"><i class="ti ti-check"></i> {{ i18n.ts._announcement.end }} ({{ i18n.ts.archive }})</MkButton>
+							<MkButton v-if="announcement.id != null && !announcement.isActive" class="button" inline @click="unarchive(announcement)"><i class="ti ti-restore"></i> {{ i18n.ts.unarchive }}</MkButton>
+							<MkButton v-if="announcement.id != null" class="button" inline danger @click="del(announcement)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+						</div>
 					</div>
-				</div>
-			</MkFolder>
-			<MkButton class="button" @click="more()">
-				<i class="ti ti-reload"></i>{{ i18n.ts.more }}
-			</MkButton>
+				</MkFolder>
+				<MkLoading v-if="loadingMore"/>
+				<MkButton class="button" @click="more()">
+					<i class="ti ti-reload"></i>{{ i18n.ts.more }}
+				</MkButton>
+			</template>
 		</div>
 	</MkSpacer>
 </MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
-import { ref, computed } from 'vue';
+import { ref, computed, watch } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
+import MkSelect from '@/components/MkSelect.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -85,11 +98,22 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 
+const announcementsStatus = ref<'active' | 'archived'>('active');
+
+const loading = ref(true);
+const loadingMore = ref(false);
+
 const announcements = ref<any[]>([]);
 
-misskeyApi('admin/announcements/list').then(announcementResponse => {
-	announcements.value = announcementResponse;
-});
+watch(announcementsStatus, (to) => {
+	loading.value = true;
+	misskeyApi('admin/announcements/list', {
+		status: to,
+	}).then(announcementResponse => {
+		announcements.value = announcementResponse;
+		loading.value = false;
+	});
+}, { immediate: true });
 
 function add() {
 	announcements.value.unshift({
@@ -125,6 +149,14 @@ async function archive(announcement) {
 	refresh();
 }
 
+async function unarchive(announcement) {
+	await os.apiWithDialog('admin/announcements/update', {
+		...announcement,
+		isActive: true,
+	});
+	refresh();
+}
+
 async function save(announcement) {
 	if (announcement.id == null) {
 		await os.apiWithDialog('admin/announcements/create', announcement);
@@ -135,24 +167,32 @@ async function save(announcement) {
 }
 
 function more() {
-	misskeyApi('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
+	loadingMore.value = true;
+	misskeyApi('admin/announcements/list', {
+		status: announcementsStatus.value,
+		untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id
+	}).then(announcementResponse => {
 		announcements.value = announcements.value.concat(announcementResponse);
+		loadingMore.value = false;
 	});
 }
 
 function refresh() {
-	misskeyApi('admin/announcements/list').then(announcementResponse => {
+	loading.value = true;
+	misskeyApi('admin/announcements/list', {
+		status: announcementsStatus.value,
+	}).then(announcementResponse => {
 		announcements.value = announcementResponse;
+		loading.value = false;
 	});
 }
 
-refresh();
-
 const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-plus',
 	text: i18n.ts.add,
 	handler: add,
+	disabled: announcementsStatus.value === 'archived',
 }]);
 
 const headerTabs = computed(() => []);
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 52a571b694..4cfe92147a 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -6091,6 +6091,11 @@ export type operations = {
           untilId?: string;
           /** Format: misskey:id */
           userId?: string | null;
+          /**
+           * @default active
+           * @enum {string}
+           */
+          status?: 'all' | 'active' | 'archived';
         };
       };
     };

From 738b3ea43b059b103deca0b1a33071ae256ef79f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 13:11:06 +0900
Subject: [PATCH 169/206] =?UTF-8?q?enhance(frontend):=20=E3=83=87=E3=83=83?=
 =?UTF-8?q?=E3=82=AD=E3=81=AE=E3=82=A2=E3=83=B3=E3=83=86=E3=83=8A=E3=83=BB?=
 =?UTF-8?q?=E3=83=AA=E3=82=B9=E3=83=88=E9=81=B8=E6=8A=9E=E7=94=BB=E9=9D=A2?=
 =?UTF-8?q?=E3=81=8B=E3=82=89=E3=81=9D=E3=82=8C=E3=81=9E=E3=82=8C=E3=82=92?=
 =?UTF-8?q?=E6=96=B0=E8=A6=8F=E4=BD=9C=E6=88=90=E3=81=A7=E3=81=8D=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#14104)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように

* Update Changelog

* fix

* fix

* lint

* add story

* typo

ねぼけていた

* Update antenna-column.vue

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            | 12 ++++
 locales/ja-JP.yml                             |  3 +
 .../MkAntennaEditor.stories.impl.ts           | 62 ++++++++++++++++
 .../MkAntennaEditor.vue}                      | 71 +++++++++++++------
 .../MkAntennaEditorDialog.stories.impl.ts     | 63 ++++++++++++++++
 .../src/components/MkAntennaEditorDialog.vue  | 63 ++++++++++++++++
 packages/frontend/src/components/MkDialog.vue | 20 ++++--
 packages/frontend/src/os.ts                   | 31 ++++----
 .../frontend/src/pages/my-antennas/create.vue | 32 +++------
 .../frontend/src/pages/my-antennas/edit.vue   | 17 +++--
 packages/frontend/src/scripts/merge.ts        |  2 +-
 packages/frontend/src/ui/deck.vue             | 29 +++-----
 .../frontend/src/ui/deck/antenna-column.vue   | 38 ++++++++--
 .../frontend/src/ui/deck/channel-column.vue   |  3 +-
 packages/frontend/src/ui/deck/deck-store.ts   | 21 +++++-
 .../frontend/src/ui/deck/direct-column.vue    |  2 +-
 packages/frontend/src/ui/deck/list-column.vue | 41 ++++++++---
 .../frontend/src/ui/deck/mentions-column.vue  |  2 +-
 .../src/ui/deck/notifications-column.vue      |  2 +-
 .../src/ui/deck/role-timeline-column.vue      |  4 +-
 packages/frontend/src/ui/deck/tl-column.vue   |  3 +-
 22 files changed, 409 insertions(+), 113 deletions(-)
 create mode 100644 packages/frontend/src/components/MkAntennaEditor.stories.impl.ts
 rename packages/frontend/src/{pages/my-antennas/editor.vue => components/MkAntennaEditor.vue} (66%)
 create mode 100644 packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkAntennaEditorDialog.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8da94f3749..f3ea5ca2d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 - Enhance: AiScriptを0.19.0にアップデート
 - Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
 - Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように
+- Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index d54b752312..848050583b 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -632,6 +632,10 @@ export interface Locale extends ILocale {
      * アンテナを編集
      */
     "editAntenna": string;
+    /**
+     * アンテナを作成
+     */
+    "createAntenna": string;
     /**
      * ウィジェットを選択
      */
@@ -5024,6 +5028,14 @@ export interface Locale extends ILocale {
      * センシティブなメディアです。表示しますか?
      */
     "sensitiveMediaRevealConfirm": string;
+    /**
+     * 作成したリスト
+     */
+    "createdLists": string;
+    /**
+     * 作成したアンテナ
+     */
+    "createdAntennas": string;
     "_delivery": {
         /**
          * 配信状態
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index d8531070fe..22228f9254 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -154,6 +154,7 @@ editList: "リストを編集"
 selectChannel: "チャンネルを選択"
 selectAntenna: "アンテナを選択"
 editAntenna: "アンテナを編集"
+createAntenna: "アンテナを作成"
 selectWidget: "ウィジェットを選択"
 editWidgets: "ウィジェットを編集"
 editWidgetsExit: "編集を終了"
@@ -1252,6 +1253,8 @@ inquiry: "お問い合わせ"
 tryAgain: "もう一度お試しください。"
 confirmWhenRevealingSensitiveMedia: "センシティブなメディアを表示するとき確認する"
 sensitiveMediaRevealConfirm: "センシティブなメディアです。表示しますか?"
+createdLists: "作成したリスト"
+createdAntennas: "作成したアンテナ"
 
 _delivery:
   status: "配信状態"
diff --git a/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts b/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts
new file mode 100644
index 0000000000..1749e07a4e
--- /dev/null
+++ b/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts
@@ -0,0 +1,62 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkAntennaEditor from './MkAntennaEditor.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkAntennaEditor,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						created: action('created'),
+						updated: action('updated'),
+						deleted: action('deleted'),
+					};
+				},
+			},
+			template: '<MkAntennaEditor v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+	},
+	parameters: {
+		layout: 'fullscreen',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/antennas/create', async ({ request }) => {
+					action('POST /api/antennas/create')(await request.json());
+					return HttpResponse.json({});
+				}),
+				http.post('/api/antennas/update', async ({ request }) => {
+					action('POST /api/antennas/update')(await request.json());
+					return HttpResponse.json({});
+				}),
+				http.post('/api/antennas/delete', async ({ request }) => {
+					action('POST /api/antennas/delete')(await request.json());
+					return HttpResponse.json();
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkAntennaEditor>;
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/components/MkAntennaEditor.vue
similarity index 66%
rename from packages/frontend/src/pages/my-antennas/editor.vue
rename to packages/frontend/src/components/MkAntennaEditor.vue
index 02e8f98265..cb7ee3d6ca 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/components/MkAntennaEditor.vue
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="$style.actions">
 			<div class="_buttons">
 				<MkButton inline primary @click="saveAntenna()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
-				<MkButton v-if="antenna.id != null" inline danger @click="deleteAntenna()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+				<MkButton v-if="initialAntenna.id != null" inline danger @click="deleteAntenna()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
 			</div>
 		</div>
 	</div>
@@ -61,28 +61,53 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
+import { deepMerge } from '@/scripts/merge.js';
+import type { DeepPartial } from '@/scripts/merge.js';
+
+type PartialAllowedAntenna = Omit<Misskey.entities.Antenna, 'id' | 'createdAt' | 'updatedAt'> & {
+	id?: string;
+	createdAt?: string;
+	updatedAt?: string;
+};
 
 const props = defineProps<{
-	antenna: Misskey.entities.Antenna
+	antenna?: DeepPartial<PartialAllowedAntenna>;
 }>();
 
+const initialAntenna = deepMerge<PartialAllowedAntenna>(props.antenna ?? {}, {
+	name: '',
+	src: 'all',
+	userListId: null,
+	users: [],
+	keywords: [],
+	excludeKeywords: [],
+	excludeBots: false,
+	withReplies: false,
+	caseSensitive: false,
+	localOnly: false,
+	withFile: false,
+	isActive: true,
+	hasUnreadNote: false,
+	notify: false,
+});
+
 const emit = defineEmits<{
-	(ev: 'created'): void,
-	(ev: 'updated'): void,
+	(ev: 'created', newAntenna: Misskey.entities.Antenna): void,
+	(ev: 'updated', editedAntenna: Misskey.entities.Antenna): void,
 	(ev: 'deleted'): void,
 }>();
 
-const name = ref<string>(props.antenna.name);
-const src = ref<Misskey.entities.AntennasCreateRequest['src']>(props.antenna.src);
-const userListId = ref<string | null>(props.antenna.userListId);
-const users = ref<string>(props.antenna.users.join('\n'));
-const keywords = ref<string>(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
-const excludeKeywords = ref<string>(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
-const caseSensitive = ref<boolean>(props.antenna.caseSensitive);
-const localOnly = ref<boolean>(props.antenna.localOnly);
-const excludeBots = ref<boolean>(props.antenna.excludeBots);
-const withReplies = ref<boolean>(props.antenna.withReplies);
-const withFile = ref<boolean>(props.antenna.withFile);
+const name = ref<string>(initialAntenna.name);
+const src = ref<Misskey.entities.AntennasCreateRequest['src']>(initialAntenna.src);
+const userListId = ref<string | null>(initialAntenna.userListId);
+const users = ref<string>(initialAntenna.users.join('\n'));
+const keywords = ref<string>(initialAntenna.keywords.map(x => x.join(' ')).join('\n'));
+const excludeKeywords = ref<string>(initialAntenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
+const caseSensitive = ref<boolean>(initialAntenna.caseSensitive);
+const localOnly = ref<boolean>(initialAntenna.localOnly);
+const excludeBots = ref<boolean>(initialAntenna.excludeBots);
+const withReplies = ref<boolean>(initialAntenna.withReplies);
+const withFile = ref<boolean>(initialAntenna.withFile);
 const userLists = ref<Misskey.entities.UserList[] | null>(null);
 
 watch(() => src.value, async () => {
@@ -106,24 +131,26 @@ async function saveAntenna() {
 		excludeKeywords: excludeKeywords.value.trim().split('\n').map(x => x.trim().split(' ')),
 	};
 
-	if (props.antenna.id == null) {
-		await os.apiWithDialog('antennas/create', antennaData);
-		emit('created');
+	if (initialAntenna.id == null) {
+		const res = await os.apiWithDialog('antennas/create', antennaData);
+		emit('created', res);
 	} else {
-		await os.apiWithDialog('antennas/update', { ...antennaData, antennaId: props.antenna.id });
-		emit('updated');
+		const res = await os.apiWithDialog('antennas/update', { ...antennaData, antennaId: initialAntenna.id });
+		emit('updated', res);
 	}
 }
 
 async function deleteAntenna() {
+	if (initialAntenna.id == null) return;
+
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: i18n.tsx.removeAreYouSure({ x: props.antenna.name }),
+		text: i18n.tsx.removeAreYouSure({ x: initialAntenna.name }),
 	});
 	if (canceled) return;
 
 	await misskeyApi('antennas/delete', {
-		antennaId: props.antenna.id,
+		antennaId: initialAntenna.id,
 	});
 
 	os.success();
diff --git a/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts b/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts
new file mode 100644
index 0000000000..1c6ca83b47
--- /dev/null
+++ b/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts
@@ -0,0 +1,63 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkAntennaEditorDialog from './MkAntennaEditorDialog.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkAntennaEditorDialog,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						created: action('created'),
+						updated: action('updated'),
+						deleted: action('deleted'),
+						closed: action('closed'),
+					};
+				},
+			},
+			template: '<MkAntennaEditorDialog v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+	},
+	parameters: {
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/antennas/create', async ({ request }) => {
+					action('POST /api/antennas/create')(await request.json());
+					return HttpResponse.json({});
+				}),
+				http.post('/api/antennas/update', async ({ request }) => {
+					action('POST /api/antennas/update')(await request.json());
+					return HttpResponse.json({});
+				}),
+				http.post('/api/antennas/delete', async ({ request }) => {
+					action('POST /api/antennas/delete')(await request.json());
+					return HttpResponse.json();
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkAntennaEditorDialog>;
diff --git a/packages/frontend/src/components/MkAntennaEditorDialog.vue b/packages/frontend/src/components/MkAntennaEditorDialog.vue
new file mode 100644
index 0000000000..6d815d29f3
--- /dev/null
+++ b/packages/frontend/src/components/MkAntennaEditorDialog.vue
@@ -0,0 +1,63 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+	ref="dialog"
+	:withOkButton="false"
+	:width="500"
+	:height="550"
+	@close="close()"
+	@closed="emit('closed')"
+>
+	<template #header>{{ antenna == null ? i18n.ts.createAntenna : i18n.ts.editAntenna }}</template>
+	<XAntennaEditor
+		:antenna="antenna"
+		@created="onAntennaCreated"
+		@updated="onAntennaUpdated"
+		@deleted="onAntennaDeleted"
+	/>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import XAntennaEditor from '@/components/MkAntennaEditor.vue';
+import { i18n } from '@/i18n.js';
+
+defineProps<{
+	antenna?: Misskey.entities.Antenna;
+}>();
+
+const emit = defineEmits<{
+	(ev: 'created', newAntenna: Misskey.entities.Antenna): void,
+	(ev: 'updated', editedAntenna: Misskey.entities.Antenna): void,
+	(ev: 'deleted'): void,
+	(ev: 'closed'): void,
+}>();
+
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
+
+function onAntennaCreated(newAntenna: Misskey.entities.Antenna) {
+	emit('created', newAntenna);
+	dialog.value?.close();
+}
+
+function onAntennaUpdated(editedAntenna: Misskey.entities.Antenna) {
+	emit('updated', editedAntenna);
+	dialog.value?.close();
+}
+
+function onAntennaDeleted() {
+	emit('deleted');
+	dialog.value?.close();
+}
+
+function close() {
+	dialog.value?.close();
+}
+</script>
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 5c3c6aa51d..16cf5b1b75 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -36,7 +36,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</MkInput>
 		<MkSelect v-if="select" v-model="selectedValue" autofocus>
 			<template v-if="select.items">
-				<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
+				<template v-for="item in select.items">
+					<optgroup v-if="'sectionTitle' in item" :label="item.sectionTitle">
+						<option v-for="subItem in item.items" :value="subItem.value">{{ subItem.text }}</option>
+					</optgroup>
+					<option v-else :value="item.value">{{ item.text }}</option>
+				</template>
 			</template>
 		</MkSelect>
 		<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
@@ -67,11 +72,16 @@ type Input = {
 	maxLength?: number;
 };
 
+type SelectItem = {
+	value: any;
+	text: string;
+};
+
 type Select = {
-	items: {
-		value: any;
-		text: string;
-	}[];
+	items: (SelectItem | {
+		sectionTitle: string;
+		items: SelectItem[];
+	})[];
 	default: string | null;
 };
 
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 3085f33e21..a8dd99c854 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -447,15 +447,20 @@ export function authenticateDialog(): Promise<{
 	});
 }
 
+type SelectItem<C> = {
+	value: C;
+	text: string;
+};
+
 // default が指定されていたら result は null になり得ないことを保証する overload function
 export function select<C = any>(props: {
 	title?: string;
 	text?: string;
 	default: string;
-	items: {
-		value: C;
-		text: string;
-	}[];
+	items: (SelectItem<C> | {
+		sectionTitle: string;
+		items: SelectItem<C>[];
+	} | undefined)[];
 }): Promise<{
 	canceled: true; result: undefined;
 } | {
@@ -465,10 +470,10 @@ export function select<C = any>(props: {
 	title?: string;
 	text?: string;
 	default?: string | null;
-	items: {
-		value: C;
-		text: string;
-	}[];
+	items: (SelectItem<C> | {
+		sectionTitle: string;
+		items: SelectItem<C>[];
+	} | undefined)[];
 }): Promise<{
 	canceled: true; result: undefined;
 } | {
@@ -478,10 +483,10 @@ export function select<C = any>(props: {
 	title?: string;
 	text?: string;
 	default?: string | null;
-	items: {
-		value: C;
-		text: string;
-	}[];
+	items: (SelectItem<C> | {
+		sectionTitle: string;
+		items: SelectItem<C>[];
+	} | undefined)[];
 }): Promise<{
 	canceled: true; result: undefined;
 } | {
@@ -492,7 +497,7 @@ export function select<C = any>(props: {
 			title: props.title,
 			text: props.text,
 			select: {
-				items: props.items,
+				items: props.items.filter(x => x !== undefined),
 				default: props.default ?? null,
 			},
 		}, {
diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue
index 2d026d2fa9..2b8518747f 100644
--- a/packages/frontend/src/pages/my-antennas/create.vue
+++ b/packages/frontend/src/pages/my-antennas/create.vue
@@ -4,43 +4,33 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div>
-	<XAntenna :antenna="draft" @created="onAntennaCreated"/>
-</div>
+<MkStickyContainer>
+	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
+
+	<MkAntennaEditor @created="onAntennaCreated"/>
+</MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
-import XAntenna from './editor.vue';
+import { computed } from 'vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { antennasCache } from '@/cache.js';
 import { useRouter } from '@/router/supplier.js';
+import MkAntennaEditor from '@/components/MkAntennaEditor.vue';
 
 const router = useRouter();
 
-const draft = ref({
-	name: '',
-	src: 'all',
-	userListId: null,
-	users: [],
-	keywords: [],
-	excludeKeywords: [],
-	excludeBots: false,
-	withReplies: false,
-	caseSensitive: false,
-	localOnly: false,
-	withFile: false,
-	notify: false,
-});
-
 function onAntennaCreated() {
 	antennasCache.delete();
 	router.push('/my/antennas');
 }
 
+const headerActions = computed(() => []);
+const headerTabs = computed(() => []);
+
 definePageMetadata(() => ({
-	title: i18n.ts.manageAntennas,
+	title: i18n.ts.createAntenna,
 	icon: 'ti ti-antenna',
 }));
 </script>
diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue
index 9471be8575..9f927cd1a0 100644
--- a/packages/frontend/src/pages/my-antennas/edit.vue
+++ b/packages/frontend/src/pages/my-antennas/edit.vue
@@ -4,15 +4,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div class="">
-	<XAntenna v-if="antenna" :antenna="antenna" @updated="onAntennaUpdated"/>
-</div>
+<MkStickyContainer>
+	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
+
+	<MkAntennaEditor v-if="antenna" :antenna="antenna" @updated="onAntennaUpdated"/>
+</MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import XAntenna from './editor.vue';
+import MkAntennaEditor from '@/components/MkAntennaEditor.vue';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -36,8 +38,11 @@ misskeyApi('antennas/show', { antennaId: props.antennaId }).then((antennaRespons
 	antenna.value = antennaResponse;
 });
 
+const headerActions = computed(() => []);
+const headerTabs = computed(() => []);
+
 definePageMetadata(() => ({
-	title: i18n.ts.manageAntennas,
+	title: i18n.ts.editAntenna,
 	icon: 'ti ti-antenna',
 }));
 </script>
diff --git a/packages/frontend/src/scripts/merge.ts b/packages/frontend/src/scripts/merge.ts
index 4e39a0fa06..9794a300da 100644
--- a/packages/frontend/src/scripts/merge.ts
+++ b/packages/frontend/src/scripts/merge.ts
@@ -6,7 +6,7 @@
 import { deepClone } from './clone.js';
 import type { Cloneable } from './clone.js';
 
-type DeepPartial<T> = {
+export type DeepPartial<T> = {
 	[P in keyof T]?: T[P] extends Record<string | number | symbol, unknown> ? DeepPartial<T[P]> : T[P];
 };
 
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index bdb62dca15..af46b0641d 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					:ref="id"
 					:key="id"
 					:class="$style.column"
-					:column="columns.find(c => c.id === id)"
+					:column="columns.find(c => c.id === id)!"
 					:isStacked="ids.length > 1"
 					@headerWheel="onWheel"
 				/>
@@ -95,7 +95,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, defineAsyncComponent, ref, watch, shallowRef } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XCommon from './_common_/common.vue';
-import { deckStore, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
+import { deckStore, columnTypes, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
+import type { ColumnType } from './deck/deck-store.js';
 import XSidebar from '@/ui/_common_/navbar.vue';
 import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -152,10 +153,12 @@ window.addEventListener('resize', () => {
 const snapScroll = deviceKind === 'smartphone' || deviceKind === 'tablet';
 const drawerMenuShowing = ref(false);
 
+/*
 const route = 'TODO';
 watch(route, () => {
 	drawerMenuShowing.value = false;
 });
+*/
 
 const columns = deckStore.reactiveState.columns;
 const layout = deckStore.reactiveState.layout;
@@ -174,32 +177,20 @@ function showSettings() {
 const columnsEl = shallowRef<HTMLElement>();
 
 const addColumn = async (ev) => {
-	const columns = [
-		'main',
-		'widgets',
-		'notifications',
-		'tl',
-		'antenna',
-		'list',
-		'channel',
-		'mentions',
-		'direct',
-		'roleTimeline',
-	];
-
 	const { canceled, result: column } = await os.select({
 		title: i18n.ts._deck.addColumn,
-		items: columns.map(column => ({
+		items: columnTypes.map(column => ({
 			value: column, text: i18n.ts._deck._columns[column],
 		})),
 	});
-	if (canceled) return;
+	if (canceled || column == null) return;
 
 	addColumnToStore({
 		type: column,
 		id: uuid(),
 		name: i18n.ts._deck._columns[column],
 		width: 330,
+		soundSetting: { type: null, volume: 1 },
 	});
 };
 
@@ -211,7 +202,7 @@ const onContextmenu = (ev) => {
 };
 
 function onWheel(ev: WheelEvent) {
-	if (ev.deltaX === 0) {
+	if (ev.deltaX === 0 && columnsEl.value != null) {
 		columnsEl.value.scrollLeft += ev.deltaY;
 	}
 }
@@ -242,7 +233,7 @@ function changeProfile(ev: MouseEvent) {
 					title: i18n.ts._deck.profile,
 					minLength: 1,
 				});
-				if (canceled) return;
+				if (canceled || name == null) return;
 
 				deckStore.set('profile', name);
 				unisonReload();
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index c3dc1e4fce..987bd4db55 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="() => timeline.reloadTimeline()">
+<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
 	<template #header>
 		<i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
@@ -14,7 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref, shallowRef, watch } from 'vue';
+import { onMounted, ref, shallowRef, watch, defineAsyncComponent } from 'vue';
+import type { entities as MisskeyEntities } from 'misskey-js';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
@@ -22,6 +23,7 @@ import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { MenuItem } from '@/types/menu.js';
+import { antennasCache } from '@/cache.js';
 import { SoundStore } from '@/store.js';
 import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
 import * as sound from '@/scripts/sound.js';
@@ -46,14 +48,36 @@ watch(soundSetting, v => {
 
 async function setAntenna() {
 	const antennas = await misskeyApi('antennas/list');
-	const { canceled, result: antenna } = await os.select({
+	const { canceled, result: antenna } = await os.select<MisskeyEntities.Antenna | '_CREATE_'>({
 		title: i18n.ts.selectAntenna,
-		items: antennas.map(x => ({
-			value: x, text: x.name,
-		})),
+		items: [
+			{ value: '_CREATE_', text: i18n.ts.createNew },
+			(antennas.length > 0 ? {
+				sectionTitle: i18n.ts.createdAntennas,
+				items: antennas.map(x => ({
+					value: x, text: x.name,
+				})),
+			} : undefined),
+		],
 		default: props.column.antennaId,
 	});
-	if (canceled) return;
+	if (canceled || antenna == null) return;
+
+	if (antenna === '_CREATE_') {
+		const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkAntennaEditorDialog.vue')), {}, {
+			created: (newAntenna: MisskeyEntities.Antenna) => {
+				antennasCache.delete();
+				updateColumn(props.column.id, {
+					antennaId: newAntenna.id,
+				});
+			},
+			closed: () => {
+				dispose();
+			},
+		});
+		return;
+	}
+
 	updateColumn(props.column.id, {
 		antennaId: antenna.id,
 	});
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index 7c5b13eaf1..42c07056e7 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="() => timeline.reloadTimeline()">
+<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
 	<template #header>
 		<i class="ti ti-device-tv"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
@@ -68,6 +68,7 @@ async function setChannel() {
 }
 
 async function post() {
+	if (props.column.channelId == null) return;
 	if (!channel.value || channel.value.id !== props.column.channelId) {
 		channel.value = await misskeyApi('channels/show', {
 			channelId: props.column.channelId,
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index bb3c04cd5c..139621cf57 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -17,9 +17,24 @@ type ColumnWidget = {
 	data: Record<string, any>;
 };
 
+export const columnTypes = [
+	'main',
+	'widgets',
+	'notifications',
+	'tl',
+	'antenna',
+	'list',
+	'channel',
+	'mentions',
+	'direct',
+	'roleTimeline',
+] as const;
+
+export type ColumnType = typeof columnTypes[number];
+
 export type Column = {
 	id: string;
-	type: 'main' | 'widgets' | 'notifications' | 'tl' | 'antenna' | 'channel' | 'list' | 'mentions' | 'direct';
+	type: ColumnType;
 	name: string | null;
 	width: number;
 	widgets?: ColumnWidget[];
@@ -265,7 +280,7 @@ export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) {
 	const columns = deepClone(deckStore.state.columns);
 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
 	const column = deepClone(deckStore.state.columns[columnIndex]);
-	if (column == null) return;
+	if (column == null || column.widgets == null) return;
 	column.widgets = column.widgets.filter(w => w.id !== widget.id);
 	columns[columnIndex] = column;
 	deckStore.set('columns', columns);
@@ -287,7 +302,7 @@ export function updateColumnWidget(id: Column['id'], widgetId: string, widgetDat
 	const columns = deepClone(deckStore.state.columns);
 	const columnIndex = deckStore.state.columns.findIndex(c => c.id === id);
 	const column = deepClone(deckStore.state.columns[columnIndex]);
-	if (column == null) return;
+	if (column == null || column.widgets == null) return;
 	column.widgets = column.widgets.map(w => w.id === widgetId ? {
 		...w,
 		data: widgetData,
diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue
index e011de0e3b..d12a18f760 100644
--- a/packages/frontend/src/ui/deck/direct-column.vue
+++ b/packages/frontend/src/ui/deck/direct-column.vue
@@ -34,7 +34,7 @@ const tlComponent = ref<InstanceType<typeof MkNotes>>();
 
 function reloadTimeline() {
 	return new Promise<void>((res) => {
-		tlComponent.value.pagingComponent?.reload().then(() => {
+		tlComponent.value?.pagingComponent?.reload().then(() => {
 			res();
 		});
 	});
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 5369112494..9aa8f06476 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="() => timeline.reloadTimeline()">
+<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
 	<template #header>
 		<i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
@@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { watch, shallowRef, ref } from 'vue';
+import type { entities as MisskeyEntities } from 'misskey-js';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
@@ -23,6 +24,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { MenuItem } from '@/types/menu.js';
 import { SoundStore } from '@/store.js';
+import { userListsCache } from '@/cache.js';
 import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
 import * as sound from '@/scripts/sound.js';
 
@@ -51,17 +53,38 @@ watch(soundSetting, v => {
 
 async function setList() {
 	const lists = await misskeyApi('users/lists/list');
-	const { canceled, result: list } = await os.select({
+	const { canceled, result: list } = await os.select<MisskeyEntities.UserList | '_CREATE_'>({
 		title: i18n.ts.selectList,
-		items: lists.map(x => ({
-			value: x, text: x.name,
-		})),
+		items: [
+			{ value: '_CREATE_', text: i18n.ts.createNew },
+			(lists.length > 0 ? {
+				sectionTitle: i18n.ts.createdLists,
+				items: lists.map(x => ({
+					value: x, text: x.name,
+				})),
+			} : undefined),
+		],
 		default: props.column.listId,
 	});
-	if (canceled) return;
-	updateColumn(props.column.id, {
-		listId: list.id,
-	});
+	if (canceled || list == null) return;
+
+	if (list === '_CREATE_') {
+		const { canceled, result: name } = await os.inputText({
+			title: i18n.ts.enterListName,
+		});
+		if (canceled || name == null || name === '') return;
+
+		const res = await os.apiWithDialog('users/lists/create', { name: name });
+		userListsCache.delete();
+
+		updateColumn(props.column.id, {
+			listId: res.id,
+		});
+	} else {
+		updateColumn(props.column.id, {
+			listId: list.id,
+		});
+	}
 }
 
 function editList() {
diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue
index 81926dd7cb..7b25a55ec3 100644
--- a/packages/frontend/src/ui/deck/mentions-column.vue
+++ b/packages/frontend/src/ui/deck/mentions-column.vue
@@ -26,7 +26,7 @@ const tlComponent = ref<InstanceType<typeof MkNotes>>();
 
 function reloadTimeline() {
 	return new Promise<void>((res) => {
-		tlComponent.value.pagingComponent?.reload().then(() => {
+		tlComponent.value?.pagingComponent?.reload().then(() => {
 			res();
 		});
 	});
diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue
index 23b0fd4f7b..19ccfc1f7c 100644
--- a/packages/frontend/src/ui/deck/notifications-column.vue
+++ b/packages/frontend/src/ui/deck/notifications-column.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<XColumn :column="column" :isStacked="isStacked" :menu="menu" :refresher="() => notificationsComponent.reload()">
+<XColumn :column="column" :isStacked="isStacked" :menu="menu" :refresher="async () => { await notificationsComponent?.reload() }">
 	<template #header><i class="ti ti-bell" style="margin-right: 8px;"></i>{{ column.name }}</template>
 
 	<XNotifications ref="notificationsComponent" :excludeTypes="props.column.excludeTypes"/>
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index 32ab7527b4..a375e9c574 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="() => timeline.reloadTimeline()">
+<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
 	<template #header>
 		<i class="ti ti-badge"></i><span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
@@ -53,7 +53,7 @@ async function setRole() {
 		})),
 		default: props.column.roleId,
 	});
-	if (canceled) return;
+	if (canceled || role == null) return;
 	updateColumn(props.column.id, {
 		roleId: role.id,
 	});
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index a967335edf..b4bc8bb748 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="() => timeline.reloadTimeline()">
+<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
 	<template #header>
 		<i v-if="column.tl === 'home'" class="ti ti-home"></i>
 		<i v-else-if="column.tl === 'local'" class="ti ti-planet"></i>
@@ -113,6 +113,7 @@ async function setType() {
 		}
 		return;
 	}
+	if (src == null) return;
 	updateColumn(props.column.id, {
 		tl: src,
 	});

From 45f909ef33056ac8f429d2a1707d1d7da96d2970 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 14:42:46 +0900
Subject: [PATCH 170/206] =?UTF-8?q?enhance(frontend):=20=E3=83=89=E3=83=A9?=
 =?UTF-8?q?=E3=82=A4=E3=83=96=E3=81=AE=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?=
 =?UTF-8?q?=E3=83=BB=E3=83=95=E3=82=A9=E3=83=AB=E3=83=80=E3=82=92=E3=83=89?=
 =?UTF-8?q?=E3=83=A9=E3=83=83=E3=82=B0=E3=81=97=E3=81=AA=E3=81=8F=E3=81=A6?=
 =?UTF-8?q?=E3=82=82=E7=A7=BB=E5=8B=95=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#14318)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(drive): ファイルをフォルダに移動するメニューを実装

(cherry picked from commit b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4)

* tweak ui

* Update Changelog

* ファイル詳細からも移動できるように

* feat(drive) フォルダのネストを移動するメニューを実装

(cherry picked from commit 8a7d710c6acb83f50c83f050bd1423c764d60a99)

* Update Changelog

* Update Changelog

* lint

* tweak ui

---------

Co-authored-by: nafu-at <satsuki@nafusoft.dev>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  2 +
 .../src/components/MkDrive.folder.vue         | 70 ++++++++++++++++---
 packages/frontend/src/components/MkDrive.vue  |  6 ++
 .../frontend/src/pages/drive.file.info.vue    | 43 ++++++++++--
 .../src/scripts/get-drive-file-menu.ts        | 13 ++++
 5 files changed, 117 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f3ea5ca2d5..0be36a2cb3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@
 - Enhance: AiScriptを0.19.0にアップデート
 - Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
 - Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように
+- Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように  
+  (Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99)
 - Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index c940596cde..d6dfaf34e5 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -27,7 +27,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<p v-if="defaultStore.state.uploadFolder == folder.id" :class="$style.upload">
 		{{ i18n.ts.uploadFolder }}
 	</p>
-	<button v-if="selectMode" class="_button" :class="[$style.checkbox, { [$style.checked]: isSelected }]" @click.prevent.stop="checkboxClicked"></button>
+	<button v-if="selectMode" class="_button" :class="$style.checkboxWrapper" @click.prevent.stop="checkboxClicked">
+		<div :class="[$style.checkbox, { [$style.checked]: isSelected }]"></div>
+	</button>
 </div>
 </template>
 
@@ -53,6 +55,7 @@ const props = withDefaults(defineProps<{
 
 const emit = defineEmits<{
 	(ev: 'chosen', v: Misskey.entities.DriveFolder): void;
+	(ev: 'unchose', v: Misskey.entities.DriveFolder): void;
 	(ev: 'move', v: Misskey.entities.DriveFolder): void;
 	(ev: 'upload', file: File, folder: Misskey.entities.DriveFolder);
 	(ev: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
@@ -68,7 +71,11 @@ const isDragging = ref(false);
 const title = computed(() => props.folder.name);
 
 function checkboxClicked() {
-	emit('chosen', props.folder);
+	if (props.isSelected) {
+		emit('unchose', props.folder);
+	} else {
+		emit('chosen', props.folder);
+	}
 }
 
 function onClick() {
@@ -222,6 +229,17 @@ function rename() {
 	});
 }
 
+function move() {
+	os.selectDriveFolder(false).then(folder => {
+		if (folder[0] && folder[0].id === props.folder.id) return;
+
+		misskeyApi('drive/folders/update', {
+			folderId: props.folder.id,
+			parentId: folder[0] ? folder[0].id : null,
+		});
+	});
+}
+
 function deleteFolder() {
 	misskeyApi('drive/folders/delete', {
 		folderId: props.folder.id,
@@ -267,6 +285,10 @@ function onContextmenu(ev: MouseEvent) {
 		text: i18n.ts.rename,
 		icon: 'ti ti-forms',
 		action: rename,
+	}, {
+		text: i18n.ts.move,
+		icon: 'ti ti ti-folder-symlink',
+		action: move,
 	}, { type: 'divider' }, {
 		text: i18n.ts.delete,
 		icon: 'ti ti-trash',
@@ -310,17 +332,43 @@ function onContextmenu(ev: MouseEvent) {
 	}
 }
 
-.checkbox {
+.checkboxWrapper {
 	position: absolute;
-	bottom: 8px;
-	right: 8px;
-	width: 16px;
-	height: 16px;
-	background: #fff;
-	border: solid 1px #000;
+	border-radius: 50%;
+	bottom: 2px;
+	right: 2px;
+	padding: 8px;
+	box-sizing: border-box;
 
-	&.checked {
-		background: var(--accent);
+	> .checkbox {
+		position: relative;
+		width: 18px;
+		height: 18px;
+		background: #fff;
+		border: solid 2px var(--divider);
+		border-radius: 4px;
+		box-sizing: border-box;
+
+		&.checked {
+			border-color: var(--accent);
+			background: var(--accent);
+
+			&::after {
+				content: "\ea5e";
+				font-family: 'tabler-icons';
+				position: absolute;
+				top: 50%;
+				left: 50%;
+				transform: translate(-50%, -50%);
+				color: #fff;
+				font-size: 12px;
+				line-height: 22px;
+			}
+		}
+	}
+
+	&:hover {
+		background: var(--accentedBg);
 	}
 }
 
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index a9717b4fb7..dbb4917069 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					:selectMode="select === 'folder'"
 					:isSelected="selectedFolders.some(x => x.id === f.id)"
 					@chosen="chooseFolder"
+					@unchose="unchoseFolder"
 					@move="move"
 					@upload="upload"
 					@removeFile="removeFile"
@@ -428,6 +429,11 @@ function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
 	}
 }
 
+function unchoseFolder(folderToUnchose: Misskey.entities.DriveFolder) {
+	selectedFolders.value = selectedFolders.value.filter(f => f.id !== folderToUnchose.id);
+	emit('change-selection', selectedFolders.value);
+}
+
 function move(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id' | 'parentId']) {
 	if (!target) {
 		goRoot();
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index a774412f83..3026d00a2c 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -37,11 +37,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</button>
 			</div>
 		</div>
-		<div>
-			<button class="_button" :class="$style.fileAltEditBtn" @click="describe()">
+		<div class="_gaps_s">
+			<button class="_button" :class="$style.kvEditBtn" @click="move()">
+				<MkKeyValue>
+					<template #key>{{ i18n.ts.folder }}</template>
+					<template #value>{{ folderHierarchy.join(' > ') }}<i class="ti ti-pencil" :class="$style.kvEditIcon"></i></template>
+				</MkKeyValue>
+			</button>
+			<button class="_button" :class="$style.kvEditBtn" @click="describe()">
 				<MkKeyValue>
 					<template #key>{{ i18n.ts.description }}</template>
-					<template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ti ti-pencil" :class="$style.fileAltEditIcon"></i></template>
+					<template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ti ti-pencil" :class="$style.kvEditIcon"></i></template>
 				</MkKeyValue>
 			</button>
 			<MkKeyValue :class="$style.fileMetaDataChildren">
@@ -90,6 +96,18 @@ const props = defineProps<{
 
 const fetching = ref(true);
 const file = ref<Misskey.entities.DriveFile>();
+const folderHierarchy = computed(() => {
+	if (!file.value) return [i18n.ts.drive];
+	const folderNames = [i18n.ts.drive];
+	
+	function get(folder: Misskey.entities.DriveFolder) {
+		if (folder.parent) get(folder.parent);
+		folderNames.push(folder.name);
+	}
+	
+	if (file.value.folder) get(file.value.folder);
+	return folderNames;
+});
 const isImage = computed(() => file.value?.type.startsWith('image/'));
 
 async function fetch() {
@@ -122,6 +140,19 @@ function crop() {
 	});
 }
 
+function move() {
+	if (!file.value) return;
+
+	os.selectDriveFolder(false).then(folder => {
+		misskeyApi('drive/files/update', {
+			fileId: file.value.id,
+			folderId: folder[0] ? folder[0].id : null,
+		}).then(async () => {
+			await fetch();
+		});
+	});
+}
+
 function toggleSensitive() {
 	if (!file.value) return;
 
@@ -282,14 +313,14 @@ onMounted(async () => {
 	padding: .5rem 1rem;
 }
 
-.fileAltEditBtn {
+.kvEditBtn {
 	text-align: start;
 	display: block;
 	width: 100%;
 	padding: .5rem 1rem;
 	border-radius: var(--radius);
 
-	.fileAltEditIcon {
+	.kvEditIcon {
 		display: inline-block;
 		color: transparent;
 		visibility: hidden;
@@ -300,7 +331,7 @@ onMounted(async () => {
 		color: var(--accent);
 		background-color: var(--accentedBg);
 
-		.fileAltEditIcon {
+		.kvEditIcon {
 			color: var(--accent);
 			visibility: visible;
 		}
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index 7c6c4b4db4..108648d640 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -41,6 +41,15 @@ function describe(file: Misskey.entities.DriveFile) {
 	});
 }
 
+function move(file: Misskey.entities.DriveFile) {
+	os.selectDriveFolder(false).then(folder => {
+		misskeyApi('drive/files/update', {
+			fileId: file.id,
+			folderId: folder[0] ? folder[0].id : null,
+		});
+	});
+}
+
 function toggleSensitive(file: Misskey.entities.DriveFile) {
 	misskeyApi('drive/files/update', {
 		fileId: file.id,
@@ -88,6 +97,10 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
 		text: i18n.ts.rename,
 		icon: 'ti ti-forms',
 		action: () => rename(file),
+	}, {
+		text: i18n.ts.move,
+		icon: 'ti ti-folder-symlink',
+		action: () => move(file),
 	}, {
 		text: file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
 		icon: file.isSensitive ? 'ti ti-eye' : 'ti ti-eye-exclamation',

From 866abff54d8e3c377361a0d75ef7ee59ba2fcd84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 14:45:53 +0900
Subject: [PATCH 171/206] =?UTF-8?q?enhance(frontend):=20=E3=83=96=E3=83=A9?=
 =?UTF-8?q?=E3=82=A6=E3=82=B6=E3=81=AE=E3=82=B3=E3=83=B3=E3=83=86=E3=82=AD?=
 =?UTF-8?q?=E3=82=B9=E3=83=88=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E3=82=92?=
 =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#14076)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): ブラウザのコンテキストメニューを使用できるように

* Update Changelog

* shiftにした

* change keys

* fix

* fix

* fix

* update translation keys

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
---
 CHANGELOG.md                                   |  1 +
 locales/index.d.ts                             | 18 ++++++++++++++++++
 locales/ja-JP.yml                              |  6 ++++++
 packages/frontend/src/os.ts                    |  8 ++++++++
 .../frontend/src/pages/settings/general.vue    |  8 ++++++++
 packages/frontend/src/store.ts                 |  4 ++++
 6 files changed, 45 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0be36a2cb3..17cf4d3275 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@
 - Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように  
   (Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99)
 - Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
+- Enhance: ブラウザのコンテキストメニューを使用できるように
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 848050583b..30c21c92e7 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -10118,6 +10118,24 @@ export interface Locale extends ILocale {
          */
         "loop": string;
     };
+    "_contextMenu": {
+        /**
+         * コンテキストメニュー
+         */
+        "title": string;
+        /**
+         * アプリケーション
+         */
+        "app": string;
+        /**
+         * Shiftキーでアプリケーション
+         */
+        "appWithShift": string;
+        /**
+         * ブラウザのUI
+         */
+        "native": string;
+    };
 }
 declare const locales: {
     [lang: string]: Locale;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 22228f9254..ea2ffb9c0d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2697,3 +2697,9 @@ _mediaControls:
   pip: "ピクチャインピクチャ"
   playbackRate: "再生速度"
   loop: "ループ再生"
+
+_contextMenu:
+  title: "コンテキストメニュー"
+  app: "アプリケーション"
+  appWithShift: "Shiftキーでアプリケーション"
+  native: "ブラウザのUI"
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index a8dd99c854..f42e2ed3c5 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -11,6 +11,7 @@ import * as Misskey from 'misskey-js';
 import type { ComponentProps as CP } from 'vue-component-type-helpers';
 import type { Form, GetFormResultType } from '@/scripts/form.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
+import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import MkPostFormDialog from '@/components/MkPostFormDialog.vue';
 import MkWaitingDialog from '@/components/MkWaitingDialog.vue';
@@ -654,6 +655,13 @@ export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | n
 }
 
 export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
+	if (
+		defaultStore.state.contextMenu === 'native' ||
+		(defaultStore.state.contextMenu === 'appWithShift' && !ev.shiftKey)
+	) {
+		return Promise.resolve();
+	}
+
 	let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(document.activeElement);
 	ev.preventDefault();
 	return new Promise(resolve => nextTick(() => {
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 9e429f8dbd..94ef3b8485 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -177,6 +177,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option>
 				<option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
 			</MkSelect>
+			<MkSelect v-model="contextMenu">
+				<template #label>{{ i18n.ts._contextMenu.title }}</template>
+				<option value="app">{{ i18n.ts._contextMenu.app }}</option>
+				<option value="appWithShift">{{ i18n.ts._contextMenu.appWithShift }}</option>
+				<option value="native">{{ i18n.ts._contextMenu.native }}</option>
+			</MkSelect>
 			<MkRange v-model="numberOfPageCache" :min="1" :max="10" :step="1" easing>
 				<template #label>{{ i18n.ts.numberOfPageCache }}</template>
 				<template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template>
@@ -317,6 +323,7 @@ const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHori
 const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer'));
 const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
 const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia'));
+const contextMenu = computed(defaultStore.makeGetterSetter('contextMenu'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
@@ -360,6 +367,7 @@ watch([
 	enableSeasonalScreenEffect,
 	alwaysConfirmFollow,
 	confirmWhenRevealingSensitiveMedia,
+	contextMenu,
 ], async () => {
 	await reloadAsk();
 });
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index dbf6b8716f..437314074a 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -458,6 +458,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: false,
 	},
+  contextMenu: {
+		where: 'device',
+		default: 'app' as 'app' | 'appWithShift' | 'native',
+  },
 
 	sound_masterVolume: {
 		where: 'device',

From 5eea41b089b20017da2becb9ce0fc1d4d48a5e7f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 30 Jul 2024 06:11:15 +0000
Subject: [PATCH 172/206] Bump version to 2024.7.0-rc.5

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

diff --git a/package.json b/package.json
index e33383916c..64f7b88a9f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-rc.4",
+	"version": "2024.7.0-rc.5",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index dcddc8087c..b112975328 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-rc.4",
+	"version": "2024.7.0-rc.5",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 7e9c38d6fbdab1691056f411787801d87701c593 Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:29:24 +0900
Subject: [PATCH 173/206] =?UTF-8?q?Fix(backend):=20=E3=83=89=E3=83=A9?=
 =?UTF-8?q?=E3=82=A4=E3=83=96=E3=81=AE=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?=
 =?UTF-8?q?=E3=81=AEurl,=20uri,=20src=20=E3=81=AE=E4=B8=8A=E9=99=90?=
 =?UTF-8?q?=E5=BC=95=E3=81=8D=E4=B8=8A=E3=81=92=20=20(#14323)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: ドライブurlの上限文字数を引き上げ

* Fix: おそらくフォーク独自の変更のように見える部分(metaに関する変更部分)を削除

* UPDATE changelog

* Add SPDX prefixes

* Fix: インデックスの張り直しを消した

---------

Co-authored-by: slofp <phy.public@gmail.com>
---
 CHANGELOG.md                                  |  5 ++++
 .../migration/1721666053703-fixDriveUrl.js    | 24 +++++++++++++++++++
 packages/backend/src/models/DriveFile.ts      |  6 ++---
 3 files changed, 32 insertions(+), 3 deletions(-)
 create mode 100644 packages/backend/migration/1721666053703-fixDriveUrl.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 17cf4d3275..33256a8674 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -88,6 +88,11 @@
 - Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正  
   (Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1)
 - Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 #14251
+- Fix: 一部のMisskey以外のソフトウェアからファイルを受け取れない問題
+  (Cherry-picked from https://github.com/Secineralyr/misskey.dream/pull/73/commits/652eaff1e8aa00b890d71d2e1e52c263c1e67c76)
+  - NOTE: `drive_file`の`url`, `uri`, `src`の上限が512から1024に変更されます
+	  Migrationではカラム定義の変更のみが行われます。
+		サーバー管理者は各サーバーの必要に応じ`drive_file` `("uri")`に対するインデックスを張りなおすことでより安定しDBの探索が行われる可能性があります。詳細 は [GitHub](https://github.com/misskey-dev/misskey/pull/14323#issuecomment-2257562228)で確認可能です
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
diff --git a/packages/backend/migration/1721666053703-fixDriveUrl.js b/packages/backend/migration/1721666053703-fixDriveUrl.js
new file mode 100644
index 0000000000..d8512fb835
--- /dev/null
+++ b/packages/backend/migration/1721666053703-fixDriveUrl.js
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class FixDriveUrl1721666053703 {
+    name = 'FixDriveUrl1721666053703'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "drive_file" ALTER COLUMN "url" TYPE character varying(1024), ALTER COLUMN "url" SET NOT NULL`);
+        await queryRunner.query(`COMMENT ON COLUMN "drive_file"."url" IS 'The URL of the DriveFile.'`);
+        await queryRunner.query(`ALTER TABLE "drive_file" ALTER COLUMN "uri" TYPE character varying(1024)`);
+        await queryRunner.query(`COMMENT ON COLUMN "drive_file"."uri" IS 'The URI of the DriveFile. it will be null when the DriveFile is local.'`);
+        await queryRunner.query(`ALTER TABLE "drive_file" ALTER COLUMN "src" TYPE character varying(1024)`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "drive_file" ALTER COLUMN "src" TYPE character varying(512)`);
+        await queryRunner.query(`COMMENT ON COLUMN "drive_file"."uri" IS 'The URI of the DriveFile. it will be null when the DriveFile is local.'`);
+        await queryRunner.query(`ALTER TABLE "drive_file" ALTER COLUMN "uri" TYPE character varying(512)`);
+        await queryRunner.query(`COMMENT ON COLUMN "drive_file"."url" IS 'The URL of the DriveFile.'`);
+        await queryRunner.query(`ALTER TABLE "drive_file" ALTER COLUMN "url" TYPE character varying(512), ALTER COLUMN "url" SET NOT NULL`);
+    }
+}
diff --git a/packages/backend/src/models/DriveFile.ts b/packages/backend/src/models/DriveFile.ts
index 438b32f79a..7b03e3e494 100644
--- a/packages/backend/src/models/DriveFile.ts
+++ b/packages/backend/src/models/DriveFile.ts
@@ -82,7 +82,7 @@ export class MiDriveFile {
 	public storedInternal: boolean;
 
 	@Column('varchar', {
-		length: 512,
+		length: 1024,
 		comment: 'The URL of the DriveFile.',
 	})
 	public url: string;
@@ -124,13 +124,13 @@ export class MiDriveFile {
 
 	@Index()
 	@Column('varchar', {
-		length: 512, nullable: true,
+		length: 1024, nullable: true,
 		comment: 'The URI of the DriveFile. it will be null when the DriveFile is local.',
 	})
 	public uri: string | null;
 
 	@Column('varchar', {
-		length: 512, nullable: true,
+		length: 1024, nullable: true,
 	})
 	public src: string | null;
 

From bff813042e670dcb59a6b0dcc334ca44f880752b Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:51:08 +0900
Subject: [PATCH 174/206] =?UTF-8?q?feat:=20=E3=81=93=E3=81=AE=E3=83=A6?=
 =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E3=82=92=E6=A4=9C=E7=B4=A2,=20=E3=82=AF=E3=82=A8=E3=83=AA?=
 =?UTF-8?q?=E3=81=AB=E5=9F=BA=E3=81=A5=E3=81=8F=E6=A4=9C=E7=B4=A2=E3=81=AE?=
 =?UTF-8?q?=E5=88=9D=E6=9C=9F=E5=80=A4=20&=20=E3=83=8E=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E6=A4=9C=E7=B4=A2=E3=81=AEUI=E6=94=B9=E5=96=84=20(#14128)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(frontend): noteSearchAvailableをaccountsに移動

* feat: searchページでのクエリの受取りとtypeによる表示タブの変更

* user検索でsearchの親から受け取った値を基に入力値を初期化

* feat(frontend): ノート検索で親(search)からの情報を基にユーザー情報を取得

* feat(frontend): ユーザーのノートを検索するページに遷移するボタン

* feat(frontend): ノート検索にホスト名指定のオプション追加
also :art:

* style: ただ照会部分を囲っただけ(可読性確保のために)

* refactor: remove unneed import
defineProps and withDefaults are compiler micro when using `<script setup>`
FYI: https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits:~:text=defineProps%20and%20defineEmits%20are%20compiler%20macros%20only%20usable%20inside%20%3Cscript%20setup%3E.%20They%20do%20not%20need%20to%20be%20imported%2C%20and%20are%20compiled%20away%20when%20%3Cscript%20setup%3E%20is%20processed.

* Update CHANGELOG

* Fix: ノート検索の初期値が常にホスト指定になってしまう

* notesSearchAvailableをaccountに持たせるのをやめる

* SDPX-Licence-Identifier

* Fix: Vitest fails due to instance.policies being undefined

* Add Storybook for search

* Fix(storybook): ノート検索が利用できないと出てしまう問題

* storybookでユーザー選択ができないのを修正

* feat: ノート検索で自分を選択可能に
& :art:

* feat(background): api/metaで検索可能なノートのスコープを参照できるように

* globalのノートが検索不可能な場合、検索オプションを表示しないように

* Update CHANGELOG.md

* config.meilisearch.scopeがstring[]を取ることがあるので修正

* meilisearchを利用かつscopeがlocalの場合、リモートユーザーのメニューで「このユーザーのノートを検索」を出さないように

* hostが空文字の時の挙動を修正

* ローカルのみしかノートがインデックスされていない場合、リモートユーザーも選択できなくした
---
 CHANGELOG.md                                  |   4 +
 locales/index.d.ts                            |  12 ++
 locales/ja-JP.yml                             |   3 +
 .../src/core/entities/MetaEntityService.ts    |   1 +
 .../backend/src/models/json-schema/meta.ts    |   6 +
 packages/frontend/.storybook/fakes.ts         |   2 +-
 packages/frontend/.storybook/generate.tsx     |   1 +
 packages/frontend/src/components/MkRadios.vue |   4 +
 packages/frontend/src/pages/search.note.vue   | 152 +++++++++++++++---
 .../frontend/src/pages/search.stories.impl.ts |  88 ++++++++++
 packages/frontend/src/pages/search.user.vue   |  20 ++-
 packages/frontend/src/pages/search.vue        |  34 ++--
 packages/frontend/src/router/definition.ts    |   3 +
 .../frontend/src/scripts/check-permissions.ts |  19 +++
 .../frontend/src/scripts/get-user-menu.ts     |  10 +-
 packages/misskey-js/src/autogen/types.ts      |   5 +
 16 files changed, 327 insertions(+), 37 deletions(-)
 create mode 100644 packages/frontend/src/pages/search.stories.impl.ts
 create mode 100644 packages/frontend/src/scripts/check-permissions.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33256a8674..f58bec42e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,9 @@
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
 
 ### Client
+- Feat: ユーザーページから「このユーザーのノートを検索」できるように (#14128)
+- Feat: 検索ページはクエリを受け付けるようになりました (#14128)
+- Enhance: 検索ページのUI改善 (#14128)
 - Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善
 - Enhance: 非ログイン時に他サーバーに遷移するアクションを追加
 - Enhance: 非ログイン時のハイライトTLのデザインを改善
@@ -61,6 +64,7 @@
 - Enhance: エンドポイント`i/webhook/update`の必須項目を`webhookId`のみに
 - Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに
 - Enhance: `default.yml`内の`url`, `db.db`, `db.user`, `db.pass`を環境変数から読み込めるように
+- Enhance: エンドポイント`api/meta`にプロパティ`noteSearchableScope`が増え、`string`値`local`または`global`を返却します
 - Fix: チャート生成時にinstance.suspensionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正
 - Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006)
 - Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036)
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 30c21c92e7..14dc862745 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -256,6 +256,10 @@ export interface Locale extends ILocale {
      * ユーザーを検索
      */
     "searchUser": string;
+    /**
+     * ユーザーのノートを検索
+     */
+    "searchThisUsersNotes": string;
     /**
      * 返信
      */
@@ -796,6 +800,10 @@ export interface Locale extends ILocale {
      * ホスト
      */
     "host": string;
+    /**
+     * 自分を選択
+     */
+    "selectSelf": string;
     /**
      * ユーザーを選択
      */
@@ -4492,6 +4500,10 @@ export interface Locale extends ILocale {
      * ユーザー指定
      */
     "specifyUser": string;
+    /**
+     * ホスト指定
+     */
+    "specifyHost": string;
     /**
      * プレビューできません
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ea2ffb9c0d..cd26b71d57 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -60,6 +60,7 @@ copyFileId: "ファイルIDをコピー"
 copyFolderId: "フォルダーIDをコピー"
 copyProfileUrl: "プロフィールURLをコピー"
 searchUser: "ユーザーを検索"
+searchThisUsersNotes: "ユーザーのノートを検索"
 reply: "返信"
 loadMore: "もっと見る"
 showMore: "もっと見る"
@@ -195,6 +196,7 @@ followConfirm: "{name}をフォローしますか?"
 proxyAccount: "プロキシアカウント"
 proxyAccountDescription: "プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがサーバーに配達されないため、代わりにプロキシアカウントがフォローするようにします。"
 host: "ホスト"
+selectSelf: "自分を選択"
 selectUser: "ユーザーを選択"
 recipient: "宛先"
 annotation: "注釈"
@@ -1119,6 +1121,7 @@ preventAiLearning: "生成AIによる学習を拒否"
 preventAiLearningDescription: "外部の文章生成AIや画像生成AIに対して、投稿したノートや画像などのコンテンツを学習の対象にしないように要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されますが、この要求に従うかはそのAI次第であるため、学習を完全に防止するものではありません。"
 options: "オプション"
 specifyUser: "ユーザー指定"
+specifyHost: "ホスト指定"
 failedToPreviewUrl: "プレビューできません"
 update: "更新"
 rolesThatCanBeUsedThisEmojiAsReaction: "リアクションとして使えるロール"
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index 09641ce485..bcfd8ae41c 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -128,6 +128,7 @@ export class MetaEntityService {
 
 			mediaProxy: this.config.mediaProxy,
 			enableUrlPreview: instance.urlPreviewEnabled,
+			noteSearchableScope: this.config.meilisearch == null || this.config.meilisearch.scope === 'local' ? 'local' : 'global',
 		};
 
 		return packed;
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
index e7bc6356e5..3bcf9cac92 100644
--- a/packages/backend/src/models/json-schema/meta.ts
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -247,6 +247,12 @@ export const packedMetaLiteSchema = {
 			optional: false, nullable: false,
 			ref: 'RolePolicies',
 		},
+		noteSearchableScope: {
+			type: 'string',
+			enum: ['local', 'global'],
+			optional: false, nullable: false,
+			default: 'local',
+		},
 	},
 } as const;
 
diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index 9d789a34ff..01f1046609 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -154,7 +154,7 @@ export function federationInstance(): entities.FederationInstance {
 	};
 }
 
-export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed {
+export function userDetailed(id = 'someuserid', username = 'miskist', host:entities.UserDetailed['host'] = 'misskey-hub.net', name:entities.UserDetailed['name'] = 'Misskey User'): entities.UserDetailed {
 	return {
 		id,
 		username,
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index 7b6c86447e..b94dfc53e3 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -405,6 +405,7 @@ function toStories(component: string): Promise<string> {
 		glob('src/components/MkUserSetupDialog.*.vue'),
 		glob('src/components/MkInstanceCardMini.vue'),
 		glob('src/components/MkInviteCode.vue'),
+		glob('src/pages/search.vue'),
 		glob('src/pages/user/home.vue'),
 	]);
 	const components = globs.flat();
diff --git a/packages/frontend/src/components/MkRadios.vue b/packages/frontend/src/components/MkRadios.vue
index 549438f61b..705c93f770 100644
--- a/packages/frontend/src/components/MkRadios.vue
+++ b/packages/frontend/src/components/MkRadios.vue
@@ -29,6 +29,9 @@ export default defineComponent({
 		// なぜかFragmentになることがあるため
 		if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
 
+		// vnodeのうちv-if=falseなものを除外する(trueになるものはoptionなど他typeになる)
+		options = options.filter(vnode => !(typeof vnode.type === 'symbol' && vnode.type.description === 'v-cmt' && vnode.children === 'v-if'));
+
 		return () => h('div', {
 			class: 'novjtcto',
 		}, [
@@ -40,6 +43,7 @@ export default defineComponent({
 			}, options.map(option => h(MkRadio, {
 				key: option.key as string,
 				value: option.props?.value,
+				disabled: option.props?.disabled,
 				modelValue: value.value,
 				'onUpdate:modelValue': _v => value.value = _v,
 			}, () => option.children)),
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index d68bbaeeca..05dc77b94b 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -9,26 +9,35 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
 			<template #prefix><i class="ti ti-search"></i></template>
 		</MkInput>
-		<MkFolder>
-			<template #label>{{ i18n.ts.options }}</template>
+		<MkFoldableSection :expanded="true">
+			<template #header>{{ i18n.ts.options }}</template>
 
 			<div class="_gaps_m">
-				<MkSwitch v-model="isLocalOnly">{{ i18n.ts.localOnly }}</MkSwitch>
+				<MkRadios v-model="hostSelect">
+					<template #label>{{ i18n.ts.host }}</template>
+					<option value="all" default>{{ i18n.ts.all }}</option>
+					<option value="local">{{ i18n.ts.local }}</option>
+					<option v-if="noteSearchableScope === 'global'" value="specified">{{ i18n.ts.specifyHost }}</option>
+				</MkRadios>
+				<MkInput v-if="noteSearchableScope === 'global'" v-model="hostInput" :disabled="hostSelect !== 'specified'" :large="true" type="search">
+					<template #prefix><i class="ti ti-server"></i></template>
+				</MkInput>
 
 				<MkFolder :defaultOpen="true">
 					<template #label>{{ i18n.ts.specifyUser }}</template>
-					<template v-if="user" #suffix>@{{ user.username }}</template>
+					<template v-if="user" #suffix>@{{ user.username }}{{ user.host ? `@${user.host}` : "" }}</template>
 
-					<div style="text-align: center;" class="_gaps">
-						<div v-if="user">@{{ user.username }}</div>
-						<div>
-							<MkButton v-if="user == null" primary rounded inline @click="selectUser">{{ i18n.ts.selectUser }}</MkButton>
-							<MkButton v-else danger rounded inline @click="user = null">{{ i18n.ts.remove }}</MkButton>
+					<div class="_gaps">
+						<div :class="$style.userItem">
+							<MkUserCardMini v-if="user" :class="$style.userCard" :user="user" :withChart="false"/>
+							<MkButton v-if="user == null && $i != null" transparent :class="$style.addMeButton" @click="selectSelf"><div :class="$style.addUserButtonInner"><span><i class="ti ti-plus"></i><i class="ti ti-user"></i></span><span>{{ i18n.ts.selectSelf }}</span></div></MkButton>
+							<MkButton v-if="user == null" transparent :class="$style.addUserButton" @click="selectUser"><div :class="$style.addUserButtonInner"><i class="ti ti-plus"></i><span>{{ i18n.ts.selectUser }}</span></div></MkButton>
+							<button class="_button" :class="$style.remove" :disabled="user == null" @click="removeUser"><i class="ti ti-x"></i></button>
 						</div>
 					</div>
 				</MkFolder>
 			</div>
-		</MkFolder>
+		</MkFoldableSection>
 		<div>
 			<MkButton large primary gradate rounded style="margin: 0 auto;" @click="search">{{ i18n.ts.search }}</MkButton>
 		</div>
@@ -42,31 +51,90 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { computed, ref, toRef, watch } from 'vue';
+import type { UserDetailed } from 'misskey-js/entities.js';
+import type { Paging } from '@/components/MkPagination.vue';
 import MkNotes from '@/components/MkNotes.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import { useRouter } from '@/router/supplier.js';
+import MkUserCardMini from '@/components/MkUserCardMini.vue';
+import MkRadios from '@/components/MkRadios.vue';
+import { $i } from '@/account.js';
+import { instance } from '@/instance.js';
+
+const props = withDefaults(defineProps<{
+	query?: string;
+	userId?: string;
+	username?: string;
+	host?: string | null;
+}>(), {
+	query: '',
+	userId: undefined,
+	username: undefined,
+	host: '',
+});
 
 const router = useRouter();
-
 const key = ref(0);
-const searchQuery = ref('');
-const searchOrigin = ref('combined');
-const notePagination = ref();
-const user = ref<any>(null);
-const isLocalOnly = ref(false);
+const searchQuery = ref(toRef(props, 'query').value);
+const notePagination = ref<Paging>();
+const user = ref<UserDetailed | null>(null);
+const hostInput = ref(toRef(props, 'host').value);
 
-function selectUser() {
-	os.selectUser({ includeSelf: true }).then(_user => {
+const noteSearchableScope = instance.noteSearchableScope ?? 'local';
+
+const hostSelect = ref<'all' | 'local' | 'specified'>('all');
+
+const setHostSelectWithInput = (after:string|undefined|null, before:string|undefined|null) => {
+	if (before === after) return;
+	if (after === '') hostSelect.value = 'all';
+	else hostSelect.value = 'specified';
+};
+
+setHostSelectWithInput(hostInput.value, undefined);
+
+watch(hostInput, setHostSelectWithInput);
+
+const searchHost = computed(() => {
+	if (hostSelect.value === 'local') return '.';
+	if (hostSelect.value === 'specified') return hostInput.value;
+	return null;
+});
+
+if (props.userId != null) {
+	misskeyApi('users/show', { userId: props.userId }).then(_user => {
 		user.value = _user;
 	});
+} else if (props.username != null) {
+	misskeyApi('users/show', {
+		username: props.username,
+		...(props.host != null && props.host !== '') ? { host: props.host } : {},
+	}).then(_user => {
+		user.value = _user;
+	});
+}
+
+function selectUser() {
+	os.selectUser({ includeSelf: true, localOnly: instance.noteSearchableScope === 'local' }).then(_user => {
+		user.value = _user;
+		hostInput.value = _user.host ?? '';
+	});
+}
+
+function selectSelf() {
+	user.value = $i as UserDetailed | null;
+	hostInput.value = null;
+}
+
+function removeUser() {
+	user.value = null;
+	hostInput.value = '';
 }
 
 async function search() {
@@ -74,6 +142,7 @@ async function search() {
 
 	if (query == null || query === '') return;
 
+	//#region AP lookup
 	if (query.startsWith('https://')) {
 		const promise = misskeyApi('ap/show', {
 			uri: query,
@@ -91,6 +160,7 @@ async function search() {
 
 		return;
 	}
+	//#endregion
 
 	notePagination.value = {
 		endpoint: 'notes/search',
@@ -98,11 +168,49 @@ async function search() {
 		params: {
 			query: searchQuery.value,
 			userId: user.value ? user.value.id : null,
+			...(searchHost.value ? { host: searchHost.value } : {}),
 		},
 	};
 
-	if (isLocalOnly.value) notePagination.value.params.host = '.';
-
 	key.value++;
 }
 </script>
+<style lang="scss" module>
+.userItem {
+	display: flex;
+	justify-content: center;
+}
+.addMeButton {
+  border: 2px dashed var(--fgTransparent);
+	padding: 12px;
+	margin-right: 16px;
+}
+.addUserButton {
+  border: 2px dashed var(--fgTransparent);
+	padding: 12px;
+	flex-grow: 1;
+}
+.addUserButtonInner {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: space-between;
+	min-height: 38px;
+}
+.userCard {
+	flex-grow: 1;
+}
+.remove {
+	width: 32px;
+	height: 32px;
+	align-self: center;
+
+	& > i:before {
+		color: #ff2a2a;
+	}
+
+	&:disabled {
+		opacity: 0;
+	}
+}
+</style>
diff --git a/packages/frontend/src/pages/search.stories.impl.ts b/packages/frontend/src/pages/search.stories.impl.ts
new file mode 100644
index 0000000000..0110a7ab8e
--- /dev/null
+++ b/packages/frontend/src/pages/search.stories.impl.ts
@@ -0,0 +1,88 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { StoryObj } from '@storybook/vue3';
+import { HttpResponse, http } from 'msw';
+import search_ from './search.vue';
+import { userDetailed } from '@/../.storybook/fakes.js';
+import { commonHandlers } from '@/../.storybook/mocks.js';
+
+const localUser = userDetailed('someuserid', 'miskist', null, 'Local Misskey User');
+
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				search_,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<search_ v-bind="props" />',
+		};
+	},
+	args: {
+		ignoreNotesSearchAvailable: true,
+	},
+	parameters: {
+		layout: 'fullscreen',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/users/show', () => {
+					return HttpResponse.json(userDetailed());
+				}),
+				http.post('/api/users/search', () => {
+					return HttpResponse.json([userDetailed(), localUser]);
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof search_>;
+
+export const NoteSearchDisabled = {
+	...Default,
+	args: {},
+} satisfies StoryObj<typeof search_>;
+
+export const WithUsernameLocal = {
+	...Default,
+
+	args: {
+		...Default.args,
+		username: localUser.username,
+		host: localUser.host,
+	},
+	parameters: {
+		layout: 'fullscreen',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/users/show', () => {
+					return HttpResponse.json(localUser);
+				}),
+				http.post('/api/users/search', () => {
+					return HttpResponse.json([userDetailed(), localUser]);
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof search_>;
+
+export const WithUserType = {
+	...Default,
+	args: {
+		type: 'user',
+	},
+} satisfies StoryObj<typeof search_>;
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index b9c2704bc7..85d869d9cb 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -25,7 +25,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, toRef } from 'vue';
+import type { Endpoints } from 'misskey-js';
+import type { Paging } from '@/components/MkPagination.vue';
 import MkUserList from '@/components/MkUserList.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
@@ -36,18 +38,27 @@ import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router/supplier.js';
 
+const props = withDefaults(defineProps<{
+	query?: string,
+	origin?: Endpoints['users/search']['req']['origin'],
+}>(), {
+	query: '',
+	origin: 'combined',
+});
+
 const router = useRouter();
 
 const key = ref('');
-const searchQuery = ref('');
-const searchOrigin = ref('combined');
-const userPagination = ref();
+const searchQuery = ref(toRef(props, 'query').value);
+const searchOrigin = ref(toRef(props, 'origin').value);
+const userPagination = ref<Paging>();
 
 async function search() {
 	const query = searchQuery.value.toString().trim();
 
 	if (query == null || query === '') return;
 
+	//#region AP lookup
 	if (query.startsWith('https://')) {
 		const promise = misskeyApi('ap/show', {
 			uri: query,
@@ -65,6 +76,7 @@ async function search() {
 
 		return;
 	}
+	//#endregion
 
 	userPagination.value = {
 		endpoint: 'users/search',
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index a3dcda77be..38d7548fa8 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -9,8 +9,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
 		<MkSpacer v-if="tab === 'note'" key="note" :contentMax="800">
-			<div v-if="notesSearchAvailable">
-				<XNote/>
+			<div v-if="notesSearchAvailable || ignoreNotesSearchAvailable">
+				<XNote v-bind="props"/>
 			</div>
 			<div v-else>
 				<MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo>
@@ -18,27 +18,43 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</MkSpacer>
 
 		<MkSpacer v-else-if="tab === 'user'" key="user" :contentMax="800">
-			<XUser/>
+			<XUser v-bind="props"/>
 		</MkSpacer>
 	</MkHorizontalSwipe>
 </MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, ref } from 'vue';
+import { computed, defineAsyncComponent, ref, toRef } from 'vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { $i } from '@/account.js';
-import { instance } from '@/instance.js';
+import { notesSearchAvailable } from '@/scripts/check-permissions.js';
 import MkInfo from '@/components/MkInfo.vue';
 import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
 
+const props = withDefaults(defineProps<{
+	query?: string,
+	userId?: string,
+	username?: string,
+	host?: string | null,
+	type?: 'note' | 'user',
+	origin?: 'combined' | 'local' | 'remote',
+	// For storybook only
+	ignoreNotesSearchAvailable?: boolean,
+}>(), {
+	query: '',
+	userId: undefined,
+	username: undefined,
+	host: undefined,
+	type: 'note',
+	origin: 'combined',
+	ignoreNotesSearchAvailable: false,
+});
+
 const XNote = defineAsyncComponent(() => import('./search.note.vue'));
 const XUser = defineAsyncComponent(() => import('./search.user.vue'));
 
-const tab = ref('note');
-
-const notesSearchAvailable = (($i == null && instance.policies.canSearchNotes) || ($i != null && $i.policies.canSearchNotes));
+const tab = ref(toRef(props, 'type').value);
 
 const headerActions = computed(() => []);
 
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index f7a219c57e..995a2055b8 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -232,6 +232,9 @@ const routes: RouteDef[] = [{
 	component: page(() => import('@/pages/search.vue')),
 	query: {
 		q: 'query',
+		userId: 'userId',
+		username: 'username',
+		host: 'host',
 		channel: 'channel',
 		type: 'type',
 		origin: 'origin',
diff --git a/packages/frontend/src/scripts/check-permissions.ts b/packages/frontend/src/scripts/check-permissions.ts
new file mode 100644
index 0000000000..ed86529d5b
--- /dev/null
+++ b/packages/frontend/src/scripts/check-permissions.ts
@@ -0,0 +1,19 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { instance } from '@/instance.js';
+import { $i } from '@/account.js';
+
+export const notesSearchAvailable = (
+	// FIXME: instance.policies would be null in Vitest
+	// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+	($i == null && instance.policies != null && instance.policies.canSearchNotes) ||
+	($i != null && $i.policies.canSearchNotes) ||
+	false
+) as boolean;
+
+export const canSearchNonLocalNotes = (
+	instance.noteSearchableScope === 'global'
+);
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index dacafb859f..e9e7ae771d 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -13,6 +13,7 @@ import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore, userActions } from '@/store.js';
 import { $i, iAmModerator } from '@/account.js';
+import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-permissions.js';
 import { IRouter } from '@/nirax.js';
 import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
 import { mainRouter } from '@/router/main.js';
@@ -160,7 +161,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 		action: () => {
 			copyToClipboard(`@${user.username}@${user.host ?? host}`);
 		},
-	}, ...(iAmModerator ? [{
+	}, ...( notesSearchAvailable && (user.host == null || canSearchNonLocalNotes) ? [{
+		icon: 'ti ti-search',
+		text: i18n.ts.searchThisUsersNotes,
+		action: () => {
+			router.push(`/search?username=${encodeURIComponent(user.username)}${user.host != null ? '&host=' + encodeURIComponent(user.host) : ''}`);
+		},
+	}] : [])
+	, ...(iAmModerator ? [{
 		icon: 'ti ti-user-exclamation',
 		text: i18n.ts.moderation,
 		action: () => {
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 4cfe92147a..cb7d82d2d8 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4939,6 +4939,11 @@ export type components = {
       serverRules: string[];
       themeColor: string | null;
       policies: components['schemas']['RolePolicies'];
+      /**
+       * @default local
+       * @enum {string}
+       */
+      noteSearchableScope: 'local' | 'global';
     };
     MetaDetailedOnly: {
       features?: {

From 7135da7887abeab88789235f715d0396c87d99cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 16:16:20 +0900
Subject: [PATCH 175/206] refactor(actions): remove duplicated paths

---
 .github/workflows/get-api-diff.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index af0ab8dde4..81e8134fb7 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -9,7 +9,6 @@ on:
     paths:
       - packages/backend/**
       - .github/workflows/get-api-diff.yml
-      - .github/workflows/get-api-diff.yml
 jobs:
   get-from-misskey:
     runs-on: ubuntu-latest

From 6bd46e790beb8f5afedbd2c4599d6aa918e05a06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 16:18:18 +0900
Subject: [PATCH 176/206] refactor(backend): remove unrelated comments

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

diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts
index 4dc689238b..a238f4973a 100644
--- a/packages/backend/src/const.ts
+++ b/packages/backend/src/const.ts
@@ -3,7 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-// dummy
 export const MAX_NOTE_TEXT_LENGTH = 3000;
 
 export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min

From b359e3c95beee5acd1d32abee112f34ec602bcee Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Tue, 30 Jul 2024 16:51:50 +0900
Subject: [PATCH 177/206] Fix condition of noteSearchableScope (#14325)

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

diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index bcfd8ae41c..44ec0d6a7b 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -128,7 +128,7 @@ export class MetaEntityService {
 
 			mediaProxy: this.config.mediaProxy,
 			enableUrlPreview: instance.urlPreviewEnabled,
-			noteSearchableScope: this.config.meilisearch == null || this.config.meilisearch.scope === 'local' ? 'local' : 'global',
+			noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local',
 		};
 
 		return packed;

From f0ec68c3cf0381acb518f5518b4f30c123cd1815 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 16:56:07 +0900
Subject: [PATCH 178/206] :art:

---
 packages/frontend/src/components/MkSystemWebhookEditor.vue | 1 +
 packages/frontend/src/pages/emoji-edit-dialog.vue          | 1 +
 2 files changed, 2 insertions(+)

diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index f6ce2ccc9f..a141b4ff74 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -230,6 +230,7 @@ onMounted(async () => {
 	left: 0;
 	padding: 12px;
 	border-top: solid 0.5px var(--divider);
+	background: var(--acrylicBg);
 	-webkit-backdrop-filter: var(--blur, blur(15px));
 	backdrop-filter: var(--blur, blur(15px));
 }
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 16769ef360..d0a6bba47b 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -243,6 +243,7 @@ async function del() {
 	left: 0;
 	padding: 12px;
 	border-top: solid 0.5px var(--divider);
+	background: var(--acrylicBg);
 	-webkit-backdrop-filter: var(--blur, blur(15px));
 	backdrop-filter: var(--blur, blur(15px));
 }

From cb3106cdc6ed6598736aae2273f116a9c01cfeb8 Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Tue, 30 Jul 2024 16:57:41 +0900
Subject: [PATCH 179/206] =?UTF-8?q?enhance(frontend):=20=E9=80=A3=E5=90=88?=
 =?UTF-8?q?=E3=81=AE=E3=80=8C=E9=80=A3=E5=90=88=E4=B8=AD=E3=80=8D,?=
 =?UTF-8?q?=E3=80=8C=E8=B3=BC=E8=AA=AD=E4=B8=AD=E3=80=8D,=E3=80=8C?=
 =?UTF-8?q?=E9=85=8D=E4=BF=A1=E4=B8=AD=E3=80=8D=E3=81=AB=E5=AF=BE=E3=81=97?=
 =?UTF-8?q?=E3=81=A6=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=97=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=82=8B=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=80=81?=
 =?UTF-8?q?=E9=85=8D=E4=BF=A1=E5=81=9C=E6=AD=A2=E3=81=97=E3=81=A6=E3=81=84?=
 =?UTF-8?q?=E3=82=8B=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=82=92=E5=90=AB?=
 =?UTF-8?q?=E3=82=81=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=20(#1432?=
 =?UTF-8?q?4)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 連合の「連合中」,「購読中」,「配信中」に対してブロックしているサーバー、配信停止しているサーバーを含めないように

* update CHANGELOG.md
---
 CHANGELOG.md                                     | 1 +
 packages/frontend/src/pages/about.federation.vue | 6 +++---
 packages/frontend/src/pages/admin/federation.vue | 6 +++---
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f58bec42e6..527c0e0b6b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@
   (Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99)
 - Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
 - Enhance: ブラウザのコンテキストメニューを使用できるように
+- Enhance: 連合の「連合中」,「購読中」,「配信中」に対してブロックしているサーバー、配信停止しているサーバーを含めないように
 - Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
 - Fix: リバーシの対局を正しく共有できないことがある問題を修正
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index 24e96b4f4e..b3776c67e6 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -71,9 +71,9 @@ const pagination = {
 		sort: sort.value,
 		host: host.value !== '' ? host.value : null,
 		...(
-			state.value === 'federating' ? { federating: true } :
-			state.value === 'subscribing' ? { subscribing: true } :
-			state.value === 'publishing' ? { publishing: true } :
+			state.value === 'federating' ? { federating: true, suspended: false, blocked: false } :
+			state.value === 'subscribing' ? { subscribing: true, suspended: false, blocked: false } :
+			state.value === 'publishing' ? { publishing: true, suspended: false, blocked: false } :
 			state.value === 'suspended' ? { suspended: true } :
 			state.value === 'blocked' ? { blocked: true } :
 			state.value === 'silenced' ? { silenced: true } :
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index 0aaa398584..debf684c9b 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -80,9 +80,9 @@ const pagination = {
 		sort: sort.value,
 		host: host.value !== '' ? host.value : null,
 		...(
-			state.value === 'federating' ? { federating: true } :
-			state.value === 'subscribing' ? { subscribing: true } :
-			state.value === 'publishing' ? { publishing: true } :
+			state.value === 'federating' ? { federating: true, suspended: false, blocked: false } :
+			state.value === 'subscribing' ? { subscribing: true, suspended: false, blocked: false } :
+			state.value === 'publishing' ? { publishing: true, suspended: false, blocked: false } :
 			state.value === 'suspended' ? { suspended: true } :
 			state.value === 'blocked' ? { blocked: true } :
 			state.value === 'silenced' ? { silenced: true } :

From f965f65dcd03fe93042d90a25fb3ad54b07baf4f Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:23:29 +0900
Subject: [PATCH 180/206] fix(frontend): pure renote cannot create with url
 based quote (#14270)

* fix(frontend): pure renote cannot create with url based quote

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 527c0e0b6b..0d7f50519a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,7 @@
 - Fix: ダイレクト投稿の"削除して編集"において、宛先が保持されていなかった問題を修正
 - Fix: 投稿フォームへのURL貼り付けによる引用が下書きに保存されていなかった問題を修正
 - Fix: "削除して編集"や下書きにおいて、リアクションの受け入れ設定が保持/保存されていなかった問題を修正
+- Fix: 投稿フォームにノートのURLを貼り付けて"引用として添付"した場合、投稿文を空にすることによるRenote化が出来なかった問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 64ad60ae85..fa3e2b90b0 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -259,7 +259,7 @@ const canPost = computed((): boolean => {
 			1 <= files.value.length ||
 			poll.value != null ||
 			props.renote != null ||
-			(props.reply != null && quoteId.value != null)
+			quoteId.value != null
 		) &&
 		(textLength.value <= maxTextLength.value) &&
 		(!poll.value || poll.value.choices.length >= 2);

From 3548ffba26a23537772f6d91bd48830266a41dde Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:24:36 +0900
Subject: [PATCH 181/206] =?UTF-8?q?enhance(frontend):=20=E8=87=AA=E5=88=86?=
 =?UTF-8?q?=E3=81=AE=E3=82=AF=E3=83=AA=E3=83=83=E3=83=97=E4=B8=80=E8=A6=A7?=
 =?UTF-8?q?=E3=81=A7=E3=81=AF=E3=82=A2=E3=83=90=E3=82=BF=E3=83=BC=E3=82=92?=
 =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#14256)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 自分のクリップ一覧ではアバターを表示しないように

* Update Changelog

* rename

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 .../components/MkClipPreview.stories.impl.ts    |  1 +
 .../frontend/src/components/MkClipPreview.vue   | 17 +++++++++++------
 packages/frontend/src/pages/my-clips/index.vue  |  2 +-
 3 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/packages/frontend/src/components/MkClipPreview.stories.impl.ts b/packages/frontend/src/components/MkClipPreview.stories.impl.ts
index 1011254e7a..62503fb98a 100644
--- a/packages/frontend/src/components/MkClipPreview.stories.impl.ts
+++ b/packages/frontend/src/components/MkClipPreview.stories.impl.ts
@@ -31,6 +31,7 @@ export const Default = {
 	},
 	args: {
 		clip: clip(),
+		noUserInfo: false,
 	},
 	parameters: {
 		layout: 'fullscreen',
diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue
index 2e9a172c23..dd550733cb 100644
--- a/packages/frontend/src/components/MkClipPreview.vue
+++ b/packages/frontend/src/components/MkClipPreview.vue
@@ -12,10 +12,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div v-if="clip.lastClippedAt">{{ i18n.ts.updatedAt }}: <MkTime :time="clip.lastClippedAt" mode="detail"/></div>
 			<div v-if="clip.notesCount != null">{{ i18n.ts.notesCount }}: {{ number(clip.notesCount) }} / {{ $i?.policies.noteEachClipsLimit }} ({{ i18n.tsx.remainingN({ n: remaining }) }})</div>
 		</div>
-		<div :class="$style.divider"></div>
-		<div>
-			<MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
-		</div>
+		<template v-if="!props.noUserInfo">
+			<div :class="$style.divider"></div>
+			<div>
+				<MkAvatar :user="clip.user" :class="$style.userAvatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
+			</div>
+		</template>
 	</div>
 </MkA>
 </template>
@@ -27,9 +29,12 @@ import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 import number from '@/filters/number.js';
 
-const props = defineProps<{
+const props = withDefaults(defineProps<{
 	clip: Misskey.entities.Clip;
-}>();
+	noUserInfo?: boolean;
+}>(), {
+	noUserInfo: false,
+});
 
 const remaining = computed(() => {
 	return ($i?.policies && props.clip.notesCount != null) ? ($i.policies.noteEachClipsLimit - props.clip.notesCount) : i18n.ts.unknown;
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 1a0d7177fc..ece998a7a5 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
 
 				<MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
-					<MkClipPreview v-for="item in items" :key="item.id" :clip="item"/>
+					<MkClipPreview v-for="item in items" :key="item.id" :clip="item" :noUserInfo="true"/>
 				</MkPagination>
 			</div>
 			<div v-else-if="tab === 'favorites'" key="favorites" class="_gaps">

From 9181eb277ea2a04a52eb53e8e3a81c8de9bbbce4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:28:08 +0900
Subject: [PATCH 182/206] =?UTF-8?q?fix(frontend):=20emojiPicker=E3=82=92?=
 =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=A6=E7=B5=B5=E6=96=87=E5=AD=97?=
 =?UTF-8?q?=E3=82=92=E6=8C=BF=E5=85=A5=E3=81=99=E3=82=8B=E9=9A=9B=E3=80=81?=
 =?UTF-8?q?ref=E3=81=AB=E7=9B=B4=E6=8E=A5=E6=8C=BF=E5=85=A5=E3=81=99?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#14282)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): emojiPickerを使用して絵文字を挿入する際、refに直接挿入するように

* add comment
---
 packages/frontend/src/components/MkPostForm.vue | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index fa3e2b90b0..51ec941c97 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -906,10 +906,23 @@ async function insertEmoji(ev: MouseEvent) {
 	textAreaReadOnly.value = true;
 	const target = ev.currentTarget ?? ev.target;
 	if (target == null) return;
+
+	// emojiPickerはダイアログが閉じずにtextareaとやりとりするので、
+	// focustrapをかけているとinsertTextAtCursorが効かない
+	// そのため、投稿フォームのテキストに直接注入する
+	// See: https://github.com/misskey-dev/misskey/pull/14282
+	//      https://github.com/misskey-dev/misskey/issues/14274
+
+	let pos = textareaEl.value?.selectionStart ?? 0;
+	let posEnd = textareaEl.value?.selectionEnd ?? text.value.length;
 	emojiPicker.show(
 		target as HTMLElement,
 		emoji => {
-			insertTextAtCursor(textareaEl.value, emoji);
+			const textBefore = text.value.substring(0, pos);
+			const textAfter = text.value.substring(posEnd);
+			text.value = textBefore + emoji + textAfter;
+			pos += emoji.length;
+			posEnd += emoji.length;
 		},
 		() => {
 			textAreaReadOnly.value = false;

From d1eb10af24b930c68c2e89f649da1a57217d1b41 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:28:28 +0900
Subject: [PATCH 183/206] New Crowdin updates (#14316)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (English)

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

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

* New translations ja-jp.yml (Chinese Simplified)
---
 locales/en-US.yml |   2 +-
 locales/ko-KR.yml |   9 ++++
 locales/th-TH.yml | 131 +++++++++++++++++++++++-----------------------
 locales/zh-CN.yml |  14 +++++
 locales/zh-TW.yml |  12 +++++
 5 files changed, 102 insertions(+), 66 deletions(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index b751b039e6..95227357d7 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1384,7 +1384,7 @@ _serverSettings:
   fanoutTimelineDescription: "Greatly increases performance of timeline retrieval and reduces load on the database when enabled. In exchange, memory usage of Redis will increase. Consider disabling this in case of low server memory or server instability."
   fanoutTimelineDbFallback: "Fallback to database"
   fanoutTimelineDbFallbackDescription: "When enabled, the timeline will fall back to the database for additional queries if the timeline is not cached. Disabling it further reduces the server load by eliminating the fallback process, but limits the range of timelines that can be retrieved."
-  inquiryUrl: "Query URL"
+  inquiryUrl: "Inquiry URL"
   inquiryUrlDescription: "Specify a URL for the inquiry form to the server maintainer or a web page for the contact information."
 _accountMigration:
   moveFrom: "Migrate another account to this one"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index e3b6d31910..7fcb681c9a 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -180,6 +180,10 @@ addAccount: "계정 추가"
 reloadAccountsList: "계정 목록 새로고침"
 loginFailed: "로그인에 실패했습니다"
 showOnRemote: "리모트에서 보기"
+continueOnRemote: "리모트에서 계속"
+chooseServerOnMisskeyHub: "Misskey Hub에서 서버 찾아보기"
+specifyServerHost: "서버 도메인 직접 지정"
+inputHostName: "도메인을 입력하세요"
 general: "일반"
 wallpaper: "배경"
 setWallpaper: "배경 설정"
@@ -477,6 +481,7 @@ noMessagesYet: "아직 대화가 없습니다"
 newMessageExists: "새 메시지가 있습니다"
 onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
 signinRequired: "진행하기 전에 로그인을 해 주세요"
+signinOrContinueOnRemote: "계속하려면 사용하는 서버로 이동하거나 이 서버에 로그인해야 합니다."
 invitations: "초대"
 invitationCode: "초대 코드"
 checking: "확인하는 중입니다"
@@ -1242,6 +1247,8 @@ keepOriginalFilenameDescription: "이 설정을 끄면 업로드를 할 때 파
 noDescription: "설명문이 없습니다"
 alwaysConfirmFollow: "팔로우일 때 항상 확인하기"
 inquiry: "문의하기"
+tryAgain: "다시 시도해 주세요."
+confirmWhenRevealingSensitiveMedia: "민감한 미디어를 열 때 두 번 확인"
 _delivery:
   status: "전송 상태"
   stop: "정지됨"
@@ -1694,6 +1701,7 @@ _role:
     canManageAvatarDecorations: "아바타 꾸미기 관리"
     driveCapacity: "드라이브 용량"
     alwaysMarkNsfw: "파일을 항상 NSFW로 지정"
+    canUpdateBioMedia: "아바타 및 배너 이미지 변경 허용"
     pinMax: "고정할 수 있는 노트 수"
     antennaMax: "만들 수 있는 안테나 수"
     wordMuteMax: "단어 뮤트할 수 있는 문자 수"
@@ -2416,6 +2424,7 @@ _webhookSettings:
   _systemEvents:
     abuseReport: "유저로부터 신고를 받았을 때"
     abuseReportResolved: "받은 신고를 처리했을 때"
+    userCreated: "유저가 생성되었을 때"
   deleteConfirm: "Webhook을 삭제할까요?"
 _abuseReport:
   _notificationRecipient:
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index 117920e88d..eb7cdc2365 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -443,7 +443,7 @@ moderator: "ผู้ควบคุม"
 moderation: "การกลั่นกรอง"
 moderationNote: "โน้ตการกลั่นกรอง"
 addModerationNote: "เพิ่มโน้ตการกลั่นกรอง"
-moderationLogs: "ปูมการแก้ไข"
+moderationLogs: "ปูมการควบคุมดูแล"
 nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} ราย"
 securityKeyAndPasskey: "ความปลอดภัยและรหัสผ่าน"
 securityKey: "กุญแจความปลอดภัย"
@@ -509,7 +509,7 @@ showReactionsCount: "แสดงจำนวนรีแอกชั่นใ
 noHistory: "ไม่มีประวัติ"
 signinHistory: "ประวัติการเข้าสู่ระบบ"
 enableAdvancedMfm: "เปิดใช้งาน MFM ขั้นสูง"
-enableAnimatedMfm: "เปิดการใช้งาน MFM ด้วยแอนิเมชั่น"
+enableAnimatedMfm: "เปิดการใช้งาน MFM แบบเคลื่อนไหว"
 doing: "กำลังประมวลผล......"
 category: "หมวดหมู่"
 tags: "นามแฝง"
@@ -625,7 +625,7 @@ enablePlayer: "เปิดเครื่องเล่นวิดีโอ"
 disablePlayer: "ปิดเครื่องเล่นวิดีโอ"
 expandTweet: "ขยายทวีต"
 themeEditor: "ตัวแก้ไขธีม"
-description: "รายละเอียด"
+description: "คำอธิบาย"
 describeFile: "เพิ่มแคปชั่น"
 enterFileDescription: "ใส่แคปชั่น"
 author: "ผู้เขียน"
@@ -666,7 +666,7 @@ smtpSecure: "ใช้โดยนัย SSL/TLS สำหรับการเ
 smtpSecureInfo: "ปิดสิ่งนี้เมื่อใช้ STARTTLS"
 testEmail: "ทดสอบการส่งอีเมล"
 wordMute: "ปิดเสียงคำ"
-hardWordMute: "ปิดเสียงคำยาก"
+hardWordMute: "ปิดเสียงคำแบบแข็งโป๊ก"
 regexpError: "เกิดข้อผิดพลาดใน regular expression"
 regexpErrorDescription: "เกิดข้อผิดพลาดใน regular expression บรรทัดที่ {line} ของการปิดเสียงคำ {tab} :"
 instanceMute: "ปิดเสียงเซิร์ฟเวอร์"
@@ -1034,7 +1034,7 @@ thisPostMayBeAnnoyingIgnore: "โพสต์ยังไงก็แล้ว
 collapseRenotes: "ยุบรีโน้ตที่คุณเคยเห็นแล้ว"
 collapseRenotesDescription: "พับย่อโน้ตที่เคยตอบสนองหรือรีโน้ตแล้ว"
 internalServerError: "เซิร์ฟเวอร์ภายในเกิดข้อผิดพลาด"
-internalServerErrorDescription: "เซิร์ฟเวอร์รันค้นพบข้อผิดพลาดที่ไม่คาดคิด"
+internalServerErrorDescription: "เกิดข้อผิดพลาดที่ไม่คาดคิดภายในเซิร์ฟเวอร์"
 copyErrorInfo: "คัดลอกรายละเอียดข้อผิดพลาด"
 joinThisServer: "ลงทะเบียนบนเซิร์ฟเวอร์นี้"
 exploreOtherServers: "มองหาเซิร์ฟเวอร์อื่น"
@@ -1042,7 +1042,7 @@ letsLookAtTimeline: "มาดูไทม์ไลน์กัน"
 disableFederationConfirm: "ปิดใช้งานสหพันธ์เลยใช่ไหม?"
 disableFederationConfirmWarn: "โพสต์จะยังคงเป็นสาธารณะต่อไป เว้นแต่จะตั้งค่าเป็นอย่างอื่น"
 disableFederationOk: "ปิดการใช้งาน"
-invitationRequiredToRegister: "ขณะนี้เซิร์ฟเวอร์นี้เปิดรับสมาชิกใหม่ผ่านระบบเชิญเท่านั้น ผู้ที่มีรหัสเชิญสามารถลงทะเบียนได้"
+invitationRequiredToRegister: "เซิร์ฟเวอร์นี้เป็นแบบรับเชิญ เฉพาะผู้มีรหัสเชิญเท่านั้นถึงสามารถลงทะเบียนได้"
 emailNotSupported: "เซิร์ฟเวอร์นี้ไม่รองรับการส่งอีเมล"
 postToTheChannel: "โพสต์ลงช่อง"
 cannotBeChangedLater: "สิ่งนี้ไม่สามารถเปลี่ยนแปลงได้ในภายหลังนะ"
@@ -1052,7 +1052,7 @@ likeOnlyForRemote: "ทั้งหมด (เฉพาะการถูกใ
 nonSensitiveOnly: "เฉพาะไม่มีเนื้อหาละเอียดอ่อน"
 nonSensitiveOnlyForLocalLikeOnlyForRemote: "เฉพาะไม่มีเนื้อหาละเอียดอ่อน (เฉพาะการถูกใจจากระยะไกลเท่านั้น)"
 rolesAssignedToMe: "บทบาทที่ได้รับมอบหมายให้ฉัน"
-resetPasswordConfirm: "รีเซ็ตรหัสผ่านของคุณจริงๆหรอ?"
+resetPasswordConfirm: "ต้องการรีเซ็ตรหัสผ่านใช่ไหม?"
 sensitiveWords: "คำที่มีเนื้อหาละเอียดอ่อน"
 sensitiveWordsDescription: "โน้ตที่มีคำที่ระบุไว้จะถูกตั้งค่าการมองเห็นของให้แสดงเฉพาะในหน้าหลักเท่านั้น คั่นคำด้วยการขึ้นบรรทัดใหม่"
 sensitiveWordsDescription2: "ถ้าแยกด้วยเว้นวรรคจะเป็นการระบุ AND และถ้าล้อมคำด้วยสแลช (/) จะเป็นการใช้ regular expression"
@@ -1169,7 +1169,7 @@ notifyNotes: "แจ้งเตือนเกี่ยวกับโพสต
 unnotifyNotes: "หยุดการแจ้งเตือนเกี่ยวกับโน้ตใหม่"
 authentication: "การตรวจสอบสิทธิ์"
 authenticationRequiredToContinue: "กรุณายืนยันตัวตนทางอิเล็กทรอนิกส์เพื่อดำเนินการต่อ"
-dateAndTime: "เวลาประทับ"
+dateAndTime: "วันเวลา"
 showRenotes: "แสดงรีโน้ต"
 edited: "แก้ไขแล้ว"
 notificationRecieveConfig: "การตั้งค่าการแจ้งเตือน"
@@ -1184,7 +1184,7 @@ confirmShowRepliesAll: "การดำเนินการนี้ไม่
 confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ ไปจากไทม์ไลน์ใช่ไหม?"
 externalServices: "บริการภายนอก"
 sourceCode: "ซอร์สโค้ด"
-sourceCodeIsNotYetProvided: "ซอร์สโค้ดยังไม่พร้อมใช้งาน โปรดติดต่อผู้ดูแลระบบของคุณเพื่อแก้ไขปัญหานี้"
+sourceCodeIsNotYetProvided: "ซอร์สโค้ดยังไม่พร้อมใช้งาน โปรดติดต่อผู้ดูแลระบบเพื่อแก้ไขปัญหานี้"
 repositoryUrl: "URL ของ repository"
 repositoryUrlDescription: "หากมีที่เก็บซอร์สโค้ดที่เปิดเผยต่อสาธารณะ ให้ป้อน URL ที่เก็บซอร์สโค้ดนั้น แต่หากคุณใช้ Misskey ตามต้นฉบับ (ไม่มีการเปลี่ยนแปลงซอร์สโค้ด) ให้ป้อน https://github.com/misskey-dev/misskey"
 repositoryUrlOrTarballRequired: "หากคุณไม่มี repository สาธารณะ คุณจะต้องจัดเตรียม tarball แทน ดู .config/example.yml สำหรับรายละเอียด"
@@ -1275,20 +1275,20 @@ _bubbleGame:
     section2: "เมื่อวัตถุประเภทเดียวกันมารวมกัน พวกมันจะกลายเป็นวัตถุใหม่และคุณจะได้รับคะแนน"
     section3: "หากวัตถุล้นออกมาจากกล่อง เกมก็จะจบลง ตั้งเป้าทำคะแนนให้สูงด้วยการหลอมวัตถุต่าง ๆ โดยไม่ทำให้ล้นกล่อง!"
 _announcement:
-  forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น"
-  forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน"
+  forExistingUsers: "ผู้ใช้งานที่มีอยู่ตอนนี้เท่านั้น"
+  forExistingUsersDescription: "หากเปิดใช้งาน การประกาศนี้จะแสดงเฉพาะกับผู้ใช้ที่สร้างบัญชีก่อน/ที่มีอยู่ในขณะที่สร้างประกาศนี้เท่านั้น หากปิดใช้งาน การประกาศนี้จะแสดงกับผู้ใช้ที่สร้างบัญชีหลังจากสร้างประกาศนี้ด้วย"
   needConfirmationToRead: "จำเป็นต้องยืนยันว่าอ่านแล้ว"
   needConfirmationToReadDescription: "กล่องโต้ตอบการยืนยันจะปรากฏขึ้นเมื่อจะทำเครื่องหมายว่าอ่านแล้ว นอกจากนี้ยังทำให้ประกาศนี้ยังไม่ถูกอ่านเมื่อใช้ฟังก์ชั่น “ทำเครื่องหมายฯ ทั้งหมดว่าอ่านแล้ว”"
   end: "เก็บประกาศ"
-  tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ"
+  tooManyActiveAnnouncementDescription: "เนื่องจากมีการประกาศที่ยังใช้งานอยู่จำนวนมาก อาจทำให้ UX ลดลง แนะนำให้พิจารณาการเก็บประกาศที่สิ้นสุดไปแล้ว"
   readConfirmTitle: "ทำเครื่องหมายว่าอ่านแล้วเลยไหม?"
   readConfirmText: "จะทำเครื่องหมายใส่ “{title}” ว่าอ่านแล้ว"
-  shouldNotBeUsedToPresentPermanentInfo: "เราขอแนะนำให้ใช้ประกาศเพื่อโพสต์ข้อมูลแบบ flow มากกว่าข้อมูลแบบ stock เนื่องจากมีแนวโน้มที่จะส่งผลเสียต่อ UX โดยเฉพาะสำหรับผู้ใช้ใหม่"
+  shouldNotBeUsedToPresentPermanentInfo: "เนื่องจากมีความเป็นไปได้สูงที่จะส่งผลเสียต่อง UX ของผู้ใช้ใหม่ จึงขอแนะนำให้ใช้ประกาศสำหรับข้อมูลที่ต้องการการตอบสนองในทันที ไม่ใช่ข้อมูลที่ต้องการแสดงตลอดเวลา"
   dialogAnnouncementUxWarn: "เราขอแนะนำให้ใช้ด้วยความระมัดระวัง เนื่องจากการแจ้งเตือนแบบกล่องโต้ตอบตั้งแต่ 2 รายการขึ้นไปพร้อมกันอาจส่งผลเสียต่อ UX ได้อย่างมาก"
   silence: "ไม่มีการแจ้งเตือน"
   silenceDescription: "หากเปิดใช้งาน จะไม่มีการแจ้งเตือนประกาศนี้ และผู้ใช้จะไม่จำเป็นต้องทำเครื่องหมายว่าอ่านแล้ว"
 _initialAccountSetting:
-  accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!"
+  accountCreated: "สร้างบัญชีเสร็จสมบูรณ์!"
   letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ"
   letsFillYourProfile: "ก่อนอื่นมาตั้งค่าโปรไฟล์ของคุณ"
   profileSetting: "ตั้งค่าโปรไฟล์"
@@ -1387,26 +1387,26 @@ _serverSettings:
   inquiryUrl: "URL สำหรับการติดต่อสอบถาม"
   inquiryUrlDescription: "ระบุ URL ของหน้าเว็บที่มีแบบฟอร์มสำหรับติดต่อผู้ดูแลเซิร์ฟเวอร์ หรือข้อมูลการติดต่อของผู้ดูแลเซิร์ฟเวอร์"
 _accountMigration:
-  moveFrom: "ย้ายข้อมูลบัญชีอื่นไปยังอีกบัญชีนี้หนึ่ง"
+  moveFrom: "ย้ายจากบัญชีอื่นมาที่บัญชีนี้"
   moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น"
   moveFromLabel: "บัญชีที่จะย้ายจาก #{n}"
   moveFromDescription: "หากต้องการโอนข้อมูลจากบัญชีอื่นมายังบัญชีนี้ จำเป็นต้องสร้างบัญชีนามแฝง (alias) ไว้ที่นี่ด้วย\nกรุณากรอกบัญชีเดิมในรูปแบบ: @username@server.example.com\nหากต้องการลบ alias, ให้เว้นว่างไว้แล้วบันทึก (ไม่แนะนำ)"
-  moveTo: "ย้ายข้อมูลบัญชีนี้ไปยังบัญชีอีกหนึ่ง"
+  moveTo: "ย้ายบัญชีนี้ไปยังบัญชีใหม่"
   moveToLabel: "บัญชีที่จะย้ายไปที่:"
   moveCannotBeUndone: "ไม่สามารถยกเลิกการโอนย้ายบัญชีได้"
   moveAccountDescription: "การดำเนินการนี้จะย้ายบัญชีของคุณไปยังบัญชีอื่น\n・ผู้ที่กำลังติดตามคุณจากบัญชีนี้จะถูกย้ายไปยังบัญชีใหม่โดยอัตโนมัติ\n・บัญชีนี้จะเลิกติดตามผู้ใช้ทั้งหมดที่กำลังติดตามอยู่\n・คุณจะไม่สามารถสร้างโน้ต ฯลฯ ในบัญชีนี้ได้\n\nแม้ว่าการย้ายผู้ที่ติดตามคุณจะเป็นไปโดยอัตโนมัติ แต่คุณต้องเตรียมขั้นตอนบางอย่างด้วยตนเอง เพื่อย้ายรายชื่อผู้ใช้ที่คุณกำลังติดตาม โดยดำเนินการส่งออกรายชื่อแล้วค่อยนำเข้ามาภายหลังในเมนูการตั้งค่าของบัญชีใหม่ ใช้ขั้นตอนเดียวกันนี้ใช้รายชื่อผู้ใช้ที่ถูกปิดเสียงและถูกบล็อก\n\n(คำอธิบายนี้ใช้กับ Misskey v13.12.0 ขึ้นไป, ซอฟต์แวร์ ActivityPub อื่นๆ เช่น Mastodon อาจทำงานแตกต่างออกไป)"
-  moveAccountHowTo: "หากต้องการย้ายข้อมูลก่อนอื่นให้สร้างชื่อแทนสำหรับบัญชีนี้ ในบัญชีที่จะต้องการย้ายไป\nหลังจากที่คุณสร้างนามแฝงนั้นแล้ว ให้ป้อนบัญชีที่ต้องการจะย้ายไปในรูปแบบดังต่อไปนี้: @username@server.example.com"
+  moveAccountHowTo: "การย้ายบัญชีจะเริ่มต้นโดยการสร้างบัญชีนามแฝง (alias) ของบัญชีนี้ ณ บัญชีที่เป็นปลายทาง หลังจากสร้างนามแฝงแล้ว ให้ป้อนบัญชีปลายทางในรูปแบบดังนี้: @username@server.example.com"
   startMigration: "โอนย้าย"
   migrationConfirm: "ยืนยันการย้ายข้อมูลบัญชีนี้ไปที่ {account} เมื่อเริ่มแล้วจะไม่สามารถหยุดหรือนำกลับคืนมาได้ และคุณจะไม่สามารถใช้บัญชีนี้ในสถานะดั้งเดิมได้อีกต่อไป\n\nนอกจากนี้ คุณจำเป็นต้องสร้างบัญชีสำรองสำหรับการย้ายบัญชี"
-  movedAndCannotBeUndone: "\nบัญชีนี้ถูกโอนย้ายไปแล้ว\nไม่สามารถย้อนกลับโอนย้ายข้อมูลได้"
-  postMigrationNote: "บัญชีนี้จะถูกเลิกติดตามบัญชีทั้งหมดที่กำลังติดตามภายใน 24 ชั่วโมงหลังจากการย้ายข้อมูลนั้นเสร็จสิ้น ทั้งจำนวนผู้ติดตามและผู้ติดตามนั้นจะกลายเป็นศูนย์ เพื่อหลีกเลี่ยงป้องกันไม่ให้ผู้ติดตามของคุณนั้นไม่สามารถเห็นโพสต์เฉพาะผู้ติดตามของบัญชีนี้ได้ แต่อย่างไรก็ตามแล้วพวกเขาจะยังคงติดตามบัญชีนี้ต่อไป"
-  movedTo: "บัญชีที่จะย้ายไปที่:"
+  movedAndCannotBeUndone: "\nบัญชีนี้ถูกโอนย้ายไปแล้ว\nไม่สามารถยกเลิกการโอนย้ายได้"
+  postMigrationNote: "บัญชีนี้จะดำเนินการยกเลิกการติดตามทั้งหมดหลังจากการย้ายข้อมูลไปแล้ว 24 ชั่วโมง จำนวนกำลังติดตามและจำนวนผู้ติดตามของบัญชีนี้จะเป็น 0 และเพื่อหลีกเลี่ยงไม่ให้ผู้ติดตามคุณนั้นไม่สามารถเห็นโพสต์เฉพาะผู้ติดตามฯได้  การยกเลิกการติดตามจะไม่กระทบกับผู้ติดตามคุณ ดังนั้นผู้ติดตามคุณยังคงสามารถดูโพสต์ของบัญชีนี้ได้"
+  movedTo: "บัญชีที่จะย้ายไป:"
 _achievements:
   earnedAt: "ได้รับเมื่อ"
   _types:
     _notes1:
       title: "just setting up my msky"
-      description: "โพสต์โน้ตแรกของคุณ"
+      description: "โพสต์โน้ตเป็นครั้งแรก"
       flavor: "ขอให้มีช่วงเวลาที่ดีกับ Misskey นะคะ!"
     _notes10:
       title: "โน้ตไม่กี่ชิ้น"
@@ -1506,19 +1506,19 @@ _achievements:
       flavor: "ขอบคุณที่ใช้ Misskey นะ !"
     _noteClipped1:
       title: "อดไม่ได้ที่จะต้องคลิปมันเอาไว้"
-      description: "คลิปโน้ตตัวแรกของคุณ"
+      description: "คลิปโน้ตเป็นครั้งแรก"
     _noteFavorited1:
       title: "สตาร์เกเซอร์"
-      description: "ชื่นชอบโน้ตแรกของคุณ"
+      description: "ใส่โน้ตเป็นรายการโปรดเป็นครั้งแรก"
     _myNoteFavorited1:
       title: "แสวงหาดวงดาว"
-      description: "มีคนอื่นๆที่ชื่นชอบหนึ่งในโน้ตของคุณ"
+      description: "โน้ตตัวเองถูกคนอื่นเพิ่มลงรายการโปรดของเขา"
     _profileFilled:
       title: "เตรียมตัวอย่างดี"
-      description: "ตั้งค่าโปรไฟล์ของคุณ"
+      description: "ตั้งค่าโปรไฟล์"
     _markedAsCat:
       title: "ฉันเป็นแมว"
-      description: "ทำเครื่องหมายบัญชีของคุณว่าเป็นแมว"
+      description: "ตั้งค่าบัญชีเป็นแมวเมี้ยวเมี้ยว"
       flavor: "แมวน้อยไร้ชื่อ"
     _following1:
       title: "ก้าวแรกสู่...กดติดตาม"
@@ -1561,7 +1561,7 @@ _achievements:
       description: "ได้รับความสำเร็จ 30 ครั้ง"
     _viewAchievements3min:
       title: "ชอบบรรลุความสําเร็จ"
-      description: "มองดูรายการความสำเร็จของคุณเป็นเวลาอย่างน้อย 3 นาที"
+      description: "มองดูรายการความสำเร็จเป็นเวลานานกว่า 3 นาที"
     _iLoveMisskey:
       title: "ฉันรัก Misskey"
       description: "โพสต์ “I ❤ #Misskey”"
@@ -1588,13 +1588,13 @@ _achievements:
       flavor: "โป๊ะ โป๊ะ โป๊ะ ปิ้งงงงง"
     _selfQuote:
       title: "อ้างอิงตนเอง"
-      description: "อ้างโน้ตของคุณเอง"
+      description: "อ้างอิงโน้ตตัวเอง"
     _htl20npm:
       title: "ไทม์ไลน์ไหล"
       description: "มีการทำความเร็วของไทม์ไลน์หลักเกิน 20 npm (โน้ตต่อนาที)"
     _viewInstanceChart:
       title: "วิเคราะห์"
-      description: "ดูแผนภูมิเซิร์ฟเวอร์ของคุณ"
+      description: "ดูแผนภูมิของเซิร์ฟเวอร์"
     _outputHelloWorldOnScratchpad:
       title: "หวัดดีชาวโลก!"
       description: "เอาพุต \"hello world\" ใน Scratchpad"
@@ -1615,16 +1615,16 @@ _achievements:
       description: "มีโอกาสที่จะได้รับด้วยความน่าจะเป็นไปได้ 0.005% ทุก ๆ 10 วินาที"
     _setNameToSyuilo:
       title: "คอมเพล็กซ์ของพระเจ้า"
-      description: "ตั้งชื่อของคุณเป็น “syuilo”"
+      description: "ตั้งชื่อเป็น “syuilo”"
     _passedSinceAccountCreated1:
       title: "ครบรอบหนึ่งปี"
-      description: "ผ่านไปหนึ่งปีแล้วนะตั้งแต่บัญชีของคุณถูกสร้างขึ้นมาน่ะ"
+      description: "ผ่านไป 1 ปีนับตั้งแต่สร้างบัญชี"
     _passedSinceAccountCreated2:
       title: "ครบรอบสองปี"
-      description: "ผ่านไปสองปีแล้วนะตั้งแต่บัญชีของคุณถูกสร้างขึ้นมาน่ะ"
+      description: "ผ่านไป 2 ปีนับตั้งแต่สร้างบัญชี"
     _passedSinceAccountCreated3:
       title: "ครบรอบสามปี"
-      description: "ผ่านไปสามปีแล้วนะตั้งแต่บัญชีของคุณถูกสร้างขึ้นมาน่ะ"
+      description: "ผ่านไป 3 ปีนับตั้งแต่สร้างบัญชี"
     _loggedInOnBirthday:
       title: "สุขสันต์วันเกิด"
       description: "เข้าสู่ระบบในวันเกิดของคุณ"
@@ -1672,8 +1672,8 @@ _role:
   descriptionOfIsPublic: "บทบาทจะปรากฏบนโปรไฟล์ของผู้ใช้และเปิดเผยต่อสาธารณะ (ทุกคนสามารถเห็นได้ว่าผู้ใช้รายนี้มีบทบาทนี้)"
   options: "ตัวเลือกบทบาท"
   policies: "นโยบาย"
-  baseRole: "เทมเพลตบทบาท"
-  useBaseValue: "ใช้ตามเทมเพลตบทบาท"
+  baseRole: "แม่แบบบทบาท"
+  useBaseValue: "ใช้ตามแม่แบบบทบาท"
   chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด"
   iconUrl: "URL ไอคอน"
   asBadge: "แสดงเป็นตรา"
@@ -1797,7 +1797,7 @@ _plugin:
   viewSource: "ดูต้นฉบับ"
   viewLog: "แสดงปูม"
 _preferencesBackups:
-  list: "การตั้งค่าสำรองที่สร้างไว้"
+  list: "การตั้งค่าที่สำรองไว้"
   saveNew: "บันทึกการตั้งค่าสำรองใหม่"
   loadFile: "โหลดจากไฟล์"
   apply: "นำไปใช้กับอุปกรณ์นี้"
@@ -1864,7 +1864,7 @@ _menuDisplay:
   hide: "ซ่อน"
 _wordMute:
   muteWords: "ปิดเสียงคำ"
-  muteWordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR นะ"
+  muteWordsDescription: "คั่นด้วยเว้นวรรคสำหรับเงื่อนไข AND, หรือขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR"
   muteWordsDescription2: "ล้อมรอบคีย์เวิร์ดด้วยเครื่องหมายทับเพื่อใช้นิพจน์ทั่วไป"
 _instanceMute:
   instanceMuteDescription: "ปิดเสียง “โน้ต/รีโน้ต” ทั้งหมดจากเซิร์ฟเวอร์ที่ระบุไว้ รวมถึงโน้ตของผู้ใช้ที่ตอบกลับผู้ใช้จากเซิร์ฟเวอร์ที่ถูกปิดเสียง"
@@ -2008,38 +2008,38 @@ _2fa:
   backupCodesExhaustedWarning: "รหัสแบ๊กอัปทั้งหมดถูกใช้งานแล้ว หากยังไม่สามารถใช้แอปพลิเคชันการยืนยันตัวตนได้ก็จะไม่สามารถเข้าถึงบัญชีนี้ได้อีกต่อไป กรุณาลงทะเบียนแอปพลิเคชันการยืนยันตัวตนใหม่"
   moreDetailedGuideHere: "คลิกที่นี่เพื่อดูคำแนะนำโดยละเอียด"
 _permissions:
-  "read:account": "ดูข้อมูลบัญชีของคุณ"
-  "write:account": "แก้ไขข้อมูลบัญชีของคุณ"
-  "read:blocks": "ดูรายชื่อผู้ใช้ที่ถูกบล็อกของคุณ"
-  "write:blocks": "แก้ไขรายชื่อผู้ใช้ที่ถูกบล็อกของคุณ"
-  "read:drive": "เข้าถึงไฟล์และโฟลเดอร์ในไดรฟ์ของคุณ"
-  "write:drive": "แก้ไขหรือลบไฟล์และโฟลเดอร์ในไดรฟ์ของคุณ"
+  "read:account": "ดูข้อมูลบัญชี"
+  "write:account": "แก้ไขข้อมูลบัญชี"
+  "read:blocks": "ดูรายชื่อผู้ใช้ที่ถูกบล็อก"
+  "write:blocks": "แก้ไขรายชื่อผู้ใช้ที่ถูกบล็อก"
+  "read:drive": "เข้าถึงไดรฟ์"
+  "write:drive": "จัดการไดรฟ์"
   "read:favorites": "ดูรายการโปรด"
   "write:favorites": "แก้ไขรายการโปรด"
   "read:following": "ดูข้อมูลว่าใครที่คุณติดตาม"
   "write:following": "ติดตามหรือเลิกติดตามบัญชีอื่น"
-  "read:messaging": "ดูแชทของคุณ"
+  "read:messaging": "ดูแชท"
   "write:messaging": "เขียนหรือลบข้อความแชท"
-  "read:mutes": "ดูรายชื่อผู้ใช้ที่ปิดเสียงของคุณ"
+  "read:mutes": "ดูรายชื่อผู้ใช้ที่ถูกปิดเสียง"
   "write:mutes": "แก้ไขรายชื่อผู้ใช้ที่ถูกปิดเสียง"
   "write:notes": "เขียนหรือลบโน้ต"
-  "read:notifications": "ดูการแจ้งเตือนของคุณ"
-  "write:notifications": "จัดการแจ้งเตือนของคุณ"
-  "read:reactions": "ดูรีแอคชั่นของคุณ"
-  "write:reactions": "แก้ไขรีแอคชั่นของคุณ"
+  "read:notifications": "ดูการแจ้งเตือน"
+  "write:notifications": "จัดการแจ้งเตือน"
+  "read:reactions": "ดูรีแอคชั่น"
+  "write:reactions": "แก้ไขรีแอคชั่น"
   "write:votes": "โหวตบนสำรวจความคิดเห็น"
   "read:pages": "ดูหน้าเพจ"
-  "write:pages": "แก้ไขหรือลบเพจของคุณ"
+  "write:pages": "แก้ไขหรือลบเพจ"
   "read:page-likes": "ดูรายการเพจที่ถูกใจไว้"
   "write:page-likes": "แก้ไขรายการเพจที่ถูกใจ"
-  "read:user-groups": "ดูกลุ่มผู้ใช้ของคุณ"
-  "write:user-groups": "แก้ไขหรือลบกลุ่มผู้ใช้ของคุณ"
-  "read:channels": "ดูแชนแนลของคุณ"
-  "write:channels": "แก้ไขแชนแนลของคุณ"
+  "read:user-groups": "ดูกลุ่มผู้ใช้"
+  "write:user-groups": "แก้ไขหรือลบกลุ่มผู้ใช้"
+  "read:channels": "ดูช่อง"
+  "write:channels": "แก้ไขช่อง"
   "read:gallery": "ดูแกลเลอรี่"
-  "write:gallery": "แก้ไขแกลเลอรี่ของคุณ"
-  "read:gallery-likes": "ดูรายการโพสต์แกลเลอรีที่ถูกใจไว้"
-  "write:gallery-likes": "แก้ไขรายการโพสต์แกลเลอรีที่ถูกใจไว้"
+  "write:gallery": "แก้ไขแกลเลอรี"
+  "read:gallery-likes": "ดูแกลเลอรีที่ถูกใจไว้"
+  "write:gallery-likes": "จัดการแกลเลอรีที่ถูกใจไว้"
   "read:flash": "ดู Play"
   "write:flash": "แก้ไข Play"
   "read:flash-likes": "ดูรายการ  play ที่ถูกใจไว้"
@@ -2048,20 +2048,20 @@ _permissions:
   "write:admin:delete-account": "ลบบัญชีผู้ใช้"
   "write:admin:delete-all-files-of-a-user": "ลบไฟล์ทั้งหมดของผู้ใช้"
   "read:admin:index-stats": "ดูข้อมูลเกี่ยวกับดัชนีฐานข้อมูล"
-  "read:admin:table-stats": "ดูข้อมูลเกี่ยวกับตารางฐานข้อมูล"
+  "read:admin:table-stats": "ดูข้อมูลเกี่ยวกับตารางในฐานข้อมูล"
   "read:admin:user-ips": "ดูที่อยู่ IP ของผู้ใช้"
-  "read:admin:meta": "ดูข้อมูลเมตาของอินสแตนซ์"
+  "read:admin:meta": "ดูข้อมูลอภิพันธุ์ของอินสแตนซ์"
   "write:admin:reset-password": "รีเซ็ตรหัสผ่านของผู้ใช้"
   "write:admin:resolve-abuse-user-report": "แก้ไขรายงานจากผู้ใช้"
   "write:admin:send-email": "ส่งอีเมล"
   "read:admin:server-info": "ดูข้อมูลเซิร์ฟเวอร์"
-  "read:admin:show-moderation-log": "ดูปูมการแก้ไข"
+  "read:admin:show-moderation-log": "ดูปูมการควบคุมดูแล"
   "read:admin:show-user": "ดูข้อมูลส่วนตัวของผู้ใช้"
   "write:admin:suspend-user": "ระงับผู้ใช้"
   "write:admin:unset-user-avatar": "ลบอวตารผู้ใช้"
   "write:admin:unset-user-banner": "ลบแบนเนอร์ผู้ใช้"
   "write:admin:unsuspend-user": "ยกเลิกการระงับผู้ใช้"
-  "write:admin:meta": "จัดการข้อมูลเมตาของอินสแตนซ์"
+  "write:admin:meta": "จัดการข้อมูลอภิพันธุ์ของอินสแตนซ์"
   "write:admin:user-note": "จัดการโน้ตการกลั่นกรอง"
   "write:admin:roles": "จัดการบทบาท"
   "read:admin:roles": "ดูบทบาท"
@@ -2088,8 +2088,8 @@ _permissions:
   "read:admin:ad": "ดูโฆษณา"
   "write:invite-codes": "สร้างรหัสเชิญ"
   "read:invite-codes": "รับรหัสเชิญ"
-  "write:clip-favorite": "ควบคุมการถูกใจของคลิป"
-  "read:clip-favorite": "ดูการถูกใจของคลิป"
+  "write:clip-favorite": "จัดการคลิปที่ถูกใจ"
+  "read:clip-favorite": "ดูคลิปที่ถูกใจ"
   "read:federation": "รับข้อมูลเกี่ยวกับสหพันธ์"
   "write:report-abuse": "รายงานการละเมิด"
 _auth:
@@ -2165,7 +2165,7 @@ _poll:
   deadlineTime: "เวลา"
   duration: "ระยะเวลา"
   votesCount: "{n} คะแนนเสียง"
-  totalVotes: "{n} คะแนนเสียงทั้งหมด"
+  totalVotes: "ทั้งหมด {n} คะแนนเสียง"
   vote: "โหวต"
   showResult: "ดูผลลัพธ์"
   voted: "โหวตแล้ว"
@@ -2266,7 +2266,7 @@ _play:
   featured: "เป็นที่นิยม"
   title: "หัวข้อ"
   script: "สคริปต์"
-  summary: "รายละเอียด"
+  summary: "คำอธิบาย"
   visibilityDescription: "หากตั้งค่าเป็นส่วนตัว มันจะไม่ปรากฏในโปรไฟล์อีกต่อไป แต่ผู้ที่ทราบ URL ของมันจะยังสามารถเข้าถึงได้"
 _pages:
   newPage: "สร้างหน้าเพจใหม่"
@@ -2425,6 +2425,7 @@ _webhookSettings:
   _systemEvents:
     abuseReport: "เมื่อมีการรายงานจากผู้ใช้"
     abuseReportResolved: "เมื่อมีการจัดการกับการรายงานจากผู้ใช้"
+    userCreated: "เมื่อผู้ใช้ถูกสร้างขึ้น"
   deleteConfirm: "ต้องการลบ Webhook ใช่ไหม?"
 _abuseReport:
   _notificationRecipient:
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 6f10788743..8eae32ca32 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -60,6 +60,7 @@ copyFileId: "复制文件ID"
 copyFolderId: "复制文件夹ID"
 copyProfileUrl: "复制个人资料URL"
 searchUser: "搜索用户"
+searchThisUsersNotes: "搜索用户帖子"
 reply: "回复"
 loadMore: "查看更多"
 showMore: "查看更多"
@@ -154,6 +155,7 @@ editList: "编辑列表"
 selectChannel: "选择频道"
 selectAntenna: "选择天线"
 editAntenna: "编辑天线"
+createAntenna: "创建天线"
 selectWidget: "选择小工具"
 editWidgets: "编辑部件"
 editWidgetsExit: "完成编辑"
@@ -194,6 +196,7 @@ followConfirm: "你确定要关注 {name} 吗?"
 proxyAccount: "代理账户"
 proxyAccountDescription: "代理账户是在某些情况下替代用户进行远程关注用的账户。 例如说,当用户将一位远程用户放入一个列表中时,如果本地服务器上没有任何人关注这位远程用户,则这位远程用户的账户活动将不会被送到本地服务器上。作为替代,此时将使用代理账户进行关注。"
 host: "主机名"
+selectSelf: "选择自己"
 selectUser: "选择用户"
 recipient: "收件人"
 annotation: "注解"
@@ -1106,6 +1109,8 @@ preservedUsernames: "保留的用户名"
 preservedUsernamesDescription: "列出需要保留的用户名,使用换行来作为分割。被指定的用户名在建立账户时无法使用,但由管理员所创建的账户不受该限制。此外,现有的账户也不会受到影响。"
 createNoteFromTheFile: "从文件创建帖子"
 archive: "归档"
+archived: "已归档"
+unarchive: "取消归档"
 channelArchiveConfirmTitle: "要将 {name} 归档吗?"
 channelArchiveConfirmDescription: "归档后,在频道列表与搜索结果中不会显示,也无法发布新的贴文。"
 thisChannelArchived: "该频道已被归档。"
@@ -1116,6 +1121,7 @@ preventAiLearning: "拒绝接受生成式 AI 的学习"
 preventAiLearningDescription: "要求文章生成 AI 或图像生成 AI 不能够以发布的帖子和图像等内容作为学习对象。这是通过在 HTML 响应中包含 noai 标志来实现的,这不能完全阻止 AI 学习你的发布内容,并不是所有 AI 都会遵守这类请求。"
 options: "选项"
 specifyUser: "用户指定"
+specifyHost: "指定主机名"
 failedToPreviewUrl: "无法预览"
 update: "更新"
 rolesThatCanBeUsedThisEmojiAsReaction: "可以使用表情作为回应的角色"
@@ -1250,6 +1256,8 @@ inquiry: "联系我们"
 tryAgain: "请再试一次"
 confirmWhenRevealingSensitiveMedia: "显示敏感内容前需要确认"
 sensitiveMediaRevealConfirm: "这是敏感内容。是否显示?"
+createdLists: "已创建的列表"
+createdAntennas: "已创建的天线"
 _delivery:
   status: "投递状态"
   stop: "停止投递"
@@ -2424,6 +2432,7 @@ _webhookSettings:
   _systemEvents:
     abuseReport: "当收到举报时"
     abuseReportResolved: "当举报被处理时"
+    userCreated: "当用户被创建时"
   deleteConfirm: "要删除 webhook 吗?"
 _abuseReport:
   _notificationRecipient:
@@ -2614,3 +2623,8 @@ _mediaControls:
   pip: "画中画"
   playbackRate: "播放速度"
   loop: "循环播放"
+_contextMenu:
+  title: "上下文菜单"
+  app: "应用"
+  appWithShift: "Shift 键应用"
+  native: "浏览器的用户界面"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 0929984252..8b53273c3b 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -60,6 +60,7 @@ copyFileId: "複製檔案 ID"
 copyFolderId: "複製資料夾ID"
 copyProfileUrl: "複製個人資料網址"
 searchUser: "搜尋使用者"
+searchThisUsersNotes: "搜尋這個使用者的貼文"
 reply: "回覆"
 loadMore: "載入更多"
 showMore: "載入更多"
@@ -154,6 +155,7 @@ editList: "編輯清單"
 selectChannel: "選擇頻道"
 selectAntenna: "選擇天線"
 editAntenna: "編輯天線"
+createAntenna: "建立天線"
 selectWidget: "選擇小工具"
 editWidgets: "編輯小工具"
 editWidgetsExit: "完成"
@@ -194,6 +196,7 @@ followConfirm: "你真的要追隨{name}嗎?"
 proxyAccount: "代理帳戶"
 proxyAccountDescription: "代理帳戶是在特定條件下充當遠端追隨者的帳戶。例如,當使用者新增遠端使用者至其列表時,若沒有本地使用者追隨該遠端使用者,則其活動將不會傳送至伺服器,此時便會由代理帳戶代為追隨以解決問題。"
 host: "主機"
+selectSelf: "選擇自己"
 selectUser: "選取使用者"
 recipient: "收件人"
 annotation: "註解"
@@ -1106,6 +1109,8 @@ preservedUsernames: "保留的使用者名稱"
 preservedUsernamesDescription: "換行列舉要保留的使用者名稱。此處出現的名稱將在註冊時禁用,但由管理者建立帳戶則不受此限。此外,既有的帳戶也不受影響。"
 createNoteFromTheFile: "由此檔案建立貼文"
 archive: "封存"
+archived: "已封存"
+unarchive: "取消封存"
 channelArchiveConfirmTitle: "要封存{name}嗎?"
 channelArchiveConfirmDescription: "封存後,將不會在頻道列表與搜尋結果中顯示,也無法發佈新貼文。"
 thisChannelArchived: "這個頻道已被封存。"
@@ -1116,6 +1121,7 @@ preventAiLearning: "拒絕接受生成式AI的訓練"
 preventAiLearningDescription: "要求站外生成式 AI 不使用您發佈的內容訓練模型。此功能會使伺服器於 HTML 回應新增「noai」標籤,而因為要視乎 AI 會否遵守該標籤,所以此功能無法完全阻止所有 AI 使用您的內容。"
 options: "選項"
 specifyUser: "指定使用者"
+specifyHost: "指定主機"
 failedToPreviewUrl: "無法預覽"
 update: "更新"
 rolesThatCanBeUsedThisEmojiAsReaction: "可以使用此表情符號為反應的角色"
@@ -1250,6 +1256,8 @@ inquiry: "聯絡我們"
 tryAgain: "請再試一次。"
 confirmWhenRevealingSensitiveMedia: "要顯示敏感媒體時需確認"
 sensitiveMediaRevealConfirm: "這是敏感媒體。確定要顯示嗎?"
+createdLists: "已建立的清單"
+createdAntennas: "已建立的天線"
 _delivery:
   status: "傳送狀態"
   stop: "停止發送"
@@ -2615,3 +2623,7 @@ _mediaControls:
   pip: "畫中畫"
   playbackRate: "播放速度"
   loop: "循環播放"
+_contextMenu:
+  title: "內容功能表"
+  app: "應用程式"
+  native: "瀏覽器的使用者介面"

From 674a424db3c2de15a113831b8388cb84927cb531 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 30 Jul 2024 08:39:38 +0000
Subject: [PATCH 184/206] Bump version to 2024.7.0-rc.6

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

diff --git a/package.json b/package.json
index 64f7b88a9f..54fd36f11a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-rc.5",
+	"version": "2024.7.0-rc.6",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index b112975328..eec39349c5 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-rc.5",
+	"version": "2024.7.0-rc.6",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 3411b9c16c220709ddda0492d1b61c961a116b5e Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:47:17 +0900
Subject: [PATCH 185/206] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d7f50519a..f074ed65e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
 - Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正
+- 翻訳の更新
+- 依存関係の更新
 
 ### Client
 - Feat: ユーザーページから「このユーザーのノートを検索」できるように (#14128)

From 8bae2ecabd770afe5533baec148c6e21124b9756 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 30 Jul 2024 18:37:07 +0900
Subject: [PATCH 186/206] =?UTF-8?q?fix(misskey-js):=20=E3=83=A2=E3=83=87?=
 =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=81=AE=E3=83=95=E3=82=A3=E3=83=AB=E3=82=BF?=
 =?UTF-8?q?=E3=81=AB=E8=BF=BD=E5=8A=A0=E6=BC=8F=E3=82=8C=E3=81=8C=E3=81=82?=
 =?UTF-8?q?=E3=81=A3=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1432?=
 =?UTF-8?q?8)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index b509d3280c..aa8eeb48cb 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -143,6 +143,12 @@ export const moderationLogTypes = [
 	'deleteAvatarDecoration',
 	'unsetUserAvatar',
 	'unsetUserBanner',
+	'createSystemWebhook',
+	'updateSystemWebhook',
+	'deleteSystemWebhook',
+	'createAbuseReportNotificationRecipient',
+	'updateAbuseReportNotificationRecipient',
+	'deleteAbuseReportNotificationRecipient',
 ] as const;
 
 // See: packages/backend/src/core/ReversiService.ts@L410

From 2307849c9ae1f4e10231479ff8e1d5556e3a3c05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 30 Jul 2024 19:01:47 +0900
Subject: [PATCH 187/206] =?UTF-8?q?fix(misskey-js):=20misskey-js.api.md?=
 =?UTF-8?q?=E3=81=AE=E3=82=B3=E3=83=9F=E3=83=83=E3=83=88=E6=BC=8F=E3=82=8C?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#14329)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/misskey-js/etc/misskey-js.api.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 377dd6f658..16cb560a52 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2481,7 +2481,7 @@ type ModerationLog = {
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner", "createSystemWebhook", "updateSystemWebhook", "deleteSystemWebhook", "createAbuseReportNotificationRecipient", "updateAbuseReportNotificationRecipient", "deleteAbuseReportNotificationRecipient"];
 
 // @public (undocumented)
 type MuteCreateRequest = operations['mute___create']['requestBody']['content']['application/json'];

From d0b7c74fd15673604a682726c817c57475cfa5d4 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Tue, 30 Jul 2024 19:18:43 +0900
Subject: [PATCH 188/206] =?UTF-8?q?=E6=A4=9C=E7=B4=A2=E3=81=8B=E3=82=89?=
 =?UTF-8?q?=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF=E3=82=B0=E3=81=AE?=
 =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=8C=E9=96=8B=E3=81=91=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=80=81users/search=E3=81=AB`@`?=
 =?UTF-8?q?=E3=81=8B=E3=82=89=E5=A7=8B=E3=81=BE=E3=82=8B=E6=96=87=E5=AD=97?=
 =?UTF-8?q?=E5=88=97=E3=81=8C=E4=B8=8E=E3=81=88=E3=82=89=E3=82=8C=E3=81=9F?=
 =?UTF-8?q?=E9=9A=9B=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20=E7=AD=89=20(#13858)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): 検索からハッシュタグのページを開けるように

* fix(frontend): 照会で入力が`#`のみの場合は`/tags/`に遷移しないように

* docs(changelog): update changelog

* enhance(frontend): ユーザー検索からもハッシュタグのページを開けるように

* docs(changelog): update changelog

* enhance(frontend): 検索範囲等が指定されている時は照会/ハッシュタグページを開かないように

* enhance(frontend): 検索内容に空白が含まれている場合は照会/ハッシュタグページを開かないように

* docs(changelog): update changelog

* Revert "enhance(frontend): 検索範囲等が指定されている時は照会/ハッシュタグページを開かないように"

This reverts commit f84eecea964b90e9b115eac19ed6f19a603a6bbc.

* enhance(frontend): 検索から照会/ハッシュタグページを開くかどうか確認するように

* docs(changelog): update changelog

* chore: fix lint

* docs(changelog): update changelog insertion position

* enhance(frontend): 検索から`@user@host`の形式で照会出来るように

* fix(frontend): 照会で入力が`@`のみの場合に`/@`に遷移しないように

* fix(backend): `users/search`において`@`から始まるqueryに対する処理が正しくなかった問題を修正

* docs(changelog): update changelog

* chore(backend): fix lint error

* fix(backend): more improvements for users/search when query startswith `@`

* chore: unify common conditions

* docs(changelog): refine changelog

* chore(backend): fix lint error

* MkInputをpreventに対応させ、enterの意図せぬ伝搬を防ぐ

* chore(frontend/search.user): use .prevent to prevent the propagation of enter instead of setTimeout

---------

Co-authored-by: samunohito <46447427+samunohito@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Co-authored-by: taichanne30 <dev@taichan.site>
---
 CHANGELOG.md                                  |   8 ++
 locales/index.d.ts                            |   8 ++
 locales/ja-JP.yml                             |   2 +
 .../src/server/api/endpoints/users/search.ts  | 126 ++++++++----------
 packages/frontend/src/components/MkInput.vue  |   4 +-
 packages/frontend/src/pages/search.note.vue   |  54 ++++++--
 packages/frontend/src/pages/search.user.vue   |  58 ++++++--
 packages/frontend/src/scripts/lookup.ts       |   2 +-
 8 files changed, 159 insertions(+), 103 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f074ed65e0..4051566651 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,10 @@
 - Enhance: AiScriptを0.19.0にアップデート
 - Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)
 - Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように
+- Enhance: 検索(ノート/ユーザー)で `#` から始まる文字列を入力すると、そのハッシュタグのノート/ユーザー一覧ページが表示できるように
+- Enhance: 検索(ノート/ユーザー)において、入力に空白が含まれている場合は照会を行わないように
+- Enhance: 検索(ノート/ユーザー)において、照会を行うかどうか、ハッシュタグのノート/ユーザー一覧ページを表示するかどうかの確認ダイアログを出すように
+- Enhance: 検索(ノート/ユーザー)で `@` から始まる文字列(`@user@host`など)を入力すると、そのユーザーを照会できるように
 - Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように  
   (Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99)
 - Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように
@@ -57,6 +61,8 @@
 - Fix: ダイレクト投稿の"削除して編集"において、宛先が保持されていなかった問題を修正
 - Fix: 投稿フォームへのURL貼り付けによる引用が下書きに保存されていなかった問題を修正
 - Fix: "削除して編集"や下書きにおいて、リアクションの受け入れ設定が保持/保存されていなかった問題を修正
+- Fix: 照会に `#` から始まる文字列を入力してそのハッシュタグのページを表示する際、入力が `#` のみの場合に「指定されたURLに該当するページはありませんでした。」が表示されてしまう問題を修正
+- Fix: 照会に `@` から始まる文字列を入力してユーザーを照会する際、入力が `@` のみの場合に「問題が発生しました」が表示されてしまう問題を修正
 - Fix: 投稿フォームにノートのURLを貼り付けて"引用として添付"した場合、投稿文を空にすることによるRenote化が出来なかった問題を修正
 
 ### Server
@@ -96,6 +102,8 @@
 - Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正  
   (Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1)
 - Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 #14251
+- Fix: `users/search`において `@` から始まる文字列が与えられた際の処理が正しくなかった問題を修正
+  - 名前や自己紹介に `@` から始まる文言が含まれるユーザーも検索できるようになります
 - Fix: 一部のMisskey以外のソフトウェアからファイルを受け取れない問題
   (Cherry-picked from https://github.com/Secineralyr/misskey.dream/pull/73/commits/652eaff1e8aa00b890d71d2e1e52c263c1e67c76)
   - NOTE: `drive_file`の`url`, `uri`, `src`の上限が512から1024に変更されます
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 14dc862745..aee0b6127e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -4500,6 +4500,14 @@ export interface Locale extends ILocale {
      * ユーザー指定
      */
     "specifyUser": string;
+    /**
+     * 照会しますか?
+     */
+    "lookupConfirm": string;
+    /**
+     * ハッシュタグのページを開きますか?
+     */
+    "openTagPageConfirm": string;
     /**
      * ホスト指定
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index cd26b71d57..9b907f0971 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1121,6 +1121,8 @@ preventAiLearning: "生成AIによる学習を拒否"
 preventAiLearningDescription: "外部の文章生成AIや画像生成AIに対して、投稿したノートや画像などのコンテンツを学習の対象にしないように要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されますが、この要求に従うかはそのAI次第であるため、学習を完全に防止するものではありません。"
 options: "オプション"
 specifyUser: "ユーザー指定"
+lookupConfirm: "照会しますか?"
+openTagPageConfirm: "ハッシュタグのページを開きますか?"
 specifyHost: "ホスト指定"
 failedToPreviewUrl: "プレビューできません"
 update: "更新"
diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts
index df9d9f6312..0b0136066d 100644
--- a/packages/backend/src/server/api/endpoints/users/search.ts
+++ b/packages/backend/src/server/api/endpoints/users/search.ts
@@ -57,88 +57,66 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
 
 			ps.query = ps.query.trim();
-			const isUsername = ps.query.startsWith('@');
+			const isUsername = ps.query.startsWith('@') && !ps.query.includes(' ') && ps.query.indexOf('@', 1) === -1;
 
 			let users: MiUser[] = [];
 
-			if (isUsername) {
-				const usernameQuery = this.usersRepository.createQueryBuilder('user')
-					.where('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' })
-					.andWhere(new Brackets(qb => {
-						qb
-							.where('user.updatedAt IS NULL')
-							.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
-					}))
-					.andWhere('user.isSuspended = FALSE');
+			const nameQuery = this.usersRepository.createQueryBuilder('user')
+				.where(new Brackets(qb => {
+					qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
 
-				if (ps.origin === 'local') {
-					usernameQuery.andWhere('user.host IS NULL');
-				} else if (ps.origin === 'remote') {
-					usernameQuery.andWhere('user.host IS NOT NULL');
-				}
-
-				users = await usernameQuery
-					.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
-					.limit(ps.limit)
-					.offset(ps.offset)
-					.getMany();
-			} else {
-				const nameQuery = this.usersRepository.createQueryBuilder('user')
-					.where(new Brackets(qb => {
-						qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
-
-						// Also search username if it qualifies as username
-						if (this.userEntityService.validateLocalUsername(ps.query)) {
-							qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' });
-						}
-					}))
-					.andWhere(new Brackets(qb => {
-						qb
-							.where('user.updatedAt IS NULL')
-							.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
-					}))
-					.andWhere('user.isSuspended = FALSE');
-
-				if (ps.origin === 'local') {
-					nameQuery.andWhere('user.host IS NULL');
-				} else if (ps.origin === 'remote') {
-					nameQuery.andWhere('user.host IS NOT NULL');
-				}
-
-				users = await nameQuery
-					.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
-					.limit(ps.limit)
-					.offset(ps.offset)
-					.getMany();
-
-				if (users.length < ps.limit) {
-					const profQuery = this.userProfilesRepository.createQueryBuilder('prof')
-						.select('prof.userId')
-						.where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
-
-					if (ps.origin === 'local') {
-						profQuery.andWhere('prof.userHost IS NULL');
-					} else if (ps.origin === 'remote') {
-						profQuery.andWhere('prof.userHost IS NOT NULL');
+					if (isUsername) {
+						qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' });
+					} else if (this.userEntityService.validateLocalUsername(ps.query)) { // Also search username if it qualifies as username
+						qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' });
 					}
+				}))
+				.andWhere(new Brackets(qb => {
+					qb
+						.where('user.updatedAt IS NULL')
+						.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+				}))
+				.andWhere('user.isSuspended = FALSE');
 
-					const query = this.usersRepository.createQueryBuilder('user')
-						.where(`user.id IN (${ profQuery.getQuery() })`)
-						.andWhere(new Brackets(qb => {
-							qb
-								.where('user.updatedAt IS NULL')
-								.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
-						}))
-						.andWhere('user.isSuspended = FALSE')
-						.setParameters(profQuery.getParameters());
+			if (ps.origin === 'local') {
+				nameQuery.andWhere('user.host IS NULL');
+			} else if (ps.origin === 'remote') {
+				nameQuery.andWhere('user.host IS NOT NULL');
+			}
 
-					users = users.concat(await query
-						.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
-						.limit(ps.limit)
-						.offset(ps.offset)
-						.getMany(),
-					);
+			users = await nameQuery
+				.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
+				.limit(ps.limit)
+				.offset(ps.offset)
+				.getMany();
+
+			if (users.length < ps.limit) {
+				const profQuery = this.userProfilesRepository.createQueryBuilder('prof')
+					.select('prof.userId')
+					.where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' });
+
+				if (ps.origin === 'local') {
+					profQuery.andWhere('prof.userHost IS NULL');
+				} else if (ps.origin === 'remote') {
+					profQuery.andWhere('prof.userHost IS NOT NULL');
 				}
+
+				const query = this.usersRepository.createQueryBuilder('user')
+					.where(`user.id IN (${ profQuery.getQuery() })`)
+					.andWhere(new Brackets(qb => {
+						qb
+							.where('user.updatedAt IS NULL')
+							.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
+					}))
+					.andWhere('user.isSuspended = FALSE')
+					.setParameters(profQuery.getParameters());
+
+				users = users.concat(await query
+					.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
+					.limit(ps.limit)
+					.offset(ps.offset)
+					.getMany(),
+				);
 			}
 
 			return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' });
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index 88ef4635e6..e695564f92 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -79,7 +79,7 @@ const props = defineProps<{
 const emit = defineEmits<{
 	(ev: 'change', _ev: KeyboardEvent): void;
 	(ev: 'keydown', _ev: KeyboardEvent): void;
-	(ev: 'enter'): void;
+	(ev: 'enter', _ev: KeyboardEvent): void;
 	(ev: 'update:modelValue', value: string | number): void;
 }>();
 
@@ -111,7 +111,7 @@ const onKeydown = (ev: KeyboardEvent) => {
 	emit('keydown', ev);
 
 	if (ev.code === 'Enter') {
-		emit('enter');
+		emit('enter', ev);
 	}
 };
 
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index 05dc77b94b..9cf7fbe8d8 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div class="_gaps">
 	<div class="_gaps">
-		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
+		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter.prevent="search">
 			<template #prefix><i class="ti ti-search"></i></template>
 		</MkInput>
 		<MkFoldableSection :expanded="true">
@@ -143,25 +143,55 @@ async function search() {
 	if (query == null || query === '') return;
 
 	//#region AP lookup
-	if (query.startsWith('https://')) {
-		const promise = misskeyApi('ap/show', {
-			uri: query,
+	if (query.startsWith('https://') && !query.includes(' ')) {
+		const confirm = await os.confirm({
+			type: 'info',
+			text: i18n.ts.lookupConfirm,
 		});
+		if (!confirm.canceled) {
+			const promise = misskeyApi('ap/show', {
+				uri: query,
+			});
 
-		os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
+			os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
 
-		const res = await promise;
+			const res = await promise;
 
-		if (res.type === 'User') {
-			router.push(`/@${res.object.username}@${res.object.host}`);
-		} else if (res.type === 'Note') {
-			router.push(`/notes/${res.object.id}`);
+			if (res.type === 'User') {
+				router.push(`/@${res.object.username}@${res.object.host}`);
+			} else if (res.type === 'Note') {
+				router.push(`/notes/${res.object.id}`);
+			}
+
+			return;
 		}
-
-		return;
 	}
 	//#endregion
 
+	if (query.length > 1 && !query.includes(' ')) {
+		if (query.startsWith('@')) {
+			const confirm = await os.confirm({
+				type: 'info',
+				text: i18n.ts.lookupConfirm,
+			});
+			if (!confirm.canceled) {
+				router.push(`/${query}`);
+				return;
+			}
+		}
+
+		if (query.startsWith('#')) {
+			const confirm = await os.confirm({
+				type: 'info',
+				text: i18n.ts.openTagPageConfirm,
+			});
+			if (!confirm.canceled) {
+				router.push(`/tags/${encodeURIComponent(query.substring(1))}`);
+				return;
+			}
+		}
+	}
+
 	notePagination.value = {
 		endpoint: 'notes/search',
 		limit: 10,
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index 85d869d9cb..724fbfdfbd 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div class="_gaps">
 	<div class="_gaps">
-		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
+		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter.prevent="search">
 			<template #prefix><i class="ti ti-search"></i></template>
 		</MkInput>
 		<MkRadios v-model="searchOrigin" @update:modelValue="search()">
@@ -39,8 +39,8 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router/supplier.js';
 
 const props = withDefaults(defineProps<{
-	query?: string,
-	origin?: Endpoints['users/search']['req']['origin'],
+  query?: string,
+  origin?: Endpoints['users/search']['req']['origin'],
 }>(), {
 	query: '',
 	origin: 'combined',
@@ -59,25 +59,55 @@ async function search() {
 	if (query == null || query === '') return;
 
 	//#region AP lookup
-	if (query.startsWith('https://')) {
-		const promise = misskeyApi('ap/show', {
-			uri: query,
+	if (query.startsWith('https://') && !query.includes(' ')) {
+		const confirm = await os.confirm({
+			type: 'info',
+			text: i18n.ts.lookupConfirm,
 		});
+		if (!confirm.canceled) {
+			const promise = misskeyApi('ap/show', {
+				uri: query,
+			});
 
-		os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
+			os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
 
-		const res = await promise;
+			const res = await promise;
 
-		if (res.type === 'User') {
-			router.push(`/@${res.object.username}@${res.object.host}`);
-		} else if (res.type === 'Note') {
-			router.push(`/notes/${res.object.id}`);
+			if (res.type === 'User') {
+				router.push(`/@${res.object.username}@${res.object.host}`);
+			} else if (res.type === 'Note') {
+				router.push(`/notes/${res.object.id}`);
+			}
+
+			return;
 		}
-
-		return;
 	}
 	//#endregion
 
+	if (query.length > 1 && !query.includes(' ')) {
+		if (query.startsWith('@')) {
+			const confirm = await os.confirm({
+				type: 'info',
+				text: i18n.ts.lookupConfirm,
+			});
+			if (!confirm.canceled) {
+				router.push(`/${query}`);
+				return;
+			}
+		}
+
+		if (query.startsWith('#')) {
+			const confirm = await os.confirm({
+				type: 'info',
+				text: i18n.ts.openTagPageConfirm,
+			});
+			if (!confirm.canceled) {
+				router.push(`/user-tags/${encodeURIComponent(query.substring(1))}`);
+				return;
+			}
+		}
+	}
+
 	userPagination.value = {
 		endpoint: 'users/search',
 		limit: 10,
diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts
index 7f020b15cc..a261ec0669 100644
--- a/packages/frontend/src/scripts/lookup.ts
+++ b/packages/frontend/src/scripts/lookup.ts
@@ -16,7 +16,7 @@ export async function lookup(router?: Router) {
 		title: i18n.ts.lookup,
 	});
 	const query = temp ? temp.trim() : '';
-	if (canceled) return;
+	if (canceled || query.length <= 1) return;
 
 	if (query.startsWith('@') && !query.includes(' ')) {
 		_router.push(`/${query}`);

From 916ed49441b396e51d1c627bfcc24676903b9859 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 19:20:37 +0900
Subject: [PATCH 189/206] New translations ja-jp.yml (English) (#14327)

---
 locales/en-US.yml | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 95227357d7..cfb5783a95 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -60,6 +60,7 @@ copyFileId: "Copy file ID"
 copyFolderId: "Copy folder ID"
 copyProfileUrl: "Copy profile URL"
 searchUser: "Search for a user"
+searchThisUsersNotes: "Search this user’s notes"
 reply: "Reply"
 loadMore: "Load more"
 showMore: "Show more"
@@ -154,6 +155,7 @@ editList: "Edit list"
 selectChannel: "Select a channel"
 selectAntenna: "Select an antenna"
 editAntenna: "Edit antenna"
+createAntenna: "Create an antenna"
 selectWidget: "Select a widget"
 editWidgets: "Edit widgets"
 editWidgetsExit: "Done"
@@ -194,6 +196,7 @@ followConfirm: "Are you sure that you want to follow {name}?"
 proxyAccount: "Proxy account"
 proxyAccountDescription: "A proxy account is an account that acts as a remote follower for users under certain conditions. For example, when a user adds a remote user to the list, the remote user's activity will not be delivered to the instance if no local user is following that user, so the proxy account will follow instead."
 host: "Host"
+selectSelf: "Select myself"
 selectUser: "Select a user"
 recipient: "Recipient"
 annotation: "Comments"
@@ -1106,6 +1109,8 @@ preservedUsernames: "Reserved usernames"
 preservedUsernamesDescription: "List usernames to reserve separated by linebreaks. These will become unable during normal account creation, but can be used by administrators to manually create accounts. Already existing accounts using these usernames will not be affected."
 createNoteFromTheFile: "Compose note from this file"
 archive: "Archive"
+archived: "Archived"
+unarchive: "Unarchive"
 channelArchiveConfirmTitle: "Really archive {name}?"
 channelArchiveConfirmDescription: "An archived channel won't appear in the channel list or search results anymore. New posts can also not be added to it anymore."
 thisChannelArchived: "This channel has been archived."
@@ -1116,6 +1121,7 @@ preventAiLearning: "Reject usage in Machine Learning (Generative AI)"
 preventAiLearningDescription: "Requests crawlers to not use posted text or image material etc. in machine learning (Predictive / Generative AI) data sets. This is achieved by adding a \"noai\" HTML-Response flag to the respective content. A complete prevention can however not be achieved through this flag, as it may simply be ignored."
 options: "Options"
 specifyUser: "Specific user"
+specifyHost: "Specify a host"
 failedToPreviewUrl: "Could not preview"
 update: "Update"
 rolesThatCanBeUsedThisEmojiAsReaction: "Roles that can use this emoji as reaction"
@@ -1250,6 +1256,8 @@ inquiry: "Contact"
 tryAgain: "Please try again later"
 confirmWhenRevealingSensitiveMedia: "Confirm when revealing sensitive media"
 sensitiveMediaRevealConfirm: "This might be a sensitive media. Are you sure to reveal?"
+createdLists: "Created lists"
+createdAntennas: "Created antennas"
 _delivery:
   status: "Delivery status"
   stop: "Suspended"
@@ -2425,6 +2433,7 @@ _webhookSettings:
   _systemEvents:
     abuseReport: "When received a new abuse report"
     abuseReportResolved: "When resolved abuse reports"
+    userCreated: "When user is created"
   deleteConfirm: "Are you sure you want to delete the Webhook?"
 _abuseReport:
   _notificationRecipient:
@@ -2615,3 +2624,8 @@ _mediaControls:
   pip: "Picture in Picture"
   playbackRate: "Playback Speed"
   loop: "Loop playback"
+_contextMenu:
+  title: "Context menu"
+  app: "Application"
+  appWithShift: "Application with shift key"
+  native: "Native"

From 86b4f49880730256da4b281914f2be799977330f Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 30 Jul 2024 10:25:10 +0000
Subject: [PATCH 190/206] Bump version to 2024.7.0-rc.7

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

diff --git a/package.json b/package.json
index 54fd36f11a..7846e5cb51 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-rc.6",
+	"version": "2024.7.0-rc.7",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index eec39349c5..8b9b0c6b44 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-rc.6",
+	"version": "2024.7.0-rc.7",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 8f40f932e4bac6b4a90a464041a00df315a3c693 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 30 Jul 2024 19:44:08 +0900
Subject: [PATCH 191/206] =?UTF-8?q?=E8=87=AA=E5=88=86=E3=81=AE=E3=83=95?=
 =?UTF-8?q?=E3=82=A9=E3=83=AD=E3=83=AF=E3=83=BC=E9=99=90=E5=AE=9A=E6=8A=95?=
 =?UTF-8?q?=E7=A8=BF=E3=81=AB=E5=AF=BE=E3=81=99=E3=82=8B=E3=83=AA=E3=83=97?=
 =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=81=8C=E3=83=9B=E3=83=BC=E3=83=A0=E3=82=BF?=
 =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=A7=E8=A6=8B?=
 =?UTF-8?q?=E3=81=88=E3=81=AA=E3=81=84=E3=81=93=E3=81=A8=E3=81=8C=E6=9C=89?=
 =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1383?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: reply to my follower notes are not shown on the home timeline

* fix: reply to follower note by non-following is on social timeline

* docs: changelog

* test: add endpoint test for changes

* test(e2e): 自分のfollowers投稿に対するリプライが流れる

* test(e2e/streaming): 自分のfollowers投稿に対するリプライが流れる

* test(e2e/streaming): フォローしていないユーザによるフォロワー限定投稿に対するリプライがソーシャルタイムラインで表示されることがある問題

* test(e2e/timelines): try fixing typecheck error

---------

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
---
 CHANGELOG.md                                  |  2 +
 .../api/endpoints/notes/hybrid-timeline.ts    | 13 ++++
 .../server/api/endpoints/notes/timeline.ts    |  2 +-
 .../api/stream/channels/home-timeline.ts      |  4 +-
 .../api/stream/channels/hybrid-timeline.ts    | 12 ++-
 packages/backend/test/e2e/streaming.ts        | 62 +++++++++++++++
 packages/backend/test/e2e/timelines.ts        | 75 +++++++++++++++++++
 7 files changed, 165 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4051566651..28e4b236eb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -109,6 +109,8 @@
   - NOTE: `drive_file`の`url`, `uri`, `src`の上限が512から1024に変更されます
 	  Migrationではカラム定義の変更のみが行われます。
 		サーバー管理者は各サーバーの必要に応じ`drive_file` `("uri")`に対するインデックスを張りなおすことでより安定しDBの探索が行われる可能性があります。詳細 は [GitHub](https://github.com/misskey-dev/misskey/pull/14323#issuecomment-2257562228)で確認可能です
+- Fix: 自分のフォロワー限定投稿に対するリプライがホームタイムラインで見えないことが有る問題を修正
+- Fix: フォローしていないユーザによるフォロワー限定投稿に対するリプライがソーシャルタイムラインで表示されることがある問題を修正
 
 ### Misskey.js
 - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)
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 f6084b5763..2a2c659942 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -143,6 +143,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				];
 			}
 
+			const [
+				followings,
+			] = await Promise.all([
+				this.cacheService.userFollowingsCache.fetch(me.id),
+			]);
+
 			const redisTimeline = await this.fanoutTimelineEndpointService.timeline({
 				untilId,
 				sinceId,
@@ -153,6 +159,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
 				alwaysIncludeMyNotes: true,
 				excludePureRenotes: !ps.withRenotes,
+				noteFilter: note => {
+					if (note.reply && note.reply.visibility === 'followers') {
+						if (!Object.hasOwn(followings, note.reply.userId) && note.reply.userId !== me.id) return false;
+					}
+
+					return true;
+				},
 				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
 					untilId,
 					sinceId,
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 8b87908bd3..c9b43b5359 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -114,7 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				excludePureRenotes: !ps.withRenotes,
 				noteFilter: note => {
 					if (note.reply && note.reply.visibility === 'followers') {
-						if (!Object.hasOwn(followings, note.reply.userId)) return false;
+						if (!Object.hasOwn(followings, note.reply.userId) && note.reply.userId !== me.id) return false;
 					}
 
 					return true;
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 1f440732a6..66644ed58c 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -60,7 +60,7 @@ class HomeTimelineChannel extends Channel {
 			const reply = note.reply;
 			if (this.following[note.userId]?.withReplies) {
 				// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
-				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId)) return;
+				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId) && reply.userId !== this.user!.id) return;
 			} else {
 				// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 				if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
@@ -73,7 +73,7 @@ class HomeTimelineChannel extends Channel {
 			if (note.renote.reply) {
 				const reply = note.renote.reply;
 				// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
-				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId)) return;
+				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId) && reply.userId !== this.user!.id) return;
 			}
 		}
 
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 6938b6e3ea..75bd13221f 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -76,14 +76,22 @@ class HybridTimelineChannel extends Channel {
 			const reply = note.reply;
 			if ((this.following[note.userId]?.withReplies ?? false) || this.withReplies) {
 				// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
-				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId)) return;
+				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId) && reply.userId !== this.user!.id) return;
 			} else {
 				// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 				if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
 			}
 		}
 
-		if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;
+		// 純粋なリノート(引用リノートでないリノート)の場合
+		if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
+			if (!this.withRenotes) return;
+			if (note.renote.reply) {
+				const reply = note.renote.reply;
+				// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
+				if (reply.visibility === 'followers' && !Object.hasOwn(this.following, reply.userId) && reply.userId !== this.user!.id) return;
+			}
+		}
 
 		if (this.user && note.renoteId && !note.text) {
 			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index b0a70074c6..72f26a38e0 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -34,6 +34,7 @@ describe('Streaming', () => {
 		let kyoko: misskey.entities.SignupResponse;
 		let chitose: misskey.entities.SignupResponse;
 		let kanako: misskey.entities.SignupResponse;
+		let erin: misskey.entities.SignupResponse;
 
 		// Remote users
 		let akari: misskey.entities.SignupResponse;
@@ -53,6 +54,7 @@ describe('Streaming', () => {
 			kyoko = await signup({ username: 'kyoko' });
 			chitose = await signup({ username: 'chitose' });
 			kanako = await signup({ username: 'kanako' });
+			erin = await signup({ username: 'erin' }); // erin:  A generic fifth participant
 
 			akari = await signup({ username: 'akari', host: 'example.com' });
 			chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
@@ -71,6 +73,12 @@ describe('Streaming', () => {
 			// Follow: kyoko => chitose
 			await api('following/create', { userId: chitose.id }, kyoko);
 
+			// Follow: erin <=> ayano each other.
+			// erin => ayano: withReplies: true
+			await api('following/create', { userId: ayano.id, withReplies: true }, erin);
+			// ayano => erin: withReplies: false
+			await api('following/create', { userId: erin.id, withReplies: false }, ayano);
+
 			// Mute: chitose => kanako
 			await api('mute/create', { userId: kanako.id }, chitose);
 
@@ -297,6 +305,28 @@ describe('Streaming', () => {
 
 				assert.strictEqual(fired, true);
 			});
+
+			test('withReplies: true のとき自分のfollowers投稿に対するリプライが流れる', async () => {
+				const erinNote = await post(erin, { text: 'hi', visibility: 'followers' });
+				const fired = await waitFire(
+					erin, 'homeTimeline',	// erin:home
+					() => api('notes/create', { text: 'hello', replyId: erinNote.id }, ayano),	// ayano reply to erin's followers post
+					msg => msg.type === 'note' && msg.body.userId === ayano.id,	// wait ayano
+				);
+
+				assert.strictEqual(fired, true);
+			});
+
+			test('withReplies: false でも自分の投稿に対するリプライが流れる', async () => {
+				const ayanoNote = await post(ayano, { text: 'hi', visibility: 'followers' });
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { text: 'hello', replyId: ayanoNote.id }, erin),	// erin reply to ayano's followers post
+					msg => msg.type === 'note' && msg.body.userId === erin.id,	// wait erin
+				);
+
+				assert.strictEqual(fired, true);
+			});
 		});	// Home
 
 		describe('Local Timeline', () => {
@@ -475,6 +505,38 @@ describe('Streaming', () => {
 
 				assert.strictEqual(fired, false);
 			});
+
+			test('withReplies: true のとき自分のfollowers投稿に対するリプライが流れる', async () => {
+				const erinNote = await post(erin, { text: 'hi', visibility: 'followers' });
+				const fired = await waitFire(
+					erin, 'homeTimeline',	// erin:home
+					() => api('notes/create', { text: 'hello', replyId: erinNote.id }, ayano),	// ayano reply to erin's followers post
+					msg => msg.type === 'note' && msg.body.userId === ayano.id,	// wait ayano
+				);
+
+				assert.strictEqual(fired, true);
+			});
+
+			test('withReplies: false でも自分の投稿に対するリプライが流れる', async () => {
+				const ayanoNote = await post(ayano, { text: 'hi', visibility: 'followers' });
+				const fired = await waitFire(
+					ayano, 'homeTimeline',	// ayano:home
+					() => api('notes/create', { text: 'hello', replyId: ayanoNote.id }, erin),	// erin reply to ayano's followers post
+					msg => msg.type === 'note' && msg.body.userId === erin.id,	// wait erin
+				);
+
+				assert.strictEqual(fired, true);
+			});
+
+			test('withReplies: true のフォローしていない人のfollowersノートに対するリプライが流れない', async () => {
+				const fired = await waitFire(
+					erin, 'homeTimeline',	// erin:home
+					() => api('notes/create', { text: 'hello', replyId: chitose.id }, ayano),	// ayano reply to chitose's post
+					msg => msg.type === 'note' && msg.body.userId === ayano.id,	// wait ayano
+				);
+
+				assert.strictEqual(fired, false);
+			});
 		});
 
 		describe('Global Timeline', () => {
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index ab65781f70..b7069dd82c 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -127,6 +127,7 @@ describe('Timelines', () => {
 		test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
+			await api('following/create', { userId: carol.id }, bob);
 			await api('following/create', { userId: bob.id }, alice);
 			await api('following/update', { userId: bob.id, withReplies: true }, alice);
 			await setTimeout(1000);
@@ -161,6 +162,24 @@ describe('Timelines', () => {
 			assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'hi');
 		});
 
+		test.concurrent('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
+			const [alice, bob] = await Promise.all([signup(), signup()]);
+
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: alice.id }, bob);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
+			await setTimeout(1000);
+			const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+			await waitForPushToTl();
+
+			const res = await api('notes/timeline', { limit: 100 }, alice);
+
+			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+		});
+
 		test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
@@ -768,6 +787,62 @@ describe('Timelines', () => {
 			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
+		test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => {
+			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+			await api('following/create', { userId: carol.id }, bob);
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
+			await setTimeout(1000);
+			const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+
+			await waitForPushToTl();
+
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+
+			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
+			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false);
+		});
+
+		test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
+			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
+
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: carol.id }, alice);
+			await api('following/create', { userId: carol.id }, bob);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
+			await setTimeout(1000);
+			const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
+			const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id });
+
+			await waitForPushToTl();
+
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+
+			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
+			assert.strictEqual(res.body.find((note: any) => note.id === carolNote.id)?.text, 'hi');
+		});
+
+		test.concurrent('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
+			const [alice, bob] = await Promise.all([signup(), signup()]);
+
+			await api('following/create', { userId: bob.id }, alice);
+			await api('following/create', { userId: alice.id }, bob);
+			await api('following/update', { userId: bob.id, withReplies: true }, alice);
+			await setTimeout(1000);
+			const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' });
+			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+			await waitForPushToTl();
+
+			const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
+
+			assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
+			assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true);
+		});
+
 		test.concurrent('他人の他人への返信が含まれない', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 

From 5c42a0e43931f62490c44e389db893b6bfe9e349 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 30 Jul 2024 19:47:45 +0900
Subject: [PATCH 192/206] feat: media silence (#13842)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: media silence

* fix: lint

* feat: deny creating custom emoji reaction and using custom emoji from media silenced hosts

* chore: メディアサイレンスの説明にカスタム絵文字の話を追加

* Update locales/ja-JP.yml

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>

* chore: update index.d.ts

* docs(changelog): update changelog

---------

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
---
 CHANGELOG.md                                  |  2 ++
 locales/index.d.ts                            | 12 +++++++++
 locales/ja-JP.yml                             |  3 +++
 .../1716197366117-MediaSilenceForHosts.js     | 16 +++++++++++
 packages/backend/src/core/DriveService.ts     |  3 +++
 .../backend/src/core/NoteCreateService.ts     |  3 +++
 packages/backend/src/core/ReactionService.ts  |  9 +++++--
 packages/backend/src/core/UtilityService.ts   |  6 +++++
 .../core/entities/InstanceEntityService.ts    |  1 +
 packages/backend/src/models/Meta.ts           |  5 ++++
 .../models/json-schema/federation-instance.ts |  4 +++
 .../src/server/api/endpoints/admin/meta.ts    | 11 ++++++++
 .../server/api/endpoints/admin/update-meta.ts | 15 +++++++++++
 .../src/pages/admin/instance-block.vue        | 27 +++++++++++++------
 packages/frontend/src/pages/instance-info.vue | 15 ++++++++++-
 packages/misskey-js/src/autogen/types.ts      |  3 +++
 16 files changed, 124 insertions(+), 11 deletions(-)
 create mode 100644 packages/backend/migration/1716197366117-MediaSilenceForHosts.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28e4b236eb..27308cb8bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@
 - Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
   - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
 - Feat: ユーザ作成時にSystemWebhookを送信可能に #14281
+- Feat: メディアサイレンスを実装 #13842
+  - メディアサイレンスされたサーバーに所属するアカウントによるファイルはすべてセンシティブとして扱われ、カスタム絵文字が使用できないようになります。
 - Enhance: 管理画面でアーカイブにしたお知らせを表示・編集できるように
 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
 - Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題
diff --git a/locales/index.d.ts b/locales/index.d.ts
index aee0b6127e..f03207e0bd 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -864,6 +864,10 @@ export interface Locale extends ILocale {
      * サーバーをサイレンス
      */
     "silenceThisInstance": string;
+    /**
+     * サーバーをメディアサイレンス
+     */
+    "mediaSilenceThisInstance": string;
     /**
      * 操作
      */
@@ -948,6 +952,14 @@ export interface Locale extends ILocale {
      * サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。
      */
     "silencedInstancesDescription": string;
+    /**
+     * メディアサイレンスしたサーバー
+     */
+    "mediaSilencedInstances": string;
+    /**
+     * メディアサイレンスしたいサーバーのホストを改行で区切って設定します。メディアサイレンスされたサーバーに所属するアカウントによるファイルはすべてセンシティブとして扱われ、カスタム絵文字が使用できないようになります。ブロックしたインスタンスには影響しません。
+     */
+    "mediaSilencedInstancesDescription": string;
     /**
      * ミュートとブロック
      */
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 9b907f0971..d4931ae90d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -212,6 +212,7 @@ perDay: "1日ごと"
 stopActivityDelivery: "アクティビティの配送を停止"
 blockThisInstance: "このサーバーをブロック"
 silenceThisInstance: "サーバーをサイレンス"
+mediaSilenceThisInstance: "サーバーをメディアサイレンス"
 operations: "操作"
 software: "ソフトウェア"
 version: "バージョン"
@@ -233,6 +234,8 @@ blockedInstances: "ブロックしたサーバー"
 blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。"
 silencedInstances: "サイレンスしたサーバー"
 silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになります。ブロックしたインスタンスには影響しません。"
+mediaSilencedInstances: "メディアサイレンスしたサーバー"
+mediaSilencedInstancesDescription: "メディアサイレンスしたいサーバーのホストを改行で区切って設定します。メディアサイレンスされたサーバーに所属するアカウントによるファイルはすべてセンシティブとして扱われ、カスタム絵文字が使用できないようになります。ブロックしたインスタンスには影響しません。"
 muteAndBlock: "ミュートとブロック"
 mutedUsers: "ミュートしたユーザー"
 blockedUsers: "ブロックしたユーザー"
diff --git a/packages/backend/migration/1716197366117-MediaSilenceForHosts.js b/packages/backend/migration/1716197366117-MediaSilenceForHosts.js
new file mode 100644
index 0000000000..10bb7f0255
--- /dev/null
+++ b/packages/backend/migration/1716197366117-MediaSilenceForHosts.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class MediaSilenceForHosts1716197366117 {
+    name = 'MediaSilenceForHosts1716197366117'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" ADD "mediaSilencedHosts" character varying(1024) array NOT NULL DEFAULT '{}'`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "mediaSilencedHosts"`);
+    }
+}
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 37c5d1adf7..8aa04b4da7 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -43,6 +43,7 @@ import { RoleService } from '@/core/RoleService.js';
 import { correctFilename } from '@/misc/correct-filename.js';
 import { isMimeImage } from '@/misc/is-mime-image.js';
 import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { UtilityService } from '@/core/UtilityService.js';
 
 type AddFileArgs = {
 	/** User who wish to add file */
@@ -127,6 +128,7 @@ export class DriveService {
 		private driveChart: DriveChart,
 		private perUserDriveChart: PerUserDriveChart,
 		private instanceChart: InstanceChart,
+		private utilityService: UtilityService,
 	) {
 		const logger = new Logger('drive', 'blue');
 		this.registerLogger = logger.createSubLogger('register', 'yellow');
@@ -587,6 +589,7 @@ export class DriveService {
 			sensitive ?? false
 			: false;
 
+		if (user && this.utilityService.isMediaSilencedHost(instance.mediaSilencedHosts, user.host)) file.isSensitive = true;
 		if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
 		if (info.sensitive && instance.setSensitiveFlagAutomatically) file.isSensitive = true;
 		if (userRoleNSFW) file.isSensitive = true;
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index fd9fac357f..32cf3f3e26 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -364,6 +364,9 @@ export class NoteCreateService implements OnApplicationShutdown {
 			mentionedUsers = data.apMentions ?? await this.extractMentionedUsers(user, combinedTokens);
 		}
 
+		// if the host is media-silenced, custom emojis are not allowed
+		if (this.utilityService.isMediaSilencedHost(meta.mediaSilencedHosts, user.host)) emojis = [];
+
 		tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32);
 
 		if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts
index 64c7b2ed03..371207c33a 100644
--- a/packages/backend/src/core/ReactionService.ts
+++ b/packages/backend/src/core/ReactionService.ts
@@ -105,6 +105,8 @@ export class ReactionService {
 
 	@bindThis
 	public async create(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot'] }, note: MiNote, _reaction?: string | null) {
+		const meta = await this.metaService.fetch();
+
 		// Check blocking
 		if (note.userId !== user.id) {
 			const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id);
@@ -148,6 +150,11 @@ export class ReactionService {
 						if ((note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && emoji.isSensitive) {
 							reaction = FALLBACK;
 						}
+
+						// for media silenced host, custom emoji reactions are not allowed
+						if (reacterHost != null && this.utilityService.isMediaSilencedHost(meta.mediaSilencedHosts, reacterHost)) {
+							reaction = FALLBACK;
+						}
 					} else {
 						// リアクションとして使う権限がない
 						reaction = FALLBACK;
@@ -220,8 +227,6 @@ export class ReactionService {
 			}
 		}
 
-		const meta = await this.metaService.fetch();
-
 		if (meta.enableChartsForRemoteUser || (user.host == null)) {
 			this.perUserReactionsChart.update(user, note);
 		}
diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts
index 652e8f7449..94729250a6 100644
--- a/packages/backend/src/core/UtilityService.ts
+++ b/packages/backend/src/core/UtilityService.ts
@@ -42,6 +42,12 @@ export class UtilityService {
 		return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
 	}
 
+	@bindThis
+	public isMediaSilencedHost(silencedHosts: string[] | undefined, host: string | null): boolean {
+		if (!silencedHosts || host == null) return false;
+		return silencedHosts.some(x => host.toLowerCase() === x);
+	}
+
 	@bindThis
 	public concatNoteContentsForKeyWordCheck(content: {
 		cw?: string | null;
diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts
index 9117b13914..4c45c13167 100644
--- a/packages/backend/src/core/entities/InstanceEntityService.ts
+++ b/packages/backend/src/core/entities/InstanceEntityService.ts
@@ -50,6 +50,7 @@ export class InstanceEntityService {
 			maintainerName: instance.maintainerName,
 			maintainerEmail: instance.maintainerEmail,
 			isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host),
+			isMediaSilenced: this.utilityService.isMediaSilencedHost(meta.mediaSilencedHosts, instance.host),
 			iconUrl: instance.iconUrl,
 			faviconUrl: instance.faviconUrl,
 			themeColor: instance.themeColor,
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index ad306fcad6..70d41801b5 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -86,6 +86,11 @@ export class MiMeta {
 	})
 	public silencedHosts: string[];
 
+	@Column('varchar', {
+		length: 1024, array: true, default: '{}',
+	})
+	public mediaSilencedHosts: string[];
+
 	@Column('varchar', {
 		length: 1024,
 		nullable: true,
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index ed40d405c6..912a0399d8 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -88,6 +88,10 @@ export const packedFederationInstanceSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		isMediaSilenced: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
 		iconUrl: {
 			type: 'string',
 			optional: false, nullable: true,
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index eee02a7123..2e7f73da73 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -128,6 +128,16 @@ export const meta = {
 					nullable: false,
 				},
 			},
+			mediaSilencedHosts: {
+				type: 'array',
+				optional: false,
+				nullable: false,
+				items: {
+					type: 'string',
+					optional: false,
+					nullable: false,
+				},
+			},
 			pinnedUsers: {
 				type: 'array',
 				optional: false, nullable: false,
@@ -552,6 +562,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				hiddenTags: instance.hiddenTags,
 				blockedHosts: instance.blockedHosts,
 				silencedHosts: instance.silencedHosts,
+				mediaSilencedHosts: instance.mediaSilencedHosts,
 				sensitiveWords: instance.sensitiveWords,
 				prohibitedWords: instance.prohibitedWords,
 				preservedUsernames: instance.preservedUsernames,
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index 4e28ee6877..5efdc9d8c4 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -150,6 +150,13 @@ export const paramDef = {
 				type: 'string',
 			},
 		},
+		mediaSilencedHosts: {
+			type: 'array',
+			nullable: true,
+			items: {
+				type: 'string',
+			},
+		},
 		summalyProxy: {
 			type: 'string', nullable: true,
 			description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
@@ -203,6 +210,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
 				});
 			}
+			if (Array.isArray(ps.mediaSilencedHosts)) {
+				let lastValue = '';
+				set.mediaSilencedHosts = ps.mediaSilencedHosts.sort().filter((h) => {
+					const lv = lastValue;
+					lastValue = h;
+					return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
+				});
+			}
 			if (ps.themeColor !== undefined) {
 				set.themeColor = ps.themeColor;
 			}
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index 6b14bd42c2..e090616b26 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -8,14 +8,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
 	<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
 		<FormSuspense :p="init">
-			<MkTextarea v-if="tab === 'block'" v-model="blockedHosts">
-				<span>{{ i18n.ts.blockedInstances }}</span>
-				<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
-			</MkTextarea>
-			<MkTextarea v-else-if="tab === 'silence'" v-model="silencedHosts" class="_formBlock">
-				<span>{{ i18n.ts.silencedInstances }}</span>
-				<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
-			</MkTextarea>
+			<template v-if="tab === 'block'">
+				<MkTextarea v-model="blockedHosts">
+					<span>{{ i18n.ts.blockedInstances }}</span>
+					<template #caption>{{ i18n.ts.blockedInstancesDescription }}</template>
+				</MkTextarea>
+			</template>
+			<template v-else-if="tab === 'silence'">
+				<MkTextarea v-model="silencedHosts" class="_formBlock">
+					<span>{{ i18n.ts.silencedInstances }}</span>
+					<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
+				</MkTextarea>
+				<MkTextarea v-model="mediaSilencedHosts" class="_formBlock">
+					<span>{{ i18n.ts.mediaSilencedInstances }}</span>
+					<template #caption>{{ i18n.ts.mediaSilencedInstancesDescription }}</template>
+				</MkTextarea>
+			</template>
 			<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
 		</FormSuspense>
 	</MkSpacer>
@@ -36,18 +44,21 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const blockedHosts = ref<string>('');
 const silencedHosts = ref<string>('');
+const mediaSilencedHosts = ref<string>('');
 const tab = ref('block');
 
 async function init() {
 	const meta = await misskeyApi('admin/meta');
 	blockedHosts.value = meta.blockedHosts.join('\n');
 	silencedHosts.value = meta.silencedHosts.join('\n');
+	mediaSilencedHosts.value = meta.mediaSilencedHosts.join('\n');
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
 		blockedHosts: blockedHosts.value.split('\n') || [],
 		silencedHosts: silencedHosts.value.split('\n') || [],
+		mediaSilencedHosts: mediaSilencedHosts.value.split('\n') || [],
 
 	}).then(() => {
 		fetchInstance(true);
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 26797ba85e..4ba428d536 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -47,6 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<MkButton v-if="suspensionState !== 'none'" :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton>
 						<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
 						<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
+						<MkSwitch v-model="isMediaSilenced" :disabled="!meta || !instance" @update:modelValue="toggleMediaSilenced">{{ i18n.ts.mediaSilenceThisInstance }}</MkSwitch>
 						<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
 						<MkTextarea v-model="moderationNote" manualSave>
 							<template #label>{{ i18n.ts.moderationNote }}</template>
@@ -167,6 +168,7 @@ const instance = ref<Misskey.entities.FederationInstance | null>(null);
 const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding'>('none');
 const isBlocked = ref(false);
 const isSilenced = ref(false);
+const isMediaSilenced = ref(false);
 const faviconUrl = ref<string | null>(null);
 const moderationNote = ref('');
 
@@ -195,8 +197,9 @@ async function fetch(): Promise<void> {
 	suspensionState.value = instance.value?.suspensionState ?? 'none';
 	isBlocked.value = instance.value?.isBlocked ?? false;
 	isSilenced.value = instance.value?.isSilenced ?? false;
+	isMediaSilenced.value = instance.value?.isMediaSilenced ?? false;
 	faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
-	moderationNote.value = instance.value?.moderationNote;
+	moderationNote.value = instance.value?.moderationNote ?? '';
 }
 
 async function toggleBlock(): Promise<void> {
@@ -218,6 +221,16 @@ async function toggleSilenced(): Promise<void> {
 	});
 }
 
+async function toggleMediaSilenced(): Promise<void> {
+	if (!meta.value) throw new Error('No meta?');
+	if (!instance.value) throw new Error('No instance?');
+	const { host } = instance.value;
+	const mediaSilencedHosts = meta.value.mediaSilencedHosts ?? [];
+	await misskeyApi('admin/update-meta', {
+		mediaSilencedHosts: isMediaSilenced.value ? mediaSilencedHosts.concat([host]) : mediaSilencedHosts.filter(x => x !== host),
+	});
+}
+
 async function stopDelivery(): Promise<void> {
 	if (!instance.value) throw new Error('No instance?');
 	suspensionState.value = 'manuallySuspended';
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index cb7d82d2d8..db5efd4a00 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4599,6 +4599,7 @@ export type components = {
       maintainerName: string | null;
       maintainerEmail: string | null;
       isSilenced: boolean;
+      isMediaSilenced: boolean;
       /** Format: url */
       iconUrl: string | null;
       /** Format: url */
@@ -5044,6 +5045,7 @@ export type operations = {
             enableServiceWorker: boolean;
             translatorAvailable: boolean;
             silencedHosts?: string[];
+            mediaSilencedHosts: string[];
             pinnedUsers: string[];
             hiddenTags: string[];
             blockedHosts: string[];
@@ -9371,6 +9373,7 @@ export type operations = {
           perUserListTimelineCacheMax?: number;
           notesPerOneAd?: number;
           silencedHosts?: string[] | null;
+          mediaSilencedHosts?: string[] | null;
           /** @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead. */
           summalyProxy?: string | null;
           urlPreviewEnabled?: boolean;

From c7354c5e306c80d25db838ff64d4f3d26a47bd7f Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Tue, 30 Jul 2024 19:48:16 +0900
Subject: [PATCH 193/206] test(#10336): add `components/Mk[D-E].*` stories
 (#14118)

* test(storybook): add `components/Mk[D-E].*` stories

* fix: mock instance name

* fix: invalid `reactionAcceptance` value

* style: missing trailing commas
---
 packages/frontend/.storybook/fakes.ts         |  52 ++++--
 packages/frontend/.storybook/generate.tsx     |   3 +-
 .../MkDateSeparatedList.stories.impl.ts       |   7 +
 .../src/components/MkDialog.stories.impl.ts   | 159 ++++++++++++++++++
 .../src/components/MkDivider.stories.impl.ts  |   7 +
 .../src/components/MkDonation.stories.impl.ts |  54 ++++++
 .../components/MkDrive.file.stories.impl.ts   |  48 ++++++
 .../components/MkDrive.folder.stories.impl.ts |  70 ++++++++
 .../MkDrive.navFolder.stories.impl.ts         |   7 +
 .../src/components/MkDrive.stories.impl.ts    |  82 +++++++++
 .../MkDriveFileThumbnail.stories.impl.ts      |  41 +++++
 .../src/components/MkDriveFileThumbnail.vue   |   2 +-
 .../MkDriveSelectDialog.stories.impl.ts       |   7 +
 .../components/MkDriveWindow.stories.impl.ts  |   7 +
 .../MkEmojiPicker.section.stories.impl.ts     |   7 +
 .../components/MkEmojiPicker.stories.impl.ts  |  54 ++++++
 .../MkEmojiPickerDialog.stories.impl.ts       |   7 +
 17 files changed, 597 insertions(+), 17 deletions(-)
 create mode 100644 packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDialog.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDivider.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDonation.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDrive.file.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDrive.folder.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDrive.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkDriveWindow.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
 create mode 100644 packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts

diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index 01f1046609..ab04d3e60c 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -47,18 +47,7 @@ export function clip(id = 'someclipid', name = 'Some Clip'): entities.Clip {
 		createdAt: '2016-12-28T22:49:51.000Z',
 		lastClippedAt: null,
 		userId: 'someuserid',
-		user: {
-			id: 'someuserid',
-			name: 'Misskey User',
-			username: 'miskist',
-			host: 'misskey-hub.net',
-			avatarUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
-			avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay',
-			avatarDecorations: [],
-			emojis: {},
-			badgeRoles: [],
-			onlineStatus: 'unknown',
-		},
+		user: userLite(),
 		notesCount: undefined,
 		name,
 		description: 'Some clip description',
@@ -125,6 +114,15 @@ export function file(isSensitive = false) {
 	};
 }
 
+export function folder(id = 'somefolderid', name = 'Some Folder', parentId: string | null = null): entities.DriveFolder {
+	return {
+		id,
+		createdAt: '2016-12-28T22:49:51.000Z',
+		name,
+		parentId,
+	};
+}
+
 export function federationInstance(): entities.FederationInstance {
 	return {
 		id: 'someinstanceid',
@@ -154,7 +152,27 @@ export function federationInstance(): entities.FederationInstance {
 	};
 }
 
-export function userDetailed(id = 'someuserid', username = 'miskist', host:entities.UserDetailed['host'] = 'misskey-hub.net', name:entities.UserDetailed['name'] = 'Misskey User'): entities.UserDetailed {
+export function note(id = 'somenoteid'): entities.Note {
+	return {
+		id,
+		createdAt: '2016-12-28T22:49:51.000Z',
+		deletedAt: null,
+		text: 'some note',
+		cw: null,
+		userId: 'someuserid',
+		user: userLite(),
+		visibility: 'public',
+		reactionAcceptance: 'nonSensitiveOnly',
+		reactionEmojis: {},
+		reactions: {},
+		myReaction: null,
+		reactionCount: 0,
+		renoteCount: 0,
+		repliesCount: 0,
+	};
+}
+
+export function userLite(id = 'someuserid', username = 'miskist', host: entities.UserDetailed['host'] = 'misskey-hub.net', name: entities.UserDetailed['name'] = 'Misskey User'): entities.UserLite {
 	return {
 		id,
 		username,
@@ -165,6 +183,12 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host:entit
 		avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay',
 		avatarDecorations: [],
 		emojis: {},
+	};
+}
+
+export function userDetailed(id = 'someuserid', username = 'miskist', host: entities.UserDetailed['host'] = 'misskey-hub.net', name: entities.UserDetailed['name'] = 'Misskey User'): entities.UserDetailed {
+	return {
+		...userLite(id, username, host, name),
 		bannerBlurhash: 'eQA^IW^-MH8w9tE8I=S^o{$*R4RikXtSxutRozjEnNR.RQadoyozog',
 		bannerUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true',
 		birthday: '2014-06-20',
@@ -215,7 +239,7 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host:entit
 		movedTo: null,
 		alsoKnownAs: null,
 		notify: 'none',
-		memo: null
+		memo: null,
 	};
 }
 
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index b94dfc53e3..52c01aaf70 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -397,8 +397,7 @@ function toStories(component: string): Promise<string> {
 	const globs = await Promise.all([
 		glob('src/components/global/Mk*.vue'),
 		glob('src/components/global/RouterView.vue'),
-		glob('src/components/Mk[A-C]*.vue'),
-		glob('src/components/MkDigitalClock.vue'),
+		glob('src/components/Mk[A-E]*.vue'),
 		glob('src/components/MkGalleryPostPreview.vue'),
 		glob('src/components/MkSignupServerRules.vue'),
 		glob('src/components/MkUserSetupDialog.vue'),
diff --git a/packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts b/packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts
new file mode 100644
index 0000000000..0e5635754c
--- /dev/null
+++ b/packages/frontend/src/components/MkDateSeparatedList.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDateSeparatedList from './MkDateSeparatedList.vue';
+void MkDateSeparatedList;
diff --git a/packages/frontend/src/components/MkDialog.stories.impl.ts b/packages/frontend/src/components/MkDialog.stories.impl.ts
new file mode 100644
index 0000000000..2d8d3661f2
--- /dev/null
+++ b/packages/frontend/src/components/MkDialog.stories.impl.ts
@@ -0,0 +1,159 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { expect, userEvent, waitFor, within } from '@storybook/test';
+import { StoryObj } from '@storybook/vue3';
+import { i18n } from '@/i18n.js';
+import MkDialog from './MkDialog.vue';
+const Base = {
+	render(args) {
+		return {
+			components: {
+				MkDialog,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						done: action('done'),
+						closed: action('closed'),
+					};
+				},
+			},
+			template: '<MkDialog v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+		text: 'Hello, world!',
+	},
+	parameters: {
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const Success = {
+	...Base,
+	args: {
+		...Base.args,
+		type: 'success',
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const Error = {
+	...Base,
+	args: {
+		...Base.args,
+		type: 'error',
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const Warning = {
+	...Base,
+	args: {
+		...Base.args,
+		type: 'warning',
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const Info = {
+	...Base,
+	args: {
+		...Base.args,
+		type: 'info',
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const Question = {
+	...Base,
+	args: {
+		...Base.args,
+		type: 'question',
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const Waiting = {
+	...Base,
+	args: {
+		...Base.args,
+		type: 'waiting',
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const DialogWithActions = {
+	...Question,
+	args: {
+		...Question.args,
+		text: i18n.ts.areYouSure,
+		actions: [
+			{
+				text: i18n.ts.yes,
+				primary: true,
+				callback() {
+					action('YES')();
+				},
+			},
+			{
+				text: i18n.ts.no,
+				callback() {
+					action('NO')();
+				},
+			},
+		],
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const DialogWithDangerActions = {
+	...Warning,
+	args: {
+		...Warning.args,
+		text: i18n.ts.resetAreYouSure,
+		actions: [
+			{
+				text: i18n.ts.yes,
+				danger: true,
+				primary: true,
+				callback() {
+					action('YES')();
+				},
+			},
+			{
+				text: i18n.ts.no,
+				callback() {
+					action('NO')();
+				},
+			},
+		],
+	},
+} satisfies StoryObj<typeof MkDialog>;
+export const DialogWithInput = {
+	...Question,
+	args: {
+		...Question.args,
+		title: 'Hello, world!',
+		text: undefined,
+		input: {
+			placeholder: i18n.ts.inputMessageHere,
+			type: 'text',
+			default: null,
+			minLength: 2,
+			maxLength: 3,
+		},
+	},
+	async play({ canvasElement }) {
+		const canvas = within(canvasElement);
+		await expect(canvasElement).toHaveTextContent(i18n.tsx._dialog.charactersBelow({ current: 0, min: 2 }));
+		const okButton = canvas.getByRole('button', { name: i18n.ts.ok });
+		await expect(okButton).toBeDisabled();
+		const input = canvas.getByRole<HTMLInputElement>('combobox');
+		await waitFor(() => userEvent.hover(input));
+		await waitFor(() => userEvent.click(input));
+		await waitFor(() => userEvent.type(input, 'M'));
+		await expect(canvasElement).toHaveTextContent(i18n.tsx._dialog.charactersBelow({ current: 1, min: 2 }));
+		await waitFor(() => userEvent.type(input, 'i'));
+		await expect(okButton).toBeEnabled();
+	},
+} satisfies StoryObj<typeof MkDialog>;
diff --git a/packages/frontend/src/components/MkDivider.stories.impl.ts b/packages/frontend/src/components/MkDivider.stories.impl.ts
new file mode 100644
index 0000000000..a593111987
--- /dev/null
+++ b/packages/frontend/src/components/MkDivider.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDivider from './MkDivider.vue';
+void MkDivider;
diff --git a/packages/frontend/src/components/MkDonation.stories.impl.ts b/packages/frontend/src/components/MkDonation.stories.impl.ts
new file mode 100644
index 0000000000..27d6b7df6c
--- /dev/null
+++ b/packages/frontend/src/components/MkDonation.stories.impl.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { onBeforeUnmount } from 'vue';
+import MkDonation from './MkDonation.vue';
+import { instance } from '@/instance.js';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkDonation,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						closed: action('closed'),
+					};
+				},
+			},
+			template: '<MkDonation v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+		// @ts-expect-error name is used for mocking instance
+		name: 'Misskey Hub',
+	},
+	decorators: [
+		(_, { args }) => ({
+			setup() {
+				// @ts-expect-error name is used for mocking instance
+				instance.name = args.name;
+				onBeforeUnmount(() => instance.name = null);
+			},
+			template: '<story/>',
+		}),
+	],
+	parameters: {
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkDonation>;
diff --git a/packages/frontend/src/components/MkDrive.file.stories.impl.ts b/packages/frontend/src/components/MkDrive.file.stories.impl.ts
new file mode 100644
index 0000000000..5f6e6a0667
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.file.stories.impl.ts
@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import MkDrive_file from './MkDrive.file.vue';
+import { file } from '../../.storybook/fakes.js';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkDrive_file,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						chosen: action('chosen'),
+						dragstart: action('dragstart'),
+						dragend: action('dragend'),
+					};
+				},
+			},
+			template: '<MkDrive_file v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+		file: file(),
+	},
+	parameters: {
+		chromatic: {
+			// NOTE: ロードが終わるまで待つ
+			delay: 3000,
+		},
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkDrive_file>;
diff --git a/packages/frontend/src/components/MkDrive.folder.stories.impl.ts b/packages/frontend/src/components/MkDrive.folder.stories.impl.ts
new file mode 100644
index 0000000000..5f8ef48520
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.folder.stories.impl.ts
@@ -0,0 +1,70 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { http, HttpResponse } from 'msw';
+import * as Misskey from 'misskey-js';
+import MkDrive_folder from './MkDrive.folder.vue';
+import { folder } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkDrive_folder,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						chosen: action('chosen'),
+						move: action('move'),
+						upload: action('upload'),
+						removeFile: action('removeFile'),
+						removeFolder: action('removeFolder'),
+						dragstart: action('dragstart'),
+						dragend: action('dragend'),
+					};
+				},
+			},
+			template: '<MkDrive_folder v-bind="props" v-on="events" />',
+		};
+	},
+	args: {
+		folder: folder(),
+	},
+	parameters: {
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/drive/folders/delete', async ({ request }) => {
+					action('POST /api/drive/folders/delete')(await request.json());
+					return HttpResponse.json(undefined, { status: 204 });
+				}),
+				http.post('/api/drive/folders/update', async ({ request }) => {
+					const req = await request.json() as Misskey.entities.DriveFoldersUpdateRequest;
+					action('POST /api/drive/folders/update')(req);
+					return HttpResponse.json({
+						...folder(),
+						id: req.folderId,
+						name: req.name ?? folder().name,
+						parentId: req.parentId ?? folder().parentId,
+					});
+				}),
+			],
+		},
+	},
+} satisfies StoryObj<typeof MkDrive_folder>;
diff --git a/packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts b/packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts
new file mode 100644
index 0000000000..9d49f24fa4
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.navFolder.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDrive_navFolder from './MkDrive.navFolder.vue';
+void MkDrive_navFolder;
diff --git a/packages/frontend/src/components/MkDrive.stories.impl.ts b/packages/frontend/src/components/MkDrive.stories.impl.ts
new file mode 100644
index 0000000000..fe20e61415
--- /dev/null
+++ b/packages/frontend/src/components/MkDrive.stories.impl.ts
@@ -0,0 +1,82 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { StoryObj } from '@storybook/vue3';
+import { http, HttpResponse } from 'msw';
+import * as Misskey from 'misskey-js';
+import MkDrive from './MkDrive.vue';
+import { file, folder } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkDrive,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						selected: action('selected'),
+						'change-selection': action('change-selection'),
+						'move-root': action('move-root'),
+						cd: action('cd'),
+						'open-folder': action('open-folder'),
+					};
+				},
+			},
+			template: '<MkDrive v-bind="props" v-on="events" />',
+		};
+	},
+	parameters: {
+		chromatic: {
+			// NOTE: ロードが終わるまで待つ
+			delay: 3000,
+		},
+		layout: 'centered',
+		msw: {
+			handlers: [
+				...commonHandlers,
+				http.post('/api/drive/files', async ({ request }) => {
+					action('POST /api/drive/files')(await request.json());
+					return HttpResponse.json([file()]);
+				}),
+				http.post('/api/drive/folders', async ({ request }) => {
+					action('POST /api/drive/folders')(await request.json());
+					return HttpResponse.json([folder(crypto.randomUUID())]);
+				}),
+				http.post('/api/drive/folders/create', async ({ request }) => {
+					const req = await request.json() as Misskey.entities.DriveFoldersCreateRequest;
+					action('POST /api/drive/folders/create')(req);
+					return HttpResponse.json(folder(crypto.randomUUID(), req.name, req.parentId));
+				}),
+				http.post('/api/drive/folders/delete', async ({ request }) => {
+					action('POST /api/drive/folders/delete')(await request.json());
+					return HttpResponse.json(undefined, { status: 204 });
+				}),
+				http.post('/api/drive/folders/update', async ({ request }) => {
+					const req = await request.json() as Misskey.entities.DriveFoldersUpdateRequest;
+					action('POST /api/drive/folders/update')(req);
+					return HttpResponse.json({
+						...folder(),
+						id: req.folderId,
+						name: req.name ?? folder().name,
+						parentId: req.parentId ?? folder().parentId,
+					});
+				}),
+			]
+		},
+	},
+} satisfies StoryObj<typeof MkDrive>;
diff --git a/packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts b/packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts
new file mode 100644
index 0000000000..3fa24d7edb
--- /dev/null
+++ b/packages/frontend/src/components/MkDriveFileThumbnail.stories.impl.ts
@@ -0,0 +1,41 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { StoryObj } from '@storybook/vue3';
+import MkDriveFileThumbnail from './MkDriveFileThumbnail.vue';
+import { file } from '../../.storybook/fakes.js';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkDriveFileThumbnail,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+			},
+			template: '<MkDriveFileThumbnail v-bind="props" />',
+		};
+	},
+	args: {
+		file: file(),
+		fit: 'contain',
+	},
+	parameters: {
+		chromatic: {
+			// NOTE: ロードが終わるまで待つ
+			delay: 3000,
+		},
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkDriveFileThumbnail>;
diff --git a/packages/frontend/src/components/MkDriveFileThumbnail.vue b/packages/frontend/src/components/MkDriveFileThumbnail.vue
index 706c9a55c7..2c47a70970 100644
--- a/packages/frontend/src/components/MkDriveFileThumbnail.vue
+++ b/packages/frontend/src/components/MkDriveFileThumbnail.vue
@@ -26,7 +26,7 @@ import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
 
 const props = defineProps<{
 	file: Misskey.entities.DriveFile;
-	fit: string;
+	fit: 'cover' | 'contain';
 }>();
 
 const is = computed(() => {
diff --git a/packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts b/packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts
new file mode 100644
index 0000000000..fe8f705165
--- /dev/null
+++ b/packages/frontend/src/components/MkDriveSelectDialog.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDriveSelectDialog from './MkDriveSelectDialog.vue';
+void MkDriveSelectDialog;
diff --git a/packages/frontend/src/components/MkDriveWindow.stories.impl.ts b/packages/frontend/src/components/MkDriveWindow.stories.impl.ts
new file mode 100644
index 0000000000..faa1f7fd5f
--- /dev/null
+++ b/packages/frontend/src/components/MkDriveWindow.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkDriveWindow from './MkDriveWindow.vue';
+void MkDriveWindow;
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts b/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts
new file mode 100644
index 0000000000..69aef577de
--- /dev/null
+++ b/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkEmojiPicker_section from './MkEmojiPicker.section.vue';
+void MkEmojiPicker_section;
diff --git a/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts b/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
new file mode 100644
index 0000000000..d38d8de808
--- /dev/null
+++ b/packages/frontend/src/components/MkEmojiPicker.stories.impl.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { action } from '@storybook/addon-actions';
+import { expect, userEvent, waitFor, within } from '@storybook/test';
+import { StoryObj } from '@storybook/vue3';
+import { i18n } from '@/i18n.js';
+import MkEmojiPicker from './MkEmojiPicker.vue';
+export const Default = {
+	render(args) {
+		return {
+			components: {
+				MkEmojiPicker,
+			},
+			setup() {
+				return {
+					args,
+				};
+			},
+			computed: {
+				props() {
+					return {
+						...this.args,
+					};
+				},
+				events() {
+					return {
+						chosen: action('chosen'),
+					};
+				},
+			},
+			template: '<MkEmojiPicker v-bind="props" v-on="events" />',
+		};
+	},
+	async play({ canvasElement }) {
+		const canvas = within(canvasElement);
+		const faceSection = canvas.getByText(/face/i);
+		await waitFor(() => userEvent.click(faceSection));
+		const grinning = canvasElement.querySelector('[data-emoji="😀"]');
+		await expect(grinning).toBeInTheDocument();
+		if (grinning == null) throw new Error(); // NOTE: not called
+		await waitFor(() => userEvent.click(grinning));
+		const recentUsedSection = canvas.getByText(new RegExp(i18n.ts.recentUsed)).parentElement;
+		await expect(recentUsedSection).toBeInTheDocument();
+		if (recentUsedSection == null) throw new Error(); // NOTE: not called
+		await expect(within(recentUsedSection).getByAltText('😀')).toBeInTheDocument();
+		await expect(within(recentUsedSection).queryByAltText('😬')).toEqual(null);
+	},
+	parameters: {
+		layout: 'centered',
+	},
+} satisfies StoryObj<typeof MkEmojiPicker>;
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts b/packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts
new file mode 100644
index 0000000000..131087ad45
--- /dev/null
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkEmojiPickerDialog from './MkEmojiPickerDialog.vue';
+void MkEmojiPickerDialog;

From 0bb5ac0fca67be31ba00438fa276df433cc7c42d Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 30 Jul 2024 19:55:18 +0900
Subject: [PATCH 194/206] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E4=B8=AD=E3=81=AE=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AB?=
 =?UTF-8?q?=E9=96=A2=E3=81=99=E3=82=8B"TL=E3=81=AB=E4=BB=96=E3=81=AE?=
 =?UTF-8?q?=E4=BA=BA=E3=81=B8=E3=81=AE=E8=BF=94=E4=BF=A1=E3=82=92=E5=90=AB?=
 =?UTF-8?q?=E3=82=81=E3=82=8B"=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=81=8C?=
 =?UTF-8?q?=E5=88=86=E3=81=8B=E3=82=8A=E3=81=A5=E3=82=89=E3=81=84=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13895)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore: improve withReplies toggle for user following

* chore: improve withReplies toggle for list

* docs(changelog): フォロー中のユーザーに関する"TLに他の人への返信を含める"の設定が分かりづらい問題を修正

* Fix CHANGELOG.md

* docs(changelog): update insertion position

---------

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 packages/frontend/src/pages/my-lists/list.vue | 31 ++++++++++---------
 .../frontend/src/scripts/get-user-menu.ts     | 28 +++++++++--------
 3 files changed, 33 insertions(+), 27 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 27308cb8bc..89d5ac214d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -66,6 +66,7 @@
 - Fix: 照会に `#` から始まる文字列を入力してそのハッシュタグのページを表示する際、入力が `#` のみの場合に「指定されたURLに該当するページはありませんでした。」が表示されてしまう問題を修正
 - Fix: 照会に `@` から始まる文字列を入力してユーザーを照会する際、入力が `@` のみの場合に「問題が発生しました」が表示されてしまう問題を修正
 - Fix: 投稿フォームにノートのURLを貼り付けて"引用として添付"した場合、投稿文を空にすることによるRenote化が出来なかった問題を修正
+- Fix: フォロー中のユーザーに関する"TLに他の人への返信を含める"の設定が分かりづらい問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 7492b099ea..a2ceb222fe 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -133,22 +133,25 @@ async function removeUser(item, ev) {
 }
 
 async function showMembershipMenu(item, ev) {
+	const withRepliesRef = ref(item.withReplies);
 	os.popupMenu([{
-		text: item.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline,
-		icon: item.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
-		action: async () => {
-			misskeyApi('users/lists/update-membership', {
-				listId: list.value.id,
-				userId: item.userId,
-				withReplies: !item.withReplies,
-			}).then(() => {
-				paginationEl.value.updateItem(item.id, (old) => ({
-					...old,
-					withReplies: !item.withReplies,
-				}));
-			});
-		},
+		type: 'switch',
+		text: i18n.ts.showRepliesToOthersInTimeline,
+		icon: 'ti ti-messages',
+		ref: withRepliesRef,
 	}], ev.currentTarget ?? ev.target);
+	watch(withRepliesRef, withReplies => {
+		misskeyApi('users/lists/update-membership', {
+			listId: list.value!.id,
+			userId: item.userId,
+			withReplies,
+		}).then(() => {
+			paginationEl.value!.updateItem(item.id, (old) => ({
+				...old,
+				withReplies,
+			}));
+		});
+	});
 }
 
 async function deleteList() {
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index e9e7ae771d..33f16a68aa 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -17,6 +17,7 @@ import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-pe
 import { IRouter } from '@/nirax.js';
 import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
 import { mainRouter } from '@/router/main.js';
+import { MenuItem } from '@/types/menu.js';
 
 export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) {
 	const meId = $i ? $i.id : null;
@@ -82,15 +83,6 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 		});
 	}
 
-	async function toggleWithReplies() {
-		os.apiWithDialog('following/update', {
-			userId: user.id,
-			withReplies: !user.withReplies,
-		}).then(() => {
-			user.withReplies = !user.withReplies;
-		});
-	}
-
 	async function toggleNotify() {
 		os.apiWithDialog('following/update', {
 			userId: user.id,
@@ -155,7 +147,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 		});
 	}
 
-	let menu = [{
+	let menu: MenuItem[] = [{
 		icon: 'ti ti-at',
 		text: i18n.ts.copyUsername,
 		action: () => {
@@ -314,15 +306,25 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter
 
 		// フォローしたとしても user.isFollowing はリアルタイム更新されないので不便なため
 		//if (user.isFollowing) {
+		const withRepliesRef = ref(user.withReplies);
 		menu = menu.concat([{
-			icon: user.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
-			text: user.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline,
-			action: toggleWithReplies,
+			type: 'switch',
+			icon: 'ti ti-messages',
+			text: i18n.ts.showRepliesToOthersInTimeline,
+			ref: withRepliesRef,
 		}, {
 			icon: user.notify === 'none' ? 'ti ti-bell' : 'ti ti-bell-off',
 			text: user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes,
 			action: toggleNotify,
 		}]);
+		watch(withRepliesRef, (withReplies) => {
+			misskeyApi('following/update', {
+				userId: user.id,
+				withReplies,
+			}).then(() => {
+				user.withReplies = withReplies;
+			});
+		});
 		//}
 
 		menu = menu.concat([{ type: 'divider' }, {

From fccc5b6d620ca6893a0a5649ea9f0d0693727b3b Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 30 Jul 2024 20:13:00 +0900
Subject: [PATCH 195/206] frontend timeline fixes & improvements (#13727)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: withRepliesがオフのときにwithFilesのとぐるをいじれない問題

* fix: type errors in tl-column

* fix: deck uiでタイムラインを切り替えた際にTLの設定項目が更新されない

* refactor: タイムラインの各種知識を一つのファイルに統合

fix: ウィジェットのタイムライン選択欄に表示できないタイムラインが表示される

* docs(changelog): timeline improvements

* fix: missing license header

* chore: timeline > basic timeline

* use BasicTimelineType in deck-store

* Update CHANGELOG.md

---------

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
---
 CHANGELOG.md                                  |  3 +
 .../frontend/src/components/MkTimeline.vue    |  3 +-
 .../components/MkTutorialDialog.Timeline.vue  |  9 +--
 packages/frontend/src/pages/timeline.vue      | 73 ++++++-------------
 packages/frontend/src/timelines.ts            | 56 ++++++++++++++
 packages/frontend/src/ui/deck/deck-store.ts   |  3 +-
 packages/frontend/src/ui/deck/tl-column.vue   | 30 +++-----
 .../frontend/src/widgets/WidgetTimeline.vue   | 34 ++-------
 8 files changed, 108 insertions(+), 103 deletions(-)
 create mode 100644 packages/frontend/src/timelines.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89d5ac214d..08e75ea200 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,6 +67,9 @@
 - Fix: 照会に `@` から始まる文字列を入力してユーザーを照会する際、入力が `@` のみの場合に「問題が発生しました」が表示されてしまう問題を修正
 - Fix: 投稿フォームにノートのURLを貼り付けて"引用として添付"した場合、投稿文を空にすることによるRenote化が出来なかった問題を修正
 - Fix: フォロー中のユーザーに関する"TLに他の人への返信を含める"の設定が分かりづらい問題を修正
+- Fix: タイムラインページを開いた時、`TLに他の人への返信を含める`がオフのときに`ファイル付きのみ`をオンにできない問題を修正
+- Fix: deck uiでタイムラインを切り替えた際にTLの設定項目が更新されず、`TLに他の人への返信を含める`のトグルが表示されない問題を修正
+- Fix: ウィジェットのタイムライン選択欄に無効化されたタイムラインが表示される問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 03dccb18e9..ca87316bf7 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -19,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
+import type { BasicTimelineType } from '@/timelines.js';
 import MkNotes from '@/components/MkNotes.vue';
 import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
 import { useStream } from '@/stream.js';
@@ -29,7 +30,7 @@ import { defaultStore } from '@/store.js';
 import { Paging } from '@/components/MkPagination.vue';
 
 const props = withDefaults(defineProps<{
-	src: 'home' | 'local' | 'social' | 'global' | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role';
+	src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role';
 	list?: string;
 	antenna?: string;
 	channel?: string;
diff --git a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
index 6f2930ebc9..2d4da3fbd4 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
@@ -7,10 +7,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div class="_gaps">
 	<div style="text-align: center; padding: 0 16px;">{{ i18n.ts._initialTutorial._timeline.description1 }}</div>
 	<div class="_gaps_s">
-		<div><i class="ti ti-home"></i> <b>{{ i18n.ts._timelines.home }}</b> … {{ i18n.ts._initialTutorial._timeline.home }}</div>
-		<div><i class="ti ti-planet"></i> <b>{{ i18n.ts._timelines.local }}</b> … {{ i18n.ts._initialTutorial._timeline.local }}</div>
-		<div><i class="ti ti-universe"></i> <b>{{ i18n.ts._timelines.social }}</b> … {{ i18n.ts._initialTutorial._timeline.social }}</div>
-		<div><i class="ti ti-whirl"></i> <b>{{ i18n.ts._timelines.global }}</b> … {{ i18n.ts._initialTutorial._timeline.global }}</div>
+		<div v-for="tl in basicTimelineTypes">
+			<i :class="basicTimelineIconClass(tl)"></i> <b>{{ i18n.ts._timelines[tl] }}</b> … {{ i18n.ts._initialTutorial._timeline[tl] }}
+		</div>
 	</div>
 	<div class="_gaps_s">
 		<div>{{ i18n.ts._initialTutorial._timeline.description2 }}</div>
@@ -22,12 +21,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<a href="https://misskey-hub.net/docs/for-users/features/timeline/" target="_blank" class="_link">{{ i18n.ts.help }}</a>
 		</template>
 	</I18n>
-
 </div>
 </template>
 
 <script setup lang="ts">
 import { i18n } from '@/i18n.js';
+import { basicTimelineIconClass, basicTimelineTypes } from '@/timelines.js';
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 813cc326d0..c5905c7ada 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkSpacer :contentMax="800">
 		<MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin">
 			<div :key="src" ref="rootEl">
-				<MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()">
+				<MkInfo v-if="isBasicTimeline(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()">
 					{{ i18n.ts._timelineDescription[src] }}
 				</MkInfo>
 				<MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/>
@@ -45,7 +45,6 @@ import * as os from '@/os.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
-import { instance } from '@/instance.js';
 import { $i } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { antennasCache, userListsCache, favoritedChannelsCache } from '@/cache.js';
@@ -53,17 +52,15 @@ import { deviceKind } from '@/scripts/device-kind.js';
 import { deepMerge } from '@/scripts/merge.js';
 import { MenuItem } from '@/types/menu.js';
 import { miLocalStorage } from '@/local-storage.js';
+import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js';
 
 provide('shouldOmitHeaderTitle', true);
 
-const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable);
-const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable);
-
 const tlComponent = shallowRef<InstanceType<typeof MkTimeline>>();
 const rootEl = shallowRef<HTMLElement>();
 
 const queue = ref(0);
-const srcWhenNotSignin = ref<'local' | 'global'>(isLocalTimelineAvailable ? 'local' : 'global');
+const srcWhenNotSignin = ref<'local' | 'global'>(isAvailableBasicTimeline('local') ? 'local' : 'global');
 const src = computed<'home' | 'local' | 'social' | 'global' | `list:${string}`>({
 	get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin.value),
 	set: (x) => saveSrc(x),
@@ -74,7 +71,11 @@ const withRenotes = computed<boolean>({
 });
 
 // computed内での無限ループを防ぐためのフラグ
-const localSocialTLFilterSwitchStore = ref<'withReplies' | 'onlyFiles' | false>('withReplies');
+const localSocialTLFilterSwitchStore = ref<'withReplies' | 'onlyFiles' | false>(
+	defaultStore.reactiveState.tl.value.filter.withReplies ? 'withReplies' :
+	defaultStore.reactiveState.tl.value.filter.onlyFiles ? 'onlyFiles' :
+	false,
+);
 
 const withReplies = computed<boolean>({
 	get: () => {
@@ -229,7 +230,7 @@ function focus(): void {
 }
 
 function closeTutorial(): void {
-	if (!['home', 'local', 'social', 'global'].includes(src.value)) return;
+	if (!isBasicTimeline(src.value)) return;
 	const before = defaultStore.state.timelineTutorials;
 	before[src.value] = true;
 	defaultStore.set('timelineTutorials', before);
@@ -245,7 +246,7 @@ const headerActions = computed(() => {
 					type: 'switch',
 					text: i18n.ts.showRenotes,
 					ref: withRenotes,
-				}, src.value === 'local' || src.value === 'social' ? {
+				}, isBasicTimeline(src.value) && hasWithReplies(src.value) ? {
 					type: 'switch',
 					text: i18n.ts.showRepliesToOthersInTimeline,
 					ref: withReplies,
@@ -258,7 +259,7 @@ const headerActions = computed(() => {
 					type: 'switch',
 					text: i18n.ts.fileAttachedOnly,
 					ref: onlyFiles,
-					disabled: src.value === 'local' || src.value === 'social' ? withReplies : false,
+					disabled: isBasicTimeline(src.value) && hasWithReplies(src.value) ? withReplies : false,
 				}], ev.currentTarget ?? ev.target);
 			},
 		},
@@ -280,32 +281,12 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList
 	title: l.name,
 	icon: 'ti ti-star',
 	iconOnly: true,
-}))), {
-	key: 'home',
-	title: i18n.ts._timelines.home,
-	icon: 'ti ti-home',
+}))), ...availableBasicTimelines().map(tl => ({
+	key: tl,
+	title: i18n.ts._timelines[tl],
+	icon: basicTimelineIconClass(tl),
 	iconOnly: true,
-}, ...(isLocalTimelineAvailable ? [{
-	key: 'local',
-	title: i18n.ts._timelines.local,
-	icon: 'ti ti-planet',
-	iconOnly: true,
-}, {
-	key: 'social',
-	title: i18n.ts._timelines.social,
-	icon: 'ti ti-universe',
-	iconOnly: true,
-}] : []), ...(isGlobalTimelineAvailable ? [{
-	key: 'global',
-	title: i18n.ts._timelines.global,
-	icon: 'ti ti-whirl',
-	iconOnly: true,
-}] : []), {
-	icon: 'ti ti-list',
-	title: i18n.ts.lists,
-	iconOnly: true,
-	onClick: chooseList,
-}, {
+})), {
 	icon: 'ti ti-antenna',
 	title: i18n.ts.antennas,
 	iconOnly: true,
@@ -317,24 +298,16 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList
 	onClick: chooseChannel,
 }] as Tab[]);
 
-const headerTabsWhenNotLogin = computed(() => [
-	...(isLocalTimelineAvailable ? [{
-		key: 'local',
-		title: i18n.ts._timelines.local,
-		icon: 'ti ti-planet',
-		iconOnly: true,
-	}] : []),
-	...(isGlobalTimelineAvailable ? [{
-		key: 'global',
-		title: i18n.ts._timelines.global,
-		icon: 'ti ti-whirl',
-		iconOnly: true,
-	}] : []),
-] as Tab[]);
+const headerTabsWhenNotLogin = computed(() => [...availableBasicTimelines().map(tl => ({
+	key: tl,
+	title: i18n.ts._timelines[tl],
+	icon: basicTimelineIconClass(tl),
+	iconOnly: true,
+}))] as Tab[]);
 
 definePageMetadata(() => ({
 	title: i18n.ts.timeline,
-	icon: src.value === 'local' ? 'ti ti-planet' : src.value === 'social' ? 'ti ti-universe' : src.value === 'global' ? 'ti ti-whirl' : 'ti ti-home',
+	icon: isBasicTimeline(src.value) ? basicTimelineIconClass(src.value) : 'ti ti-home',
 }));
 </script>
 
diff --git a/packages/frontend/src/timelines.ts b/packages/frontend/src/timelines.ts
new file mode 100644
index 0000000000..3ef95fd272
--- /dev/null
+++ b/packages/frontend/src/timelines.ts
@@ -0,0 +1,56 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { $i } from '@/account.js';
+import { instance } from '@/instance.js';
+
+export const basicTimelineTypes = [
+	'home',
+	'local',
+	'social',
+	'global',
+] as const;
+
+export type BasicTimelineType = typeof basicTimelineTypes[number];
+
+export function isBasicTimeline(timeline: string): timeline is BasicTimelineType {
+	return basicTimelineTypes.includes(timeline as BasicTimelineType);
+}
+
+export function basicTimelineIconClass(timeline: BasicTimelineType): string {
+	switch (timeline) {
+		case 'home':
+			return 'ti ti-home';
+		case 'local':
+			return 'ti ti-planet';
+		case 'social':
+			return 'ti ti-universe';
+		case 'global':
+			return 'ti ti-whirl';
+	}
+}
+
+export function isAvailableBasicTimeline(timeline: BasicTimelineType | undefined | null): boolean {
+	switch (timeline) {
+		case 'home':
+			return $i != null;
+		case 'local':
+			return ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable);
+		case 'social':
+			return $i != null && instance.policies.ltlAvailable;
+		case 'global':
+			return ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable);
+		default:
+			return false;
+	}
+}
+
+export function availableBasicTimelines(): BasicTimelineType[] {
+	return basicTimelineTypes.filter(isAvailableBasicTimeline);
+}
+
+export function hasWithReplies(timeline: BasicTimelineType | undefined | null): boolean {
+	return timeline === 'local' || timeline === 'social';
+}
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 139621cf57..eb587554b9 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -6,6 +6,7 @@
 import { throttle } from 'throttle-debounce';
 import { markRaw } from 'vue';
 import { notificationTypes } from 'misskey-js';
+import type { BasicTimelineType } from '@/timelines.js';
 import { Storage } from '@/pizzax.js';
 import { misskeyApi } from '@/scripts/misskey-api.js';
 import { deepClone } from '@/scripts/clone.js';
@@ -45,7 +46,7 @@ export type Column = {
 	channelId?: string;
 	roleId?: string;
 	excludeTypes?: typeof notificationTypes[number][];
-	tl?: 'home' | 'local' | 'social' | 'global';
+	tl?: BasicTimelineType;
 	withRenotes?: boolean;
 	withReplies?: boolean;
 	onlyFiles?: boolean;
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index b4bc8bb748..e210ee7b7a 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -6,14 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
 	<template #header>
-		<i v-if="column.tl === 'home'" class="ti ti-home"></i>
-		<i v-else-if="column.tl === 'local'" class="ti ti-planet"></i>
-		<i v-else-if="column.tl === 'social'" class="ti ti-universe"></i>
-		<i v-else-if="column.tl === 'global'" class="ti ti-whirl"></i>
+		<i v-if="column.tl != null" :class="basicTimelineIconClass(column.tl)"/>
 		<span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
 
-	<div v-if="(((column.tl === 'local' || column.tl === 'social') && !isLocalTimelineAvailable) || (column.tl === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
+	<div v-if="!isAvailableBasicTimeline(column.tl)" :class="$style.disabled">
 		<p :class="$style.disabledTitle">
 			<i class="ti ti-circle-minus"></i>
 			{{ i18n.ts._disabledTimeline.title }}
@@ -34,15 +31,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, watch, ref, shallowRef } from 'vue';
+import { onMounted, watch, ref, shallowRef, computed } from 'vue';
 import XColumn from './column.vue';
 import { removeColumn, updateColumn, Column } from './deck-store.js';
+import type { MenuItem } from '@/types/menu.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
-import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
+import { hasWithReplies, isAvailableBasicTimeline, basicTimelineIconClass } from '@/timelines.js';
 import { instance } from '@/instance.js';
-import { MenuItem } from '@/types/menu.js';
 import { SoundStore } from '@/store.js';
 import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
 import * as sound from '@/scripts/sound.js';
@@ -52,11 +49,8 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-const disabled = ref(false);
 const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
 
-const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
-const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
 const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
 const withRenotes = ref(props.column.withRenotes ?? true);
 const withReplies = ref(props.column.withReplies ?? false);
@@ -87,10 +81,6 @@ watch(soundSetting, v => {
 onMounted(() => {
 	if (props.column.tl == null) {
 		setType();
-	} else if ($i) {
-		disabled.value = (
-			(!((instance.policies.ltlAvailable) || ($i.policies.ltlAvailable)) && ['local', 'social'].includes(props.column.tl)) ||
-			(!((instance.policies.gtlAvailable) || ($i.policies.gtlAvailable)) && ['global'].includes(props.column.tl)));
 	}
 });
 
@@ -115,7 +105,7 @@ async function setType() {
 	}
 	if (src == null) return;
 	updateColumn(props.column.id, {
-		tl: src,
+		tl: src ?? undefined,
 	});
 }
 
@@ -123,7 +113,7 @@ function onNote() {
 	sound.playMisskeySfxFile(soundSetting.value);
 }
 
-const menu: MenuItem[] = [{
+const menu = computed<MenuItem[]>(() => [{
 	icon: 'ti ti-pencil',
 	text: i18n.ts.timeline,
 	action: setType,
@@ -135,7 +125,7 @@ const menu: MenuItem[] = [{
 	type: 'switch',
 	text: i18n.ts.showRenotes,
 	ref: withRenotes,
-}, props.column.tl === 'local' || props.column.tl === 'social' ? {
+}, hasWithReplies(props.column.tl) ? {
 	type: 'switch',
 	text: i18n.ts.showRepliesToOthersInTimeline,
 	ref: withReplies,
@@ -144,8 +134,8 @@ const menu: MenuItem[] = [{
 	type: 'switch',
 	text: i18n.ts.fileAttachedOnly,
 	ref: onlyFiles,
-	disabled: props.column.tl === 'local' || props.column.tl === 'social' ? withReplies : false,
-}];
+	disabled: hasWithReplies(props.column.tl) ? withReplies : false,
+}]);
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index 150e838582..d02f9b8e22 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -6,10 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <MkContainer :showHeader="widgetProps.showHeader" :style="`height: ${widgetProps.height}px;`" :scrollable="true" data-cy-mkw-timeline class="mkw-timeline">
 	<template #icon>
-		<i v-if="widgetProps.src === 'home'" class="ti ti-home"></i>
-		<i v-else-if="widgetProps.src === 'local'" class="ti ti-planet"></i>
-		<i v-else-if="widgetProps.src === 'social'" class="ti ti-universe"></i>
-		<i v-else-if="widgetProps.src === 'global'" class="ti ti-whirl"></i>
+		<i v-if="isBasicTimeline(widgetProps.src)" :class="basicTimelineIconClass(widgetProps.src)"></i>
 		<i v-else-if="widgetProps.src === 'list'" class="ti ti-list"></i>
 		<i v-else-if="widgetProps.src === 'antenna'" class="ti ti-antenna"></i>
 	</template>
@@ -20,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</button>
 	</template>
 
-	<div v-if="(((widgetProps.src === 'local' || widgetProps.src === 'social') && !isLocalTimelineAvailable) || (widgetProps.src === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
+	<div v-if="isBasicTimeline(widgetProps.src) && !isAvailableBasicTimeline(widgetProps.src)" :class="$style.disabled">
 		<p :class="$style.disabledTitle">
 			<i class="ti ti-minus"></i>
 			{{ i18n.ts._disabledTimeline.title }}
@@ -42,12 +39,9 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
-import { instance } from '@/instance.js';
+import { availableBasicTimelines, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js';
 
 const name = 'timeline';
-const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
-const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
 
 const widgetPropsDef = {
 	showHeader: {
@@ -115,23 +109,11 @@ const choose = async (ev) => {
 			setSrc('list');
 		},
 	}));
-	os.popupMenu([{
-		text: i18n.ts._timelines.home,
-		icon: 'ti ti-home',
-		action: () => { setSrc('home'); },
-	}, {
-		text: i18n.ts._timelines.local,
-		icon: 'ti ti-planet',
-		action: () => { setSrc('local'); },
-	}, {
-		text: i18n.ts._timelines.social,
-		icon: 'ti ti-universe',
-		action: () => { setSrc('social'); },
-	}, {
-		text: i18n.ts._timelines.global,
-		icon: 'ti ti-whirl',
-		action: () => { setSrc('global'); },
-	}, antennaItems.length > 0 ? { type: 'divider' } : undefined, ...antennaItems, listItems.length > 0 ? { type: 'divider' } : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => {
+	os.popupMenu([...availableBasicTimelines().map(tl => ({
+		text: i18n.ts._timelines[tl],
+		icon: basicTimelineIconClass(tl),
+		action: () => { setSrc(tl); },
+	})), antennaItems.length > 0 ? { type: 'divider' } : undefined, ...antennaItems, listItems.length > 0 ? { type: 'divider' } : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => {
 		menuOpened.value = false;
 	});
 };

From 676c599e48eb48b36f67ac0a44e29f26501d1e69 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 20:20:21 +0900
Subject: [PATCH 196/206] Update about-misskey.vue

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

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 8459f0f9d5..8db71c8881 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -258,6 +258,12 @@ const patronsWithIcon = [{
 }, {
 	name: 'えとゔぁす',
 	icon: 'https://assets.misskey-hub.net/patrons/2578f441b82a44cfaa55ba83a318b26e.jpg',
+}, {
+	name: 'Soli',
+	icon: 'https://assets.misskey-hub.net/patrons/448070c81ebd41eda4ea2328291b2efe.jpg',
+}, {
+	name: 'ささくれりょう',
+	icon: 'https://assets.misskey-hub.net/patrons/cf55022cee6c41da8e70a43587aaad9a.jpg',
 }];
 
 const patrons = [

From 8b163cd3fbae0cac43c79807ef891532de740797 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 30 Jul 2024 20:30:41 +0900
Subject: [PATCH 197/206] =?UTF-8?q?fix(frontend):=20=E3=83=89=E3=83=A9?=
 =?UTF-8?q?=E3=82=A4=E3=83=96=E3=81=AE=E9=9F=B3=E5=A3=B0=E3=81=8C=E5=86=8D?=
 =?UTF-8?q?=E7=94=9F=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88?=
 =?UTF-8?q?=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1407?=
 =?UTF-8?q?3)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): ドライブの音声が再生できない場合の処理を追加

* Update Changelog

* fix lint

* Update packages/frontend/src/scripts/sound.ts

* lint

* Update sound.ts

* fix merge mistakes

* use shorthand operator

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  4 ++
 locales/ja-JP.yml                             |  1 +
 .../src/pages/settings/sounds.sound.vue       | 44 ++++++++++++++++---
 .../frontend/src/pages/settings/sounds.vue    | 10 ++++-
 packages/frontend/src/scripts/sound.ts        | 35 ++++++++++-----
 6 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08e75ea200..b996216ac1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -70,6 +70,7 @@
 - Fix: タイムラインページを開いた時、`TLに他の人への返信を含める`がオフのときに`ファイル付きのみ`をオンにできない問題を修正
 - Fix: deck uiでタイムラインを切り替えた際にTLの設定項目が更新されず、`TLに他の人への返信を含める`のトグルが表示されない問題を修正
 - Fix: ウィジェットのタイムライン選択欄に無効化されたタイムラインが表示される問題を修正
+- Fix: サウンドにドライブの音声を使用している際にドライブの音声が再生できなくなると設定が変更できなくなる問題を修正
 
 ### Server
 - Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949)
diff --git a/locales/index.d.ts b/locales/index.d.ts
index f03207e0bd..c58e4ace7b 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -7633,6 +7633,10 @@ export interface Locale extends ILocale {
          * 長い音声を使用するとMisskeyの使用に支障をきたす可能性があります。それでも続行しますか?
          */
         "driveFileDurationWarnDescription": string;
+        /**
+         * 音声が読み込めませんでした。設定を変更してください
+         */
+        "driveFileError": string;
     };
     "_ago": {
         /**
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index d4931ae90d..522ad7e22a 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2002,6 +2002,7 @@ _soundSettings:
   driveFileTypeWarnDescription: "音声ファイルを選択してください"
   driveFileDurationWarn: "音声が長すぎます"
   driveFileDurationWarnDescription: "長い音声を使用するとMisskeyの使用に支障をきたす可能性があります。それでも続行しますか?"
+  driveFileError: "音声が読み込めませんでした。設定を変更してください"
 
 _ago:
   future: "未来"
diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue
index 113abd708b..81478fede5 100644
--- a/packages/frontend/src/pages/settings/sounds.sound.vue
+++ b/packages/frontend/src/pages/settings/sounds.sound.vue
@@ -9,7 +9,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #label>{{ i18n.ts.sound }}</template>
 		<option v-for="x in soundsTypes" :key="x ?? 'null'" :value="x">{{ getSoundTypeName(x) }}</option>
 	</MkSelect>
-	<div v-if="type === '_driveFile_'" :class="$style.fileSelectorRoot">
+	<div v-if="type === '_driveFile_' && driveFileError === true" :class="$style.fileSelectorRoot">
+		<MkButton :class="$style.fileSelectorButton" inline rounded primary @click="selectSound">{{ i18n.ts.selectFile }}</MkButton>
+		<div :class="$style.fileErrorRoot">
+			<MkCondensedLine>{{ i18n.ts._soundSettings.driveFileError }}</MkCondensedLine>
+		</div>
+	</div>
+	<div v-else-if="type === '_driveFile_'" :class="$style.fileSelectorRoot">
 		<MkButton :class="$style.fileSelectorButton" inline rounded primary @click="selectSound">{{ i18n.ts.selectFile }}</MkButton>
 		<div :class="['_nowrap', !fileUrl && $style.fileNotSelected]">{{ friendlyFileName }}</div>
 	</div>
@@ -19,13 +25,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<div class="_buttons">
 		<MkButton inline @click="listen"><i class="ti ti-player-play"></i> {{ i18n.ts.listen }}</MkButton>
-		<MkButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
+		<MkButton inline primary :disabled="!hasChanged || driveFileError" @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
 	</div>
 </div>
 </template>
 
 <script lang="ts" setup>
-import { ref, computed } from 'vue';
+import { ref, computed, watch } from 'vue';
 import type { SoundType } from '@/scripts/sound.js';
 import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -51,13 +57,18 @@ const type = ref<SoundType>(props.type);
 const fileId = ref(props.fileId);
 const fileUrl = ref(props.fileUrl);
 const fileName = ref<string>('');
+const driveFileError = ref(false);
+const hasChanged = ref(false);
 const volume = ref(props.volume);
 
 if (type.value === '_driveFile_' && fileId.value) {
-	const apiRes = await misskeyApi('drive/files/show', {
+	await misskeyApi('drive/files/show', {
 		fileId: fileId.value,
+	}).then((res) => {
+		fileName.value = res.name;
+	}).catch((res) => {
+		driveFileError.value = true;
 	});
-	fileName.value = apiRes.name;
 }
 
 function getSoundTypeName(f: SoundType): string {
@@ -107,9 +118,21 @@ function selectSound(ev) {
 		fileUrl.value = file.url;
 		fileName.value = file.name;
 		fileId.value = file.id;
+		driveFileError.value = false;
+		hasChanged.value = true;
 	});
 }
 
+watch([type, volume], ([typeTo, volumeTo], [typeFrom, volumeFrom]) => {
+	if (typeFrom !== typeTo && typeTo !== '_driveFile_') {
+		fileUrl.value = undefined;
+		fileName.value = '';
+		fileId.value = undefined;
+		driveFileError.value = false;
+	}
+	hasChanged.value = true;
+});
+
 function listen() {
 	if (type.value === '_driveFile_' && (!fileUrl.value || !fileId.value)) {
 		os.alert({
@@ -131,6 +154,10 @@ function listen() {
 }
 
 function save() {
+	if (hasChanged.value === false || driveFileError.value === true) {
+		return;
+	}
+
 	if (type.value === '_driveFile_' && !fileUrl.value) {
 		os.alert({
 			type: 'warning',
@@ -163,6 +190,13 @@ function save() {
 	gap: 8px;
 }
 
+.fileErrorRoot {
+	flex-grow: 1;
+	min-width: 0;
+	font-weight: 700;
+	color: var(--error);
+}
+
 .fileSelectorButton {
 	flex-shrink: 0;
 }
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 0f1b725fae..9fcf564e55 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -21,8 +21,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkFolder v-for="type in operationTypes" :key="type">
 				<template #label>{{ i18n.ts._sfx[type] }}</template>
 				<template #suffix>{{ getSoundTypeName(sounds[type].type) }}</template>
-
-				<XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/>
+				<Suspense>
+					<template #default>
+						<XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/>
+					</template>
+					<template #fallback>
+						<MkLoading/>
+					</template>
+				</Suspense>
 			</MkFolder>
 		</div>
 	</FormSection>
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 814e080811..05f82fce7d 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -124,23 +124,33 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
  */
 export function playMisskeySfx(operationType: OperationType) {
 	const sound = defaultStore.state[`sound_${operationType}`];
-	playMisskeySfxFile(sound);
+	playMisskeySfxFile(sound).then((succeed) => {
+		if (!succeed && sound.type === '_driveFile_') {
+			// ドライブファイルが存在しない場合はデフォルトのサウンドを再生する
+			const soundName = defaultStore.def[`sound_${operationType}`].default.type as Exclude<SoundType, '_driveFile_'>;
+			if (_DEV_) console.log(`Failed to play sound: ${sound.fileUrl}, so play default sound: ${soundName}`);
+			playMisskeySfxFileInternal({
+				type: soundName,
+				volume: sound.volume,
+			});
+		}
+	});
 }
 
 /**
  * サウンド設定形式で指定された音声を再生する
  * @param soundStore サウンド設定
  */
-export function playMisskeySfxFile(soundStore: SoundStore) {
+export async function playMisskeySfxFile(soundStore: SoundStore): Promise<boolean> {
 	// 連続して再生しない
-	if (!canPlay) return;
+	if (!canPlay) return false;
 	// ユーザーアクティベーションが必要な場合はそれがない場合は再生しない
-	if ('userActivation' in navigator && !navigator.userActivation.hasBeenActive) return;
+	if ('userActivation' in navigator && !navigator.userActivation.hasBeenActive) return false;
 	// サウンドがない場合は再生しない
-	if (soundStore.type === null || soundStore.type === '_driveFile_' && !soundStore.fileUrl) return;
+	if (soundStore.type === null || soundStore.type === '_driveFile_' && !soundStore.fileUrl) return false;
 
 	canPlay = false;
-	playMisskeySfxFileInternal(soundStore).finally(() => {
+	return await playMisskeySfxFileInternal(soundStore).finally(() => {
 		// ごく短時間に音が重複しないように
 		setTimeout(() => {
 			canPlay = true;
@@ -148,19 +158,22 @@ export function playMisskeySfxFile(soundStore: SoundStore) {
 	});
 }
 
-async function playMisskeySfxFileInternal(soundStore: SoundStore) {
+async function playMisskeySfxFileInternal(soundStore: SoundStore): Promise<boolean> {
 	if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
-		return;
+		return false;
 	}
 	const masterVolume = defaultStore.state.sound_masterVolume;
 	if (isMute() || masterVolume === 0 || soundStore.volume === 0) {
-		return;
+		return true; // ミュート時は成功として扱う
 	}
 	const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`;
-	const buffer = await loadAudio(url);
-	if (!buffer) return;
+	const buffer = await loadAudio(url).catch(() => {
+		return undefined;
+	});
+	if (!buffer) return false;
 	const volume = soundStore.volume * masterVolume;
 	createSourceNode(buffer, { volume }).soundSource.start();
+	return true;
 }
 
 export async function playUrl(url: string, opts: {

From 400ae6ef0107881c241f928b551d116a1292f4a5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 30 Jul 2024 11:35:33 +0000
Subject: [PATCH 198/206] Bump version to 2024.7.0-rc.8

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

diff --git a/package.json b/package.json
index 7846e5cb51..42751be196 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-rc.7",
+	"version": "2024.7.0-rc.8",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 8b9b0c6b44..45f42e1846 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-rc.7",
+	"version": "2024.7.0-rc.8",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",

From 63f9c271ca9958abb00d879be01a045d25d261d1 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Tue, 30 Jul 2024 20:58:25 +0900
Subject: [PATCH 199/206] :art:

---
 .../src/components/MkSystemWebhookEditor.vue  |  5 ++-
 .../notification-recipient.editor.vue         | 43 +++++++++++--------
 .../frontend/src/pages/emoji-edit-dialog.vue  |  5 ++-
 3 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index a141b4ff74..cad2e99e4d 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -19,8 +19,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		{{ mode === 'create' ? i18n.ts._webhookSettings.createWebhook : i18n.ts._webhookSettings.modifyWebhook }}
 	</template>
 
-	<div>
-		<MkSpacer :marginMin="20" :marginMax="28">
+	<div style="display: flex; flex-direction: column; min-height: 100%;">
+		<MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
 			<MkLoading v-if="loading !== 0"/>
 			<div v-else :class="$style.root" class="_gaps_m">
 				<MkInput v-model="title">
@@ -226,6 +226,7 @@ onMounted(async () => {
 
 .footer {
 	position: sticky;
+	z-index: 10000;
 	bottom: 0;
 	left: 0;
 	padding: 12px;
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
index 015109f54e..827e22e8ae 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
@@ -16,8 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #header>
 		{{ mode === 'create' ? i18n.ts._abuseReport._notificationRecipient.createRecipient : i18n.ts._abuseReport._notificationRecipient.modifyRecipient }}
 	</template>
-	<div v-if="loading === 0">
-		<MkSpacer :marginMin="20" :marginMax="28">
+	<div v-if="loading === 0" style="display: flex; flex-direction: column; min-height: 100%;">
+		<MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
 			<div :class="$style.root" class="_gaps_m">
 				<MkInput v-model="title">
 					<template #label>{{ i18n.ts.title }}</template>
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 								{{ webhook.name }}
 							</option>
 						</MkSelect>
-						<MkButton rounded @click="onEditSystemWebhookClicked">
+						<MkButton rounded :class="$style.systemWebhookEditButton" @click="onEditSystemWebhookClicked">
 							<span v-if="systemWebhookId === null" class="ti ti-plus" style="line-height: normal"/>
 							<span v-else class="ti ti-settings" style="line-height: normal"/>
 						</MkButton>
@@ -60,8 +60,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</MkSpacer>
 
 		<div :class="$style.footer" class="_buttonsCenter">
-			<MkButton primary :disabled="disableSubmitButton" @click="onSubmitClicked"><i class="ti ti-check"></i> {{ i18n.ts.ok }}</MkButton>
-			<MkButton @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
+			<MkButton primary rounded :disabled="disableSubmitButton" @click="onSubmitClicked"><i class="ti ti-check"></i> {{ i18n.ts.ok }}</MkButton>
+			<MkButton rounded @click="onCancelClicked"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
 		</div>
 	</div>
 	<div v-else>
@@ -289,10 +289,15 @@ onMounted(async () => {
 }
 
 .footer {
-	display: flex;
-	justify-content: center;
-	align-items: flex-end;
-	margin-top: 20px;
+	position: sticky;
+	z-index: 10000;
+	bottom: 0;
+	left: 0;
+	padding: 12px;
+	border-top: solid 0.5px var(--divider);
+	background: var(--acrylicBg);
+	-webkit-backdrop-filter: var(--blur, blur(15px));
+	backdrop-filter: var(--blur, blur(15px));
 }
 
 .systemWebhook {
@@ -301,16 +306,16 @@ onMounted(async () => {
 	justify-content: stretch;
 	align-items: flex-end;
 	gap: 8px;
+}
 
-	button {
-		min-width: 0;
-		min-height: 0;
-		width: 34px;
-		height: 34px;
-		flex-shrink: 0;
-		box-sizing: border-box;
-		margin: 1px 0;
-		padding: 6px;
-	}
+.systemWebhookEditButton {
+	min-width: 0;
+	min-height: 0;
+	width: 34px;
+	height: 34px;
+	flex-shrink: 0;
+	box-sizing: border-box;
+	margin: 1px 0;
+	padding: 6px;
 }
 </style>
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index d0a6bba47b..853c1d6b0b 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -15,8 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template v-if="emoji" #header>:{{ emoji.name }}:</template>
 	<template v-else #header>New emoji</template>
 
-	<div>
-		<MkSpacer :marginMin="20" :marginMax="28">
+	<div style="display: flex; flex-direction: column; min-height: 100%;">
+		<MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
 			<div class="_gaps_m">
 				<div v-if="imgUrl != null" :class="$style.imgs">
 					<div style="background: #000;" :class="$style.imgContainer">
@@ -239,6 +239,7 @@ async function del() {
 
 .footer {
 	position: sticky;
+	z-index: 10000;
 	bottom: 0;
 	left: 0;
 	padding: 12px;

From 3137c104f23ac966340a66f7452f3a903d134633 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Wed, 31 Jul 2024 07:23:38 +0900
Subject: [PATCH 200/206] =?UTF-8?q?test:=20=E3=83=95=E3=82=A9=E3=83=AD?=
 =?UTF-8?q?=E3=83=BC=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=E3=83=A6?=
 =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=8B=E3=82=89=E3=81=AE=E8=87=AA?=
 =?UTF-8?q?=E5=88=86=E3=81=B8=E3=81=AE=E8=BF=94=E4=BF=A1=E3=81=8C=E5=90=AB?=
 =?UTF-8?q?=E3=81=BE=E3=82=8C=E3=82=8B=E3=81=93=E3=81=A8=E3=82=92=E7=A2=BA?=
 =?UTF-8?q?=E8=AA=8D=E3=81=99=E3=82=8B=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?=
 =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20(#14333)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/test/e2e/timelines.ts | 30 ++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index b7069dd82c..d12be2a9ac 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -703,6 +703,21 @@ describe('Timelines', () => {
 			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
+		test.concurrent('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
+			const [alice, bob] = await Promise.all([signup(), signup()]);
+
+			await setTimeout(1000);
+			const aliceNote = await post(alice, { text: 'hi' });
+			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+			await waitForPushToTl();
+
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
+
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+		});
+
 		test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 
@@ -899,6 +914,21 @@ describe('Timelines', () => {
 			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
 		});
 
+		test.concurrent('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
+			const [alice, bob] = await Promise.all([signup(), signup()]);
+
+			await setTimeout(1000);
+			const aliceNote = await post(alice, { text: 'hi' });
+			const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id });
+
+			await waitForPushToTl();
+
+			const res = await api('notes/local-timeline', { limit: 100 }, alice);
+
+			assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
+			assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
+		});
+
 		test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
 			const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
 

From 9dacc20d67777185c729dd71899885db72874692 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 31 Jul 2024 07:23:58 +0900
Subject: [PATCH 201/206] New Crowdin updates (#14331)

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

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

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (English)
---
 locales/en-US.yml | 10 ++++++++--
 locales/zh-CN.yml | 10 ++++++++--
 locales/zh-TW.yml |  6 ++++++
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index cfb5783a95..451300d973 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -167,7 +167,7 @@ emojiUrl: "Emoji URL"
 addEmoji: "Add an emoji"
 settingGuide: "Recommended settings"
 cacheRemoteFiles: "Cache remote files"
-cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote instance. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated."
+cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote servers. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated."
 youCanCleanRemoteFilesCache: "You can clear the cache by clicking the 🗑️ button in the file management view."
 cacheRemoteSensitiveFiles: "Cache sensitive remote files"
 cacheRemoteSensitiveFilesDescription: "When this setting is disabled, sensitive remote files are loaded directly from the remote instance without caching."
@@ -212,6 +212,7 @@ perDay: "Per Day"
 stopActivityDelivery: "Stop sending activities"
 blockThisInstance: "Block this instance"
 silenceThisInstance: "Silence this instance"
+mediaSilenceThisInstance: "Media-silence this server"
 operations: "Operations"
 software: "Software"
 version: "Version"
@@ -232,7 +233,9 @@ clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote
 blockedInstances: "Blocked Instances"
 blockedInstancesDescription: "List the hostnames of the instances you want to block separated by linebreaks. Listed instances will no longer be able to communicate with this instance."
 silencedInstances: "Silenced instances"
-silencedInstancesDescription: "List the hostnames of the instances that you want to silence. All accounts of the listed instances will be treated as silenced, can only make follow requests, and cannot mention local accounts if not followed. This will not affect blocked instances."
+silencedInstancesDescription: "List the host names of the servers that you want to silence, separated by a new line. All accounts belonging to the listed servers will be treated as silenced, and can only make follow requests, and cannot mention local accounts if not followed. This will not affect the blocked servers."
+mediaSilencedInstances: "Media-silenced servers"
+mediaSilencedInstancesDescription: "List the host names of the servers that you want to media-silence, separated by a new line. All accounts belonging to the listed servers will be treated as sensitive, and can't use custom emojis. This will not affect the blocked servers."
 muteAndBlock: "Mutes and Blocks"
 mutedUsers: "Muted users"
 blockedUsers: "Blocked users"
@@ -1121,6 +1124,8 @@ preventAiLearning: "Reject usage in Machine Learning (Generative AI)"
 preventAiLearningDescription: "Requests crawlers to not use posted text or image material etc. in machine learning (Predictive / Generative AI) data sets. This is achieved by adding a \"noai\" HTML-Response flag to the respective content. A complete prevention can however not be achieved through this flag, as it may simply be ignored."
 options: "Options"
 specifyUser: "Specific user"
+lookupConfirm: "Do you want to look up?"
+openTagPageConfirm: "Do you want to open a hashtag page?"
 specifyHost: "Specify a host"
 failedToPreviewUrl: "Could not preview"
 update: "Update"
@@ -1962,6 +1967,7 @@ _soundSettings:
   driveFileTypeWarnDescription: "Select an audio file"
   driveFileDurationWarn: "The audio is too long."
   driveFileDurationWarnDescription: "Long audio may disrupt using Misskey. Still continue?"
+  driveFileError: "It couldn't load the sound. Please change the setting."
 _ago:
   future: "Future"
   justNow: "Just now"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 8eae32ca32..0a868aab44 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -212,6 +212,7 @@ perDay: "每天"
 stopActivityDelivery: "停止发送活动"
 blockThisInstance: "阻止此服务器向本服务器推流"
 silenceThisInstance: "使服务器静音"
+mediaSilenceThisInstance: "隐藏此服务器的媒体文件"
 operations: "操作"
 software: "软件"
 version: "版本"
@@ -230,9 +231,11 @@ clearQueueConfirmText: "未送达的帖子将不会被投递。 通常无需执
 clearCachedFiles: "清除缓存"
 clearCachedFilesConfirm: "确定要清除所有缓存的远程文件?"
 blockedInstances: "被封锁的服务器"
-blockedInstancesDescription: "设定要封锁的服务器,以换行来进行分割。被封锁的服务器将无法与本服务器进行交换通讯。子域名也同样会被封锁。"
+blockedInstancesDescription: "设定要封锁的服务器,以换行分隔。被封锁的服务器将无法与本服务器进行交换通讯。子域名也同样会被封锁。"
 silencedInstances: "被静音的服务器"
-silencedInstancesDescription: "设置要静音的服务器,以换行符分隔。被静音的服务器内所有的账户将默认处于「静音」状态,仅能发送关注请求,并且在未关注状态下无法提及本地账户。被阻止的实例不受影响。"
+silencedInstancesDescription: "设置要静音的服务器,以换行分隔。被静音的服务器内所有的账户将默认处于「静音」状态,仅能发送关注请求,并且在未关注状态下无法提及本地账户。被阻止的实例不受影响。"
+mediaSilencedInstances: "已隐藏媒体文件的服务器"
+mediaSilencedInstancesDescription: "设置要隐藏媒体文件的服务器,以换行分隔。被设置为隐藏媒体文件服务器内所有账号的文件均按照「敏感内容」处理,且将无法使用自定义表情符号。被阻止的实例不受影响。"
 muteAndBlock: "静音/拉黑"
 mutedUsers: "已静音用户"
 blockedUsers: "已拉黑的用户"
@@ -1121,6 +1124,8 @@ preventAiLearning: "拒绝接受生成式 AI 的学习"
 preventAiLearningDescription: "要求文章生成 AI 或图像生成 AI 不能够以发布的帖子和图像等内容作为学习对象。这是通过在 HTML 响应中包含 noai 标志来实现的,这不能完全阻止 AI 学习你的发布内容,并不是所有 AI 都会遵守这类请求。"
 options: "选项"
 specifyUser: "用户指定"
+lookupConfirm: "确定查询?"
+openTagPageConfirm: "确定打开话题标签页面?"
 specifyHost: "指定主机名"
 failedToPreviewUrl: "无法预览"
 update: "更新"
@@ -1961,6 +1966,7 @@ _soundSettings:
   driveFileTypeWarnDescription: "请选择音频文件"
   driveFileDurationWarn: "音频过长"
   driveFileDurationWarnDescription: "使用长音频可能会影响 Misskey 的使用。即使这样也要继续吗?"
+  driveFileError: "无法读取声音。请更改设置。"
 _ago:
   future: "未来"
   justNow: "最近"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 8b53273c3b..476ccd8799 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -212,6 +212,7 @@ perDay: "每日"
 stopActivityDelivery: "停止發送活動"
 blockThisInstance: "封鎖此伺服器"
 silenceThisInstance: "禁言此伺服器"
+mediaSilenceThisInstance: "將這個伺服器的媒體設為禁言"
 operations: "操作"
 software: "軟體"
 version: "版本"
@@ -233,6 +234,8 @@ blockedInstances: "已封鎖的伺服器"
 blockedInstancesDescription: "請逐行輸入需要封鎖的伺服器。已封鎖的伺服器將無法與本伺服器進行通訊。"
 silencedInstances: "被禁言的伺服器"
 silencedInstancesDescription: "設定要禁言的伺服器主機名稱,以換行分隔。隸屬於禁言伺服器的所有帳戶都將被視為「禁言帳戶」,只能發出「追隨請求」,而且無法提及未追隨的本地帳戶。這不會影響已封鎖的實例。"
+mediaSilencedInstances: "媒體被禁言的伺服器"
+mediaSilencedInstancesDescription: "設定您想要對媒體設定禁言的伺服器,以換行符號區隔。來自被媒體禁言的伺服器所屬帳戶的所有檔案都會被視為敏感檔案,且自訂表情符號不能使用。被封鎖的伺服器不受影響。"
 muteAndBlock: "靜音和封鎖"
 mutedUsers: "被靜音的使用者"
 blockedUsers: "被封鎖的使用者"
@@ -1121,6 +1124,8 @@ preventAiLearning: "拒絕接受生成式AI的訓練"
 preventAiLearningDescription: "要求站外生成式 AI 不使用您發佈的內容訓練模型。此功能會使伺服器於 HTML 回應新增「noai」標籤,而因為要視乎 AI 會否遵守該標籤,所以此功能無法完全阻止所有 AI 使用您的內容。"
 options: "選項"
 specifyUser: "指定使用者"
+lookupConfirm: "要查詢嗎?"
+openTagPageConfirm: "要開啟標籤的頁面嗎?"
 specifyHost: "指定主機"
 failedToPreviewUrl: "無法預覽"
 update: "更新"
@@ -1962,6 +1967,7 @@ _soundSettings:
   driveFileTypeWarnDescription: "請選擇音效檔案"
   driveFileDurationWarn: "音效太長了"
   driveFileDurationWarnDescription: "使用長音效檔可能會影響 Misskey 的使用體驗。仍要使用此檔案嗎?"
+  driveFileError: "無法載入語音。請更改設定"
 _ago:
   future: "未來"
   justNow: "剛剛"

From d63b854f96d9437f9764f9170c3ed3537cc98a2c Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 31 Jul 2024 08:12:35 +0900
Subject: [PATCH 202/206] tweak localization

---
 locales/ja-JP.yml                                          | 2 +-
 packages/frontend/src/components/MkSystemWebhookEditor.vue | 2 +-
 packages/frontend/src/pages/settings/webhook.edit.vue      | 2 +-
 packages/frontend/src/pages/settings/webhook.new.vue       | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 522ad7e22a..b493183974 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2492,7 +2492,7 @@ _webhookSettings:
   modifyWebhook: "Webhookを編集"
   name: "名前"
   secret: "シークレット"
-  events: "Webhookを実行するタイミング"
+  trigger: "トリガー"
   active: "有効"
   _events:
     follow: "フォローしたとき"
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index cad2e99e4d..f5c7a3160b 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<template #label>{{ i18n.ts._webhookSettings.secret }}</template>
 				</MkInput>
 				<MkFolder :defaultOpen="true">
-					<template #label>{{ i18n.ts._webhookSettings.events }}</template>
+					<template #label>{{ i18n.ts._webhookSettings.trigger }}</template>
 
 					<div class="_gaps_s">
 						<MkSwitch v-model="events.abuseReport" :disabled="disabledEvents.abuseReport">
diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue
index e9fb1e471e..058ef69c35 100644
--- a/packages/frontend/src/pages/settings/webhook.edit.vue
+++ b/packages/frontend/src/pages/settings/webhook.edit.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</MkInput>
 
 	<FormSection>
-		<template #label>{{ i18n.ts._webhookSettings.events }}</template>
+		<template #label>{{ i18n.ts._webhookSettings.trigger }}</template>
 
 		<div class="_gaps_s">
 			<MkSwitch v-model="event_follow">{{ i18n.ts._webhookSettings._events.follow }}</MkSwitch>
diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue
index 5bf85e48f4..d62357caaf 100644
--- a/packages/frontend/src/pages/settings/webhook.new.vue
+++ b/packages/frontend/src/pages/settings/webhook.new.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</MkInput>
 
 	<FormSection>
-		<template #label>{{ i18n.ts._webhookSettings.events }}</template>
+		<template #label>{{ i18n.ts._webhookSettings.trigger }}</template>
 
 		<div class="_gaps_s">
 			<MkSwitch v-model="event_follow">{{ i18n.ts._webhookSettings._events.follow }}</MkSwitch>

From 4b04b2989b3c8957cee292326846b3e019b4a1c6 Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Wed, 31 Jul 2024 17:22:51 +0900
Subject: [PATCH 203/206] chore(locale): update index.d.ts (#14339)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

https://github.com/misskey-dev/misskey/commit/d63b854f96d9437f9764f9170c3ed3537cc98a2c での更新漏れ
---
 locales/index.d.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index c58e4ace7b..91d36a14a6 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -9402,9 +9402,9 @@ export interface Locale extends ILocale {
          */
         "secret": string;
         /**
-         * Webhookを実行するタイミング
+         * トリガー
          */
-        "events": string;
+        "trigger": string;
         /**
          * 有効
          */

From d6ba12e24c78611d80a005efef4340681949931b Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Wed, 31 Jul 2024 18:10:36 +0900
Subject: [PATCH 204/206] =?UTF-8?q?Fix(frontend):=20LTL=E7=84=A1=E5=8A=B9?=
 =?UTF-8?q?=E6=99=82=E3=81=AB=E3=83=98=E3=83=83=E3=83=80=E3=83=BC=E3=81=AB?=
 =?UTF-8?q?STL=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=86=20&=20=E3=83=87=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=AB=E3=83=88=E3=80=81=E3=82=AF=E3=83=A9=E3=82=B7=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=81=A7=E3=83=AA=E3=82=B9=E3=83=88=E3=81=8C=E6=B6=88?=
 =?UTF-8?q?=E3=81=88=E3=81=A6=E3=81=84=E3=82=8B=20(#14337)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fix condition of STL available

* Fix: condition of stl

* Listがタイムラインのヘッダーから消えている
---
 packages/frontend/src/pages/timeline.vue | 5 +++++
 packages/frontend/src/timelines.ts       | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index c5905c7ada..32f6dd0e5a 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -287,6 +287,11 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList
 	icon: basicTimelineIconClass(tl),
 	iconOnly: true,
 })), {
+	icon: 'ti ti-list',
+	title: i18n.ts.lists,
+	iconOnly: true,
+	onClick: chooseList,
+}, {
 	icon: 'ti ti-antenna',
 	title: i18n.ts.antennas,
 	iconOnly: true,
diff --git a/packages/frontend/src/timelines.ts b/packages/frontend/src/timelines.ts
index 3ef95fd272..94eda3545e 100644
--- a/packages/frontend/src/timelines.ts
+++ b/packages/frontend/src/timelines.ts
@@ -39,7 +39,7 @@ export function isAvailableBasicTimeline(timeline: BasicTimelineType | undefined
 		case 'local':
 			return ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable);
 		case 'social':
-			return $i != null && instance.policies.ltlAvailable;
+			return $i != null && $i.policies.ltlAvailable;
 		case 'global':
 			return ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable);
 		default:

From 1a521a44c0c0e3f5a8156a2cf4b2f8001ff3d004 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Wed, 31 Jul 2024 18:13:20 +0900
Subject: [PATCH 205/206] New Crowdin updates (#14335)

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

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

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (Chinese Traditional)
---
 locales/cs-CZ.yml |  1 -
 locales/de-DE.yml |  1 -
 locales/en-US.yml |  4 ++--
 locales/es-ES.yml |  1 -
 locales/id-ID.yml |  1 -
 locales/it-IT.yml |  1 -
 locales/ja-KS.yml |  1 -
 locales/ko-KR.yml |  1 -
 locales/pl-PL.yml |  1 -
 locales/th-TH.yml | 22 ++++++++++++++++++++--
 locales/vi-VN.yml |  1 -
 locales/zh-CN.yml |  2 +-
 locales/zh-TW.yml |  2 +-
 13 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index aa970f823a..7db7424762 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -2010,7 +2010,6 @@ _webhookSettings:
   createWebhook: "Vytvořit Webhook"
   name: "Jméno"
   secret: "Tajné"
-  events: "Události Webhook"
   active: "Zapnuto"
   _events:
     follow: "Při sledování uživatele"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index a319af56cb..8e44a3bbd4 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -2191,7 +2191,6 @@ _webhookSettings:
   createWebhook: "Webhook erstellen"
   name: "Name"
   secret: "Secret"
-  events: "Webhook-Ereignisse"
   active: "Aktiviert"
   _events:
     follow: "Wenn du jemandem folgst"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 451300d973..2cb76fa746 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -398,7 +398,7 @@ mcaptcha: "mCaptcha"
 enableMcaptcha: "Enable mCaptcha"
 mcaptchaSiteKey: "Site key"
 mcaptchaSecretKey: "Secret key"
-mcaptchaInstanceUrl: "mCaptcha instance URL"
+mcaptchaInstanceUrl: "mCaptcha server URL"
 recaptcha: "reCAPTCHA"
 enableRecaptcha: "Enable reCAPTCHA"
 recaptchaSiteKey: "Site key"
@@ -2426,7 +2426,7 @@ _webhookSettings:
   modifyWebhook: "Modify Webhook"
   name: "Name"
   secret: "Secret"
-  events: "Webhook Events"
+  trigger: "Trigger"
   active: "Enabled"
   _events:
     follow: "When following a user"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 3ae3ba3b8a..ef066a37ed 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -2382,7 +2382,6 @@ _webhookSettings:
   createWebhook: "Crear Webhook"
   name: "Nombre"
   secret: "Secreto"
-  events: "Eventos de webhook"
   active: "Activado"
   _events:
     follow: "Cuando se sigue a alguien"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index de50569e89..24f7482fca 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -2403,7 +2403,6 @@ _webhookSettings:
   modifyWebhook: "Sunting Webhook"
   name: "Nama"
   secret: "Secret"
-  events: "Webhook Events"
   active: "Aktif"
   _events:
     follow: "Ketika mengikuti pengguna"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 64cd26878e..2b4b1e425e 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -2412,7 +2412,6 @@ _webhookSettings:
   modifyWebhook: "Modifica Webhook"
   name: "Nome"
   secret: "Segreto"
-  events: "Quando eseguire il Webhook"
   active: "Attivo"
   _events:
     follow: "Quando segui un profilo"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 774031f6f5..5969082cf2 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -2391,7 +2391,6 @@ _webhookSettings:
   createWebhook: "Webhookをつくる"
   name: "名前"
   secret: "シークレット"
-  events: "Webhookを投げるタイミング"
   active: "有効"
   _events:
     follow: "フォローしたとき~!"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 7fcb681c9a..34c1cc3ebf 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -2411,7 +2411,6 @@ _webhookSettings:
   modifyWebhook: "Webhook 수정"
   name: "이름"
   secret: "시크릿"
-  events: "Webhook을 실행할 타이밍"
   active: "활성화"
   _events:
     follow: "누군가를 팔로우했을 때"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 7513d056b4..73eff0941a 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -1544,7 +1544,6 @@ _webhookSettings:
   createWebhook: "Stwórz Webhook"
   name: "Nazwa"
   secret: "Sekret"
-  events: "Uruchomienie Webhooka"
   active: "Właczono"
   _events:
     follow: "Po zaobserwowaniu użytkownika"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index eb7cdc2365..63f2793428 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -60,6 +60,7 @@ copyFileId: "คัดลอกไฟล์ ID"
 copyFolderId: "คัดลอกโฟลเดอร์ ID"
 copyProfileUrl: "คัดลอกโปรไฟล์ URL"
 searchUser: "ค้นหาผู้ใช้"
+searchThisUsersNotes: "ค้นหาโน้ตของผู้ใช้"
 reply: "ตอบกลับ"
 loadMore: "แสดงเพิ่มเติม"
 showMore: "แสดงเพิ่มเติม"
@@ -154,6 +155,7 @@ editList: "แก้ไขรายชื่อ"
 selectChannel: "เลือกช่อง"
 selectAntenna: "เลือกเสาอากาศ"
 editAntenna: "แก้ไขเสาอากาศ"
+createAntenna: "สร้างเสาอากาศ"
 selectWidget: "เลือกวิดเจ็ต"
 editWidgets: "แก้ไขวิดเจ็ต"
 editWidgetsExit: "เรียบร้อย"
@@ -194,6 +196,7 @@ followConfirm: "ต้องการติดตาม {name} ใช่ไห
 proxyAccount: "บัญชีพร็อกซี่"
 proxyAccountDescription: "บัญชีพร็อกซี คือ บัญชีที่ทำหน้าที่ติดตาม(ผู้ใช้)ระยะไกลภายใต้เงื่อนไขบางประการ ตัวอย่างเช่น เมื่อผู้ใช้ท้องถิ่นเพิ่มผู้ใช้ระยะไกลลงรายชื่อ หากไม่มีใครติดตามผู้ใช้ระยะไกลในรายชื่อนั้น กิจกรรมก็จะไม่ถูกส่งมายังเซิร์ฟเวอร์ ดังนั้นจึงมีบัญชีพร็อกซีไว้ติดตามผู้ใช้ระยะไกลเหล่านั้น"
 host: "โฮสต์"
+selectSelf: "เลือกตัวเอง"
 selectUser: "เลือกผู้ใช้งาน"
 recipient: "ผู้รับ"
 annotation: "หมายเหตุประกอบ"
@@ -209,6 +212,7 @@ perDay: "ต่อวัน"
 stopActivityDelivery: "หยุดส่งกิจกรรม"
 blockThisInstance: "บล็อกเซิร์ฟเวอร์นี้"
 silenceThisInstance: "ปิดปากเซิร์ฟเวอร์นี้"
+mediaSilenceThisInstance: "ปิดปากสื่อของเซิร์ฟเวอร์นี้"
 operations: "ดำเนินการ"
 software: "ซอฟต์แวร์"
 version: "เวอร์ชั่น"
@@ -230,6 +234,8 @@ blockedInstances: "เซิร์ฟเวอร์ที่ถูกบล็
 blockedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการบล็อก คั่นด้วยการขึ้นบรรทัดใหม่ เซิร์ฟเวอร์ที่ถูกบล็อกจะไม่สามารถติดต่อกับอินสแตนซ์นี้ได้"
 silencedInstances: "ปิดปากเซิร์ฟเวอร์นี้แล้ว"
 silencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปาก คั่นด้วยการขึ้นบรรทัดใหม่, บัญชีทั้งหมดของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปากเช่นกัน ทำได้เฉพาะคำขอติดตามเท่านั้น และไม่สามารถกล่าวถึงบัญชีในเซิร์ฟเวอร์นี้ได้หากไม่ได้ถูกติดตามกลับ | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก"
+mediaSilencedInstances: "เซิร์ฟเวอร์ที่ถูกปิดปากสื่อ"
+mediaSilencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปากสื่อ คั่นด้วยการขึ้นบรรทัดใหม่, ไฟล์ที่ถูกส่งจากบัญชีของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปาก แล้วจะถูกติดเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน และเอโมจิแบบกำหนดเองก็จะใช้ไม่ได้ด้วย | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก"
 muteAndBlock: "ปิดเสียงและบล็อก"
 mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง"
 blockedUsers: "ผู้ใช้ที่ถูกบล็อก"
@@ -1106,6 +1112,8 @@ preservedUsernames: "ชื่อผู้ใช้ที่สงวนไว
 preservedUsernamesDescription: "ระบุชื่อผู้ใช้ที่จะสงวนชื่อไว้ คั่นด้วยการขึ้นบรรทัดใหม่ ชื่อผู้ใช้ที่ระบุที่นี่จะไม่สามารถใช้งานได้อีกต่อไปเมื่อสร้างบัญชีใหม่ ยกเว้นเมื่อผู้ดูแลระบบสร้างบัญชี นอกจากนี้ บัญชีที่มีอยู่แล้วจะไม่ได้รับผลกระทบ"
 createNoteFromTheFile: "เรียบเรียงโน้ตจากไฟล์นี้"
 archive: "เก็บถาวร"
+archived: "เก็บถาวรแล้ว"
+unarchive: "เลิกการเก็บถาวร"
 channelArchiveConfirmTitle: "ต้องการเก็บถาวรเจ้า {name} ใช่ไหม?"
 channelArchiveConfirmDescription: "เมื่อเก็บถาวรแล้ว จะไม่ปรากฏในรายการช่องหรือผลการค้นหาอีกต่อไป และจะไม่สามารถโพสต์ใหม่ได้อีกต่อไป"
 thisChannelArchived: "ช่องนี้ถูกเก็บถาวรแล้วนะ"
@@ -1116,6 +1124,9 @@ preventAiLearning: "ปฏิเสธการเรียนรู้ด้ว
 preventAiLearningDescription: "ส่งคำร้องขอไม่ให้ใช้ ข้อความในโน้ตที่โพสต์, หรือเนื้อหารูปภาพ ฯลฯ ในการเรียนรู้ของเครื่อง(machine learning) / Predictive AI / Generative AI โดยการเพิ่มแฟล็ก “noai” ลง HTML-Response ให้กับเนื้อหาที่เกี่ยวข้อง แต่ทั้งนี้ ไม่ได้ป้องกัน AI จากการเรียนรู้ได้อย่างสมบูรณ์ เนื่องจากมี AI บางตัวเท่านั้นที่จะเคารพคำขอดังกล่าว"
 options: "ตัวเลือกบทบาท"
 specifyUser: "ผู้ใช้เฉพาะ"
+lookupConfirm: "ต้องการเรียกดูข้อมูลใช่ไหม?"
+openTagPageConfirm: "ต้องการเปิดหน้าแฮชแท็กใช่ไหม?"
+specifyHost: "ระบุโฮสต์"
 failedToPreviewUrl: "ไม่สามารถดูตัวอย่างได้"
 update: "อัปเดต"
 rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้เอโมจินี้เป็นรีแอคชั่นได้"
@@ -1250,6 +1261,8 @@ inquiry: "ติดต่อเรา"
 tryAgain: "โปรดลองอีกครั้ง"
 confirmWhenRevealingSensitiveMedia: "ตรวจสอบก่อนแสดงสื่อที่มีเนื้อหาละเอียดอ่อน"
 sensitiveMediaRevealConfirm: "สื่อนี้มีเนื้อหาละเอียดอ่อน, ต้องการแสดงใช่ไหม?"
+createdLists: "รายชื่อที่ถูกสร้าง"
+createdAntennas: "เสาอากาศที่ถูกสร้าง"
 _delivery:
   status: "สถานะการจัดส่ง"
   stop: "ระงับการส่ง"
@@ -1954,6 +1967,7 @@ _soundSettings:
   driveFileTypeWarnDescription: "กรุณาเลือกไฟล์เสียง"
   driveFileDurationWarn: "เสียงยาวเกินไป"
   driveFileDurationWarnDescription: "การใช้เสียงที่ยาว อาจรบกวนการใช้งาน Misskey, ต้องการดำเนินการต่อใช่ไหม?"
+  driveFileError: "ไม่สามารถโหลดไฟล์เสียงได้ กรุณาเปลี่ยนแปลงการตั้งค่า"
 _ago:
   future: "อนาคต"
   justNow: "เมื่อกี๊นี้"
@@ -2412,7 +2426,6 @@ _webhookSettings:
   modifyWebhook: "แก้ไข Webhook"
   name: "ชื่อ"
   secret: "ความลับ"
-  events: "อีเว้นท์ Webhook"
   active: "เปิดใช้งาน"
   _events:
     follow: "เมื่อกำลังติดตามผู้ใช้"
@@ -2536,7 +2549,7 @@ _externalResourceInstaller:
       description: "เกิดปัญหาระหว่างการติดตั้งธีม กรุณาลองอีกครั้ง. รายละเอียดข้อผิดพลาดสามารถดูได้ในคอนโซล Javascript"
 _dataSaver:
   _media:
-    title: "โหลดมีเดีย"
+    title: "โหลดสื่อ"
     description: "กันไม่ให้ภาพและวิดีโอโหลดโดยอัตโนมัติ แตะรูปภาพ/วิดีโอที่ซ่อนอยู่เพื่อโหลด"
   _avatar:
     title: "รูปไอคอน"
@@ -2616,3 +2629,8 @@ _mediaControls:
   pip: "รูปภาพในรูปภาม"
   playbackRate: "ความเร็วในการเล่น"
   loop: "เล่นวนซ้ำ"
+_contextMenu:
+  title: "เมนูเนื้อหา"
+  app: "แอปพลิเคชัน"
+  appWithShift: "แอปฟลิเคชันด้วยปุ่มยกแคร่ (Shift)"
+  native: "UI ของเบราว์เซอร์"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 317dea5150..aadbf8b16f 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1918,7 +1918,6 @@ _webhookSettings:
   createWebhook: "Tạo Webhook"
   name: "Tên"
   secret: "Mã bí mật"
-  events: "Sự kiện Webhook"
   active: "Đã bật"
   _events:
     reaction: "Khi nhận được sự kiện"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 0a868aab44..1deb0effc3 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -2425,7 +2425,7 @@ _webhookSettings:
   modifyWebhook: "编辑 webhook"
   name: "名称"
   secret: "密钥"
-  events: "何时运行 Webhook"
+  trigger: "触发器"
   active: "已启用"
   _events:
     follow: "关注时"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 476ccd8799..16dc464e35 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -2426,7 +2426,7 @@ _webhookSettings:
   modifyWebhook: "編輯 Webhook"
   name: "名字"
   secret: "密鑰"
-  events: "何時運行 Webhook"
+  trigger: "觸發器"
   active: "已啟用"
   _events:
     follow: "當你追隨時"

From 59e2e43a68ee39d40f7a95a068cb9cb4f235cfed Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Wed, 31 Jul 2024 11:20:28 +0000
Subject: [PATCH 206/206] Release: 2024.7.0

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

diff --git a/package.json b/package.json
index 42751be196..4bf7b0a918 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2024.7.0-rc.8",
+	"version": "2024.7.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 45f42e1846..f0e8733e67 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
 {
 	"type": "module",
 	"name": "misskey-js",
-	"version": "2024.7.0-rc.8",
+	"version": "2024.7.0",
 	"description": "Misskey SDK for JavaScript",
 	"license": "MIT",
 	"main": "./built/index.js",